forked from nhibernate/nhibernate-core
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathHanaBatchingBatcher.cs
177 lines (152 loc) · 4.74 KB
/
HanaBatchingBatcher.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Text;
using NHibernate.AdoNet.Util;
using NHibernate.Driver;
using NHibernate.Exceptions;
namespace NHibernate.AdoNet
{
/// <summary>
/// DML batcher for HANA.
/// By Jonathan Bregler
/// </summary>
public partial class HanaBatchingBatcher : AbstractBatcher
{
private int _batchSize;
private int _countOfCommands;
private int _totalExpectedRowsAffected;
private DbCommand _currentBatch;
private readonly List<DbCommand> _currentBatchCommands = new List<DbCommand>();
private StringBuilder _currentBatchCommandsLog;
public HanaBatchingBatcher(ConnectionManager connectionManager, IInterceptor interceptor)
: base(connectionManager, interceptor)
{
_batchSize = Factory.Settings.AdoBatchSize;
//we always create this, because we need to deal with a scenario in which
//the user change the logging configuration at runtime. Trying to put this
//behind an if(log.IsDebugEnabled) will cause a null reference exception
//at that point.
_currentBatchCommandsLog = new StringBuilder().AppendLine("Batch commands:");
}
public override void AddToBatch(IExpectation expectation)
{
// HanaCommands are cloneable
if (!(Driver.UnwrapDbCommand(CurrentCommand) is ICloneable cloneableCurrentCommand))
throw new InvalidOperationException("Current command is not an ICloneable");
var batchUpdate = CurrentCommand;
Prepare(batchUpdate);
Driver.AdjustCommand(batchUpdate);
_totalExpectedRowsAffected += expectation.ExpectedRowCount;
string lineWithParameters = null;
var sqlStatementLogger = Factory.Settings.SqlStatementLogger;
if (sqlStatementLogger.IsDebugEnabled || Log.IsDebugEnabled())
{
lineWithParameters = sqlStatementLogger.GetCommandLineWithParameters(batchUpdate);
var formatStyle = sqlStatementLogger.DetermineActualStyle(FormatStyle.Basic);
lineWithParameters = formatStyle.Formatter.Format(lineWithParameters);
_currentBatchCommandsLog.Append("command ")
.Append(_countOfCommands)
.Append(":")
.AppendLine(lineWithParameters);
}
if (Log.IsDebugEnabled())
{
Log.Debug("Adding to batch:{0}", lineWithParameters);
}
if (_currentBatch == null)
{
// use first command as the batching command
_currentBatch = cloneableCurrentCommand.Clone() as DbCommand;
}
_currentBatchCommands.Add(cloneableCurrentCommand.Clone() as DbCommand);
_countOfCommands++;
if (_countOfCommands >= _batchSize)
{
DoExecuteBatch(batchUpdate);
}
}
protected override void DoExecuteBatch(DbCommand ps)
{
Log.Info("Executing batch");
CheckReaders();
if (Factory.Settings.SqlStatementLogger.IsDebugEnabled)
{
Factory.Settings.SqlStatementLogger.LogBatchCommand(_currentBatchCommandsLog.ToString());
_currentBatchCommandsLog = new StringBuilder().AppendLine("Batch commands:");
}
try
{
int rowCount = 0;
if (_countOfCommands > 0)
{
_currentBatch.Parameters.Clear();
foreach (var command in _currentBatchCommands)
{
// Batching with HANA works by simply defining multiple times each command parameter.
// (Undocumented feature explained by a developer of the provider.)
foreach (DbParameter parameter in command.Parameters)
{
_currentBatch.Parameters.Add(parameter);
}
}
_currentBatch.Prepare();
try
{
rowCount = _currentBatch.ExecuteNonQuery();
}
catch (DbException e)
{
throw ADOExceptionHelper.Convert(Factory.SQLExceptionConverter, e, "could not execute batch command.");
}
}
Expectations.VerifyOutcomeBatched(_totalExpectedRowsAffected, rowCount, ps);
}
finally
{
// Cleaning up even if batched outcome is invalid
_totalExpectedRowsAffected = 0;
_countOfCommands = 0;
CloseBatchCommands();
}
}
protected override int CountOfStatementsInCurrentBatch
{
get { return _countOfCommands; }
}
public override int BatchSize
{
get { return _batchSize; }
set { _batchSize = value; }
}
public override void CloseCommands()
{
base.CloseCommands();
CloseBatchCommands();
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
CloseBatchCommands();
}
private void CloseBatchCommands()
{
try
{
foreach (var currentBatchCommand in _currentBatchCommands)
{
currentBatchCommand.Dispose();
}
_currentBatchCommands.Clear();
_currentBatch?.Dispose();
_currentBatch = null;
}
catch (Exception e)
{
// Prevent exceptions when clearing the batch from hiding any original exception
// (We do not know here if this batch closing occurs after a failure or not.)
Log.Warn(e, "Exception clearing batch");
}
}
}
}