diff --git a/src/NHibernate.Test/Async/NHSpecificTest/GH1300/Fixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/GH1300/Fixture.cs
new file mode 100644
index 00000000000..11f20de84c2
--- /dev/null
+++ b/src/NHibernate.Test/Async/NHSpecificTest/GH1300/Fixture.cs
@@ -0,0 +1,212 @@
+//------------------------------------------------------------------------------
+//
+// 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.Data;
+using System.Data.SqlClient;
+using System.Linq;
+using NHibernate.Cfg;
+using NHibernate.Connection;
+using NHibernate.Criterion;
+using NHibernate.Dialect;
+using NHibernate.Driver;
+using NHibernate.Engine;
+using NHibernate.Exceptions;
+using NHibernate.Linq;
+using NHibernate.Type;
+using NUnit.Framework;
+
+namespace NHibernate.Test.NHSpecificTest.GH1300
+{
+ using System.Threading.Tasks;
+ using System.Threading;
+ [TestFixture]
+ public class FixtureAsync : BugTestCase
+ {
+ protected override bool AppliesTo(Dialect.Dialect dialect)
+ {
+ return dialect is MsSql2000Dialect;
+ }
+
+ protected override bool AppliesTo(ISessionFactoryImplementor factory)
+ {
+ return factory.ConnectionProvider.Driver is SqlClientDriver;
+ }
+
+ protected override void Configure(Configuration configuration)
+ {
+ using (var cp = ConnectionProviderFactory.NewConnectionProvider(cfg.Properties))
+ {
+ if (cp.Driver is SqlClientDriver)
+ {
+ configuration.SetProperty(Environment.ConnectionDriver, typeof(TestSqlClientDriver).AssemblyQualifiedName);
+ }
+ }
+ }
+
+ protected override void OnTearDown()
+ {
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ session.Delete("from System.Object");
+
+ session.Flush();
+ transaction.Commit();
+ }
+ }
+
+ protected override void OnSetUp()
+ {
+ base.OnSetUp();
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ var e1 = new Entity { Name = "Bob" };
+ session.Save(e1);
+ transaction.Commit();
+ }
+ }
+
+ [Test]
+ public async Task InsertShouldUseMappedSizeAsync()
+ {
+ Driver.ClearCommands();
+
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ var e1 = new Entity { Name = "1", AnsiName = "2", FullText = "3", AnsiFullText = "4" };
+ await (session.SaveAsync(e1));
+ await (transaction.CommitAsync());
+ Assert.That(Driver.LastCommandParameters.Single(x => (string) x.Value == "1").SqlDbType, Is.EqualTo(SqlDbType.NVarChar));
+ Assert.That(Driver.LastCommandParameters.Single(x => (string) x.Value == "1").Size, Is.EqualTo(3));
+ Assert.That(Driver.LastCommandParameters.Single(x => (string) x.Value == "2").SqlDbType, Is.EqualTo(SqlDbType.VarChar));
+ Assert.That(Driver.LastCommandParameters.Single(x => (string) x.Value == "2").Size, Is.EqualTo(3));
+ Assert.That(Driver.LastCommandParameters.Single(x => (string) x.Value == "3").SqlDbType, Is.EqualTo(SqlDbType.NVarChar));
+ Assert.That(Driver.LastCommandParameters.Single(x => (string) x.Value == "3").Size, Is.EqualTo(MsSql2000Dialect.MaxSizeForClob));
+ Assert.That(Driver.LastCommandParameters.Single(x => (string) x.Value == "4").SqlDbType, Is.EqualTo(SqlDbType.VarChar));
+ Assert.That(Driver.LastCommandParameters.Single(x => (string) x.Value == "4").Size, Is.EqualTo(MsSql2000Dialect.MaxSizeForAnsiClob));
+ }
+ }
+
+ [Test]
+ public void InsertWithTooLongValuesShouldThrowAsync()
+ {
+ Driver.ClearCommands();
+
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ var e1 = new Entity { Name = "Alal", AnsiName = "Alal" };
+
+ var ex = Assert.ThrowsAsync(
+ async () =>
+ {
+ await (session.SaveAsync(e1));
+ await (transaction.CommitAsync());
+ });
+
+ var sqlEx = ex.InnerException as SqlException;
+ Assert.That(sqlEx, Is.Not.Null);
+ Assert.That(sqlEx.Number, Is.EqualTo(8152));
+ }
+ }
+
+ [TestCase("Name", SqlDbType.NVarChar)]
+ [TestCase("AnsiName", SqlDbType.VarChar)]
+ public async Task LinqEqualsShouldUseMappedSizeAsync(string property, SqlDbType expectedDbType, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ Driver.ClearCommands();
+
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ if (property == "Name")
+ {
+ await (session.Query().Where(x => x.Name == "Bob").ToListAsync(cancellationToken));
+ }
+ else
+ {
+ await (session.Query().Where(x => x.AnsiName == "Bob").ToListAsync(cancellationToken));
+ }
+ Assert.That(Driver.LastCommandParameters.First().Size, Is.EqualTo(3));
+ Assert.That(Driver.LastCommandParameters.First().SqlDbType, Is.EqualTo(expectedDbType));
+ }
+ }
+
+ [Test]
+ public async Task MappedAsShouldUseExplicitSizeAsync()
+ {
+ Driver.ClearCommands();
+
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ await (session.Query().Where(x => x.Name == "Bob".MappedAs(TypeFactory.Basic("AnsiString(200)"))).ToListAsync());
+
+ Assert.That(Driver.LastCommandParameters.First().Size, Is.EqualTo(200));
+ Assert.That(Driver.LastCommandParameters.First().SqlDbType, Is.EqualTo(SqlDbType.VarChar));
+ }
+ }
+
+ [TestCase("Name", SqlDbType.NVarChar)]
+ [TestCase("AnsiName", SqlDbType.VarChar)]
+ public async Task HqlLikeShouldUseLargerSizeAsync(string property, SqlDbType expectedDbType, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ Driver.ClearCommands();
+
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ await (session.CreateQuery("from Entity where " + property + " like :name").SetParameter("name", "%Bob%").ListAsync(cancellationToken));
+
+ Assert.That(Driver.LastCommandParameters.First().Size, Is.GreaterThanOrEqualTo(5));
+ Assert.That(Driver.LastCommandParameters.First().SqlDbType, Is.EqualTo(expectedDbType));
+ }
+ }
+
+ [TestCase("Name", SqlDbType.NVarChar)]
+ [TestCase("AnsiName", SqlDbType.VarChar)]
+ public async Task CriteriaEqualsShouldUseMappedSizeAsync(string property, SqlDbType expectedDbType, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ Driver.ClearCommands();
+
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ Driver.ClearCommands();
+
+ await (session.CreateCriteria().Add(Restrictions.Eq(property, "Bob"))
+ .ListAsync(cancellationToken));
+
+ Assert.That(Driver.LastCommandParameters.First().Size, Is.GreaterThanOrEqualTo(3));
+ Assert.That(Driver.LastCommandParameters.First().SqlDbType, Is.EqualTo(expectedDbType));
+ }
+ }
+
+ [TestCase("Name", SqlDbType.NVarChar)]
+ [TestCase("AnsiName", SqlDbType.VarChar)]
+ public async Task CriteriaLikeShouldUseLargerSizeAsync(string property, SqlDbType expectedDbType, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ Driver.ClearCommands();
+
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ await (session.CreateCriteria().Add(Restrictions.Like(property, "%Bob%"))
+ .ListAsync(cancellationToken));
+
+ Assert.That(Driver.LastCommandParameters.First().Size, Is.GreaterThanOrEqualTo(5));
+ Assert.That(Driver.LastCommandParameters.First().SqlDbType, Is.EqualTo(expectedDbType));
+ }
+ }
+ private TestSqlClientDriver Driver => Sfi.ConnectionProvider.Driver as TestSqlClientDriver;
+ }
+}
diff --git a/src/NHibernate.Test/NHSpecificTest/GH1300/Entity.cs b/src/NHibernate.Test/NHSpecificTest/GH1300/Entity.cs
new file mode 100644
index 00000000000..abd53ce2050
--- /dev/null
+++ b/src/NHibernate.Test/NHSpecificTest/GH1300/Entity.cs
@@ -0,0 +1,11 @@
+namespace NHibernate.Test.NHSpecificTest.GH1300
+{
+ class Entity
+ {
+ public virtual int Id { get; set; }
+ public virtual string Name { get; set; }
+ public virtual string AnsiName { get; set; }
+ public virtual string FullText { get; set; }
+ public virtual string AnsiFullText { get; set; }
+ }
+}
diff --git a/src/NHibernate.Test/NHSpecificTest/GH1300/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/GH1300/Fixture.cs
new file mode 100644
index 00000000000..8f994c19117
--- /dev/null
+++ b/src/NHibernate.Test/NHSpecificTest/GH1300/Fixture.cs
@@ -0,0 +1,200 @@
+using System.Data;
+using System.Data.SqlClient;
+using System.Linq;
+using NHibernate.Cfg;
+using NHibernate.Connection;
+using NHibernate.Criterion;
+using NHibernate.Dialect;
+using NHibernate.Driver;
+using NHibernate.Engine;
+using NHibernate.Exceptions;
+using NHibernate.Linq;
+using NHibernate.Type;
+using NUnit.Framework;
+
+namespace NHibernate.Test.NHSpecificTest.GH1300
+{
+ [TestFixture]
+ public class Fixture : BugTestCase
+ {
+ protected override bool AppliesTo(Dialect.Dialect dialect)
+ {
+ return dialect is MsSql2000Dialect;
+ }
+
+ protected override bool AppliesTo(ISessionFactoryImplementor factory)
+ {
+ return factory.ConnectionProvider.Driver is SqlClientDriver;
+ }
+
+ protected override void Configure(Configuration configuration)
+ {
+ using (var cp = ConnectionProviderFactory.NewConnectionProvider(cfg.Properties))
+ {
+ if (cp.Driver is SqlClientDriver)
+ {
+ configuration.SetProperty(Environment.ConnectionDriver, typeof(TestSqlClientDriver).AssemblyQualifiedName);
+ }
+ }
+ }
+
+ protected override void OnTearDown()
+ {
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ session.Delete("from System.Object");
+
+ session.Flush();
+ transaction.Commit();
+ }
+ }
+
+ protected override void OnSetUp()
+ {
+ base.OnSetUp();
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ var e1 = new Entity { Name = "Bob" };
+ session.Save(e1);
+ transaction.Commit();
+ }
+ }
+
+ [Test]
+ public void InsertShouldUseMappedSize()
+ {
+ Driver.ClearCommands();
+
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ var e1 = new Entity { Name = "1", AnsiName = "2", FullText = "3", AnsiFullText = "4" };
+ session.Save(e1);
+ transaction.Commit();
+ Assert.That(Driver.LastCommandParameters.Single(x => (string) x.Value == "1").SqlDbType, Is.EqualTo(SqlDbType.NVarChar));
+ Assert.That(Driver.LastCommandParameters.Single(x => (string) x.Value == "1").Size, Is.EqualTo(3));
+ Assert.That(Driver.LastCommandParameters.Single(x => (string) x.Value == "2").SqlDbType, Is.EqualTo(SqlDbType.VarChar));
+ Assert.That(Driver.LastCommandParameters.Single(x => (string) x.Value == "2").Size, Is.EqualTo(3));
+ Assert.That(Driver.LastCommandParameters.Single(x => (string) x.Value == "3").SqlDbType, Is.EqualTo(SqlDbType.NVarChar));
+ Assert.That(Driver.LastCommandParameters.Single(x => (string) x.Value == "3").Size, Is.EqualTo(MsSql2000Dialect.MaxSizeForClob));
+ Assert.That(Driver.LastCommandParameters.Single(x => (string) x.Value == "4").SqlDbType, Is.EqualTo(SqlDbType.VarChar));
+ Assert.That(Driver.LastCommandParameters.Single(x => (string) x.Value == "4").Size, Is.EqualTo(MsSql2000Dialect.MaxSizeForAnsiClob));
+ }
+ }
+
+ [Test]
+ public void InsertWithTooLongValuesShouldThrow()
+ {
+ Driver.ClearCommands();
+
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ var e1 = new Entity { Name = "Alal", AnsiName = "Alal" };
+
+ var ex = Assert.Throws(
+ () =>
+ {
+ session.Save(e1);
+ transaction.Commit();
+ });
+
+ var sqlEx = ex.InnerException as SqlException;
+ Assert.That(sqlEx, Is.Not.Null);
+ Assert.That(sqlEx.Number, Is.EqualTo(8152));
+ }
+ }
+
+ [TestCase("Name", SqlDbType.NVarChar)]
+ [TestCase("AnsiName", SqlDbType.VarChar)]
+ public void LinqEqualsShouldUseMappedSize(string property, SqlDbType expectedDbType)
+ {
+ Driver.ClearCommands();
+
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ if (property == "Name")
+ {
+ session.Query().Where(x => x.Name == "Bob").ToList();
+ }
+ else
+ {
+ session.Query().Where(x => x.AnsiName == "Bob").ToList();
+ }
+ Assert.That(Driver.LastCommandParameters.First().Size, Is.EqualTo(3));
+ Assert.That(Driver.LastCommandParameters.First().SqlDbType, Is.EqualTo(expectedDbType));
+ }
+ }
+
+ [Test]
+ public void MappedAsShouldUseExplicitSize()
+ {
+ Driver.ClearCommands();
+
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ session.Query().Where(x => x.Name == "Bob".MappedAs(TypeFactory.Basic("AnsiString(200)"))).ToList();
+
+ Assert.That(Driver.LastCommandParameters.First().Size, Is.EqualTo(200));
+ Assert.That(Driver.LastCommandParameters.First().SqlDbType, Is.EqualTo(SqlDbType.VarChar));
+ }
+ }
+
+ [TestCase("Name", SqlDbType.NVarChar)]
+ [TestCase("AnsiName", SqlDbType.VarChar)]
+ public void HqlLikeShouldUseLargerSize(string property, SqlDbType expectedDbType)
+ {
+ Driver.ClearCommands();
+
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ session.CreateQuery("from Entity where " + property + " like :name").SetParameter("name", "%Bob%").List();
+
+ Assert.That(Driver.LastCommandParameters.First().Size, Is.GreaterThanOrEqualTo(5));
+ Assert.That(Driver.LastCommandParameters.First().SqlDbType, Is.EqualTo(expectedDbType));
+ }
+ }
+
+ [TestCase("Name", SqlDbType.NVarChar)]
+ [TestCase("AnsiName", SqlDbType.VarChar)]
+ public void CriteriaEqualsShouldUseMappedSize(string property, SqlDbType expectedDbType)
+ {
+ Driver.ClearCommands();
+
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ Driver.ClearCommands();
+
+ session.CreateCriteria().Add(Restrictions.Eq(property, "Bob"))
+ .List();
+
+ Assert.That(Driver.LastCommandParameters.First().Size, Is.GreaterThanOrEqualTo(3));
+ Assert.That(Driver.LastCommandParameters.First().SqlDbType, Is.EqualTo(expectedDbType));
+ }
+ }
+
+ [TestCase("Name", SqlDbType.NVarChar)]
+ [TestCase("AnsiName", SqlDbType.VarChar)]
+ public void CriteriaLikeShouldUseLargerSize(string property, SqlDbType expectedDbType)
+ {
+ Driver.ClearCommands();
+
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ session.CreateCriteria().Add(Restrictions.Like(property, "%Bob%"))
+ .List();
+
+ Assert.That(Driver.LastCommandParameters.First().Size, Is.GreaterThanOrEqualTo(5));
+ Assert.That(Driver.LastCommandParameters.First().SqlDbType, Is.EqualTo(expectedDbType));
+ }
+ }
+ private TestSqlClientDriver Driver => Sfi.ConnectionProvider.Driver as TestSqlClientDriver;
+ }
+}
diff --git a/src/NHibernate.Test/NHSpecificTest/GH1300/Mappings.hbm.xml b/src/NHibernate.Test/NHSpecificTest/GH1300/Mappings.hbm.xml
new file mode 100644
index 00000000000..6c828e207ea
--- /dev/null
+++ b/src/NHibernate.Test/NHSpecificTest/GH1300/Mappings.hbm.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/NHibernate.Test/NHSpecificTest/GH1300/TestSqlClientDriver.cs b/src/NHibernate.Test/NHSpecificTest/GH1300/TestSqlClientDriver.cs
new file mode 100644
index 00000000000..40fe814c262
--- /dev/null
+++ b/src/NHibernate.Test/NHSpecificTest/GH1300/TestSqlClientDriver.cs
@@ -0,0 +1,23 @@
+using System.Collections.Generic;
+using System.Data.Common;
+using System.Data.SqlClient;
+using System.Linq;
+using NHibernate.Driver;
+
+namespace NHibernate.Test.NHSpecificTest.GH1300
+{
+ public class TestSqlClientDriver : SqlClientDriver
+ {
+ public List LastCommandParameters { get; private set; }=new List();
+
+ public override void AdjustCommand(DbCommand command)
+ {
+ base.AdjustCommand(command);
+ LastCommandParameters = command.Parameters.OfType().ToList();
+ }
+ public void ClearCommands()
+ {
+ LastCommandParameters.Clear();
+ }
+ }
+}
diff --git a/src/NHibernate/AdoNet/IParameterAdjuster.cs b/src/NHibernate/AdoNet/IParameterAdjuster.cs
new file mode 100644
index 00000000000..23121520d89
--- /dev/null
+++ b/src/NHibernate/AdoNet/IParameterAdjuster.cs
@@ -0,0 +1,22 @@
+using System.Data.Common;
+using NHibernate.Driver;
+using NHibernate.SqlTypes;
+
+namespace NHibernate.AdoNet
+{
+ ///
+ /// Supports adjusting a according to a and
+ /// the parameter's value. An may implement this interface.
+ ///
+ public interface IParameterAdjuster
+ {
+ ///
+ /// Adjust the provided parameter according to its and
+ /// .
+ ///
+ /// The parameter to adjust.
+ /// The parameter's .
+ /// The parameter's value.
+ void AdjustParameterForValue(DbParameter parameter, SqlType sqlType, object value);
+ }
+}
diff --git a/src/NHibernate/Driver/DriverExtensions.cs b/src/NHibernate/Driver/DriverExtensions.cs
new file mode 100644
index 00000000000..ef7b4a3df78
--- /dev/null
+++ b/src/NHibernate/Driver/DriverExtensions.cs
@@ -0,0 +1,16 @@
+using System.Data.Common;
+using NHibernate.AdoNet;
+using NHibernate.SqlTypes;
+using NHibernate.Type;
+
+namespace NHibernate.Driver
+{
+ internal static class DriverExtensions
+ {
+ internal static void AdjustParameterForValue(this IDriver driver, DbParameter parameter, SqlType sqlType, object value)
+ {
+ var adjustingDriver = driver as IParameterAdjuster;
+ adjustingDriver?.AdjustParameterForValue(parameter, sqlType, value);
+ }
+ }
+}
diff --git a/src/NHibernate/Driver/SqlClientDriver.cs b/src/NHibernate/Driver/SqlClientDriver.cs
index 679fe397bc4..81d46097a50 100644
--- a/src/NHibernate/Driver/SqlClientDriver.cs
+++ b/src/NHibernate/Driver/SqlClientDriver.cs
@@ -9,6 +9,7 @@
using NHibernate.Dialect;
using NHibernate.Engine;
using NHibernate.SqlTypes;
+using NHibernate.Type;
namespace NHibernate.Driver
{
@@ -17,10 +18,11 @@ namespace NHibernate.Driver
///
public class SqlClientDriver
#if NETFX
- : DriverBase, IEmbeddedBatcherFactoryProvider
+ : DriverBase,
#else
- : ReflectionBasedDriver, IEmbeddedBatcherFactoryProvider
+ : ReflectionBasedDriver,
#endif
+ IEmbeddedBatcherFactoryProvider, IParameterAdjuster
{
// Since v5.1
[Obsolete("Use MsSql2000Dialect.MaxSizeForAnsiClob")]
@@ -301,5 +303,23 @@ public override bool SupportsMultipleQueries
///
public override DateTime MinDate => new DateTime(1753, 1, 1);
+
+ public virtual void AdjustParameterForValue(DbParameter parameter, SqlType sqlType, object value)
+ {
+ if (value is string stringVal)
+ {
+ switch (parameter.DbType)
+ {
+ case DbType.AnsiString:
+ case DbType.AnsiStringFixedLength:
+ parameter.Size = IsAnsiText(parameter, sqlType) ? MsSql2000Dialect.MaxSizeForAnsiClob : Math.Max(stringVal.Length, sqlType.LengthDefined ? sqlType.Length : parameter.Size);
+ break;
+ case DbType.String:
+ case DbType.StringFixedLength:
+ parameter.Size = IsText(parameter, sqlType) ? MsSql2000Dialect.MaxSizeForClob : Math.Max(stringVal.Length, sqlType.LengthDefined ? sqlType.Length : parameter.Size);
+ break;
+ }
+ }
+ }
}
}
diff --git a/src/NHibernate/Type/AbstractStringType.cs b/src/NHibernate/Type/AbstractStringType.cs
index b8a5ade41ad..267ccec6da5 100644
--- a/src/NHibernate/Type/AbstractStringType.cs
+++ b/src/NHibernate/Type/AbstractStringType.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Data.Common;
using System.Globalization;
+using NHibernate.Driver;
using NHibernate.Engine;
using NHibernate.SqlTypes;
using NHibernate.UserTypes;
@@ -57,6 +58,9 @@ public override void Set(DbCommand cmd, object value, int index, ISessionImpleme
{
var parameter = cmd.Parameters[index];
+ //Allow the driver to adjust the parameter for the value
+ session.Factory.ConnectionProvider.Driver.AdjustParameterForValue(parameter, SqlType, value);
+
// set the parameter value before the size check, since ODBC changes the size automatically
parameter.Value = value;