From 06da1c9ff097d191d9a15b4dfcf2072439eb8cd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= <12201973+fredericDelaporte@users.noreply.github.com> Date: Fri, 21 Sep 2018 21:05:08 +0200 Subject: [PATCH] Fix failing tests for SQL Anywhere And add the Teamcity build --- ShowBuildMenu.bat | 25 ++- .../SQLAnywhere_installation.txt | 14 ++ .../SapSQLAnywhere.cfg.xml | 21 +++ src/NHibernate.DomainModel/ABCProxy.hbm.xml | 6 +- .../Northwind/Mappings/Shipper.hbm.xml | 4 +- .../Ado/GenericBatchingBatcherFixture.cs | 6 +- .../Ado/GenericBatchingBatcherFixture.cs | 6 +- .../ComponentWithUniqueConstraintTests.cs | 8 +- .../Async/Criteria/CriteriaQueryTest.cs | 3 + .../Async/Criteria/EntityProjectionsTest.cs | 10 +- .../Lambda/FunctionsIntegrationFixture.cs | 3 + .../Async/Criteria/ProjectionsTest.cs | 9 + .../Seqidentity/SequenceIdentityFixture.cs | 10 +- src/NHibernate.Test/Async/Hql/HQLFunctions.cs | 8 +- .../NH3269/FixtureNonPublicProperty.cs | 8 +- .../NH3269/FixturePublicProperty.cs | 8 +- .../Dates/DateTimeOffsetFixture.cs | 4 +- .../GH1565/LockEntityWithOuterJoinTest.cs | 2 + .../Async/NHSpecificTest/Logs/LogsFixture.cs | 9 +- .../Async/NHSpecificTest/NH1275/Fixture.cs | 14 +- .../NHSpecificTest/NH1280/NH1280Fixture.cs | 5 +- .../Async/NHSpecificTest/NH1773/Fixture.cs | 2 +- .../Async/NHSpecificTest/NH2302/Fixture.cs | 4 + .../Async/NHSpecificTest/NH3377/Fixture.cs | 5 +- .../NHSpecificTest/SimpleComponentFixture.cs | 7 +- .../SelfReferencingCollectionLoadTest.cs | 8 +- .../SystemTransactionFixture.cs | 15 +- .../Async/TypesTest/DecimalTypeFixture.cs | 3 + .../ComponentWithUniqueConstraintTests.cs | 8 +- .../Criteria/CriteriaQueryTest.cs | 3 + .../Criteria/EntityProjectionsTest.cs | 12 +- .../Lambda/FunctionsIntegrationFixture.cs | 3 + .../Criteria/ProjectionsTest.cs | 9 + ...reSQL83DialectLinqReadonlyCreateScript.sql | Bin 1437758 -> 1437774 bytes .../Seqidentity/SequenceIdentityFixture.cs | 10 +- src/NHibernate.Test/Hql/HQLFunctions.cs | 8 +- .../NH3269/FixtureNonPublicProperty.cs | 8 +- .../NH3269/FixturePublicProperty.cs | 8 +- .../Dates/DateTimeOffsetFixture.cs | 4 +- .../GH1565/LockEntityWithOuterJoinTest.cs | 2 + .../NHSpecificTest/Logs/LogsFixture.cs | 9 +- .../NHSpecificTest/NH1275/Fixture.cs | 14 +- .../NHSpecificTest/NH1280/NH1280Fixture.cs | 5 +- .../NHSpecificTest/NH1773/Fixture.cs | 2 +- .../NHSpecificTest/NH1810/Mappings.hbm.xml | 2 +- .../NHSpecificTest/NH1813/Mappings.hbm.xml | 4 +- .../NH1908ThreadSafety/Fixture.cs | 6 +- .../NHSpecificTest/NH2192/Fixture.cs | 11 +- .../NHSpecificTest/NH2302/Fixture.cs | 4 + .../NHSpecificTest/NH2409/Mappings.hbm.xml | 2 +- .../NHSpecificTest/NH3377/Fixture.cs | 5 +- .../NHSpecificTest/NH3436/Fixture.cs | 5 +- .../Properties/Mappings.hbm.xml | 4 +- .../NHSpecificTest/SimpleComponentFixture.cs | 7 +- .../SqlSimpleSelectBuilderFixture.cs | 4 +- .../SelfReferencingCollectionLoadTest.cs | 8 +- .../SystemTransactionFixture.cs | 15 +- .../SystemTransactionFixtureBase.cs | 6 +- src/NHibernate.Test/TestDialect.cs | 26 +++ .../SapSQLAnywhere17TestDialect.cs | 42 +++++ .../TypesTest/DecimalTypeFixture.cs | 3 + .../TestDatabaseSetup.cs | 56 +++++- .../Dialect/SapSQLAnywhere17Dialect.cs | 172 ++++++++++++------ .../Schema/SapSQLAnywhere17MetaData.cs | 163 +++++++++++++++++ .../Dialect/SybaseSQLAnywhere10Dialect.cs | 23 ++- .../Dialect/SybaseSQLAnywhere11Dialect.cs | 9 +- .../Dialect/SybaseSQLAnywhere12Dialect.cs | 28 ++- src/NHibernate/SqlCommand/SqlStringBuilder.cs | 5 +- teamcity.build | 6 + 69 files changed, 780 insertions(+), 168 deletions(-) create mode 100644 lib/teamcity/SapSQLAnywhere/SQLAnywhere_installation.txt create mode 100644 src/NHibernate.Config.Templates/SapSQLAnywhere.cfg.xml create mode 100644 src/NHibernate.Test/TestDialects/SapSQLAnywhere17TestDialect.cs create mode 100644 src/NHibernate/Dialect/Schema/SapSQLAnywhere17MetaData.cs diff --git a/ShowBuildMenu.bat b/ShowBuildMenu.bat index 29fe9622a7b..38ed467ae77 100644 --- a/ShowBuildMenu.bat +++ b/ShowBuildMenu.bat @@ -57,12 +57,14 @@ echo F. Add a test configuration for Oracle with managed driver. echo G. Add a test configuration for SQL Server Compact. echo H. Add a test configuration for MySql. echo I. Add a test configuration for SAP HANA. +echo J. Add a test configuration for SAP SQL Anywhere. echo. echo X. Exit to main menu. echo. -%BUILDTOOL% prompt ABCDEFGHIX -if errorlevel 9 goto main-menu +%BUILDTOOL% prompt ABCDEFGHIJX +if errorlevel 10 goto main-menu +if errorlevel 9 goto test-setup-anywhere if errorlevel 8 goto test-setup-hana if errorlevel 7 goto test-setup-mysql if errorlevel 6 goto test-setup-sqlserverce @@ -136,6 +138,13 @@ set LIB_FILES= set LIB_FILES2= goto test-setup-generic +:test-setup-anywhere +set CONFIG_NAME=SapSQLAnywhere +set TEST_PLATFORM=AnyCPU +set LIB_FILES= +set LIB_FILES2= +goto test-setup-generic + :test-setup-generic set CFGNAME= set /p CFGNAME=Enter a name for your test configuration or press enter to use default name: @@ -221,12 +230,14 @@ echo I. NHibernate Trunk - Oracle Managed (64-bit) echo J. NHibernate Trunk - SQL Server Compact (32-bit) echo K. NHibernate Trunk - SQL Server Compact (64-bit) echo L. NHibernate Trunk - SQL Server ODBC (32-bit) +echo M. NHibernate Trunk - SAP SQL Anywhere echo. echo X. Exit to main menu. echo. -%BUILDTOOL% prompt ABCDEFGHIJKLX -if errorlevel 12 goto main-menu +%BUILDTOOL% prompt ABCDEFGHIJKLMX +if errorlevel 13 goto main-menu +if errorlevel 12 goto teamcity-anywhere if errorlevel 11 goto teamcity-sqlServerOdbc if errorlevel 10 goto teamcity-sqlServerCe64 if errorlevel 9 goto teamcity-sqlServerCe32 @@ -312,5 +323,11 @@ move "%CURRENT_CONFIGURATION%" "%CURRENT_CONFIGURATION%-backup" 2> nul move "%CURRENT_CONFIGURATION%-backup" "%CURRENT_CONFIGURATION%" 2> nul goto main-menu +:teamcity-anywhere +move "%CURRENT_CONFIGURATION%" "%CURRENT_CONFIGURATION%-backup" 2> nul +%NANT% /f:teamcity.build -D:skip.manual=true -D:CCNetLabel=-1 -D:config.teamcity=sqlanywhere +move "%CURRENT_CONFIGURATION%-backup" "%CURRENT_CONFIGURATION%" 2> nul +goto main-menu + :end popd diff --git a/lib/teamcity/SapSQLAnywhere/SQLAnywhere_installation.txt b/lib/teamcity/SapSQLAnywhere/SQLAnywhere_installation.txt new file mode 100644 index 00000000000..2c93b30c7cb --- /dev/null +++ b/lib/teamcity/SapSQLAnywhere/SQLAnywhere_installation.txt @@ -0,0 +1,14 @@ +Installation steps for SAP SQL Anywhere 17 for NH TeamCity: +1. Download SAP SQL Anywhere 17 from https://www.sap.com/products/sql-anywhere.html. + Please make sure you comply with it's license. + +2. Run the installer +3. Ensure the MSDTC Windows service is enabled. All transaction scope tests, even + those normally not distributed, use it with SAP SQL Anywhere 17 + +The NHibernate.TestDatabaseSetup should normally do on its own the following operations: +1. Create the test database with default options +2. Run the query + set option ansi_update_constraints = 'Off' + (Otherwise some tests will fail.) + diff --git a/src/NHibernate.Config.Templates/SapSQLAnywhere.cfg.xml b/src/NHibernate.Config.Templates/SapSQLAnywhere.cfg.xml new file mode 100644 index 00000000000..9512fef12d6 --- /dev/null +++ b/src/NHibernate.Config.Templates/SapSQLAnywhere.cfg.xml @@ -0,0 +1,21 @@ + + + + + NHibernate.Driver.SapSQLAnywhere17Driver + + + UID=DBA;PWD=sql;Server=localhost;DBN=nhibernate;DBF=c:\nhibernate.db;ASTOP=No;Enlist=false; + + NHibernate.Dialect.SybaseSQLAnywhere12Dialect + true=1;false=0 + + diff --git a/src/NHibernate.DomainModel/ABCProxy.hbm.xml b/src/NHibernate.DomainModel/ABCProxy.hbm.xml index 92e85cb547f..1edb5eff84f 100644 --- a/src/NHibernate.DomainModel/ABCProxy.hbm.xml +++ b/src/NHibernate.DomainModel/ABCProxy.hbm.xml @@ -10,8 +10,8 @@ - - + + @@ -56,4 +56,4 @@ - \ No newline at end of file + diff --git a/src/NHibernate.DomainModel/Northwind/Mappings/Shipper.hbm.xml b/src/NHibernate.DomainModel/Northwind/Mappings/Shipper.hbm.xml index 5b3ff75196d..33dab85ceda 100755 --- a/src/NHibernate.DomainModel/Northwind/Mappings/Shipper.hbm.xml +++ b/src/NHibernate.DomainModel/Northwind/Mappings/Shipper.hbm.xml @@ -14,7 +14,7 @@ - + @@ -24,4 +24,4 @@ - \ No newline at end of file + diff --git a/src/NHibernate.Test/Ado/GenericBatchingBatcherFixture.cs b/src/NHibernate.Test/Ado/GenericBatchingBatcherFixture.cs index 24a13b614f1..b6ce4cd96ba 100644 --- a/src/NHibernate.Test/Ado/GenericBatchingBatcherFixture.cs +++ b/src/NHibernate.Test/Ado/GenericBatchingBatcherFixture.cs @@ -29,7 +29,11 @@ protected override bool AppliesTo(Dialect.Dialect dialect) return !(dialect is FirebirdDialect) && !(dialect is Oracle8iDialect) && !(dialect is MsSqlCeDialect) && - !(dialect is HanaDialectBase); + !(dialect is HanaDialectBase) && + // A workaround exists for SQL Anywhere, see https://stackoverflow.com/a/32860293/1178314 + // It would imply some tweaking in the generic batcher. The same workaround could + // be used for enabling future support. + !(dialect is SybaseSQLAnywhere10Dialect); } [Test] diff --git a/src/NHibernate.Test/Async/Ado/GenericBatchingBatcherFixture.cs b/src/NHibernate.Test/Async/Ado/GenericBatchingBatcherFixture.cs index 5d6ebc191c7..99bbb4098b3 100644 --- a/src/NHibernate.Test/Async/Ado/GenericBatchingBatcherFixture.cs +++ b/src/NHibernate.Test/Async/Ado/GenericBatchingBatcherFixture.cs @@ -42,7 +42,11 @@ protected override bool AppliesTo(Dialect.Dialect dialect) return !(dialect is FirebirdDialect) && !(dialect is Oracle8iDialect) && !(dialect is MsSqlCeDialect) && - !(dialect is HanaDialectBase); + !(dialect is HanaDialectBase) && + // A workaround exists for SQL Anywhere, see https://stackoverflow.com/a/32860293/1178314 + // It would imply some tweaking in the generic batcher. The same workaround could + // be used for enabling future support. + !(dialect is SybaseSQLAnywhere10Dialect); } [Test] diff --git a/src/NHibernate.Test/Async/Component/Basic/ComponentWithUniqueConstraintTests.cs b/src/NHibernate.Test/Async/Component/Basic/ComponentWithUniqueConstraintTests.cs index 67b894915fb..afdf2804f8f 100644 --- a/src/NHibernate.Test/Async/Component/Basic/ComponentWithUniqueConstraintTests.cs +++ b/src/NHibernate.Test/Async/Component/Basic/ComponentWithUniqueConstraintTests.cs @@ -32,8 +32,8 @@ protected override HbmMapping GetMappings() mapper.Component(comp => { - comp.Property(p => p.Name); - comp.Property(p => p.Dob); + comp.Property(p => p.Name, m => m.NotNullable(true)); + comp.Property(p => p.Dob, m => m.NotNullable(true)); comp.Unique(true); // hbm2ddl: Generate a unique constraint in the database }); @@ -94,9 +94,9 @@ public void CannotBePersistedWithNonUniqueValuesAsync() await (session.SaveAsync(e2)); await (session.FlushAsync()); }); - Assert.That(exception.InnerException, Is.AssignableTo()); + Assert.That(exception.InnerException, Is.InstanceOf()); Assert.That(exception.InnerException.Message, - Does.Contain("unique").IgnoreCase.And.Contains("constraint").IgnoreCase + Does.Contain("unique").IgnoreCase .Or.Contains("duplicate entry").IgnoreCase); } } diff --git a/src/NHibernate.Test/Async/Criteria/CriteriaQueryTest.cs b/src/NHibernate.Test/Async/Criteria/CriteriaQueryTest.cs index 4b1614186db..cba892a06aa 100644 --- a/src/NHibernate.Test/Async/Criteria/CriteriaQueryTest.cs +++ b/src/NHibernate.Test/Async/Criteria/CriteriaQueryTest.cs @@ -2719,6 +2719,9 @@ public async Task OrderProjectionTestAsync() [Test] public async Task OrderProjectionAliasedTestAsync() { + if (TestDialect.HasBrokenTypeInferenceOnSelectedParameters) + Assert.Ignore("Current dialect does not support this test"); + using (ISession session = OpenSession()) using (ITransaction t = session.BeginTransaction()) { diff --git a/src/NHibernate.Test/Async/Criteria/EntityProjectionsTest.cs b/src/NHibernate.Test/Async/Criteria/EntityProjectionsTest.cs index 1c2795568b7..5167de586a1 100644 --- a/src/NHibernate.Test/Async/Criteria/EntityProjectionsTest.cs +++ b/src/NHibernate.Test/Async/Criteria/EntityProjectionsTest.cs @@ -8,6 +8,7 @@ //------------------------------------------------------------------------------ +using NHibernate.Cfg; using NHibernate.Cfg.MappingSchema; using NHibernate.Criterion; using NHibernate.Dialect; @@ -29,6 +30,11 @@ public class EntityProjectionsTestAsync : TestCaseMappingByCode private EntityWithCompositeId _entityWithCompositeId; private EntityCustomEntityName _entityWithCustomEntityName; + protected override void Configure(Configuration configuration) + { + configuration.SetProperty(Environment.FormatSql, "false"); + } + protected override HbmMapping GetMappings() { var mapper = new ModelMapper(); @@ -252,12 +258,14 @@ public async Task EntityProjectionAsSelectExpressionForArgumentAliasAsync() [Test] public async Task EntityProjectionLockModeAsync() { + // For this test to succeed with SQL Anywhere, ansi_update_constraints must be off. + // In I-SQL: set option ansi_update_constraints = 'Off' if (Dialect is Oracle8iDialect) Assert.Ignore("Oracle is not supported due to #1352 bug (NH-3902)"); var upgradeHint = Dialect.ForUpdateString; if(string.IsNullOrEmpty(upgradeHint)) - upgradeHint = this.Dialect.AppendLockHint(LockMode.Upgrade, string.Empty); + upgradeHint = Dialect.AppendLockHint(LockMode.Upgrade, string.Empty); if (string.IsNullOrEmpty(upgradeHint)) { Assert.Ignore($"Upgrade hint is not supported by dialect {Dialect.GetType().Name}"); diff --git a/src/NHibernate.Test/Async/Criteria/Lambda/FunctionsIntegrationFixture.cs b/src/NHibernate.Test/Async/Criteria/Lambda/FunctionsIntegrationFixture.cs index 51b23d8f530..59731c3ed08 100644 --- a/src/NHibernate.Test/Async/Criteria/Lambda/FunctionsIntegrationFixture.cs +++ b/src/NHibernate.Test/Async/Criteria/Lambda/FunctionsIntegrationFixture.cs @@ -146,6 +146,9 @@ public async Task FunctionsToLowerToUpperAsync() [Test] public async Task ConcatAsync() { + if (TestDialect.HasBrokenTypeInferenceOnSelectedParameters) + Assert.Ignore("Current dialect does not support this test"); + using (var s = OpenSession()) using (s.BeginTransaction()) { diff --git a/src/NHibernate.Test/Async/Criteria/ProjectionsTest.cs b/src/NHibernate.Test/Async/Criteria/ProjectionsTest.cs index beaa407c15c..c885d804f96 100644 --- a/src/NHibernate.Test/Async/Criteria/ProjectionsTest.cs +++ b/src/NHibernate.Test/Async/Criteria/ProjectionsTest.cs @@ -69,6 +69,9 @@ protected override void OnTearDown() [Test] public async Task UsingSqlFunctions_ConcatAsync() { + if (TestDialect.HasBrokenTypeInferenceOnSelectedParameters) + Assert.Ignore("Current dialect does not support this test"); + using (ISession session = Sfi.OpenSession()) { string result = await (session.CreateCriteria(typeof(Student)) @@ -90,6 +93,9 @@ public async Task UsingSqlFunctions_Concat_WithCastAsync() { Assert.Ignore("Not supported by the active dialect:{0}.", Dialect); } + if (TestDialect.HasBrokenTypeInferenceOnSelectedParameters) + Assert.Ignore("Current dialect does not support this test"); + using (ISession session = Sfi.OpenSession()) { string result = await (session.CreateCriteria(typeof(Student)) @@ -176,6 +182,9 @@ public async Task CanUseParametersWithProjectionsAsync() [Test] public async Task UsingConditionalsAsync() { + if (TestDialect.HasBrokenTypeInferenceOnSelectedParameters) + Assert.Ignore("Current dialect does not support this test"); + using (ISession session = Sfi.OpenSession()) { string result = await (session.CreateCriteria(typeof(Student)) diff --git a/src/NHibernate.Test/Async/Generatedkeys/Seqidentity/SequenceIdentityFixture.cs b/src/NHibernate.Test/Async/Generatedkeys/Seqidentity/SequenceIdentityFixture.cs index c308fd90f8b..215ff2dfba0 100644 --- a/src/NHibernate.Test/Async/Generatedkeys/Seqidentity/SequenceIdentityFixture.cs +++ b/src/NHibernate.Test/Async/Generatedkeys/Seqidentity/SequenceIdentityFixture.cs @@ -29,9 +29,13 @@ protected override string MappingsAssembly protected override bool AppliesTo(Dialect.Dialect dialect) { - return dialect.SupportsSequences && - !(dialect is Dialect.MsSql2012Dialect) && - !(dialect is Dialect.HanaDialectBase); // SAP HANA does not support a syntax allowing to return the inserted id as an output parameter or a return value + return + dialect.SupportsSequences && + !(dialect is Dialect.MsSql2012Dialect) && + // SAP HANA does not support a syntax allowing to return the inserted id as an output parameter or a return value + !(dialect is Dialect.HanaDialectBase) && + // SQL Anywhere does not support a syntax allowing to return the inserted id as an output parameter or a return value + !(dialect is Dialect.SybaseSQLAnywhere10Dialect); } [Test] diff --git a/src/NHibernate.Test/Async/Hql/HQLFunctions.cs b/src/NHibernate.Test/Async/Hql/HQLFunctions.cs index e09e5a426ba..a67a8ad518a 100644 --- a/src/NHibernate.Test/Async/Hql/HQLFunctions.cs +++ b/src/NHibernate.Test/Async/Hql/HQLFunctions.cs @@ -1195,6 +1195,7 @@ public async Task StrAsync() hql = "from Animal a where str(123) = '123'"; Animal result = (Animal) await (s.CreateQuery(hql).UniqueResultAsync()); + Assert.That(result, Is.Not.Null); Assert.AreEqual("abcdef", result.Description); } } @@ -1375,7 +1376,8 @@ public async Task BitwiseNotAsync() using (var s = OpenSession()) using (var tx = s.BeginTransaction()) { - // ! takes not precedence over & at least with some dialects (maybe all). + // The bitwise "not" should take precedence over the bitwise "and", but when it is implemented as a + // function, its argument is the whole following statement, unless parenthesis are used to prevent this. var query = s.CreateQuery("from MaterialResource m where ((!m.State) & 3) = 3"); var result = await (query.ListAsync()); Assert.That(result, Has.Count.EqualTo(1), "((!m.State) & 3) = 3"); @@ -1415,6 +1417,10 @@ public async Task BitwiseIsThreadsafeAsync() new Tuple ("select count(*) from MaterialResource m where ((!m.State) & 3) = 2", 1), new Tuple ("select count(*) from MaterialResource m where ((!m.State) & 3) = 1", 1) }; + + if (TestDialect.MaxNumberOfConnections < queries.Count) + Assert.Ignore("Current database has a too low connection count limit."); + // Do not use a ManualResetEventSlim, it does not support async and exhausts the task thread pool in the // async counterparts of this test. SemaphoreSlim has the async support and release the thread when waiting. var semaphore = new SemaphoreSlim(0); diff --git a/src/NHibernate.Test/Async/MappingByCode/IntegrationTests/NH3269/FixtureNonPublicProperty.cs b/src/NHibernate.Test/Async/MappingByCode/IntegrationTests/NH3269/FixtureNonPublicProperty.cs index 904bec09f94..2355aa97c60 100644 --- a/src/NHibernate.Test/Async/MappingByCode/IntegrationTests/NH3269/FixtureNonPublicProperty.cs +++ b/src/NHibernate.Test/Async/MappingByCode/IntegrationTests/NH3269/FixtureNonPublicProperty.cs @@ -53,7 +53,13 @@ protected override HbmMapping GetMappings() mapper.Class(rc => { rc.Id(x => x.Id, m => m.Generator(Generators.Guid)); - rc.Property("Name", m => m.UniqueKey("Inherited1_UX_Name")); + rc.Property( + "Name", + m => + { + m.UniqueKey("Inherited1_UX_Name"); + m.NotNullable(true); + }); }); mapper.Class(rc => diff --git a/src/NHibernate.Test/Async/MappingByCode/IntegrationTests/NH3269/FixturePublicProperty.cs b/src/NHibernate.Test/Async/MappingByCode/IntegrationTests/NH3269/FixturePublicProperty.cs index 9626a05a568..17af28a7bae 100644 --- a/src/NHibernate.Test/Async/MappingByCode/IntegrationTests/NH3269/FixturePublicProperty.cs +++ b/src/NHibernate.Test/Async/MappingByCode/IntegrationTests/NH3269/FixturePublicProperty.cs @@ -53,7 +53,13 @@ protected override HbmMapping GetMappings() mapper.Class(rc => { rc.Id(x => x.Id, m => m.Generator(Generators.Guid)); - rc.Property(x => x.Name, m => m.UniqueKey("Inherited1_UX_Name")); + rc.Property( + x => x.Name, + m => + { + m.UniqueKey("Inherited1_UX_Name"); + m.NotNullable(true); + }); }); mapper.Class(rc => diff --git a/src/NHibernate.Test/Async/NHSpecificTest/Dates/DateTimeOffsetFixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/Dates/DateTimeOffsetFixture.cs index b25f8368120..d9e4bfce8e4 100644 --- a/src/NHibernate.Test/Async/NHSpecificTest/Dates/DateTimeOffsetFixture.cs +++ b/src/NHibernate.Test/Async/NHSpecificTest/Dates/DateTimeOffsetFixture.cs @@ -45,10 +45,12 @@ protected override bool AppliesTo(Engine.ISessionFactoryImplementor factory) return DbType.DateTimeOffset; } + protected virtual long DateAccuracyInTicks => Dialect.TimestampResolutionInTicks; + [Test] public async Task SavingAndRetrievingTestAsync() { - DateTimeOffset NowOS = DateTimeOffset.Now; + var NowOS = DateTimeOffsetType.Round(DateTimeOffset.Now, DateAccuracyInTicks); AllDates dates = new AllDates { Sql_datetimeoffset = NowOS }; diff --git a/src/NHibernate.Test/Async/NHSpecificTest/GH1565/LockEntityWithOuterJoinTest.cs b/src/NHibernate.Test/Async/NHSpecificTest/GH1565/LockEntityWithOuterJoinTest.cs index 48d02c3df07..d141e81e1fa 100644 --- a/src/NHibernate.Test/Async/NHSpecificTest/GH1565/LockEntityWithOuterJoinTest.cs +++ b/src/NHibernate.Test/Async/NHSpecificTest/GH1565/LockEntityWithOuterJoinTest.cs @@ -19,6 +19,8 @@ public class LockEntityWithOuterJoinTestAsync : BugTestCase [Test] public async Task LockWithOuterJoin_ShouldBePossibleAsync() { + // For this test to succeed with SQL Anywhere, ansi_update_constraints must be off. + // In I-SQL: set option ansi_update_constraints = 'Off' using (var session = OpenSession()) { using (var transaction = session.BeginTransaction()) diff --git a/src/NHibernate.Test/Async/NHSpecificTest/Logs/LogsFixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/Logs/LogsFixture.cs index 50daf503859..b528d151b8f 100644 --- a/src/NHibernate.Test/Async/NHSpecificTest/Logs/LogsFixture.cs +++ b/src/NHibernate.Test/Async/NHSpecificTest/Logs/LogsFixture.cs @@ -159,12 +159,15 @@ public async Task WillGetSessionIdFromSessionLogsConcurrentAsync() var semaphore = new SemaphoreSlim(0); var failures = new ConcurrentBag(); var sessionIds = new ConcurrentDictionary(); + var threadCount = 10; + if (threadCount > TestDialect.MaxNumberOfConnections) + threadCount = TestDialect.MaxNumberOfConnections.Value; using (var spy = new TextLogSpy("NHibernate.SQL", "%message | SessionId: %property{sessionId}")) { await (Task.WhenAll( - Enumerable.Range(1, 12 - 1).Select(async i => + Enumerable.Range(1, threadCount + 2 - 1).Select(async i => { - if (i > 10) + if (i > threadCount) { // Give some time to threads for reaching the wait, having all of them ready to do most of their job concurrently. await (Task.Delay(100)); @@ -198,7 +201,7 @@ public async Task WillGetSessionIdFromSessionLogsConcurrentAsync() Assert.That(failures, Is.Empty, $"{failures.Count} task(s) failed."); var loggingEvent = spy.GetWholeLog(); - for (var i = 1; i < 11; i++) + for (var i = 1; i < threadCount + 1; i++) for (var j = 0; j < 10; j++) { var sessionId = sessionIds[i]; diff --git a/src/NHibernate.Test/Async/NHSpecificTest/NH1275/Fixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/NH1275/Fixture.cs index d1562082b65..57ecaea5761 100644 --- a/src/NHibernate.Test/Async/NHSpecificTest/NH1275/Fixture.cs +++ b/src/NHibernate.Test/Async/NHSpecificTest/NH1275/Fixture.cs @@ -8,7 +8,10 @@ //------------------------------------------------------------------------------ +using System; +using NHibernate.Cfg; using NUnit.Framework; +using Environment = NHibernate.Cfg.Environment; namespace NHibernate.Test.NHSpecificTest.NH1275 { @@ -24,6 +27,11 @@ protected override bool AppliesTo(Dialect.Dialect dialect) return !string.IsNullOrEmpty(dialect.ForUpdateString); } + protected override void Configure(Configuration configuration) + { + configuration.SetProperty(Environment.FormatSql, "false"); + } + [Test] public async Task RetrievingAsync() { @@ -43,14 +51,14 @@ public async Task RetrievingAsync() { await (s.GetAsync(savedId, LockMode.Upgrade)); string sql = sqlLogSpy.Appender.GetEvents()[0].RenderedMessage; - Assert.Less(0, sql.IndexOf(Dialect.ForUpdateString)); + Assert.That(sql.IndexOf(Dialect.ForUpdateString, StringComparison.Ordinal), Is.GreaterThan(0)); } using (SqlLogSpy sqlLogSpy = new SqlLogSpy()) { await (s.CreateQuery("from A a where a.Id= :pid").SetLockMode("a", LockMode.Upgrade).SetParameter("pid", savedId). UniqueResultAsync()); string sql = sqlLogSpy.Appender.GetEvents()[0].RenderedMessage; - Assert.Less(0, sql.IndexOf(Dialect.ForUpdateString)); + Assert.That(sql.IndexOf(Dialect.ForUpdateString, StringComparison.Ordinal), Is.GreaterThan(0)); } await (t.CommitAsync()); } @@ -83,7 +91,7 @@ public async Task LokingAsync() { await (s.LockAsync(a, LockMode.Upgrade)); string sql = sqlLogSpy.Appender.GetEvents()[0].RenderedMessage; - Assert.Less(0, sql.IndexOf(Dialect.ForUpdateString)); + Assert.That(sql.IndexOf(Dialect.ForUpdateString, StringComparison.Ordinal), Is.GreaterThan(0)); } await (t.CommitAsync()); } diff --git a/src/NHibernate.Test/Async/NHSpecificTest/NH1280/NH1280Fixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/NH1280/NH1280Fixture.cs index 75d0f5b3ffa..cf251c98ac9 100644 --- a/src/NHibernate.Test/Async/NHSpecificTest/NH1280/NH1280Fixture.cs +++ b/src/NHibernate.Test/Async/NHSpecificTest/NH1280/NH1280Fixture.cs @@ -52,6 +52,9 @@ protected override void OnTearDown() [Test] public async Task HavingUsingSqlFunctions_ConcatAsync() { + if (TestDialect.HasBrokenTypeInferenceOnSelectedParameters) + Assert.Ignore("Current dialect does not support this test"); + using (ISession s = OpenSession()) using (ITransaction tx = s.BeginTransaction()) { @@ -272,4 +275,4 @@ public async Task HavingOnNotExpressionCountAsync() } } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/Async/NHSpecificTest/NH1773/Fixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/NH1773/Fixture.cs index 13938b1becc..8edfd512261 100644 --- a/src/NHibernate.Test/Async/NHSpecificTest/NH1773/Fixture.cs +++ b/src/NHibernate.Test/Async/NHSpecificTest/NH1773/Fixture.cs @@ -34,7 +34,7 @@ public async Task CustomHQLFunctionsShouldBeRecognizedByTheParserAsync() protected override bool AppliesTo(Dialect.Dialect dialect) { - // Fails with MS SQL Ce due to the query generating a duplicated column alias name, which this database does not support. + // Fails with MS SQL Ce & SQL Anywhere due to the query generating a duplicated column alias name, which these databases do not support. return TestDialect.SupportsDuplicatedColumnAliases; } diff --git a/src/NHibernate.Test/Async/NHSpecificTest/NH2302/Fixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/NH2302/Fixture.cs index df16c52023c..009e8c61141 100644 --- a/src/NHibernate.Test/Async/NHSpecificTest/NH2302/Fixture.cs +++ b/src/NHibernate.Test/Async/NHSpecificTest/NH2302/Fixture.cs @@ -12,6 +12,7 @@ using NHibernate.Dialect; using NHibernate.Driver; using NHibernate.Mapping; +using NHibernate.SqlTypes; using NUnit.Framework; namespace NHibernate.Test.NHSpecificTest.NH2302 @@ -52,6 +53,9 @@ public async Task StringHugeLengthAsync() if (Sfi.ConnectionProvider.Driver is OdbcDriver || Dialect is MsSqlCeDialect) Assert.Ignore("NH-4065, not fixed for Odbc and MsSqlCe"); + if (Dialect.GetTypeName(SqlTypeFactory.GetString(10000)) != Dialect.GetLongestTypeName(DbType.String)) + Assert.Ignore("Current dialect does support limited strings of 10 000 characters"); + int id; // buildup a string the exceed the mapping string str = GetFixedLengthString12000(); diff --git a/src/NHibernate.Test/Async/NHSpecificTest/NH3377/Fixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/NH3377/Fixture.cs index 43033fbc7d0..e506e620b49 100644 --- a/src/NHibernate.Test/Async/NHSpecificTest/NH3377/Fixture.cs +++ b/src/NHibernate.Test/Async/NHSpecificTest/NH3377/Fixture.cs @@ -101,7 +101,8 @@ public async Task ShouldBeAbleToCallConvertToInt32FromStringParameterInMaxAsync( [Test] public async Task ShouldBeAbleToCallConvertToBooleanFromStringParameterAsync() { - if (Dialect is SQLiteDialect || Dialect is FirebirdDialect || Dialect is MySQLDialect || Dialect is Oracle8iDialect) + if (Dialect is SQLiteDialect || Dialect is FirebirdDialect || Dialect is MySQLDialect || + Dialect is Oracle8iDialect || Dialect is SapSQLAnywhere17Dialect) Assert.Ignore(Dialect.GetType() + " is not supported"); //NH-3720 @@ -130,4 +131,4 @@ public async Task ShouldBeAbleToCallConvertToDateTimeFromStringParameterAsync() } } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/Async/NHSpecificTest/SimpleComponentFixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/SimpleComponentFixture.cs index 1d23dc8e69f..ca883765fe2 100644 --- a/src/NHibernate.Test/Async/NHSpecificTest/SimpleComponentFixture.cs +++ b/src/NHibernate.Test/Async/NHSpecificTest/SimpleComponentFixture.cs @@ -21,6 +21,11 @@ public class SimpleComponentFixtureAsync : TestCase { private DateTime testDateTime = new DateTime(2003, 8, 16); + protected override bool AppliesTo(Dialect.Dialect dialect) + { + return TestDialect.SupportsSquareBracketInIdentifiers; + } + protected override string[] Mappings { get { return new string[] {"NHSpecific.SimpleComponent.hbm.xml"}; } @@ -75,4 +80,4 @@ public async Task TestLoadAsync() } } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/Async/SqlTest/Query/SelfReferencingCollectionLoadTest.cs b/src/NHibernate.Test/Async/SqlTest/Query/SelfReferencingCollectionLoadTest.cs index cd4a83692a6..5ce7711e066 100644 --- a/src/NHibernate.Test/Async/SqlTest/Query/SelfReferencingCollectionLoadTest.cs +++ b/src/NHibernate.Test/Async/SqlTest/Query/SelfReferencingCollectionLoadTest.cs @@ -30,9 +30,9 @@ protected override string MappingsAssembly protected override bool AppliesTo(Dialect.Dialect dialect) { - // Hacky mapping causing the primary key to reference itself as a foreign key, which is not supported by MySQL. It - // fails when trying to insert data by considering the foreign key violated. - return !(Dialect is MySQLDialect); + // Hacky mapping causing the primary key to reference itself as a foreign key, which is not supported by + // some databases. It fails when trying to insert data by considering the foreign key violated. + return !(Dialect is MySQLDialect || Dialect is SapSQLAnywhere17Dialect); } [Test] @@ -64,4 +64,4 @@ public async Task LoadCollectionAsync() } } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/Async/SystemTransactions/SystemTransactionFixture.cs b/src/NHibernate.Test/Async/SystemTransactions/SystemTransactionFixture.cs index ff3ea86958f..c5089580535 100644 --- a/src/NHibernate.Test/Async/SystemTransactions/SystemTransactionFixture.cs +++ b/src/NHibernate.Test/Async/SystemTransactions/SystemTransactionFixture.cs @@ -192,12 +192,12 @@ public async Task CanUseSessionWithManyScopesAsync(bool explicitFlush) // being concurrently disposed of. See https://github.com/nhibernate/nhibernate-core/pull/1505 for more details. if (Sfi.ConnectionProvider.Driver is OdbcDriver) Assert.Ignore("ODBC sometimes fails on second scope by checking the previous transaction status, which may yield an object disposed exception"); - // SAP HANA .Net provider always causes system transactions to be distributed, causing them to complete - // on concurrent threads. This creates race conditions when chaining scopes, the subsequent scope usage + // SAP HANA & SQL Anywhere .Net providers always cause system transactions to be distributed, causing them to + // complete on concurrent threads. This creates race conditions when chaining scopes, the subsequent scope usage // finding the connection still enlisted in the previous transaction, its complete being still not finished // on its own thread. - if (Sfi.ConnectionProvider.Driver is HanaDriverBase) - Assert.Ignore("SAP HANA scope handling causes concurrency issues preventing chaining scope usages."); + if (Sfi.ConnectionProvider.Driver is HanaDriverBase || Sfi.ConnectionProvider.Driver is SapSQLAnywhere17Driver) + Assert.Ignore("SAP HANA and SQL Anywhere scope handling causes concurrency issues preventing chaining scope usages."); using (var s = WithOptions().ConnectionReleaseMode(ConnectionReleaseMode.OnClose).OpenSession()) { @@ -265,6 +265,13 @@ public async Task CanUseSessionWithManyScopesAsync(bool explicitFlush) public async Task CanUseSessionOutsideOfScopeAfterScopeAsync(bool explicitFlush) { IgnoreIfUnsupported(explicitFlush); + // SAP SQL Anywhere .Net provider always causes system transactions to be distributed, causing them to + // complete on concurrent threads. This creates race conditions when chaining session usage after a scope, + // the subsequent usage finding the connection still enlisted in the previous transaction, its complete + // being still not finished on its own thread. + if (Sfi.ConnectionProvider.Driver is SapSQLAnywhere17Driver) + Assert.Ignore("SAP SQL Anywhere scope handling causes concurrency issues preventing chaining session usages."); + using (var s = WithOptions().ConnectionReleaseMode(ConnectionReleaseMode.OnClose).OpenSession()) { using (var tx = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) diff --git a/src/NHibernate.Test/Async/TypesTest/DecimalTypeFixture.cs b/src/NHibernate.Test/Async/TypesTest/DecimalTypeFixture.cs index 189d89d9e09..c4ab2a0c6f0 100644 --- a/src/NHibernate.Test/Async/TypesTest/DecimalTypeFixture.cs +++ b/src/NHibernate.Test/Async/TypesTest/DecimalTypeFixture.cs @@ -95,6 +95,9 @@ public async Task ReadWriteAsync() [Test] public async Task HighScaleParameterSelectAsync() { + if (TestDialect.HasBrokenTypeInferenceOnSelectedParameters) + Assert.Ignore("Current dialect does not support this test"); + using (var s = OpenSession()) using (var t = s.BeginTransaction()) { diff --git a/src/NHibernate.Test/Component/Basic/ComponentWithUniqueConstraintTests.cs b/src/NHibernate.Test/Component/Basic/ComponentWithUniqueConstraintTests.cs index b4b3a5d0e5f..8a75a298e1d 100644 --- a/src/NHibernate.Test/Component/Basic/ComponentWithUniqueConstraintTests.cs +++ b/src/NHibernate.Test/Component/Basic/ComponentWithUniqueConstraintTests.cs @@ -21,8 +21,8 @@ protected override HbmMapping GetMappings() mapper.Component(comp => { - comp.Property(p => p.Name); - comp.Property(p => p.Dob); + comp.Property(p => p.Name, m => m.NotNullable(true)); + comp.Property(p => p.Dob, m => m.NotNullable(true)); comp.Unique(true); // hbm2ddl: Generate a unique constraint in the database }); @@ -83,9 +83,9 @@ public void CannotBePersistedWithNonUniqueValues() session.Save(e2); session.Flush(); }); - Assert.That(exception.InnerException, Is.AssignableTo()); + Assert.That(exception.InnerException, Is.InstanceOf()); Assert.That(exception.InnerException.Message, - Does.Contain("unique").IgnoreCase.And.Contains("constraint").IgnoreCase + Does.Contain("unique").IgnoreCase .Or.Contains("duplicate entry").IgnoreCase); } } diff --git a/src/NHibernate.Test/Criteria/CriteriaQueryTest.cs b/src/NHibernate.Test/Criteria/CriteriaQueryTest.cs index 79d925683a1..367a89a3f6e 100644 --- a/src/NHibernate.Test/Criteria/CriteriaQueryTest.cs +++ b/src/NHibernate.Test/Criteria/CriteriaQueryTest.cs @@ -2734,6 +2734,9 @@ public void OrderProjectionTest() [Test] public void OrderProjectionAliasedTest() { + if (TestDialect.HasBrokenTypeInferenceOnSelectedParameters) + Assert.Ignore("Current dialect does not support this test"); + using (ISession session = OpenSession()) using (ITransaction t = session.BeginTransaction()) { diff --git a/src/NHibernate.Test/Criteria/EntityProjectionsTest.cs b/src/NHibernate.Test/Criteria/EntityProjectionsTest.cs index 3b69d31017b..98c67dcf547 100644 --- a/src/NHibernate.Test/Criteria/EntityProjectionsTest.cs +++ b/src/NHibernate.Test/Criteria/EntityProjectionsTest.cs @@ -1,4 +1,5 @@ -using NHibernate.Cfg.MappingSchema; +using NHibernate.Cfg; +using NHibernate.Cfg.MappingSchema; using NHibernate.Criterion; using NHibernate.Dialect; using NHibernate.Mapping.ByCode; @@ -18,6 +19,11 @@ public class EntityProjectionsTest : TestCaseMappingByCode private EntityWithCompositeId _entityWithCompositeId; private EntityCustomEntityName _entityWithCustomEntityName; + protected override void Configure(Configuration configuration) + { + configuration.SetProperty(Environment.FormatSql, "false"); + } + protected override HbmMapping GetMappings() { var mapper = new ModelMapper(); @@ -241,12 +247,14 @@ public void EntityProjectionAsSelectExpressionForArgumentAlias() [Test] public void EntityProjectionLockMode() { + // For this test to succeed with SQL Anywhere, ansi_update_constraints must be off. + // In I-SQL: set option ansi_update_constraints = 'Off' if (Dialect is Oracle8iDialect) Assert.Ignore("Oracle is not supported due to #1352 bug (NH-3902)"); var upgradeHint = Dialect.ForUpdateString; if(string.IsNullOrEmpty(upgradeHint)) - upgradeHint = this.Dialect.AppendLockHint(LockMode.Upgrade, string.Empty); + upgradeHint = Dialect.AppendLockHint(LockMode.Upgrade, string.Empty); if (string.IsNullOrEmpty(upgradeHint)) { Assert.Ignore($"Upgrade hint is not supported by dialect {Dialect.GetType().Name}"); diff --git a/src/NHibernate.Test/Criteria/Lambda/FunctionsIntegrationFixture.cs b/src/NHibernate.Test/Criteria/Lambda/FunctionsIntegrationFixture.cs index efda7f08edd..0ad2fe3ed96 100644 --- a/src/NHibernate.Test/Criteria/Lambda/FunctionsIntegrationFixture.cs +++ b/src/NHibernate.Test/Criteria/Lambda/FunctionsIntegrationFixture.cs @@ -135,6 +135,9 @@ public void FunctionsToLowerToUpper() [Test] public void Concat() { + if (TestDialect.HasBrokenTypeInferenceOnSelectedParameters) + Assert.Ignore("Current dialect does not support this test"); + using (var s = OpenSession()) using (s.BeginTransaction()) { diff --git a/src/NHibernate.Test/Criteria/ProjectionsTest.cs b/src/NHibernate.Test/Criteria/ProjectionsTest.cs index 05b9b82a845..2d855f60bc2 100644 --- a/src/NHibernate.Test/Criteria/ProjectionsTest.cs +++ b/src/NHibernate.Test/Criteria/ProjectionsTest.cs @@ -58,6 +58,9 @@ protected override void OnTearDown() [Test] public void UsingSqlFunctions_Concat() { + if (TestDialect.HasBrokenTypeInferenceOnSelectedParameters) + Assert.Ignore("Current dialect does not support this test"); + using (ISession session = Sfi.OpenSession()) { string result = session.CreateCriteria(typeof(Student)) @@ -79,6 +82,9 @@ public void UsingSqlFunctions_Concat_WithCast() { Assert.Ignore("Not supported by the active dialect:{0}.", Dialect); } + if (TestDialect.HasBrokenTypeInferenceOnSelectedParameters) + Assert.Ignore("Current dialect does not support this test"); + using (ISession session = Sfi.OpenSession()) { string result = session.CreateCriteria(typeof(Student)) @@ -165,6 +171,9 @@ public void CanUseParametersWithProjections() [Test] public void UsingConditionals() { + if (TestDialect.HasBrokenTypeInferenceOnSelectedParameters) + Assert.Ignore("Current dialect does not support this test"); + using (ISession session = Sfi.OpenSession()) { string result = session.CreateCriteria(typeof(Student)) diff --git a/src/NHibernate.Test/DbScripts/PostgreSQL83DialectLinqReadonlyCreateScript.sql b/src/NHibernate.Test/DbScripts/PostgreSQL83DialectLinqReadonlyCreateScript.sql index b84c6fc1bdb7465815967364562e2ac82dbcd588..fa32b85f55bb7ea6bf69ec71cb4826381a56203a 100644 GIT binary patch delta 208 zcmdn@ApG2e@C_O1qDl-w455r~<9m>Gy!fS47C*?^cG zh&h0m6NtHhm>Y8ftUk` zIf0l9h`E872Z(urm=B2gfmi^D1%X%yh=sS;ri(~##s{J=@V} ("select count(*) from MaterialResource m where ((!m.State) & 3) = 2", 1), new Tuple ("select count(*) from MaterialResource m where ((!m.State) & 3) = 1", 1) }; + + if (TestDialect.MaxNumberOfConnections < queries.Count) + Assert.Ignore("Current database has a too low connection count limit."); + // Do not use a ManualResetEventSlim, it does not support async and exhausts the task thread pool in the // async counterparts of this test. SemaphoreSlim has the async support and release the thread when waiting. var semaphore = new SemaphoreSlim(0); diff --git a/src/NHibernate.Test/MappingByCode/IntegrationTests/NH3269/FixtureNonPublicProperty.cs b/src/NHibernate.Test/MappingByCode/IntegrationTests/NH3269/FixtureNonPublicProperty.cs index 8371a893405..a96753a016a 100644 --- a/src/NHibernate.Test/MappingByCode/IntegrationTests/NH3269/FixtureNonPublicProperty.cs +++ b/src/NHibernate.Test/MappingByCode/IntegrationTests/NH3269/FixtureNonPublicProperty.cs @@ -42,7 +42,13 @@ protected override HbmMapping GetMappings() mapper.Class(rc => { rc.Id(x => x.Id, m => m.Generator(Generators.Guid)); - rc.Property("Name", m => m.UniqueKey("Inherited1_UX_Name")); + rc.Property( + "Name", + m => + { + m.UniqueKey("Inherited1_UX_Name"); + m.NotNullable(true); + }); }); mapper.Class(rc => diff --git a/src/NHibernate.Test/MappingByCode/IntegrationTests/NH3269/FixturePublicProperty.cs b/src/NHibernate.Test/MappingByCode/IntegrationTests/NH3269/FixturePublicProperty.cs index fe786ef2df4..a6ac9047b38 100644 --- a/src/NHibernate.Test/MappingByCode/IntegrationTests/NH3269/FixturePublicProperty.cs +++ b/src/NHibernate.Test/MappingByCode/IntegrationTests/NH3269/FixturePublicProperty.cs @@ -42,7 +42,13 @@ protected override HbmMapping GetMappings() mapper.Class(rc => { rc.Id(x => x.Id, m => m.Generator(Generators.Guid)); - rc.Property(x => x.Name, m => m.UniqueKey("Inherited1_UX_Name")); + rc.Property( + x => x.Name, + m => + { + m.UniqueKey("Inherited1_UX_Name"); + m.NotNullable(true); + }); }); mapper.Class(rc => diff --git a/src/NHibernate.Test/NHSpecificTest/Dates/DateTimeOffsetFixture.cs b/src/NHibernate.Test/NHSpecificTest/Dates/DateTimeOffsetFixture.cs index 0066ef76185..ead59eac5fb 100644 --- a/src/NHibernate.Test/NHSpecificTest/Dates/DateTimeOffsetFixture.cs +++ b/src/NHibernate.Test/NHSpecificTest/Dates/DateTimeOffsetFixture.cs @@ -33,10 +33,12 @@ protected override bool AppliesTo(Engine.ISessionFactoryImplementor factory) return DbType.DateTimeOffset; } + protected virtual long DateAccuracyInTicks => Dialect.TimestampResolutionInTicks; + [Test] public void SavingAndRetrievingTest() { - DateTimeOffset NowOS = DateTimeOffset.Now; + var NowOS = DateTimeOffsetType.Round(DateTimeOffset.Now, DateAccuracyInTicks); AllDates dates = new AllDates { Sql_datetimeoffset = NowOS }; diff --git a/src/NHibernate.Test/NHSpecificTest/GH1565/LockEntityWithOuterJoinTest.cs b/src/NHibernate.Test/NHSpecificTest/GH1565/LockEntityWithOuterJoinTest.cs index 4e957f99ba6..b3f15ae9666 100644 --- a/src/NHibernate.Test/NHSpecificTest/GH1565/LockEntityWithOuterJoinTest.cs +++ b/src/NHibernate.Test/NHSpecificTest/GH1565/LockEntityWithOuterJoinTest.cs @@ -8,6 +8,8 @@ public class LockEntityWithOuterJoinTest : BugTestCase [Test] public void LockWithOuterJoin_ShouldBePossible() { + // For this test to succeed with SQL Anywhere, ansi_update_constraints must be off. + // In I-SQL: set option ansi_update_constraints = 'Off' using (var session = OpenSession()) { using (var transaction = session.BeginTransaction()) diff --git a/src/NHibernate.Test/NHSpecificTest/Logs/LogsFixture.cs b/src/NHibernate.Test/NHSpecificTest/Logs/LogsFixture.cs index 92b57a62ad9..8f1dd05fc99 100644 --- a/src/NHibernate.Test/NHSpecificTest/Logs/LogsFixture.cs +++ b/src/NHibernate.Test/NHSpecificTest/Logs/LogsFixture.cs @@ -148,13 +148,16 @@ public void WillGetSessionIdFromSessionLogsConcurrent() var semaphore = new SemaphoreSlim(0); var failures = new ConcurrentBag(); var sessionIds = new ConcurrentDictionary(); + var threadCount = 10; + if (threadCount > TestDialect.MaxNumberOfConnections) + threadCount = TestDialect.MaxNumberOfConnections.Value; using (var spy = new TextLogSpy("NHibernate.SQL", "%message | SessionId: %property{sessionId}")) { Parallel.For( - 1, 12, + 1, threadCount + 2, i => { - if (i > 10) + if (i > threadCount) { // Give some time to threads for reaching the wait, having all of them ready to do most of their job concurrently. Thread.Sleep(100); @@ -188,7 +191,7 @@ public void WillGetSessionIdFromSessionLogsConcurrent() Assert.That(failures, Is.Empty, $"{failures.Count} task(s) failed."); var loggingEvent = spy.GetWholeLog(); - for (var i = 1; i < 11; i++) + for (var i = 1; i < threadCount + 1; i++) for (var j = 0; j < 10; j++) { var sessionId = sessionIds[i]; diff --git a/src/NHibernate.Test/NHSpecificTest/NH1275/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/NH1275/Fixture.cs index 659b57ff99b..350b40f4dc9 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH1275/Fixture.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH1275/Fixture.cs @@ -1,4 +1,7 @@ +using System; +using NHibernate.Cfg; using NUnit.Framework; +using Environment = NHibernate.Cfg.Environment; namespace NHibernate.Test.NHSpecificTest.NH1275 { @@ -13,6 +16,11 @@ protected override bool AppliesTo(Dialect.Dialect dialect) return !string.IsNullOrEmpty(dialect.ForUpdateString); } + protected override void Configure(Configuration configuration) + { + configuration.SetProperty(Environment.FormatSql, "false"); + } + [Test] public void Retrieving() { @@ -32,14 +40,14 @@ public void Retrieving() { s.Get(savedId, LockMode.Upgrade); string sql = sqlLogSpy.Appender.GetEvents()[0].RenderedMessage; - Assert.Less(0, sql.IndexOf(Dialect.ForUpdateString)); + Assert.That(sql.IndexOf(Dialect.ForUpdateString, StringComparison.Ordinal), Is.GreaterThan(0)); } using (SqlLogSpy sqlLogSpy = new SqlLogSpy()) { s.CreateQuery("from A a where a.Id= :pid").SetLockMode("a", LockMode.Upgrade).SetParameter("pid", savedId). UniqueResult(); string sql = sqlLogSpy.Appender.GetEvents()[0].RenderedMessage; - Assert.Less(0, sql.IndexOf(Dialect.ForUpdateString)); + Assert.That(sql.IndexOf(Dialect.ForUpdateString, StringComparison.Ordinal), Is.GreaterThan(0)); } t.Commit(); } @@ -72,7 +80,7 @@ public void Loking() { s.Lock(a, LockMode.Upgrade); string sql = sqlLogSpy.Appender.GetEvents()[0].RenderedMessage; - Assert.Less(0, sql.IndexOf(Dialect.ForUpdateString)); + Assert.That(sql.IndexOf(Dialect.ForUpdateString, StringComparison.Ordinal), Is.GreaterThan(0)); } t.Commit(); } diff --git a/src/NHibernate.Test/NHSpecificTest/NH1280/NH1280Fixture.cs b/src/NHibernate.Test/NHSpecificTest/NH1280/NH1280Fixture.cs index 1c1549fb3f6..24613a6531c 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH1280/NH1280Fixture.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH1280/NH1280Fixture.cs @@ -41,6 +41,9 @@ protected override void OnTearDown() [Test] public void HavingUsingSqlFunctions_Concat() { + if (TestDialect.HasBrokenTypeInferenceOnSelectedParameters) + Assert.Ignore("Current dialect does not support this test"); + using (ISession s = OpenSession()) using (ITransaction tx = s.BeginTransaction()) { @@ -261,4 +264,4 @@ public void HavingOnNotExpressionCount() } } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/NHSpecificTest/NH1773/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/NH1773/Fixture.cs index 9958c258d16..e814df6bb50 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH1773/Fixture.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH1773/Fixture.cs @@ -23,7 +23,7 @@ public void CustomHQLFunctionsShouldBeRecognizedByTheParser() protected override bool AppliesTo(Dialect.Dialect dialect) { - // Fails with MS SQL Ce due to the query generating a duplicated column alias name, which this database does not support. + // Fails with MS SQL Ce & SQL Anywhere due to the query generating a duplicated column alias name, which these databases do not support. return TestDialect.SupportsDuplicatedColumnAliases; } diff --git a/src/NHibernate.Test/NHSpecificTest/NH1810/Mappings.hbm.xml b/src/NHibernate.Test/NHSpecificTest/NH1810/Mappings.hbm.xml index 610394d7bfc..d704e71a8c5 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH1810/Mappings.hbm.xml +++ b/src/NHibernate.Test/NHSpecificTest/NH1810/Mappings.hbm.xml @@ -23,7 +23,7 @@ - + diff --git a/src/NHibernate.Test/NHSpecificTest/NH1813/Mappings.hbm.xml b/src/NHibernate.Test/NHSpecificTest/NH1813/Mappings.hbm.xml index 913c3d731a4..979a5f11505 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH1813/Mappings.hbm.xml +++ b/src/NHibernate.Test/NHSpecificTest/NH1813/Mappings.hbm.xml @@ -5,6 +5,6 @@ default-lazy="false"> - + - \ No newline at end of file + diff --git a/src/NHibernate.Test/NHSpecificTest/NH1908ThreadSafety/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/NH1908ThreadSafety/Fixture.cs index 6daf21a77a6..25763395af0 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH1908ThreadSafety/Fixture.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH1908ThreadSafety/Fixture.cs @@ -24,7 +24,11 @@ public void UsingFiltersIsThreadSafe() { var errors = new List(); var threads = new List(); - for (int i = 0; i < 50; i++) + var threadCount = 50; + if (TestDialect.MaxNumberOfConnections < threadCount) + threadCount = TestDialect.MaxNumberOfConnections.Value; + + for (var i = 0; i < threadCount; i++) { var thread = new Thread(() => { diff --git a/src/NHibernate.Test/NHSpecificTest/NH2192/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/NH2192/Fixture.cs index 9ec01b44380..bf63c8c391f 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH2192/Fixture.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH2192/Fixture.cs @@ -50,8 +50,11 @@ public void HqlIsThreadsafe_UsingThreads() List exceptions = new List(); var threads = new List(); + var threadCount = _threadCount > TestDialect.MaxNumberOfConnections + ? TestDialect.MaxNumberOfConnections.Value + : _threadCount; - for (int i=0; i<_threadCount; i++) + for (var i = 0; i < threadCount; i++) { var thread = new Thread(new ThreadStart(() => { @@ -93,7 +96,11 @@ public void HqlIsThreadsafe_UsingPool() Func result = FetchRowResults; List> tasks = new List>(); - for (int i = 0; i < _threadCount; i++) + var threadCount = _threadCount > TestDialect.MaxNumberOfConnections + ? TestDialect.MaxNumberOfConnections.Value + : _threadCount; + + for (int i = 0; i < threadCount; i++) { tasks.Add(Task.Run(result)); } diff --git a/src/NHibernate.Test/NHSpecificTest/NH2302/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/NH2302/Fixture.cs index fb0b651da7b..4d3b8f56879 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH2302/Fixture.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH2302/Fixture.cs @@ -2,6 +2,7 @@ using NHibernate.Dialect; using NHibernate.Driver; using NHibernate.Mapping; +using NHibernate.SqlTypes; using NUnit.Framework; namespace NHibernate.Test.NHSpecificTest.NH2302 @@ -41,6 +42,9 @@ public void StringHugeLength() if (Sfi.ConnectionProvider.Driver is OdbcDriver || Dialect is MsSqlCeDialect) Assert.Ignore("NH-4065, not fixed for Odbc and MsSqlCe"); + if (Dialect.GetTypeName(SqlTypeFactory.GetString(10000)) != Dialect.GetLongestTypeName(DbType.String)) + Assert.Ignore("Current dialect does support limited strings of 10 000 characters"); + int id; // buildup a string the exceed the mapping string str = GetFixedLengthString12000(); diff --git a/src/NHibernate.Test/NHSpecificTest/NH2409/Mappings.hbm.xml b/src/NHibernate.Test/NHSpecificTest/NH2409/Mappings.hbm.xml index 0e87659246d..8b2a465cfe5 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH2409/Mappings.hbm.xml +++ b/src/NHibernate.Test/NHSpecificTest/NH2409/Mappings.hbm.xml @@ -7,7 +7,7 @@ - + diff --git a/src/NHibernate.Test/NHSpecificTest/NH3377/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/NH3377/Fixture.cs index bf40d343c12..06beb5b2ccf 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH3377/Fixture.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH3377/Fixture.cs @@ -90,7 +90,8 @@ public void ShouldBeAbleToCallConvertToInt32FromStringParameterInMax() [Test] public void ShouldBeAbleToCallConvertToBooleanFromStringParameter() { - if (Dialect is SQLiteDialect || Dialect is FirebirdDialect || Dialect is MySQLDialect || Dialect is Oracle8iDialect) + if (Dialect is SQLiteDialect || Dialect is FirebirdDialect || Dialect is MySQLDialect || + Dialect is Oracle8iDialect || Dialect is SapSQLAnywhere17Dialect) Assert.Ignore(Dialect.GetType() + " is not supported"); //NH-3720 @@ -119,4 +120,4 @@ public void ShouldBeAbleToCallConvertToDateTimeFromStringParameter() } } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/NHSpecificTest/NH3436/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/NH3436/Fixture.cs index 88b3a0a67d3..c08a0a4651f 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH3436/Fixture.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH3436/Fixture.cs @@ -64,7 +64,10 @@ public void TestQueryWithContainsInParallel() Guid.NewGuid(), Guid.NewGuid(), }; - const int threadsToRun = 32; + var threadsToRun = 32; + if (threadsToRun > TestDialect.MaxNumberOfConnections) + threadsToRun = TestDialect.MaxNumberOfConnections.Value; + var events = new WaitHandle[threadsToRun]; var exceptions = new List(); for (var i = 0; i < threadsToRun; i++) diff --git a/src/NHibernate.Test/NHSpecificTest/Properties/Mappings.hbm.xml b/src/NHibernate.Test/NHSpecificTest/Properties/Mappings.hbm.xml index 146f9292341..343587f868e 100644 --- a/src/NHibernate.Test/NHSpecificTest/Properties/Mappings.hbm.xml +++ b/src/NHibernate.Test/NHSpecificTest/Properties/Mappings.hbm.xml @@ -30,8 +30,8 @@ - - + + diff --git a/src/NHibernate.Test/NHSpecificTest/SimpleComponentFixture.cs b/src/NHibernate.Test/NHSpecificTest/SimpleComponentFixture.cs index 2bdb5524795..8e7c0bef0b6 100644 --- a/src/NHibernate.Test/NHSpecificTest/SimpleComponentFixture.cs +++ b/src/NHibernate.Test/NHSpecificTest/SimpleComponentFixture.cs @@ -11,6 +11,11 @@ public class SimpleComponentFixture : TestCase private DateTime testDateTime = new DateTime(2003, 8, 16); private DateTime updateDateTime = new DateTime(2003, 8, 17); + protected override bool AppliesTo(Dialect.Dialect dialect) + { + return TestDialect.SupportsSquareBracketInIdentifiers; + } + protected override string[] Mappings { get { return new string[] {"NHSpecific.SimpleComponent.hbm.xml"}; } @@ -75,4 +80,4 @@ public void TestInsert() // Do nothing, all the action is in OnSetUp. } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/SqlCommandTest/SqlSimpleSelectBuilderFixture.cs b/src/NHibernate.Test/SqlCommandTest/SqlSimpleSelectBuilderFixture.cs index 19014e5e1d5..77db33005b2 100644 --- a/src/NHibernate.Test/SqlCommandTest/SqlSimpleSelectBuilderFixture.cs +++ b/src/NHibernate.Test/SqlCommandTest/SqlSimpleSelectBuilderFixture.cs @@ -48,11 +48,11 @@ public void SimpleSelectStringSqlTest() .Append("FROM test_simple_select_builder ") .Append("WHERE identity_column = ? AND version_column = ?") .Append(" AND where_frag_column = ?") + .Append(factoryImpl.Dialect.GetForUpdateString(LockMode.Read)) .ToString(); - Assert.AreEqual(expectedSql, sqlString.ToString(), "SQL String"); Assert.AreEqual(3, sqlString.GetParameterCount(), "3 parameters"); } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/SqlTest/Query/SelfReferencingCollectionLoadTest.cs b/src/NHibernate.Test/SqlTest/Query/SelfReferencingCollectionLoadTest.cs index 1705ac073e3..c750269c6a8 100644 --- a/src/NHibernate.Test/SqlTest/Query/SelfReferencingCollectionLoadTest.cs +++ b/src/NHibernate.Test/SqlTest/Query/SelfReferencingCollectionLoadTest.cs @@ -19,9 +19,9 @@ protected override string MappingsAssembly protected override bool AppliesTo(Dialect.Dialect dialect) { - // Hacky mapping causing the primary key to reference itself as a foreign key, which is not supported by MySQL. It - // fails when trying to insert data by considering the foreign key violated. - return !(Dialect is MySQLDialect); + // Hacky mapping causing the primary key to reference itself as a foreign key, which is not supported by + // some databases. It fails when trying to insert data by considering the foreign key violated. + return !(Dialect is MySQLDialect || Dialect is SapSQLAnywhere17Dialect); } [Test] @@ -53,4 +53,4 @@ public void LoadCollection() } } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/SystemTransactions/SystemTransactionFixture.cs b/src/NHibernate.Test/SystemTransactions/SystemTransactionFixture.cs index cf0fe078414..b68bbd5d86c 100644 --- a/src/NHibernate.Test/SystemTransactions/SystemTransactionFixture.cs +++ b/src/NHibernate.Test/SystemTransactions/SystemTransactionFixture.cs @@ -181,12 +181,12 @@ public void CanUseSessionWithManyScopes(bool explicitFlush) // being concurrently disposed of. See https://github.com/nhibernate/nhibernate-core/pull/1505 for more details. if (Sfi.ConnectionProvider.Driver is OdbcDriver) Assert.Ignore("ODBC sometimes fails on second scope by checking the previous transaction status, which may yield an object disposed exception"); - // SAP HANA .Net provider always causes system transactions to be distributed, causing them to complete - // on concurrent threads. This creates race conditions when chaining scopes, the subsequent scope usage + // SAP HANA & SQL Anywhere .Net providers always cause system transactions to be distributed, causing them to + // complete on concurrent threads. This creates race conditions when chaining scopes, the subsequent scope usage // finding the connection still enlisted in the previous transaction, its complete being still not finished // on its own thread. - if (Sfi.ConnectionProvider.Driver is HanaDriverBase) - Assert.Ignore("SAP HANA scope handling causes concurrency issues preventing chaining scope usages."); + if (Sfi.ConnectionProvider.Driver is HanaDriverBase || Sfi.ConnectionProvider.Driver is SapSQLAnywhere17Driver) + Assert.Ignore("SAP HANA and SQL Anywhere scope handling causes concurrency issues preventing chaining scope usages."); using (var s = WithOptions().ConnectionReleaseMode(ConnectionReleaseMode.OnClose).OpenSession()) { @@ -254,6 +254,13 @@ public void CanUseSessionWithManyScopes(bool explicitFlush) public void CanUseSessionOutsideOfScopeAfterScope(bool explicitFlush) { IgnoreIfUnsupported(explicitFlush); + // SAP SQL Anywhere .Net provider always causes system transactions to be distributed, causing them to + // complete on concurrent threads. This creates race conditions when chaining session usage after a scope, + // the subsequent usage finding the connection still enlisted in the previous transaction, its complete + // being still not finished on its own thread. + if (Sfi.ConnectionProvider.Driver is SapSQLAnywhere17Driver) + Assert.Ignore("SAP SQL Anywhere scope handling causes concurrency issues preventing chaining session usages."); + using (var s = WithOptions().ConnectionReleaseMode(ConnectionReleaseMode.OnClose).OpenSession()) { using (var tx = new TransactionScope()) diff --git a/src/NHibernate.Test/SystemTransactions/SystemTransactionFixtureBase.cs b/src/NHibernate.Test/SystemTransactions/SystemTransactionFixtureBase.cs index 5e587563a64..02576664571 100644 --- a/src/NHibernate.Test/SystemTransactions/SystemTransactionFixtureBase.cs +++ b/src/NHibernate.Test/SystemTransactions/SystemTransactionFixtureBase.cs @@ -52,7 +52,11 @@ protected void DisableConnectionAutoEnlist(Configuration configuration) protected void IgnoreIfUnsupported(bool explicitFlush) { Assume.That( - new[] { explicitFlush, UseConnectionOnSystemTransactionPrepare }, + new[] + { + explicitFlush, + UseConnectionOnSystemTransactionPrepare && TestDialect.SupportsUsingConnectionOnSystemTransactionPrepare + }, Has.Some.EqualTo(true), "Implicit flush cannot work without using connection from system transaction prepare phase"); } diff --git a/src/NHibernate.Test/TestDialect.cs b/src/NHibernate.Test/TestDialect.cs index b18a5c556d9..246c3078f9d 100644 --- a/src/NHibernate.Test/TestDialect.cs +++ b/src/NHibernate.Test/TestDialect.cs @@ -128,5 +128,31 @@ public bool SupportsSqlType(SqlType sqlType) /// in the batch. /// public virtual bool SupportsBatchingDependentDML => true; + + /// + /// Some test editions of databases may have a simultaneous connections limits. + /// + public virtual int? MaxNumberOfConnections => null; + + /// + /// Even quoted, some databases do not support square bracket in identifiers. + /// + public virtual bool SupportsSquareBracketInIdentifiers => true; + + /// + /// Some databases fail when a connection is enlisted during the first phase of a two phase commit. + /// + public virtual bool SupportsUsingConnectionOnSystemTransactionPrepare => true; + + /// + /// Some databases (provider?) fails to compute adequate column types for queries which columns + /// computing include a parameter value. + /// + /// + /// This property is not set to true for Firebird although it seems to be affected too. But its driver + /// currently has a hack for casting all parameters in select and where clauses in order to explicit + /// their type in the query. + /// + public virtual bool HasBrokenTypeInferenceOnSelectedParameters => false; } } diff --git a/src/NHibernate.Test/TestDialects/SapSQLAnywhere17TestDialect.cs b/src/NHibernate.Test/TestDialects/SapSQLAnywhere17TestDialect.cs new file mode 100644 index 00000000000..3d08930b062 --- /dev/null +++ b/src/NHibernate.Test/TestDialects/SapSQLAnywhere17TestDialect.cs @@ -0,0 +1,42 @@ +namespace NHibernate.Test.TestDialects +{ + public class SapSQLAnywhere17TestDialect : TestDialect + { + public SapSQLAnywhere17TestDialect(Dialect.Dialect dialect) + : base(dialect) + { + } + + /// + /// + /// The personal edition of SAP SQL Anywhere does not allow more than ten simultaneous connections. + /// + public override int? MaxNumberOfConnections => 10; + + public override bool SupportsDuplicatedColumnAliases => false; + + /// + /// + /// See https://help.sap.com/viewer/40c01c3500744c85a02db71276495de5/17.0/en-US/8170eb5b6ce21014a7e1a2fd6b4a85fc.html + /// It seems they have decide to remove this support starting from version 16. + /// + public override bool SupportsSquareBracketInIdentifiers => false; + + /// + /// + /// SQL Anywhere freezes on some commit cases and the transaction ends by timeout. + /// + public override bool SupportsUsingConnectionOnSystemTransactionPrepare => false; + + /// + /// + /// SQL Anywhere treats string parameters concatenation as yielding numeric and fails casting + /// the actual string value if it is not castable to a number. Likewise, in case when + /// statement, it treats them as yielding integer if all case yields parameter values, whatever + /// the actual type of the parameters. And in case of numeric computations with a numeric + /// parameter, the result is treated as a fractional digit lossy double instead of being kept + /// numeric. See https://stackoverflow.com/q/52558715/1178314. + /// + public override bool HasBrokenTypeInferenceOnSelectedParameters => true; + } +} diff --git a/src/NHibernate.Test/TypesTest/DecimalTypeFixture.cs b/src/NHibernate.Test/TypesTest/DecimalTypeFixture.cs index 170400fa1a6..72561fc7008 100644 --- a/src/NHibernate.Test/TypesTest/DecimalTypeFixture.cs +++ b/src/NHibernate.Test/TypesTest/DecimalTypeFixture.cs @@ -112,6 +112,9 @@ public void UnsavedValue() [Test] public void HighScaleParameterSelect() { + if (TestDialect.HasBrokenTypeInferenceOnSelectedParameters) + Assert.Ignore("Current dialect does not support this test"); + using (var s = OpenSession()) using (var t = s.BeginTransaction()) { diff --git a/src/NHibernate.TestDatabaseSetup/TestDatabaseSetup.cs b/src/NHibernate.TestDatabaseSetup/TestDatabaseSetup.cs index c666e2a8e78..ec08849ce03 100644 --- a/src/NHibernate.TestDatabaseSetup/TestDatabaseSetup.cs +++ b/src/NHibernate.TestDatabaseSetup/TestDatabaseSetup.cs @@ -3,7 +3,9 @@ using System.Data.Odbc; using System.Data.SqlClient; #if NETFX +using System.Data.Common; using System.Data.SqlServerCe; +using System.Diagnostics; #endif using System.Data.SQLite; using System.IO; @@ -30,7 +32,8 @@ public class DatabaseSetup {"NHibernate.Driver.OdbcDriver", SetupSqlServerOdbc}, {"NHibernate.Driver.SQLite20Driver", SetupSQLite}, #if NETFX - {"NHibernate.Driver.SqlServerCeDriver", SetupSqlServerCe} + {"NHibernate.Driver.SqlServerCeDriver", SetupSqlServerCe}, + {"NHibernate.Driver.SapSQLAnywhere17Driver", SetupSqlAnywhere} #endif }; @@ -239,6 +242,57 @@ private static void SetupOracle(Cfg.Configuration cfg) // } //} } + +#if NETFX + private static void SetupSqlAnywhere(Cfg.Configuration cfg) + { + var connStr = cfg.Properties[Cfg.Environment.ConnectionString]; + + var factory = DbProviderFactories.GetFactory("Sap.Data.SQLAnywhere"); + var connBuilder = factory.CreateConnectionStringBuilder(); + connBuilder.ConnectionString = connStr; + var filename = (string) connBuilder["DBF"]; + + RunProcess("dbstop", $"-c \"UID=nhibernate;PWD=nhibernate;DBN=nhibernate\" -d", false); + RunProcess("dberase", $"-y {filename}", false); + // -dba: login,pwd + RunProcess("dbinit", $"-dba nhibernate,nhibernate {filename}", true); + + using (var conn = factory.CreateConnection()) + { + conn.ConnectionString = connStr; + conn.Open(); + using (var cmd = conn.CreateCommand()) + { + cmd.CommandText = "set option ansi_update_constraints = 'Off'"; + cmd.ExecuteNonQuery(); + } + } + } + + private static void RunProcess(string processName, string arguments, bool checkSuccess) + { + using (var process = new Process()) + { + process.StartInfo.FileName = processName; + process.StartInfo.Arguments = arguments; + process.StartInfo.CreateNoWindow = true; + process.StartInfo.UseShellExecute = false; + process.StartInfo.RedirectStandardOutput = true; + process.StartInfo.RedirectStandardError = true; + process.Start(); + Console.WriteLine($"{processName} output:"); + Console.Write(process.StandardOutput.ReadToEnd()); + Console.WriteLine(); + Console.WriteLine($"{processName} error output:"); + Console.Write(process.StandardError.ReadToEnd()); + Console.WriteLine(); + process.WaitForExit(); + if (checkSuccess && process.ExitCode != 0) + throw new InvalidOperationException($"{processName} has failed"); + } + } +#endif } } diff --git a/src/NHibernate/Dialect/SapSQLAnywhere17Dialect.cs b/src/NHibernate/Dialect/SapSQLAnywhere17Dialect.cs index 289711a3e8e..bcf6152f87a 100644 --- a/src/NHibernate/Dialect/SapSQLAnywhere17Dialect.cs +++ b/src/NHibernate/Dialect/SapSQLAnywhere17Dialect.cs @@ -1,102 +1,158 @@ -using System.Data; +using System; +using System.Collections.Generic; using System.Data.Common; +using NHibernate.Dialect.Function; using NHibernate.Dialect.Schema; +using NHibernate.SqlCommand; using Environment = NHibernate.Cfg.Environment; namespace NHibernate.Dialect { + /// /// - /// The SapSQLAnywhere17Dialect uses the SybaseSQLAnywhere12Dialect as its - /// base class. SybaseSQLAnywhere17Dialect includes support for ISO SQL standard - /// sequences, which are defined in the catalog table SYSSEQUENCE. - /// The dialect uses the SybaseSQLAnywhe12MetaData class for metadata API - /// calls, which correctly supports reserved words defined by SQL Anywhere. - /// + /// The SapSQLAnywhere17Dialect uses the SybaseSQLAnywhere12Dialect as its + /// base class. /// The dialect defaults the following configuration properties: /// - /// - /// Property - /// Default Value - /// - /// - /// connection.driver_class - /// - /// - /// - /// prepare_sql - /// - /// + /// + /// Property + /// Default Value + /// + /// + /// connection.driver_class + /// + /// + /// + /// prepare_sql + /// + /// /// /// public class SapSQLAnywhere17Dialect : SybaseSQLAnywhere12Dialect { public SapSQLAnywhere17Dialect() - : base() { - DefaultProperties[Environment.ConnectionDriver] = "NHibernate.Driver.SybaseSQLAnywhere17Driver"; + DefaultProperties[Environment.ConnectionDriver] = "NHibernate.Driver.SapSQLAnywhere17Driver"; } - + + #region SQL Anywhere 17 additional keywords + + private static readonly string[] DialectKeywords = + { + "executing", + "executing_user", + "extract", + "invoking", + "invoking_user", + "pivot", + "procedure_owner", + "session_user", + "unpivot" + }; + + #endregion + protected override void RegisterKeywords() { base.RegisterKeywords(); - } - protected override void RegisterDateTimeTypeMappings() - { - base.RegisterDateTimeTypeMappings(); + RegisterKeywords(DialectKeywords); } - /// - /// SQL Anywhere supports SEQUENCES using a primarily SQL Standard - /// syntax. Sequence values can be queried using the .CURRVAL identifier, and the next - /// value in a sequence can be retrieved using the .NEXTVAL identifier. Sequences - /// are retained in the SYS.SYSSEQUENCE catalog table. - /// - public override bool SupportsSequences + protected override void RegisterStringFunctions() { - get { return true; } - } + base.RegisterStringFunctions(); - /// - /// Pooled sequences does not refer to the CACHE parameter of the CREATE SEQUENCE - /// statement, but merely if the DBMS supports sequences that can be incremented or decremented - /// by values greater than 1. - /// - public override bool SupportsPooledSequences - { - get { return true; } + // SQL Anywhere locate arguments are inverted compared to other databases. As fixing this is likely + // a breaking change for users of older versions, changing it only in the new dialect. + RegisterFunction("locate", new SQLFunctionTemplateWithRequiredParameters(NHibernateUtil.Int32, "locate(?2, ?1, ?3)", new object[] { null, null, "1" })); } - /// Get the SELECT command used to retrieve the names of all sequences. - /// The SELECT command; or NULL if sequences are not supported. - public override string QuerySequencesString + protected override void RegisterMathFunctions() { - get { return "SELECT SEQUENCE_NAME FROM SYS.SYSSEQUENCE"; } - } + base.RegisterMathFunctions(); - public override string GetSequenceNextValString(string sequenceName) - { - return "SELECT " + GetSelectSequenceNextValString(sequenceName) + " FROM SYS.DUMMY"; + // While SQL AnyWhere always returns double for these functions, Linq requires them to return + // the argument type. This is done by not hard-coding their return type in the function. In + // such case, NHibernate takes care of converting the result back to the argument type, if + // needed. + // Hard-coding the return type should be done for functions where returning the argument type + // does not make sense, like on string length, where returning a string instead of an int would + // not make sense. + // Unfortunately, removing this hard coding from SybaseSQLAnywhere10Dialect would be a breaking + // change for those having coded HQL queries on projections of these functions with a hard-cast + // to double. So changing it only for this new dialect. + RegisterFunction("ceiling", new StandardSQLFunction("ceiling")); + RegisterFunction("floor", new StandardSQLFunction("floor")); } - public override string GetSelectSequenceNextValString(string sequenceName) + public override void Configure(IDictionary settings) { - return sequenceName + ".NEXTVAL"; + base.Configure(settings); + + RegisterConfigurationDependentFunctions(); } - public override string GetCreateSequenceString(string sequenceName) + protected virtual void RegisterConfigurationDependentFunctions() { - return "CREATE SEQUENCE " + sequenceName; // by default, is START WITH 1 MAXVALUE 2**63-1 + // The SQL function str is in the SQL standard and is not meant for casting anything to a string. It is + // a float formatting function. Unfortunately, HQL's str is used as a general purpose string cast. Previous + // SQL Anywhere dialects are mapping it as the SQL standard's str function. For avoiding a breaking change + // for them, it is fixed only in this new dialect. + RegisterFunction("str", new SQLFunctionTemplate(NHibernateUtil.String, $"cast(?1 as nvarchar({DefaultCastLength}))")); } - public override string GetDropSequenceString(string sequenceName) + /// + /// + /// SQL Anywhere does not supports null in unique constraints. As this disable generation of unique + /// constraints when a column is nullable, even if the application never put null in it, it could + /// be a breaking change. So this property is overriden to false only in this new 17 dialect. + /// + public override bool SupportsNullInUnique => false; + + public override SqlString GetLimitString(SqlString sql, SqlString offset, SqlString limit) { - return "DROP SEQUENCE " + sequenceName; + // SQL Anywhere uses SELECT TOP n START AT m [ select list items ] + // for LIMIT/OFFSET support. Does not support a limit of zero. + + var insertionPoint = GetAfterSelectInsertPoint(sql); + + if (insertionPoint > 0) + { + if (limit == null && offset == null) + throw new ArgumentException("Cannot limit with neither a limit nor an offset"); + + var limitBuilder = new SqlStringBuilder(); + limitBuilder.Add("select"); + if (insertionPoint > 6) + { + limitBuilder.Add(" distinct "); + } + limitBuilder.Add(" top "); + if (limit != null) + { + limitBuilder.Add(limit); + } + else + { + // This seems supported since SQL Anywhere 12.0.1 only. No reference found for previous version, + // included 12.0.0. + limitBuilder.Add("all "); + } + if (offset != null) + { + limitBuilder.Add(" start at "); + limitBuilder.Add(offset); + } + limitBuilder.Add(sql.Substring(insertionPoint)); + return limitBuilder.ToSqlString(); + } + return sql; // unchanged } public override IDataBaseSchema GetDataBaseSchema(DbConnection connection) { - return new SybaseAnywhereDataBaseMetaData(connection); + return new SapSqlAnywhere17DataBaseMetaData(connection); } } } diff --git a/src/NHibernate/Dialect/Schema/SapSQLAnywhere17MetaData.cs b/src/NHibernate/Dialect/Schema/SapSQLAnywhere17MetaData.cs new file mode 100644 index 00000000000..9ff41672b3e --- /dev/null +++ b/src/NHibernate/Dialect/Schema/SapSQLAnywhere17MetaData.cs @@ -0,0 +1,163 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.Common; + + +namespace NHibernate.Dialect.Schema +{ + // Metadata for connections using the Sap.Data.SQLAnywhere.v4.5 ADO.NET provider + public class SapSqlAnywhere17DataBaseMetaData : AbstractDataBaseSchema + { + public SapSqlAnywhere17DataBaseMetaData(DbConnection connection) : base(connection) + { + } + + public override ITableMetadata GetTableMetadata(DataRow rs, bool extras) + { + return new SapSqlAnywhere17TableMetaData(rs, this, extras); + } + + public override ISet GetReservedWords() + { + var result = new HashSet(StringComparer.OrdinalIgnoreCase); + var dtReservedWords = Connection.GetSchema(DbMetaDataCollectionNames.ReservedWords); + foreach (DataRow row in dtReservedWords.Rows) + { + result.Add(row["reserved_word"].ToString()); + } + + return result; + } + + public override DataTable GetTables( + string catalog, + string schemaPattern, + string tableNamePattern, + string[] types) + { + var restrictions = new[] { schemaPattern, tableNamePattern, null }; + var objTbl = Connection.GetSchema("Tables", restrictions); + return objTbl; + } + + public override DataTable GetIndexInfo(string catalog, string schemaPattern, string tableName) + { + var restrictions = new[] { schemaPattern, tableName, null }; + var objTbl = Connection.GetSchema("Indexes", restrictions); + return objTbl; + } + + public override DataTable GetIndexColumns( + string catalog, + string schemaPattern, + string tableName, + string indexName) + { + var restrictions = new[] { schemaPattern, tableName, indexName, null }; + var objTbl = Connection.GetSchema("IndexColumns", restrictions); + return objTbl; + } + + public override DataTable GetColumns( + string catalog, + string schemaPattern, + string tableNamePattern, + string columnNamePattern) + { + var restrictions = new[] { schemaPattern, tableNamePattern, null }; + var objTbl = Connection.GetSchema("Columns", restrictions); + return objTbl; + } + + public override DataTable GetForeignKeys(string catalog, string schema, string table) + { + var restrictions = new[] { schema, table, null }; + var objTbl = Connection.GetSchema("ForeignKeys", restrictions); + return objTbl; + } + } + + public class SapSqlAnywhere17TableMetaData : AbstractTableMetadata + { + public SapSqlAnywhere17TableMetaData(DataRow rs, IDataBaseSchema meta, bool extras) : base(rs, meta, extras) + { + } + + protected override IColumnMetadata GetColumnMetadata(DataRow rs) + { + return new SapSqlAnywhere17ColumnMetaData(rs); + } + + protected override string GetColumnName(DataRow rs) + { + return Convert.ToString(rs["COLUMN_NAME"]); + } + + protected override string GetConstraintName(DataRow rs) + { + // There is no thing like a constraint name for Anywhere - so + // we just use the column name here ... + return Convert.ToString(rs["COLUMN_NAME"]); + } + + protected override IForeignKeyMetadata GetForeignKeyMetadata(DataRow rs) + { + return new SapSqlAnywhere17ForeignKeyMetaData(rs); + } + + protected override IIndexMetadata GetIndexMetadata(DataRow rs) + { + return new SapSqlAnywhere17IndexMetaData(rs); + } + + protected override string GetIndexName(DataRow rs) + { + return (string) rs["INDEX_NAME"]; + } + + protected override void ParseTableInfo(DataRow rs) + { + Catalog = null; + Schema = Convert.ToString(rs["TABLE_SCHEMA"]); + if (string.IsNullOrEmpty(Schema)) + { + Schema = null; + } + + Name = Convert.ToString(rs["TABLE_NAME"]); + } + } + + public class SapSqlAnywhere17ColumnMetaData : AbstractColumnMetaData + { + public SapSqlAnywhere17ColumnMetaData(DataRow rs) : base(rs) + { + Name = Convert.ToString(rs["COLUMN_NAME"]); + + SetColumnSize(rs["CHARACTER_MAXIMUM_LENGTH"]); + SetNumericalPrecision(rs["NUMERIC_PRECISION"]); + + Nullable = Convert.ToString(rs["IS_NULLABLE"]); + TypeName = Convert.ToString(rs["DATA_TYPE"]); + } + } + + public class SapSqlAnywhere17IndexMetaData : AbstractIndexMetadata + { + public SapSqlAnywhere17IndexMetaData(DataRow rs) : base(rs) + { + Name = (string) rs["INDEX_NAME"]; + } + } + + public class SapSqlAnywhere17ForeignKeyMetaData : AbstractForeignKeyMetadata + { + public SapSqlAnywhere17ForeignKeyMetaData(DataRow rs) : base(rs) + { + // There is no thing like a constraint name for Anywhere - so + // we just use the column name here ... + Name = (string) rs["COLUMN_NAME"]; + } + } +} diff --git a/src/NHibernate/Dialect/SybaseSQLAnywhere10Dialect.cs b/src/NHibernate/Dialect/SybaseSQLAnywhere10Dialect.cs index 3a3eb60aab9..388947b45c4 100644 --- a/src/NHibernate/Dialect/SybaseSQLAnywhere10Dialect.cs +++ b/src/NHibernate/Dialect/SybaseSQLAnywhere10Dialect.cs @@ -93,6 +93,9 @@ protected virtual void RegisterNumericTypeMappings() RegisterColumnType(DbType.Decimal, "NUMERIC(19,5)"); // Precision ranges from 0-127 // Anywhere max precision is 127, but .Net is limited to 28-29. RegisterColumnType(DbType.Decimal, 29, "NUMERIC($p, $s)"); // Precision ranges from 0-127 + RegisterColumnType(DbType.Byte, "TINYINT"); + RegisterColumnType(DbType.SByte, "UNSIGNED TINYINT"); + RegisterColumnType(DbType.Currency, "MONEY"); // Implemented by the database as NUMERIC(19,4) } protected virtual void RegisterDateTimeTypeMappings() @@ -192,6 +195,11 @@ protected virtual void RegisterAggregationFunctions() protected virtual void RegisterBitFunctions() { + // SQL Anywhere does not respect usual priorities with the bitwise not. Unfortunately the HQL parser + // furthermore remove "undue" parenthesis according to usual rules. As the bitwise not should have maximal + // priority, we can work around this by using a template forcing parenthesis around it. + RegisterFunction("bnot", new VarArgsSQLFunction(NHibernateUtil.Int64, "(~", "", ")")); + RegisterFunction("bit_length", new StandardSQLFunction("bit_length", NHibernateUtil.Int32)); RegisterFunction("bit_substr", new StandardSQLFunction("bit_substr")); RegisterFunction("get_bit", new StandardSQLFunction("get_bit", NHibernateUtil.Boolean)); @@ -243,6 +251,7 @@ protected virtual void RegisterStringFunctions() RegisterFunction("byte_length", new StandardSQLFunction("byte_length", NHibernateUtil.Int32)); RegisterFunction("byte_substr", new VarArgsSQLFunction(NHibernateUtil.String, "byte_substr(", ",", ")")); RegisterFunction("char", new StandardSQLFunction("char", NHibernateUtil.String)); + RegisterFunction("chr", new StandardSQLFunction("char", NHibernateUtil.Character)); RegisterFunction("charindex", new StandardSQLFunction("charindex", NHibernateUtil.Int32)); RegisterFunction("char_length", new StandardSQLFunction("char_length", NHibernateUtil.Int32)); RegisterFunction("compare", new VarArgsSQLFunction(NHibernateUtil.Int32, "compare(", ",", ")")); @@ -284,7 +293,7 @@ protected virtual void RegisterStringFunctions() RegisterFunction("to_char", new VarArgsSQLFunction(NHibernateUtil.String, "to_char(", ",", ")")); RegisterFunction("to_nchar", new VarArgsSQLFunction(NHibernateUtil.String, "to_nchar(", ",", ")")); - RegisterFunction("trim", new StandardSQLFunction("trim", NHibernateUtil.String)); + RegisterFunction("trim", new AnsiTrimEmulationFunction()); RegisterFunction("ucase", new StandardSQLFunction("ucase", NHibernateUtil.String)); RegisterFunction("unicode", new StandardSQLFunction("unicode", NHibernateUtil.Int32)); RegisterFunction("unistr", new StandardSQLFunction("unistr", NHibernateUtil.String)); @@ -344,6 +353,7 @@ protected virtual void RegisterMiscellaneousFunctions() RegisterFunction("transactsql", new StandardSQLFunction("transactsql", NHibernateUtil.String)); RegisterFunction("varexists", new StandardSQLFunction("varexists", NHibernateUtil.Int32)); RegisterFunction("watcomsql", new StandardSQLFunction("watcomsql", NHibernateUtil.String)); + RegisterFunction("iif", new SQLFunctionTemplate(null, "case when ?1 then ?2 else ?3 end")); } #region private static readonly string[] DialectKeywords = { ... } @@ -531,7 +541,7 @@ public override bool OffsetStartsAtOne get { return true; } } - private static int GetAfterSelectInsertPoint(SqlString sql) + protected static int GetAfterSelectInsertPoint(SqlString sql) { // Assume no common table expressions with the statement. if (sql.StartsWithCaseInsensitive("select distinct")) @@ -556,6 +566,11 @@ public override SqlString GetLimitString(SqlString sql, SqlString offset, SqlStr if (insertionPoint > 0) { + if (limit == null && offset == null) + throw new ArgumentException("Cannot limit with neither a limit nor an offset"); + if (limit == null) + throw new NotSupportedException($"Dialect {this} does not support setting an offset without a limit"); + SqlStringBuilder limitBuilder = new SqlStringBuilder(); limitBuilder.Add("select"); if (insertionPoint > 6) @@ -948,5 +963,9 @@ public override IDataBaseSchema GetDataBaseSchema(DbConnection connection) { return new SybaseAnywhereDataBaseMetaData(connection); } + + /// + /// SQL Anywhere has a micro-second resolution. + public override long TimestampResolutionInTicks => 10L; } } diff --git a/src/NHibernate/Dialect/SybaseSQLAnywhere11Dialect.cs b/src/NHibernate/Dialect/SybaseSQLAnywhere11Dialect.cs index d28f67cdffe..93e8c1a6e03 100644 --- a/src/NHibernate/Dialect/SybaseSQLAnywhere11Dialect.cs +++ b/src/NHibernate/Dialect/SybaseSQLAnywhere11Dialect.cs @@ -1,6 +1,4 @@ -using System; - -namespace NHibernate.Dialect +namespace NHibernate.Dialect { /// /// SQL Dialect for SQL Anywhere 11 - for the NHibernate 3.0.0 distribution @@ -42,8 +40,5 @@ namespace NHibernate.Dialect /// public class SybaseSQLAnywhere11Dialect : SybaseSQLAnywhere10Dialect { - public SybaseSQLAnywhere11Dialect() : base() - { - } } -} \ No newline at end of file +} diff --git a/src/NHibernate/Dialect/SybaseSQLAnywhere12Dialect.cs b/src/NHibernate/Dialect/SybaseSQLAnywhere12Dialect.cs index 52f2ce26939..a0b0125e8b7 100644 --- a/src/NHibernate/Dialect/SybaseSQLAnywhere12Dialect.cs +++ b/src/NHibernate/Dialect/SybaseSQLAnywhere12Dialect.cs @@ -1,6 +1,5 @@ using System.Data; -using System.Data.Common; -using NHibernate.Dialect.Schema; +using NHibernate.Dialect.Function; using Environment = NHibernate.Cfg.Environment; namespace NHibernate.Dialect @@ -31,7 +30,7 @@ namespace NHibernate.Dialect /// /// The SybaseSQLAnywhere12Dialect uses the SybaseSQLAnywhere11Dialect as its /// base class. SybaseSQLAnywhere12Dialect includes support for ISO SQL standard - /// sequences, which are defined in the catalog table SYSSEQUENCE. + /// sequences, which are defined in the catalog table SYSSEQUENCE. /// The dialect uses the SybaseSQLAnywhe11MetaData class for metadata API /// calls, which correctly supports reserved words defined by SQL Anywhere. /// @@ -54,7 +53,6 @@ namespace NHibernate.Dialect public class SybaseSQLAnywhere12Dialect : SybaseSQLAnywhere11Dialect { public SybaseSQLAnywhere12Dialect() - : base() { DefaultProperties[Environment.ConnectionDriver] = "NHibernate.Driver.SybaseSQLAnywhereDotNet4Driver"; } @@ -71,9 +69,24 @@ protected override void RegisterKeywords() protected override void RegisterDateTimeTypeMappings() { base.RegisterDateTimeTypeMappings(); - RegisterColumnType(DbType.DateTimeOffset, "DATETIMEOFFSET"); + // Do not use the alias DATETIMEOFFSET here, as the database will translate it to its actual type name, + // causing schema validator failures. + RegisterColumnType(DbType.DateTimeOffset, "TIMESTAMP WITH TIME ZONE"); } + protected override void RegisterDateFunctions() + { + base.RegisterDateFunctions(); + RegisterFunction( + "current_timestamp_offset", + new NoArgSQLFunction("sysdatetimeoffset", NHibernateUtil.DateTimeOffset, true)); + } + + /// + /// SQL Anywhere the ANSI standard "DEFAULT VALUES" since 11.0.1 release (not 11.0.0). + /// + public override string NoColumnsInsertString => " default values "; + /// public override string CurrentUtcTimestampSQLFunctionName => "cast(current UTC timestamp as timestamp)"; @@ -131,10 +144,5 @@ public override string GetDropSequenceString(string sequenceName) { return "DROP SEQUENCE " + sequenceName; } - - public override IDataBaseSchema GetDataBaseSchema(DbConnection connection) - { - return new SybaseAnywhereDataBaseMetaData(connection); - } } } diff --git a/src/NHibernate/SqlCommand/SqlStringBuilder.cs b/src/NHibernate/SqlCommand/SqlStringBuilder.cs index 6bcc4325aec..87b1f03cad9 100644 --- a/src/NHibernate/SqlCommand/SqlStringBuilder.cs +++ b/src/NHibernate/SqlCommand/SqlStringBuilder.cs @@ -158,10 +158,9 @@ public SqlStringBuilder AddObject(object part) /// /// The SqlString to add to this SqlStringBuilder /// This SqlStringBuilder - /// This calls the overloaded Add(sqlString, null, null, null, false) public SqlStringBuilder Add(SqlString sqlString) { - sqlString.Visit(AddingVisitor); + sqlString?.Visit(AddingVisitor); return this; } @@ -217,7 +216,7 @@ public SqlStringBuilder Add(SqlString[] sqlStrings, string prefix, string op, st foreach (SqlString sqlString in sqlStrings) { - if (sqlString.Count == 0) + if (sqlString == null || sqlString.Count == 0) { continue; } diff --git a/teamcity.build b/teamcity.build index 58035279060..1aebbf13cbb 100644 --- a/teamcity.build +++ b/teamcity.build @@ -210,6 +210,12 @@ + + + + + +