diff --git a/src/NHibernate.Test/Async/NHSpecificTest/NH3403/Fixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/NH3403/Fixture.cs
new file mode 100644
index 00000000000..570be6870fb
--- /dev/null
+++ b/src/NHibernate.Test/Async/NHSpecificTest/NH3403/Fixture.cs
@@ -0,0 +1,188 @@
+//------------------------------------------------------------------------------
+//
+// 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.Criterion;
+using NHibernate.Dialect;
+using NHibernate.Driver;
+using NHibernate.Engine;
+using NHibernate.Exceptions;
+using NHibernate.Linq;
+using NUnit.Framework;
+
+namespace NHibernate.Test.NHSpecificTest.NH3403
+{
+ 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)
+ {
+ cfg.SetProperty(Environment.ConnectionDriver, typeof(TestSqlClientDriver).AssemblyQualifiedName);
+ }
+
+ protected override void OnTearDown()
+ {
+ using (ISession session = OpenSession())
+ using (ITransaction transaction = session.BeginTransaction())
+ {
+ session.Delete("from System.Object");
+
+ session.Flush();
+ transaction.Commit();
+ }
+ }
+
+ protected override void OnSetUp()
+ {
+ base.OnSetUp();
+ using (ISession session = OpenSession())
+ using (ITransaction transaction = session.BeginTransaction())
+ {
+ var e1 = new Entity { Name = "Bob" };
+ session.Save(e1);
+ transaction.Commit();
+ }
+ }
+
+ [Test]
+ public async Task InsertShouldUseMappedSizeAsync()
+ {
+ Driver.ClearCommands();
+
+ using (ISession session = OpenSession())
+ using (ITransaction transaction = session.BeginTransaction())
+ {
+ var e1 = new Entity { Name = "Al", AnsiName = "Al" };
+ await (session.SaveAsync(e1));
+ await (transaction.CommitAsync());
+ Assert.AreEqual(SqlDbType.NVarChar, Driver.LastCommandParameters.First().SqlDbType);
+ Assert.AreEqual(3, Driver.LastCommandParameters.First().Size);
+ Assert.AreEqual(SqlDbType.VarChar, Driver.LastCommandParameters.Last().SqlDbType);
+ Assert.AreEqual(3, Driver.LastCommandParameters.Last().Size);
+ }
+ }
+
+ [Test]
+ public void InsertWithTooLongValuesShouldThrowAsync()
+ {
+ Driver.ClearCommands();
+
+ using (ISession session = OpenSession())
+ using (ITransaction 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.IsNotNull(sqlEx);
+ 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 (ISession session = OpenSession())
+ using (ITransaction 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.AreEqual(3, Driver.LastCommandParameters.First().Size);
+ Assert.AreEqual(expectedDbType, Driver.LastCommandParameters.First().SqlDbType);
+ }
+ }
+
+ [TestCase("Name", SqlDbType.NVarChar)]
+ [TestCase("AnsiName", SqlDbType.VarChar)]
+ public async Task HqlLikeShouldUseLargerSizeAsync(string property, SqlDbType expectedDbType, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ Driver.ClearCommands();
+
+ using (ISession session = OpenSession())
+ using (ITransaction transaction = session.BeginTransaction())
+ {
+ await (session.CreateQuery("from Entity where " + property + " like :name").SetParameter("name", "%Bob%").ListAsync(cancellationToken));
+
+ Assert.GreaterOrEqual(Driver.LastCommandParameters.First().Size, 5);
+ Assert.AreEqual(expectedDbType, Driver.LastCommandParameters.First().SqlDbType);
+ }
+ }
+
+ [TestCase("Name", SqlDbType.NVarChar)]
+ [TestCase("AnsiName", SqlDbType.VarChar)]
+ public async Task CriteriaEqualsShouldUseMappedSizeAsync(string property, SqlDbType expectedDbType, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ Driver.ClearCommands();
+
+ using (ISession session = OpenSession())
+ using (ITransaction transaction = session.BeginTransaction())
+ {
+ Driver.ClearCommands();
+
+ await (session.CreateCriteria().Add(Restrictions.Eq(property, "Bob"))
+ .ListAsync(cancellationToken));
+
+ Assert.GreaterOrEqual(Driver.LastCommandParameters.First().Size, 3);
+ Assert.AreEqual(expectedDbType, Driver.LastCommandParameters.First().SqlDbType);
+ }
+ }
+
+ [TestCase("Name", SqlDbType.NVarChar)]
+ [TestCase("AnsiName", SqlDbType.VarChar)]
+ public async Task CriteriaLikeShouldUseLargerSizeAsync(string property, SqlDbType expectedDbType, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ Driver.ClearCommands();
+
+ using (ISession session = OpenSession())
+ using (ITransaction transaction = session.BeginTransaction())
+ {
+ await (session.CreateCriteria().Add(Restrictions.Like(property, "%Bob%"))
+ .ListAsync(cancellationToken));
+
+ Assert.GreaterOrEqual(Driver.LastCommandParameters.First().Size, 5);
+ Assert.AreEqual(expectedDbType, Driver.LastCommandParameters.First().SqlDbType);
+ }
+ }
+ private TestSqlClientDriver Driver
+ {
+ get { return Sfi.ConnectionProvider.Driver as TestSqlClientDriver; }
+ }
+ }
+}
diff --git a/src/NHibernate.Test/NHSpecificTest/NH3403/Entity.cs b/src/NHibernate.Test/NHSpecificTest/NH3403/Entity.cs
new file mode 100644
index 00000000000..a815e99c7f7
--- /dev/null
+++ b/src/NHibernate.Test/NHSpecificTest/NH3403/Entity.cs
@@ -0,0 +1,9 @@
+namespace NHibernate.Test.NHSpecificTest.NH3403
+{
+ class Entity
+ {
+ public virtual int Id { get; set; }
+ public virtual string Name { get; set; }
+ public virtual string AnsiName { get; set; }
+ }
+}
diff --git a/src/NHibernate.Test/NHSpecificTest/NH3403/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/NH3403/Fixture.cs
new file mode 100644
index 00000000000..6059b51630f
--- /dev/null
+++ b/src/NHibernate.Test/NHSpecificTest/NH3403/Fixture.cs
@@ -0,0 +1,176 @@
+using System.Data;
+using System.Data.SqlClient;
+using System.Linq;
+using NHibernate.Cfg;
+using NHibernate.Criterion;
+using NHibernate.Dialect;
+using NHibernate.Driver;
+using NHibernate.Engine;
+using NHibernate.Exceptions;
+using NHibernate.Linq;
+using NUnit.Framework;
+
+namespace NHibernate.Test.NHSpecificTest.NH3403
+{
+ [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)
+ {
+ cfg.SetProperty(Environment.ConnectionDriver, typeof(TestSqlClientDriver).AssemblyQualifiedName);
+ }
+
+ protected override void OnTearDown()
+ {
+ using (ISession session = OpenSession())
+ using (ITransaction transaction = session.BeginTransaction())
+ {
+ session.Delete("from System.Object");
+
+ session.Flush();
+ transaction.Commit();
+ }
+ }
+
+ protected override void OnSetUp()
+ {
+ base.OnSetUp();
+ using (ISession session = OpenSession())
+ using (ITransaction transaction = session.BeginTransaction())
+ {
+ var e1 = new Entity { Name = "Bob" };
+ session.Save(e1);
+ transaction.Commit();
+ }
+ }
+
+ [Test]
+ public void InsertShouldUseMappedSize()
+ {
+ Driver.ClearCommands();
+
+ using (ISession session = OpenSession())
+ using (ITransaction transaction = session.BeginTransaction())
+ {
+ var e1 = new Entity { Name = "Al", AnsiName = "Al" };
+ session.Save(e1);
+ transaction.Commit();
+ Assert.AreEqual(SqlDbType.NVarChar, Driver.LastCommandParameters.First().SqlDbType);
+ Assert.AreEqual(3, Driver.LastCommandParameters.First().Size);
+ Assert.AreEqual(SqlDbType.VarChar, Driver.LastCommandParameters.Last().SqlDbType);
+ Assert.AreEqual(3, Driver.LastCommandParameters.Last().Size);
+ }
+ }
+
+ [Test]
+ public void InsertWithTooLongValuesShouldThrow()
+ {
+ Driver.ClearCommands();
+
+ using (ISession session = OpenSession())
+ using (ITransaction 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.IsNotNull(sqlEx);
+ 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 (ISession session = OpenSession())
+ using (ITransaction transaction = session.BeginTransaction())
+ {
+ if (property == "Name")
+ {
+ session.Query().Where(x => x.Name == "Bob").ToList();
+ }
+ else
+ {
+ session.Query().Where(x => x.AnsiName == "Bob").ToList();
+ }
+ Assert.AreEqual(3, Driver.LastCommandParameters.First().Size);
+ Assert.AreEqual(expectedDbType, Driver.LastCommandParameters.First().SqlDbType);
+ }
+ }
+
+ [TestCase("Name", SqlDbType.NVarChar)]
+ [TestCase("AnsiName", SqlDbType.VarChar)]
+ public void HqlLikeShouldUseLargerSize(string property, SqlDbType expectedDbType)
+ {
+ Driver.ClearCommands();
+
+ using (ISession session = OpenSession())
+ using (ITransaction transaction = session.BeginTransaction())
+ {
+ session.CreateQuery("from Entity where " + property + " like :name").SetParameter("name", "%Bob%").List();
+
+ Assert.GreaterOrEqual(Driver.LastCommandParameters.First().Size, 5);
+ Assert.AreEqual(expectedDbType, Driver.LastCommandParameters.First().SqlDbType);
+ }
+ }
+
+ [TestCase("Name", SqlDbType.NVarChar)]
+ [TestCase("AnsiName", SqlDbType.VarChar)]
+ public void CriteriaEqualsShouldUseMappedSize(string property, SqlDbType expectedDbType)
+ {
+ Driver.ClearCommands();
+
+ using (ISession session = OpenSession())
+ using (ITransaction transaction = session.BeginTransaction())
+ {
+ Driver.ClearCommands();
+
+ session.CreateCriteria().Add(Restrictions.Eq(property, "Bob"))
+ .List();
+
+ Assert.GreaterOrEqual(Driver.LastCommandParameters.First().Size, 3);
+ Assert.AreEqual(expectedDbType, Driver.LastCommandParameters.First().SqlDbType);
+ }
+ }
+
+ [TestCase("Name", SqlDbType.NVarChar)]
+ [TestCase("AnsiName", SqlDbType.VarChar)]
+ public void CriteriaLikeShouldUseLargerSize(string property, SqlDbType expectedDbType)
+ {
+ Driver.ClearCommands();
+
+ using (ISession session = OpenSession())
+ using (ITransaction transaction = session.BeginTransaction())
+ {
+ session.CreateCriteria().Add(Restrictions.Like(property, "%Bob%"))
+ .List();
+
+ Assert.GreaterOrEqual(Driver.LastCommandParameters.First().Size, 5);
+ Assert.AreEqual(expectedDbType, Driver.LastCommandParameters.First().SqlDbType);
+ }
+ }
+ private TestSqlClientDriver Driver
+ {
+ get { return Sfi.ConnectionProvider.Driver as TestSqlClientDriver; }
+ }
+ }
+}
diff --git a/src/NHibernate.Test/NHSpecificTest/NH3403/Mappings.hbm.xml b/src/NHibernate.Test/NHSpecificTest/NH3403/Mappings.hbm.xml
new file mode 100644
index 00000000000..90e9cd8c42e
--- /dev/null
+++ b/src/NHibernate.Test/NHSpecificTest/NH3403/Mappings.hbm.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/NHibernate.Test/NHSpecificTest/NH3403/TestSqlClientDriver.cs b/src/NHibernate.Test/NHSpecificTest/NH3403/TestSqlClientDriver.cs
new file mode 100644
index 00000000000..c89bae725da
--- /dev/null
+++ b/src/NHibernate.Test/NHSpecificTest/NH3403/TestSqlClientDriver.cs
@@ -0,0 +1,24 @@
+using System.Collections.Generic;
+using System.Data;
+using System.Data.Common;
+using System.Data.SqlClient;
+using System.Linq;
+using NHibernate.Driver;
+
+namespace NHibernate.Test.NHSpecificTest.NH3403
+{
+ 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..4211b2a5e37
--- /dev/null
+++ b/src/NHibernate/AdoNet/IParameterAdjuster.cs
@@ -0,0 +1,11 @@
+using System.Data.Common;
+using NHibernate.SqlTypes;
+using NHibernate.Type;
+
+namespace NHibernate.AdoNet
+{
+ internal interface IParameterAdjuster
+ {
+ 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..e1d84514a6c 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,9 +18,9 @@ namespace NHibernate.Driver
///
public class SqlClientDriver
#if NETFX
- : DriverBase, IEmbeddedBatcherFactoryProvider
+ : DriverBase, IEmbeddedBatcherFactoryProvider, IParameterAdjuster
#else
- : ReflectionBasedDriver, IEmbeddedBatcherFactoryProvider
+ : ReflectionBasedDriver, IEmbeddedBatcherFactoryProvider, IParameterAdjuster
#endif
{
// Since v5.1
@@ -301,5 +302,23 @@ public override bool SupportsMultipleQueries
///
public override DateTime MinDate => new DateTime(1753, 1, 1);
+
+ public 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 505eed0ee0a..a34bb83bba4 100644
--- a/src/NHibernate/Type/AbstractStringType.cs
+++ b/src/NHibernate/Type/AbstractStringType.cs
@@ -1,5 +1,6 @@
using System;
using System.Data.Common;
+using NHibernate.Driver;
using NHibernate.Engine;
using NHibernate.SqlTypes;
@@ -17,6 +18,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;