From 4a2a40c2a053cfd6f0385d77103bce8db3530b1c Mon Sep 17 00:00:00 2001
From: maca88
Date: Thu, 1 Mar 2018 21:12:55 +0100
Subject: [PATCH 01/12] Added batching support for insert/update/delete
statements on PostgreSQL
---
src/NHibernate.Test/Ado/BatcherFixture.cs | 6 +
.../Async/Ado/BatcherFixture.cs | 6 +
.../TypesTest/AbstractDateTimeTypeFixture.cs | 11 +-
.../TypesTest/AbstractDateTimeTypeFixture.cs | 11 +-
src/NHibernate/AdoNet/AbstractBatcher.cs | 12 +
.../AdoNet/PostgreSQLClientBatchingBatcher.cs | 263 ++++++++++++++++++
.../PostgreSQLClientBatchingBatcherFactory.cs | 12 +
.../AdoNet/PostgreSQLClientBatchingBatcher.cs | 121 ++++++++
src/NHibernate/Driver/DriverBase.cs | 22 +-
9 files changed, 456 insertions(+), 8 deletions(-)
create mode 100644 src/NHibernate/AdoNet/PostgreSQLClientBatchingBatcher.cs
create mode 100644 src/NHibernate/AdoNet/PostgreSQLClientBatchingBatcherFactory.cs
create mode 100644 src/NHibernate/Async/AdoNet/PostgreSQLClientBatchingBatcher.cs
diff --git a/src/NHibernate.Test/Ado/BatcherFixture.cs b/src/NHibernate.Test/Ado/BatcherFixture.cs
index 4d7e8107673..d7e617c454c 100644
--- a/src/NHibernate.Test/Ado/BatcherFixture.cs
+++ b/src/NHibernate.Test/Ado/BatcherFixture.cs
@@ -59,6 +59,7 @@ private void FillDb()
{
s.Save(new VerySimple {Id = 1, Name = "Fabio", Weight = 119.5});
s.Save(new VerySimple {Id = 2, Name = "Fiamma", Weight = 9.8});
+ s.Save(new VerySimple {Id = 3, Name = "Roberto", Weight = 98.8 });
tx.Commit();
}
}
@@ -74,11 +75,14 @@ public void OneRoundTripUpdate()
{
var vs1 = s.Get(1);
var vs2 = s.Get(2);
+ var vs3 = s.Get(3);
vs1.Weight -= 10;
vs2.Weight -= 1;
+ vs3.Weight -= 5;
Sfi.Statistics.Clear();
s.Update(vs1);
s.Update(vs2);
+ s.Update(vs3);
tx.Commit();
}
@@ -154,9 +158,11 @@ public void OneRoundTripDelete()
{
var vs1 = s.Get(1);
var vs2 = s.Get(2);
+ var vs3 = s.Get(3);
Sfi.Statistics.Clear();
s.Delete(vs1);
s.Delete(vs2);
+ s.Delete(vs3);
tx.Commit();
}
diff --git a/src/NHibernate.Test/Async/Ado/BatcherFixture.cs b/src/NHibernate.Test/Async/Ado/BatcherFixture.cs
index 20c3cdc04fd..db11dd2450b 100644
--- a/src/NHibernate.Test/Async/Ado/BatcherFixture.cs
+++ b/src/NHibernate.Test/Async/Ado/BatcherFixture.cs
@@ -71,6 +71,7 @@ public async Task OneRoundTripInsertsAsync()
{
await (s.SaveAsync(new VerySimple {Id = 1, Name = "Fabio", Weight = 119.5}, cancellationToken));
await (s.SaveAsync(new VerySimple {Id = 2, Name = "Fiamma", Weight = 9.8}, cancellationToken));
+ await (s.SaveAsync(new VerySimple {Id = 3, Name = "Roberto", Weight = 98.8 }, cancellationToken));
await (tx.CommitAsync(cancellationToken));
}
}
@@ -86,11 +87,14 @@ public async Task OneRoundTripUpdateAsync()
{
var vs1 = await (s.GetAsync(1));
var vs2 = await (s.GetAsync(2));
+ var vs3 = await (s.GetAsync(3));
vs1.Weight -= 10;
vs2.Weight -= 1;
+ vs3.Weight -= 5;
Sfi.Statistics.Clear();
await (s.UpdateAsync(vs1));
await (s.UpdateAsync(vs2));
+ await (s.UpdateAsync(vs3));
await (tx.CommitAsync());
}
@@ -166,9 +170,11 @@ public async Task OneRoundTripDeleteAsync()
{
var vs1 = await (s.GetAsync(1));
var vs2 = await (s.GetAsync(2));
+ var vs3 = await (s.GetAsync(3));
Sfi.Statistics.Clear();
await (s.DeleteAsync(vs1));
await (s.DeleteAsync(vs2));
+ await (s.DeleteAsync(vs3));
await (tx.CommitAsync());
}
diff --git a/src/NHibernate.Test/Async/TypesTest/AbstractDateTimeTypeFixture.cs b/src/NHibernate.Test/Async/TypesTest/AbstractDateTimeTypeFixture.cs
index 5cb1c03a231..20a21363a5d 100644
--- a/src/NHibernate.Test/Async/TypesTest/AbstractDateTimeTypeFixture.cs
+++ b/src/NHibernate.Test/Async/TypesTest/AbstractDateTimeTypeFixture.cs
@@ -14,6 +14,7 @@
using System.Data.Common;
using System.Linq;
using System.Reflection;
+using NHibernate.AdoNet;
using NHibernate.Cfg;
using NHibernate.Driver;
using NHibernate.Engine;
@@ -246,8 +247,16 @@ public virtual async Task SaveUseExpectedSqlTypeAsync()
await (t.CommitAsync());
}
+ var expected = 3;
+ // PostgreSQL batcher uses IDriver.GenerateCommand method to create the batching command,
+ // so the expected result will be doubled as GenerateCommand calls IDriver.GenerateParameter
+ // for each parameter.
+ if (Sfi.Settings.BatcherFactory is PostgreSQLClientBatchingBatcherFactory)
+ {
+ expected *= 2;
+ }
// 2 properties + revision
- AssertSqlType(driver, 3, true);
+ AssertSqlType(driver, expected, true);
}
[Test]
diff --git a/src/NHibernate.Test/TypesTest/AbstractDateTimeTypeFixture.cs b/src/NHibernate.Test/TypesTest/AbstractDateTimeTypeFixture.cs
index 75fc976284e..c24886e92bc 100644
--- a/src/NHibernate.Test/TypesTest/AbstractDateTimeTypeFixture.cs
+++ b/src/NHibernate.Test/TypesTest/AbstractDateTimeTypeFixture.cs
@@ -4,6 +4,7 @@
using System.Data.Common;
using System.Linq;
using System.Reflection;
+using NHibernate.AdoNet;
using NHibernate.Cfg;
using NHibernate.Driver;
using NHibernate.Engine;
@@ -277,8 +278,16 @@ public virtual void SaveUseExpectedSqlType()
t.Commit();
}
+ var expected = 3;
+ // PostgreSQL batcher uses IDriver.GenerateCommand method to create the batching command,
+ // so the expected result will be doubled as GenerateCommand calls IDriver.GenerateParameter
+ // for each parameter.
+ if (Sfi.Settings.BatcherFactory is PostgreSQLClientBatchingBatcherFactory)
+ {
+ expected *= 2;
+ }
// 2 properties + revision
- AssertSqlType(driver, 3, true);
+ AssertSqlType(driver, expected, true);
}
[Test]
diff --git a/src/NHibernate/AdoNet/AbstractBatcher.cs b/src/NHibernate/AdoNet/AbstractBatcher.cs
index f31862b6662..ed893cd387b 100644
--- a/src/NHibernate/AdoNet/AbstractBatcher.cs
+++ b/src/NHibernate/AdoNet/AbstractBatcher.cs
@@ -67,6 +67,18 @@ protected DbCommand CurrentCommand
get { return _batchCommand; }
}
+ ///
+ /// Gets the current that is contained for this Batch
+ ///
+ /// The current .
+ protected SqlString CurrentCommandSql => _batchCommandSql;
+
+ ///
+ /// Gets the current parameters that are contained for this Batch
+ ///
+ /// The current .
+ protected SqlType[] CurrentCommandParameterTypes => _batchCommandParameterTypes;
+
public DbCommand Generate(CommandType type, SqlString sqlString, SqlType[] parameterTypes)
{
SqlString sql = GetSQL(sqlString);
diff --git a/src/NHibernate/AdoNet/PostgreSQLClientBatchingBatcher.cs b/src/NHibernate/AdoNet/PostgreSQLClientBatchingBatcher.cs
new file mode 100644
index 00000000000..141a25a9f42
--- /dev/null
+++ b/src/NHibernate/AdoNet/PostgreSQLClientBatchingBatcher.cs
@@ -0,0 +1,263 @@
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Data.Common;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading;
+using System.Threading.Tasks;
+using NHibernate.AdoNet.Util;
+using NHibernate.Driver;
+using NHibernate.Exceptions;
+using NHibernate.SqlCommand;
+using NHibernate.SqlTypes;
+using NHibernate.Util;
+
+namespace NHibernate.AdoNet
+{
+ ///
+ /// Batcher for PostgreSQL that will batch UPDATE/INSERT/DELETE commands.
+ ///
+ public partial class PostgreSQLClientBatchingBatcher : AbstractBatcher
+ {
+ private int _totalExpectedRowsAffected;
+ private PostgreSQLCommandSet _currentBatch;
+ private StringBuilder _currentBatchCommandsLog;
+
+ public PostgreSQLClientBatchingBatcher(ConnectionManager connectionManager, IInterceptor interceptor)
+ : base(connectionManager, interceptor)
+ {
+ BatchSize = Factory.Settings.AdoBatchSize;
+ _currentBatch = CreateConfiguredBatch();
+
+ //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 sealed override int BatchSize { get; set; }
+
+ protected override int CountOfStatementsInCurrentBatch => _currentBatch.CountOfCommands;
+
+ public override void AddToBatch(IExpectation expectation)
+ {
+ _totalExpectedRowsAffected += expectation.ExpectedRowCount;
+ var batchUpdate = CurrentCommand;
+ Driver.AdjustCommand(batchUpdate);
+ 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(_currentBatch.CountOfCommands)
+ .Append(":")
+ .AppendLine(lineWithParameters);
+ }
+ if (Log.IsDebugEnabled())
+ {
+ Log.Debug("Adding to batch:{0}", lineWithParameters);
+ }
+ _currentBatch.Append(CurrentCommand.Parameters);
+
+ if (_currentBatch.CountOfCommands >= BatchSize)
+ {
+ ExecuteBatchWithTiming(batchUpdate);
+ }
+ }
+
+ protected override void DoExecuteBatch(DbCommand ps)
+ {
+ try
+ {
+ Log.Debug("Executing batch");
+ CheckReaders();
+ if (Factory.Settings.SqlStatementLogger.IsDebugEnabled)
+ {
+ Factory.Settings.SqlStatementLogger.LogBatchCommand(_currentBatchCommandsLog.ToString());
+ }
+
+ int rowsAffected;
+ try
+ {
+ rowsAffected = _currentBatch.ExecuteNonQuery();
+ }
+ catch (DbException e)
+ {
+ throw ADOExceptionHelper.Convert(Factory.SQLExceptionConverter, e, "could not execute batch command.");
+ }
+
+ Expectations.VerifyOutcomeBatched(_totalExpectedRowsAffected, rowsAffected);
+ }
+ finally
+ {
+ ClearCurrentBatch();
+ }
+ }
+
+ private PostgreSQLCommandSet CreateConfiguredBatch()
+ {
+ return new PostgreSQLCommandSet(this);
+ }
+
+ private void ClearCurrentBatch()
+ {
+ _currentBatch.Dispose();
+ _totalExpectedRowsAffected = 0;
+ _currentBatch = CreateConfiguredBatch();
+
+ if (Factory.Settings.SqlStatementLogger.IsDebugEnabled)
+ {
+ _currentBatchCommandsLog = new StringBuilder().AppendLine("Batch commands:");
+ }
+ }
+
+ public override void CloseCommands()
+ {
+ base.CloseCommands();
+
+ try
+ {
+ ClearCurrentBatch();
+ }
+ 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");
+ }
+ }
+
+ protected override void Dispose(bool isDisposing)
+ {
+ base.Dispose(isDisposing);
+ // Prevent exceptions when closing the batch from hiding any original exception
+ // (We do not know here if this batch closing occurs after a failure or not.)
+ try
+ {
+ _currentBatch.Dispose();
+ }
+ catch (Exception e)
+ {
+ Log.Warn(e, "Exception closing batcher");
+ }
+ }
+
+ private partial class PostgreSQLCommandSet : IDisposable
+ {
+ private int _currentParameterIndex;
+ private DbCommand _batchCommand;
+ private readonly PostgreSQLClientBatchingBatcher _batcher;
+
+ public PostgreSQLCommandSet(PostgreSQLClientBatchingBatcher batcher)
+ {
+ _batcher = batcher;
+ }
+
+ public int CountOfCommands { get; private set; }
+
+ public void Append(DbParameterCollection parameters)
+ {
+ if (_batchCommand == null)
+ {
+ _batchCommand = _batcher.Driver.GenerateCommand(
+ _batcher.CurrentCommand.CommandType,
+ _batcher.CurrentCommandSql,
+ _batcher.CurrentCommandParameterTypes);
+ UpdateCommandParameters(_batchCommand, parameters);
+ _currentParameterIndex = parameters.Count;
+ }
+ else
+ {
+ // We need to create a new command with different parameter names to avoid duplicates
+ var command = _batcher.Driver.GenerateCommand(
+ _batcher.CurrentCommand.CommandType,
+ PrepareSqlString(_batcher.CurrentCommandSql),
+ _batcher.CurrentCommandParameterTypes);
+ UpdateCommandParameters(command, parameters);
+ _batchCommand.CommandText += ";" + command.CommandText;
+ foreach (DbParameter parameter in command.Parameters)
+ {
+ _batchCommand.Parameters.Add(CopyParameter(_batchCommand, parameter));
+ }
+ command.Dispose();
+ }
+ CountOfCommands++;
+ }
+
+ public int ExecuteNonQuery()
+ {
+ if (_batchCommand == null)
+ {
+ return 0;
+ }
+ // Npgsql will correctly prepare a multi SQL statement even if the parameter names are different
+ // for each statement. Npgsql internally parses the query and omits parameter names when comparing two queries
+ // in order to prevent having multiple prepared statements for the same query.
+ _batcher.Prepare(_batchCommand);
+ return _batchCommand.ExecuteNonQuery();
+ }
+
+ public void Dispose()
+ {
+ _batchCommand?.Dispose();
+ _batchCommand = null;
+ _currentParameterIndex = 0;
+ CountOfCommands = 0;
+ }
+
+ private void UpdateCommandParameters(DbCommand command, DbParameterCollection parameters)
+ {
+ for (var i = 0; i < parameters.Count; i++)
+ {
+ var parameter = parameters[i];
+ var cmdParam = command.Parameters[i];
+ cmdParam.Value = parameter.Value;
+ cmdParam.Direction = parameter.Direction;
+ cmdParam.Precision = parameter.Precision;
+ cmdParam.Scale = parameter.Scale;
+ cmdParam.Size = parameter.Size;
+ }
+ }
+
+ private DbParameter CopyParameter(DbCommand command, DbParameter parameter)
+ {
+ var copy = command.CreateParameter();
+ copy.DbType = parameter.DbType;
+ copy.IsNullable = parameter.IsNullable;
+ copy.ParameterName = parameter.ParameterName;
+ copy.Value = parameter.Value;
+ copy.Direction = parameter.Direction;
+ copy.Precision = parameter.Precision;
+ copy.Scale = parameter.Scale;
+ copy.Size = parameter.Size;
+ copy.SourceVersion = parameter.SourceVersion;
+ copy.SourceColumn = parameter.SourceColumn;
+ copy.SourceColumnNullMapping = parameter.SourceColumnNullMapping;
+ return copy;
+ }
+
+ private SqlString PrepareSqlString(SqlString sql)
+ {
+ if (_batchCommand == null)
+ {
+ return sql;
+ }
+ sql = sql.Copy();
+ foreach (var part in sql)
+ {
+ if (part is Parameter param)
+ {
+ param.ParameterPosition = _currentParameterIndex++;
+ }
+ }
+ return sql;
+ }
+ }
+ }
+}
diff --git a/src/NHibernate/AdoNet/PostgreSQLClientBatchingBatcherFactory.cs b/src/NHibernate/AdoNet/PostgreSQLClientBatchingBatcherFactory.cs
new file mode 100644
index 00000000000..0aabe773782
--- /dev/null
+++ b/src/NHibernate/AdoNet/PostgreSQLClientBatchingBatcherFactory.cs
@@ -0,0 +1,12 @@
+using NHibernate.Engine;
+
+namespace NHibernate.AdoNet
+{
+ public class PostgreSQLClientBatchingBatcherFactory: IBatcherFactory
+ {
+ public virtual IBatcher CreateBatcher(ConnectionManager connectionManager, IInterceptor interceptor)
+ {
+ return new PostgreSQLClientBatchingBatcher(connectionManager, interceptor);
+ }
+ }
+}
diff --git a/src/NHibernate/Async/AdoNet/PostgreSQLClientBatchingBatcher.cs b/src/NHibernate/Async/AdoNet/PostgreSQLClientBatchingBatcher.cs
new file mode 100644
index 00000000000..902099bb8c8
--- /dev/null
+++ b/src/NHibernate/Async/AdoNet/PostgreSQLClientBatchingBatcher.cs
@@ -0,0 +1,121 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by AsyncGenerator.
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Data.Common;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading;
+using System.Threading.Tasks;
+using NHibernate.AdoNet.Util;
+using NHibernate.Driver;
+using NHibernate.Exceptions;
+using NHibernate.SqlCommand;
+using NHibernate.SqlTypes;
+using NHibernate.Util;
+
+namespace NHibernate.AdoNet
+{
+ public partial class PostgreSQLClientBatchingBatcher : AbstractBatcher
+ {
+
+ public override Task AddToBatchAsync(IExpectation expectation, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return Task.FromCanceled
///
- public class NpgsqlDriver : ReflectionBasedDriver
+ public class NpgsqlDriver : ReflectionBasedDriver, IEmbeddedBatcherFactoryProvider
{
///
/// Initializes a new instance of the class.
@@ -92,5 +93,7 @@ protected override void InitializeParameter(DbParameter dbParam, string name, Sq
public override bool RequiresTimeSpanForTime => (DriverVersion?.Major ?? 3) >= 3;
public override bool HasDelayedDistributedTransactionCompletion => true;
+
+ System.Type IEmbeddedBatcherFactoryProvider.BatcherFactoryClass => typeof(GenericBatchingBatcherFactory);
}
}
diff --git a/src/NHibernate/Driver/SqlClientDriver.cs b/src/NHibernate/Driver/SqlClientDriver.cs
index b9849ae7b59..328de551fdf 100644
--- a/src/NHibernate/Driver/SqlClientDriver.cs
+++ b/src/NHibernate/Driver/SqlClientDriver.cs
@@ -2,7 +2,9 @@
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
+#if NETFX
using System.Data.SqlClient;
+#endif
using NHibernate.AdoNet;
using NHibernate.Engine;
using NHibernate.SqlTypes;
@@ -16,7 +18,7 @@ public class SqlClientDriver
#if NETFX
: DriverBase, IEmbeddedBatcherFactoryProvider
#else
- : ReflectionBasedDriver
+ : ReflectionBasedDriver, IEmbeddedBatcherFactoryProvider
#endif
{
public const int MaxSizeForAnsiClob = 2147483647; // int.MaxValue
@@ -50,6 +52,8 @@ public SqlClientDriver()
: base("System.Data.SqlClient", "System.Data.SqlClient.SqlConnection", "System.Data.SqlClient.SqlCommand")
{
}
+
+ System.Type IEmbeddedBatcherFactoryProvider.BatcherFactoryClass => typeof(GenericBatchingBatcherFactory);
#else
///
/// Creates an uninitialized object for
From ed97fa9d16a03cd1d0304734528afb4a912fd8e9 Mon Sep 17 00:00:00 2001
From: maca88
Date: Thu, 8 Mar 2018 17:44:17 +0100
Subject: [PATCH 11/12] Assert corrected and used lambda for getter
---
src/NHibernate.Test/Ado/GenericBatchingBatcherFixture.cs | 2 +-
src/NHibernate.Test/Async/Ado/GenericBatchingBatcherFixture.cs | 2 +-
src/NHibernate/Dialect/Dialect.cs | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/NHibernate.Test/Ado/GenericBatchingBatcherFixture.cs b/src/NHibernate.Test/Ado/GenericBatchingBatcherFixture.cs
index 964286b86c4..ea68c24a1a3 100644
--- a/src/NHibernate.Test/Ado/GenericBatchingBatcherFixture.cs
+++ b/src/NHibernate.Test/Ado/GenericBatchingBatcherFixture.cs
@@ -56,7 +56,7 @@ public void BatchSizeTest()
tx.Commit();
var log = sqlLog.GetWholeLog();
- Assert.That(4, Is.EqualTo(FindAllOccurrences(log, "Batch commands:")));
+ Assert.That(FindAllOccurrences(log, "Batch commands:"), Is.EqualTo(4));
}
Cleanup();
}
diff --git a/src/NHibernate.Test/Async/Ado/GenericBatchingBatcherFixture.cs b/src/NHibernate.Test/Async/Ado/GenericBatchingBatcherFixture.cs
index 7a4ecbc38ff..3a79e042e8f 100644
--- a/src/NHibernate.Test/Async/Ado/GenericBatchingBatcherFixture.cs
+++ b/src/NHibernate.Test/Async/Ado/GenericBatchingBatcherFixture.cs
@@ -69,7 +69,7 @@ public async Task BatchSizeTestAsync()
await (tx.CommitAsync());
var log = sqlLog.GetWholeLog();
- Assert.That(4, Is.EqualTo(FindAllOccurrences(log, "Batch commands:")));
+ Assert.That(FindAllOccurrences(log, "Batch commands:"), Is.EqualTo(4));
}
await (CleanupAsync());
}
diff --git a/src/NHibernate/Dialect/Dialect.cs b/src/NHibernate/Dialect/Dialect.cs
index f42c8545ef1..1b637174718 100644
--- a/src/NHibernate/Dialect/Dialect.cs
+++ b/src/NHibernate/Dialect/Dialect.cs
@@ -2449,7 +2449,7 @@ public virtual string LowercaseFunction
///
/// The maximum number of parameters allowed in a query.
///
- public virtual int? MaxNumberOfParameters { get; } = null;
+ public virtual int? MaxNumberOfParameters => null;
///
/// The syntax used to add a column to a table. Note this is deprecated
From 9232cbb9c67af8ec7cbdb95700cc551eba9e96fa Mon Sep 17 00:00:00 2001
From: maca88
Date: Fri, 9 Mar 2018 17:06:08 +0100
Subject: [PATCH 12/12] Moved statement terminator setting into dialect and
done some minor refactoring.
---
.../AdoNet/GenericBatchingBatcher.cs | 63 ++++++++++---------
.../AdoNet/GenericBatchingBatcherFactory.cs | 9 +--
.../Async/AdoNet/GenericBatchingBatcher.cs | 30 +++------
src/NHibernate/Dialect/Dialect.cs | 5 ++
.../Driver/BasicResultSetsCommand.cs | 4 +-
5 files changed, 49 insertions(+), 62 deletions(-)
diff --git a/src/NHibernate/AdoNet/GenericBatchingBatcher.cs b/src/NHibernate/AdoNet/GenericBatchingBatcher.cs
index b31998af077..26074117c4a 100644
--- a/src/NHibernate/AdoNet/GenericBatchingBatcher.cs
+++ b/src/NHibernate/AdoNet/GenericBatchingBatcher.cs
@@ -25,12 +25,11 @@ public partial class GenericBatchingBatcher : AbstractBatcher
private int _totalExpectedRowsAffected;
private StringBuilder _currentBatchCommandsLog;
- public GenericBatchingBatcher(ConnectionManager connectionManager, IInterceptor interceptor, char statementTerminator)
+ public GenericBatchingBatcher(ConnectionManager connectionManager, IInterceptor interceptor)
: base(connectionManager, interceptor)
{
BatchSize = Factory.Settings.AdoBatchSize;
- StatementTerminator = statementTerminator;
- _currentBatch = new BatchingCommandSet(this);
+ _currentBatch = new BatchingCommandSet(this, Factory.Dialect.StatementTerminator);
_maxNumberOfParameters = Factory.Dialect.MaxNumberOfParameters;
// We always create this, because we need to deal with a scenario in which
@@ -40,44 +39,26 @@ public GenericBatchingBatcher(ConnectionManager connectionManager, IInterceptor
_currentBatchCommandsLog = new StringBuilder().AppendLine("Batch commands:");
}
- public char StatementTerminator { get; }
-
public sealed override int BatchSize { get; set; }
protected override int CountOfStatementsInCurrentBatch => _currentBatch.CountOfCommands;
public override void AddToBatch(IExpectation expectation)
{
- var batchUpdate = CurrentCommand;
+ var batchCommand = CurrentCommand;
if (_maxNumberOfParameters.HasValue &&
- _currentBatch.CountOfParameters + CurrentCommand.Parameters.Count > _maxNumberOfParameters)
+ _currentBatch.CountOfParameters + batchCommand.Parameters.Count > _maxNumberOfParameters)
{
- ExecuteBatchWithTiming(batchUpdate);
+ ExecuteBatchWithTiming(batchCommand);
}
_totalExpectedRowsAffected += expectation.ExpectedRowCount;
- Driver.AdjustCommand(batchUpdate);
- 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(_currentBatch.CountOfCommands)
- .Append(":")
- .AppendLine(lineWithParameters);
- }
- if (Log.IsDebugEnabled())
- {
- Log.Debug("Adding to batch:{0}", lineWithParameters);
- }
-
- _currentBatch.Append(CurrentCommand.Parameters);
+ Driver.AdjustCommand(batchCommand);
+ LogBatchCommand(batchCommand);
+ _currentBatch.Append(batchCommand.Parameters);
if (_currentBatch.CountOfCommands >= BatchSize)
{
- ExecuteBatchWithTiming(batchUpdate);
+ ExecuteBatchWithTiming(batchCommand);
}
}
@@ -115,6 +96,26 @@ protected override void DoExecuteBatch(DbCommand ps)
}
}
+ private void LogBatchCommand(DbCommand batchCommand)
+ {
+ string lineWithParameters = null;
+ var sqlStatementLogger = Factory.Settings.SqlStatementLogger;
+ if (sqlStatementLogger.IsDebugEnabled || Log.IsDebugEnabled())
+ {
+ lineWithParameters = sqlStatementLogger.GetCommandLineWithParameters(batchCommand);
+ var formatStyle = sqlStatementLogger.DetermineActualStyle(FormatStyle.Basic);
+ lineWithParameters = formatStyle.Formatter.Format(lineWithParameters);
+ _currentBatchCommandsLog.Append("command ")
+ .Append(_currentBatch.CountOfCommands)
+ .Append(":")
+ .AppendLine(lineWithParameters);
+ }
+ if (Log.IsDebugEnabled())
+ {
+ Log.Debug("Adding to batch:{0}", lineWithParameters);
+ }
+ }
+
private void ClearCurrentBatch()
{
_currentBatch.Clear();
@@ -140,6 +141,7 @@ protected override void Dispose(bool isDisposing)
private partial class BatchingCommandSet
{
+ private readonly string _statementTerminator;
private readonly GenericBatchingBatcher _batcher;
private readonly SqlStringBuilder _sql = new SqlStringBuilder();
private readonly List _sqlTypes = new List();
@@ -159,9 +161,10 @@ private class BatchParameter
public object Value { get; set; }
}
- public BatchingCommandSet(GenericBatchingBatcher batcher)
+ public BatchingCommandSet(GenericBatchingBatcher batcher, char statementTerminator)
{
_batcher = batcher;
+ _statementTerminator = statementTerminator.ToString();
}
public int CountOfCommands { get; private set; }
@@ -172,7 +175,7 @@ public void Append(DbParameterCollection parameters)
{
if (CountOfCommands > 0)
{
- _sql.Add(_batcher.StatementTerminator.ToString());
+ _sql.Add(_statementTerminator);
}
else
{
diff --git a/src/NHibernate/AdoNet/GenericBatchingBatcherFactory.cs b/src/NHibernate/AdoNet/GenericBatchingBatcherFactory.cs
index 4b6638acc33..69a127e2ec1 100644
--- a/src/NHibernate/AdoNet/GenericBatchingBatcherFactory.cs
+++ b/src/NHibernate/AdoNet/GenericBatchingBatcherFactory.cs
@@ -4,16 +4,9 @@ namespace NHibernate.AdoNet
{
public class GenericBatchingBatcherFactory: IBatcherFactory
{
- private char _statementTerminator = ';';
-
public virtual IBatcher CreateBatcher(ConnectionManager connectionManager, IInterceptor interceptor)
{
- return new GenericBatchingBatcher(connectionManager, interceptor, _statementTerminator);
- }
-
- public void SetStatementTerminator(char value)
- {
- _statementTerminator = value;
+ return new GenericBatchingBatcher(connectionManager, interceptor);
}
}
}
diff --git a/src/NHibernate/Async/AdoNet/GenericBatchingBatcher.cs b/src/NHibernate/Async/AdoNet/GenericBatchingBatcher.cs
index 8d1bcab437b..47ebe2ed12c 100644
--- a/src/NHibernate/Async/AdoNet/GenericBatchingBatcher.cs
+++ b/src/NHibernate/Async/AdoNet/GenericBatchingBatcher.cs
@@ -27,36 +27,20 @@ public partial class GenericBatchingBatcher : AbstractBatcher
public override async Task AddToBatchAsync(IExpectation expectation, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
- var batchUpdate = CurrentCommand;
+ var batchCommand = CurrentCommand;
if (_maxNumberOfParameters.HasValue &&
- _currentBatch.CountOfParameters + CurrentCommand.Parameters.Count > _maxNumberOfParameters)
+ _currentBatch.CountOfParameters + batchCommand.Parameters.Count > _maxNumberOfParameters)
{
- await (ExecuteBatchWithTimingAsync(batchUpdate, cancellationToken)).ConfigureAwait(false);
+ await (ExecuteBatchWithTimingAsync(batchCommand, cancellationToken)).ConfigureAwait(false);
}
_totalExpectedRowsAffected += expectation.ExpectedRowCount;
- Driver.AdjustCommand(batchUpdate);
- 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(_currentBatch.CountOfCommands)
- .Append(":")
- .AppendLine(lineWithParameters);
- }
- if (Log.IsDebugEnabled())
- {
- Log.Debug("Adding to batch:{0}", lineWithParameters);
- }
-
- _currentBatch.Append(CurrentCommand.Parameters);
+ Driver.AdjustCommand(batchCommand);
+ LogBatchCommand(batchCommand);
+ _currentBatch.Append(batchCommand.Parameters);
if (_currentBatch.CountOfCommands >= BatchSize)
{
- await (ExecuteBatchWithTimingAsync(batchUpdate, cancellationToken)).ConfigureAwait(false);
+ await (ExecuteBatchWithTimingAsync(batchCommand, cancellationToken)).ConfigureAwait(false);
}
}
diff --git a/src/NHibernate/Dialect/Dialect.cs b/src/NHibernate/Dialect/Dialect.cs
index 1b637174718..1483cdd4f08 100644
--- a/src/NHibernate/Dialect/Dialect.cs
+++ b/src/NHibernate/Dialect/Dialect.cs
@@ -2451,6 +2451,11 @@ public virtual string LowercaseFunction
///
public virtual int? MaxNumberOfParameters => null;
+ ///
+ /// The character used to terminate a SQL statement.
+ ///
+ public virtual char StatementTerminator => ';';
+
///
/// The syntax used to add a column to a table. Note this is deprecated
///
diff --git a/src/NHibernate/Driver/BasicResultSetsCommand.cs b/src/NHibernate/Driver/BasicResultSetsCommand.cs
index f03c891d75d..36e6a8a8485 100644
--- a/src/NHibernate/Driver/BasicResultSetsCommand.cs
+++ b/src/NHibernate/Driver/BasicResultSetsCommand.cs
@@ -16,11 +16,13 @@ public partial class BasicResultSetsCommand: IResultSetsCommand
{
private static readonly INHibernateLogger log = NHibernateLogger.For(typeof(BasicResultSetsCommand));
private SqlString sqlString = SqlString.Empty;
+ private readonly string _statementTerminator;
public BasicResultSetsCommand(ISessionImplementor session)
{
Commands = new List();
Session = session;
+ _statementTerminator = session.Factory.Dialect.StatementTerminator.ToString();
}
protected List Commands { get; private set; }
@@ -30,7 +32,7 @@ public BasicResultSetsCommand(ISessionImplementor session)
public virtual void Append(ISqlCommand command)
{
Commands.Add(command);
- sqlString = sqlString.Append(command.Query).Append(";").Append(Environment.NewLine);
+ sqlString = sqlString.Append(command.Query).Append(_statementTerminator).Append(Environment.NewLine);
}
public bool HasQueries