From 2ef030ba766ccdb87478f4985964966197e5bf2e Mon Sep 17 00:00:00 2001 From: Vyacheslav Date: Thu, 16 Jun 2022 19:30:28 +0300 Subject: [PATCH 1/8] Added support for the CancelQuery() method in IStatelessSession (#809) --- src/NHibernate/IStatelessSession.cs | 9 +++++++++ src/NHibernate/Impl/StatelessSessionImpl.cs | 8 ++++++++ 2 files changed, 17 insertions(+) diff --git a/src/NHibernate/IStatelessSession.cs b/src/NHibernate/IStatelessSession.cs index 66d6bc0c34d..8f34fc03eb8 100644 --- a/src/NHibernate/IStatelessSession.cs +++ b/src/NHibernate/IStatelessSession.cs @@ -143,6 +143,15 @@ public partial interface IStatelessSession : IDisposable /// Close the stateless session and release the ADO.NET connection. void Close(); + /// + /// Cancel execution of the current query. + /// + /// + /// May be called from one thread to stop execution of a query in another thread. + /// Use with care! + /// + void CancelQuery(); + /// Insert an entity. /// A new transient instance /// The identifier of the instance diff --git a/src/NHibernate/Impl/StatelessSessionImpl.cs b/src/NHibernate/Impl/StatelessSessionImpl.cs index 650edc2b086..8f2d1ac3945 100644 --- a/src/NHibernate/Impl/StatelessSessionImpl.cs +++ b/src/NHibernate/Impl/StatelessSessionImpl.cs @@ -459,6 +459,14 @@ public void ManagedClose() } } + public void CancelQuery() + { + using (BeginProcess()) + { + Batcher.CancelLastQuery(); + } + } + /// Insert a entity. /// A new transient instance /// the identifier of the instance From b2840fdafa464c31f0413bf85a3e269b21a9ce9c Mon Sep 17 00:00:00 2001 From: Vyacheslav Date: Tue, 21 Jun 2022 03:15:23 +0300 Subject: [PATCH 2/8] CancelQuery has been moved from an interface to the StatelessSessionExtensions class as it has been deferred to the next major version. (#809) --- src/NHibernate/IStatelessSession.cs | 24 +++++++++++++-------- src/NHibernate/Impl/StatelessSessionImpl.cs | 8 ------- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/src/NHibernate/IStatelessSession.cs b/src/NHibernate/IStatelessSession.cs index 8f34fc03eb8..aa824e0d557 100644 --- a/src/NHibernate/IStatelessSession.cs +++ b/src/NHibernate/IStatelessSession.cs @@ -74,6 +74,21 @@ public static void FlushBatcher(this IStatelessSession session) { session.GetSessionImplementation().Flush(); } + + /// + /// Cancel execution of the current query. + /// + /// + /// May be called from one thread to stop execution of a query in another thread. + /// Use with care! + /// + public static void CancelQuery(this IStatelessSession session) + { + using (session.GetSessionImplementation().BeginProcess()) + { + session.GetSessionImplementation().Batcher.CancelLastQuery(); + } + } } /// @@ -143,15 +158,6 @@ public partial interface IStatelessSession : IDisposable /// Close the stateless session and release the ADO.NET connection. void Close(); - /// - /// Cancel execution of the current query. - /// - /// - /// May be called from one thread to stop execution of a query in another thread. - /// Use with care! - /// - void CancelQuery(); - /// Insert an entity. /// A new transient instance /// The identifier of the instance diff --git a/src/NHibernate/Impl/StatelessSessionImpl.cs b/src/NHibernate/Impl/StatelessSessionImpl.cs index 8f2d1ac3945..650edc2b086 100644 --- a/src/NHibernate/Impl/StatelessSessionImpl.cs +++ b/src/NHibernate/Impl/StatelessSessionImpl.cs @@ -459,14 +459,6 @@ public void ManagedClose() } } - public void CancelQuery() - { - using (BeginProcess()) - { - Batcher.CancelLastQuery(); - } - } - /// Insert a entity. /// A new transient instance /// the identifier of the instance From 8e97f1dc1a8215ede627f8d3995247f003ccb659 Mon Sep 17 00:00:00 2001 From: Vyacheslav Date: Thu, 23 Jun 2022 07:22:53 +0300 Subject: [PATCH 3/8] Added tests for the CancelQuery method(#809) --- .../StatelessSessionCancelQueryFixture.cs | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 src/NHibernate.Test/Stateless/StatelessSessionCancelQueryFixture.cs diff --git a/src/NHibernate.Test/Stateless/StatelessSessionCancelQueryFixture.cs b/src/NHibernate.Test/Stateless/StatelessSessionCancelQueryFixture.cs new file mode 100644 index 00000000000..6e69da3b94b --- /dev/null +++ b/src/NHibernate.Test/Stateless/StatelessSessionCancelQueryFixture.cs @@ -0,0 +1,93 @@ +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using NHibernate.Dialect; +using NUnit.Framework; + +namespace NHibernate.Test.Stateless +{ + [TestFixture] + public class StatelessSessionCancelQueryFixture : TestCase + { + protected override string MappingsAssembly + { + get { return "NHibernate.Test"; } + } + + protected override string[] Mappings + { + get { return new[] { "Stateless.Document.hbm.xml" }; } + } + + private async Task CancelQueryTest(Action queryAction) + { + if (!(Sfi.Dialect is PostgreSQLDialect)) + { + Assert.Ignore(); + } + + using (IStatelessSession s = Sfi.OpenStatelessSession()) + using (var transactionS = s.BeginTransaction()) + { + var command = s.Connection.CreateCommand(); + command.CommandText = @" +LOCK TABLE Document IN ACCESS EXCLUSIVE MODE; +"; + command.ExecuteNonQuery(); + + using (IStatelessSession ss = Sfi.OpenStatelessSession()) + using (var transactionSS = ss.BeginTransaction()) + { + var queryTask = Task.Factory.StartNew(() => + { + queryAction.Invoke(ss); + }); + + Thread.Sleep(2000); + ss.CancelQuery(); + Assert.CatchAsync(async () => { await queryTask; }); + try + { + await queryTask; + } + catch (Exception ex) + { + if (Sfi.Dialect is PostgreSQLDialect) + { + Assert.IsNotNull(ex.InnerException); + Assert.AreEqual(ex.InnerException.GetType(), typeof(OperationCanceledException)); + } + } + } + } + } + + [Test] + public async Task CancelHqlQuery() + { + await CancelQueryTest((s) => + { + var documents = s.CreateQuery("from Document").List(); + }); + } + + [Test] + public async Task CancelQuery() + { + await CancelQueryTest((s) => + { + var documents = s.Query().ToList(); + }); + } + + [Test] + public async Task CancelSQLQuery() + { + await CancelQueryTest((s) => + { + var documents = s.CreateSQLQuery("select * from Document").List(); + }); + } + } +} From 275fa67a8b37333adb55e051cc1fc3a9550eb2ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= <12201973+fredericdelaporte@users.noreply.github.com> Date: Sun, 26 Jun 2022 20:00:09 +0200 Subject: [PATCH 4/8] Make tests database agnostic --- .../StatelessSessionCancelQueryFixture.cs | 120 ++++++++++++++++++ .../StatelessSessionCancelQueryFixture.cs | 111 +++++++++------- src/NHibernate/IStatelessSession.cs | 5 +- 3 files changed, 187 insertions(+), 49 deletions(-) create mode 100644 src/NHibernate.Test/Async/Stateless/StatelessSessionCancelQueryFixture.cs diff --git a/src/NHibernate.Test/Async/Stateless/StatelessSessionCancelQueryFixture.cs b/src/NHibernate.Test/Async/Stateless/StatelessSessionCancelQueryFixture.cs new file mode 100644 index 00000000000..df27857d41c --- /dev/null +++ b/src/NHibernate.Test/Async/Stateless/StatelessSessionCancelQueryFixture.cs @@ -0,0 +1,120 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by AsyncGenerator. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + + +using System; +using System.Data; +using System.Data.SqlClient; +using System.Globalization; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using NHibernate.Linq; +using NUnit.Framework; + +namespace NHibernate.Test.Stateless +{ + [TestFixture] + public class StatelessSessionCancelQueryFixtureAsync : TestCase + { + protected override string MappingsAssembly + { + get { return "NHibernate.Test"; } + } + + protected override string[] Mappings + { + get { return new[] { "Stateless.Document.hbm.xml" }; } + } + + private const string _documentName = "SomeDocument"; + private CultureInfo _backupCulture; + private CultureInfo _backupUICulture; + + protected override void OnSetUp() + { + if (CultureInfo.CurrentCulture.TwoLetterISOLanguageName != CultureInfo.InvariantCulture.TwoLetterISOLanguageName) + { + // This test needs to run in English + _backupCulture = CultureInfo.CurrentCulture; + _backupUICulture = CultureInfo.CurrentUICulture; + CultureInfo.CurrentCulture = CultureInfo.InvariantCulture; + CultureInfo.CurrentUICulture = CultureInfo.InvariantCulture; + } + + using (var s = Sfi.OpenStatelessSession()) + using (var t = s.BeginTransaction()) + { + s.Insert(new Document("Some text", _documentName)); + t.Commit(); + } + } + + protected override void OnTearDown() + { + using (var s = Sfi.OpenStatelessSession()) + using (var t = s.BeginTransaction()) + { + s.CreateQuery("delete Document").ExecuteUpdate(); + t.Commit(); + } + + if (_backupCulture != null) + { + CultureInfo.CurrentCulture = _backupCulture; + CultureInfo.CurrentUICulture = _backupUICulture; + } + } + + protected override bool AppliesTo(Dialect.Dialect dialect) + { + return TestDialect.SupportsSelectForUpdate; + } + + private async Task CancelQueryTestAsync(Action queryAction, CancellationToken cancellationToken = default(CancellationToken)) + { + using (var s1 = Sfi.OpenStatelessSession()) + using (var t1 = s1.BeginTransaction()) + { + await (s1.GetAsync(_documentName, LockMode.Upgrade, cancellationToken)); + + using (var s2 = Sfi.OpenStatelessSession()) + using (var t2 = s2.BeginTransaction()) + { + var queryTask = Task.Factory.StartNew(() => queryAction(s2)); + + await (Task.Delay(200, cancellationToken)); + s2.CancelQuery(); + Assert.That(() => queryTask, + Throws.InnerException.TypeOf(typeof(OperationCanceledException)) + .Or.InnerException.Message.Contains("cancelled") + .Or.InnerException.Message.Contains("canceled")); + } + } + } + + [Test] + public async Task CancelHqlQueryAsync() + { + await (CancelQueryTestAsync(s => s.CreateQuery("from Document d").SetLockMode("d", LockMode.Upgrade).List())); + } + + [Test] + public async Task CancelLinqQueryAsync() + { + await (CancelQueryTestAsync(s => s.Query().WithLock(LockMode.Upgrade).ToList())); + } + + [Test] + public async Task CancelQueryOverQueryAsync() + { + await (CancelQueryTestAsync(s => s.QueryOver().Lock().Upgrade.List())); + } + } +} diff --git a/src/NHibernate.Test/Stateless/StatelessSessionCancelQueryFixture.cs b/src/NHibernate.Test/Stateless/StatelessSessionCancelQueryFixture.cs index 6e69da3b94b..f5b70060b68 100644 --- a/src/NHibernate.Test/Stateless/StatelessSessionCancelQueryFixture.cs +++ b/src/NHibernate.Test/Stateless/StatelessSessionCancelQueryFixture.cs @@ -1,8 +1,11 @@ using System; +using System.Data; +using System.Data.SqlClient; +using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; -using NHibernate.Dialect; +using NHibernate.Linq; using NUnit.Framework; namespace NHibernate.Test.Stateless @@ -20,74 +23,88 @@ protected override string[] Mappings get { return new[] { "Stateless.Document.hbm.xml" }; } } - private async Task CancelQueryTest(Action queryAction) + private const string _documentName = "SomeDocument"; + private CultureInfo _backupCulture; + private CultureInfo _backupUICulture; + + protected override void OnSetUp() + { + if (CultureInfo.CurrentCulture.TwoLetterISOLanguageName != CultureInfo.InvariantCulture.TwoLetterISOLanguageName) + { + // This test needs to run in English + _backupCulture = CultureInfo.CurrentCulture; + _backupUICulture = CultureInfo.CurrentUICulture; + CultureInfo.CurrentCulture = CultureInfo.InvariantCulture; + CultureInfo.CurrentUICulture = CultureInfo.InvariantCulture; + } + + using (var s = Sfi.OpenStatelessSession()) + using (var t = s.BeginTransaction()) + { + s.Insert(new Document("Some text", _documentName)); + t.Commit(); + } + } + + protected override void OnTearDown() { - if (!(Sfi.Dialect is PostgreSQLDialect)) + using (var s = Sfi.OpenStatelessSession()) + using (var t = s.BeginTransaction()) + { + s.CreateQuery("delete Document").ExecuteUpdate(); + t.Commit(); + } + + if (_backupCulture != null) { - Assert.Ignore(); + CultureInfo.CurrentCulture = _backupCulture; + CultureInfo.CurrentUICulture = _backupUICulture; } + } + + protected override bool AppliesTo(Dialect.Dialect dialect) + { + return TestDialect.SupportsSelectForUpdate; + } - using (IStatelessSession s = Sfi.OpenStatelessSession()) - using (var transactionS = s.BeginTransaction()) + private void CancelQueryTest(Action queryAction) + { + using (var s1 = Sfi.OpenStatelessSession()) + using (var t1 = s1.BeginTransaction()) { - var command = s.Connection.CreateCommand(); - command.CommandText = @" -LOCK TABLE Document IN ACCESS EXCLUSIVE MODE; -"; - command.ExecuteNonQuery(); + s1.Get(_documentName, LockMode.Upgrade); - using (IStatelessSession ss = Sfi.OpenStatelessSession()) - using (var transactionSS = ss.BeginTransaction()) + using (var s2 = Sfi.OpenStatelessSession()) + using (var t2 = s2.BeginTransaction()) { - var queryTask = Task.Factory.StartNew(() => - { - queryAction.Invoke(ss); - }); + var queryTask = Task.Factory.StartNew(() => queryAction(s2)); - Thread.Sleep(2000); - ss.CancelQuery(); - Assert.CatchAsync(async () => { await queryTask; }); - try - { - await queryTask; - } - catch (Exception ex) - { - if (Sfi.Dialect is PostgreSQLDialect) - { - Assert.IsNotNull(ex.InnerException); - Assert.AreEqual(ex.InnerException.GetType(), typeof(OperationCanceledException)); - } - } + Thread.Sleep(200); + s2.CancelQuery(); + Assert.That(() => queryTask, + Throws.InnerException.TypeOf(typeof(OperationCanceledException)) + .Or.InnerException.Message.Contains("cancelled") + .Or.InnerException.Message.Contains("canceled")); } } } [Test] - public async Task CancelHqlQuery() + public void CancelHqlQuery() { - await CancelQueryTest((s) => - { - var documents = s.CreateQuery("from Document").List(); - }); + CancelQueryTest(s => s.CreateQuery("from Document d").SetLockMode("d", LockMode.Upgrade).List()); } [Test] - public async Task CancelQuery() + public void CancelLinqQuery() { - await CancelQueryTest((s) => - { - var documents = s.Query().ToList(); - }); + CancelQueryTest(s => s.Query().WithLock(LockMode.Upgrade).ToList()); } [Test] - public async Task CancelSQLQuery() + public void CancelQueryOverQuery() { - await CancelQueryTest((s) => - { - var documents = s.CreateSQLQuery("select * from Document").List(); - }); + CancelQueryTest(s => s.QueryOver().Lock().Upgrade.List()); } } } diff --git a/src/NHibernate/IStatelessSession.cs b/src/NHibernate/IStatelessSession.cs index aa824e0d557..aa64e0188a9 100644 --- a/src/NHibernate/IStatelessSession.cs +++ b/src/NHibernate/IStatelessSession.cs @@ -84,9 +84,10 @@ public static void FlushBatcher(this IStatelessSession session) /// public static void CancelQuery(this IStatelessSession session) { - using (session.GetSessionImplementation().BeginProcess()) + var implementation = session.GetSessionImplementation(); + using (implementation.BeginProcess()) { - session.GetSessionImplementation().Batcher.CancelLastQuery(); + implementation.Batcher.CancelLastQuery(); } } } From f512fefc0531eaa8695a84b95d7aa6605620135a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= <12201973+fredericdelaporte@users.noreply.github.com> Date: Sun, 26 Jun 2022 22:23:45 +0200 Subject: [PATCH 5/8] Improve the MySql cancelation case --- src/NHibernate/AdoNet/AbstractBatcher.cs | 5 +++++ src/NHibernate/Async/AdoNet/AbstractBatcher.cs | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/src/NHibernate/AdoNet/AbstractBatcher.cs b/src/NHibernate/AdoNet/AbstractBatcher.cs index 520b4e152a3..99522150a2a 100644 --- a/src/NHibernate/AdoNet/AbstractBatcher.cs +++ b/src/NHibernate/AdoNet/AbstractBatcher.cs @@ -253,6 +253,11 @@ private DbDataReader DoExecuteReader(DbCommand cmd) try { var reader = cmd.ExecuteReader(); + if (reader == null) + { + // MySql may return null instead of an exception, by example when the query is canceled by another thread. + throw new InvalidOperationException("The query execution has yielded a null reader. (Has it been canceled?)"); + } return _factory.ConnectionProvider.Driver.SupportsMultipleOpenReaders ? reader : NHybridDataReader.Create(reader); diff --git a/src/NHibernate/Async/AdoNet/AbstractBatcher.cs b/src/NHibernate/Async/AdoNet/AbstractBatcher.cs index ed60b6c704c..21168ef3ea6 100644 --- a/src/NHibernate/Async/AdoNet/AbstractBatcher.cs +++ b/src/NHibernate/Async/AdoNet/AbstractBatcher.cs @@ -168,6 +168,11 @@ private async Task DoExecuteReaderAsync(DbCommand cmd, Cancellatio try { var reader = await (cmd.ExecuteReaderAsync(cancellationToken)).ConfigureAwait(false); + if (reader == null) + { + // MySql may return null instead of an exception, by example when the query is canceled by another thread. + throw new InvalidOperationException("The query execution has yielded a null reader. (Has it been canceled?)"); + } return _factory.ConnectionProvider.Driver.SupportsMultipleOpenReaders ? reader : await (NHybridDataReader.CreateAsync(reader, cancellationToken)).ConfigureAwait(false); From dcbff9755d968a359cbf205d45f6bdbef8dbf09a Mon Sep 17 00:00:00 2001 From: Vyacheslav Date: Wed, 29 Jun 2022 05:30:19 +0300 Subject: [PATCH 6/8] Fixed failed test on Oracle. Changed on 'cancel': In oracle message contain word 'cancel'(ORA-01013: user requested cancel of current operation) and not 'cancelled' or 'canceled'. Word 'cancel' contains in message for all database types. --- .../Stateless/StatelessSessionCancelQueryFixture.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/NHibernate.Test/Stateless/StatelessSessionCancelQueryFixture.cs b/src/NHibernate.Test/Stateless/StatelessSessionCancelQueryFixture.cs index f5b70060b68..2f2461aff2e 100644 --- a/src/NHibernate.Test/Stateless/StatelessSessionCancelQueryFixture.cs +++ b/src/NHibernate.Test/Stateless/StatelessSessionCancelQueryFixture.cs @@ -83,8 +83,7 @@ private void CancelQueryTest(Action queryAction) s2.CancelQuery(); Assert.That(() => queryTask, Throws.InnerException.TypeOf(typeof(OperationCanceledException)) - .Or.InnerException.Message.Contains("cancelled") - .Or.InnerException.Message.Contains("canceled")); + .Or.InnerException.Message.Contains("cancel")); } } } From ddfbde4337a97489794b9ddbf08cbc62b2dbe5b3 Mon Sep 17 00:00:00 2001 From: Vyacheslav Date: Wed, 29 Jun 2022 06:11:03 +0300 Subject: [PATCH 7/8] Regenerate async code --- .../Async/Stateless/StatelessSessionCancelQueryFixture.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/NHibernate.Test/Async/Stateless/StatelessSessionCancelQueryFixture.cs b/src/NHibernate.Test/Async/Stateless/StatelessSessionCancelQueryFixture.cs index df27857d41c..1f04d92375b 100644 --- a/src/NHibernate.Test/Async/Stateless/StatelessSessionCancelQueryFixture.cs +++ b/src/NHibernate.Test/Async/Stateless/StatelessSessionCancelQueryFixture.cs @@ -93,8 +93,7 @@ protected override bool AppliesTo(Dialect.Dialect dialect) s2.CancelQuery(); Assert.That(() => queryTask, Throws.InnerException.TypeOf(typeof(OperationCanceledException)) - .Or.InnerException.Message.Contains("cancelled") - .Or.InnerException.Message.Contains("canceled")); + .Or.InnerException.Message.Contains("cancel")); } } } From 9ac65b239ddccbb6fb70559430cfe32ed486a3c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= <12201973+fredericdelaporte@users.noreply.github.com> Date: Sun, 25 Sep 2022 20:18:27 +0200 Subject: [PATCH 8/8] Ignore cancel query test under linux for Sql Server and Oracle --- .../Async/Stateless/StatelessSessionCancelQueryFixture.cs | 3 ++- .../Stateless/StatelessSessionCancelQueryFixture.cs | 3 ++- src/NHibernate.Test/TestDialect.cs | 5 +++++ src/NHibernate.Test/TestDialects/MsSql2008TestDialect.cs | 8 +++++++- src/NHibernate.Test/TestDialects/Oracle10gTestDialect.cs | 8 +++++++- 5 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/NHibernate.Test/Async/Stateless/StatelessSessionCancelQueryFixture.cs b/src/NHibernate.Test/Async/Stateless/StatelessSessionCancelQueryFixture.cs index 1f04d92375b..b89528ef58c 100644 --- a/src/NHibernate.Test/Async/Stateless/StatelessSessionCancelQueryFixture.cs +++ b/src/NHibernate.Test/Async/Stateless/StatelessSessionCancelQueryFixture.cs @@ -74,7 +74,8 @@ protected override void OnTearDown() protected override bool AppliesTo(Dialect.Dialect dialect) { - return TestDialect.SupportsSelectForUpdate; + return TestDialect.SupportsCancelQuery && + TestDialect.SupportsSelectForUpdate; } private async Task CancelQueryTestAsync(Action queryAction, CancellationToken cancellationToken = default(CancellationToken)) diff --git a/src/NHibernate.Test/Stateless/StatelessSessionCancelQueryFixture.cs b/src/NHibernate.Test/Stateless/StatelessSessionCancelQueryFixture.cs index 2f2461aff2e..a86a430826b 100644 --- a/src/NHibernate.Test/Stateless/StatelessSessionCancelQueryFixture.cs +++ b/src/NHibernate.Test/Stateless/StatelessSessionCancelQueryFixture.cs @@ -64,7 +64,8 @@ protected override void OnTearDown() protected override bool AppliesTo(Dialect.Dialect dialect) { - return TestDialect.SupportsSelectForUpdate; + return TestDialect.SupportsCancelQuery && + TestDialect.SupportsSelectForUpdate; } private void CancelQueryTest(Action queryAction) diff --git a/src/NHibernate.Test/TestDialect.cs b/src/NHibernate.Test/TestDialect.cs index 4e77c7e0460..49b2f7e0f69 100644 --- a/src/NHibernate.Test/TestDialect.cs +++ b/src/NHibernate.Test/TestDialect.cs @@ -198,5 +198,10 @@ public bool SupportsSqlType(SqlType sqlType) /// Returns true if you can modify the same table which you use in the SELECT part. /// public virtual bool SupportsModifyAndSelectSameTable => true; + + /// + /// Returns true if you can cancel a query. + /// + public virtual bool SupportsCancelQuery => true; } } diff --git a/src/NHibernate.Test/TestDialects/MsSql2008TestDialect.cs b/src/NHibernate.Test/TestDialects/MsSql2008TestDialect.cs index bf4c375e761..ec1d00fb563 100644 --- a/src/NHibernate.Test/TestDialects/MsSql2008TestDialect.cs +++ b/src/NHibernate.Test/TestDialects/MsSql2008TestDialect.cs @@ -1,4 +1,6 @@ -namespace NHibernate.Test.TestDialects +using System.Runtime.InteropServices; + +namespace NHibernate.Test.TestDialects { public class MsSql2008TestDialect : TestDialect { @@ -11,5 +13,9 @@ public MsSql2008TestDialect(Dialect.Dialect dialect) /// Does not support SELECT FOR UPDATE with paging /// public override bool SupportsSelectForUpdateWithPaging => false; + + /// + /// Canceling a query hangs under Linux with Sql2008ClientDriver. (It may be a data provider bug fixed with MicrosoftDataSqlClientDriver.) + public override bool SupportsCancelQuery => !RuntimeInformation.IsOSPlatform(OSPlatform.Linux); } } diff --git a/src/NHibernate.Test/TestDialects/Oracle10gTestDialect.cs b/src/NHibernate.Test/TestDialects/Oracle10gTestDialect.cs index dcedb6b60ac..4a5fb8d2322 100644 --- a/src/NHibernate.Test/TestDialects/Oracle10gTestDialect.cs +++ b/src/NHibernate.Test/TestDialects/Oracle10gTestDialect.cs @@ -1,4 +1,6 @@ -namespace NHibernate.Test.TestDialects +using System.Runtime.InteropServices; + +namespace NHibernate.Test.TestDialects { public class Oracle10gTestDialect : TestDialect { @@ -12,5 +14,9 @@ public Oracle10gTestDialect(Dialect.Dialect dialect) : base(dialect) public override bool SupportsSelectForUpdateWithPaging => false; public override bool SupportsAggregateInSubSelect => true; + + /// + /// Canceling a query hangs under Linux with OracleManagedDataClientDriver 21.6.1. + public override bool SupportsCancelQuery => !RuntimeInformation.IsOSPlatform(OSPlatform.Linux); } }