diff --git a/src/NHibernate.Test/Async/NHSpecificTest/GH3474/Fixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/GH3474/Fixture.cs
new file mode 100644
index 00000000000..6aacc68bcbc
--- /dev/null
+++ b/src/NHibernate.Test/Async/NHSpecificTest/GH3474/Fixture.cs
@@ -0,0 +1,81 @@
+//------------------------------------------------------------------------------
+//
+// 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.Linq;
+using NUnit.Framework;
+using NHibernate.Linq;
+
+namespace NHibernate.Test.NHSpecificTest.GH3474
+{
+ using System.Threading.Tasks;
+ [TestFixture]
+ public class FixtureAsync : BugTestCase
+ {
+ protected override void OnSetUp()
+ {
+ using var session = OpenSession();
+ using var transaction = session.BeginTransaction();
+
+ var e1 = new CreditCardPayment { CreditCardType = "Visa", Amount = 50 };
+ session.Save(e1);
+
+ var e2 = new ChequePayment { Bank = "CA", Amount = 32 };
+ session.Save(e2);
+
+ var e3 = new CashPayment { Amount = 18.5m };
+ session.Save(e3);
+
+ transaction.Commit();
+ }
+
+ protected override void OnTearDown()
+ {
+ using var session = OpenSession();
+ using var transaction = session.BeginTransaction();
+
+ // The HQL delete does all the job inside the database without loading the entities, but it does
+ // not handle delete order for avoiding violating constraints if any. Use
+ // session.Delete("from System.Object");
+ // instead if in need of having NHibernate ordering the deletes, but this will cause
+ // loading the entities in the session.
+ session.CreateQuery("delete from System.Object").ExecuteUpdate();
+
+ transaction.Commit();
+ }
+
+ [Test]
+ public async Task PolymorphicUpdateShouldNotCommitAsync()
+ {
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ var payment = await (session.Query().FirstAsync());
+ payment.Amount = 100;
+ await (session.FlushAsync());
+
+ await (session.CreateQuery("update ChequePayment set Amount = 64").ExecuteUpdateAsync());
+
+ await (transaction.RollbackAsync());
+ }
+
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ IPayment payment = await (session.Query().FirstAsync());
+ Assert.That(payment.Amount, Is.EqualTo(50m));
+
+ payment = await (session.Query().FirstAsync());
+ Assert.That(payment.Amount, Is.EqualTo(32m));
+
+ await (transaction.CommitAsync());
+ }
+ }
+ }
+}
diff --git a/src/NHibernate.Test/NHSpecificTest/GH3474/Entities.cs b/src/NHibernate.Test/NHSpecificTest/GH3474/Entities.cs
new file mode 100644
index 00000000000..d00828d29f2
--- /dev/null
+++ b/src/NHibernate.Test/NHSpecificTest/GH3474/Entities.cs
@@ -0,0 +1,30 @@
+using System;
+
+namespace NHibernate.Test.NHSpecificTest.GH3474
+{
+ public interface IPayment
+ {
+ public Guid Id { get; set; }
+ public decimal Amount { get; set; }
+ }
+
+ public class CreditCardPayment : IPayment
+ {
+ public virtual Guid Id { get; set; }
+ public virtual decimal Amount { get; set; }
+ public virtual string CreditCardType { get; set; }
+ }
+
+ public class CashPayment : IPayment
+ {
+ public virtual Guid Id { get; set; }
+ public virtual decimal Amount { get; set; }
+ }
+
+ public class ChequePayment : IPayment
+ {
+ public virtual Guid Id { get; set; }
+ public virtual decimal Amount { get; set; }
+ public virtual string Bank { get; set; }
+ }
+}
diff --git a/src/NHibernate.Test/NHSpecificTest/GH3474/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/GH3474/Fixture.cs
new file mode 100644
index 00000000000..fd76cad70f8
--- /dev/null
+++ b/src/NHibernate.Test/NHSpecificTest/GH3474/Fixture.cs
@@ -0,0 +1,69 @@
+using System.Linq;
+using NUnit.Framework;
+
+namespace NHibernate.Test.NHSpecificTest.GH3474
+{
+ [TestFixture]
+ public class Fixture : BugTestCase
+ {
+ protected override void OnSetUp()
+ {
+ using var session = OpenSession();
+ using var transaction = session.BeginTransaction();
+
+ var e1 = new CreditCardPayment { CreditCardType = "Visa", Amount = 50 };
+ session.Save(e1);
+
+ var e2 = new ChequePayment { Bank = "CA", Amount = 32 };
+ session.Save(e2);
+
+ var e3 = new CashPayment { Amount = 18.5m };
+ session.Save(e3);
+
+ transaction.Commit();
+ }
+
+ protected override void OnTearDown()
+ {
+ using var session = OpenSession();
+ using var transaction = session.BeginTransaction();
+
+ // The HQL delete does all the job inside the database without loading the entities, but it does
+ // not handle delete order for avoiding violating constraints if any. Use
+ // session.Delete("from System.Object");
+ // instead if in need of having NHibernate ordering the deletes, but this will cause
+ // loading the entities in the session.
+ session.CreateQuery("delete from System.Object").ExecuteUpdate();
+
+ transaction.Commit();
+ }
+
+ [Test]
+ public void PolymorphicUpdateShouldNotCommit()
+ {
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ var payment = session.Query().First();
+ payment.Amount = 100;
+ session.Flush();
+
+ session.CreateQuery("update ChequePayment set Amount = 64").ExecuteUpdate();
+
+ transaction.Rollback();
+ }
+
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ IPayment payment = session.Query().First();
+ Assert.That(payment.Amount, Is.EqualTo(50m));
+
+ payment = session.Query().First();
+ Assert.That(payment.Amount, Is.EqualTo(32m));
+
+ transaction.Commit();
+ }
+ }
+ }
+}
diff --git a/src/NHibernate.Test/NHSpecificTest/GH3474/Mappings.hbm.xml b/src/NHibernate.Test/NHSpecificTest/GH3474/Mappings.hbm.xml
new file mode 100644
index 00000000000..6ea3fc8c2b2
--- /dev/null
+++ b/src/NHibernate.Test/NHSpecificTest/GH3474/Mappings.hbm.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/NHibernate/Async/Hql/Ast/ANTLR/Exec/AbstractStatementExecutor.cs b/src/NHibernate/Async/Hql/Ast/ANTLR/Exec/AbstractStatementExecutor.cs
index 26e84fd0b65..0dac2ade9f0 100644
--- a/src/NHibernate/Async/Hql/Ast/ANTLR/Exec/AbstractStatementExecutor.cs
+++ b/src/NHibernate/Async/Hql/Ast/ANTLR/Exec/AbstractStatementExecutor.cs
@@ -162,7 +162,7 @@ public async Task DoWorkAsync(DbConnection connection, DbTransaction transaction
{
stmnt = connection.CreateCommand();
stmnt.Transaction = transaction;
- stmnt.CommandText = "drop table " + persister.TemporaryIdTableName;
+ stmnt.CommandText = $"{session.Factory.Dialect.DropTemporaryTableString} {persister.TemporaryIdTableName}";
await (stmnt.ExecuteNonQueryAsync(cancellationToken)).ConfigureAwait(false);
session.Factory.Settings.SqlStatementLogger.LogCommand(stmnt, FormatStyle.Ddl);
}
diff --git a/src/NHibernate/Dialect/Dialect.cs b/src/NHibernate/Dialect/Dialect.cs
index 9a4db11880e..b917a708db2 100644
--- a/src/NHibernate/Dialect/Dialect.cs
+++ b/src/NHibernate/Dialect/Dialect.cs
@@ -717,28 +717,28 @@ public virtual string GenerateTemporaryTableName(string baseTableName)
///
/// Does the dialect require that temporary table DDL statements occur in
- /// isolation from other statements? This would be the case if the creation
+ /// isolation from other statements? This would be the case if the creation
/// would cause any current transaction to get committed implicitly.
- ///
- /// see the result matrix above.
+ ///
+ /// See the result matrix in the remarks.
///
- /// JDBC defines a standard way to query for this information via the
- /// {@link java.sql.DatabaseMetaData#dataDefinitionCausesTransactionCommit()}
- /// method. However, that does not distinguish between temporary table
- /// DDL and other forms of DDL; MySQL, for example, reports DDL causing a
- /// transaction commit via its driver, even though that is not the case for
- /// temporary table DDL.
- ///
- /// Possible return values and their meanings:
- /// - {@link Boolean#TRUE} - Unequivocally, perform the temporary table DDL in isolation.
- /// - {@link Boolean#FALSE} - Unequivocally, do not perform the temporary table DDL in isolation.
- /// - null - defer to the JDBC driver response in regards to {@link java.sql.DatabaseMetaData#dataDefinitionCausesTransactionCommit()}
- ///
+ /// Possible return values and their meanings:
+ ///
+ /// -
+ ///
+ /// Unequivocally, perform the temporary table DDL in isolation.
+ ///
+ /// -
+ ///
+ /// Unequivocally, do not perform the temporary table DDL in isolation.
+ ///
+ /// -
+ ///
+ /// Defer to .
+ ///
+ ///
///
- public virtual bool? PerformTemporaryTableDDLInIsolation()
- {
- return null;
- }
+ public virtual bool? PerformTemporaryTableDDLInIsolation() => null;
/// Do we need to drop the temporary table after use?
public virtual bool DropTemporaryTableAfterUse()
@@ -2471,6 +2471,9 @@ public virtual string CreateTemporaryTableString
get { return "create table"; }
}
+ /// Command used to drop a temporary table.
+ public virtual string DropTemporaryTableString => "drop table";
+
///
/// Get any fragments needing to be postfixed to the command for
/// temporary table creation.
diff --git a/src/NHibernate/Dialect/MySQLDialect.cs b/src/NHibernate/Dialect/MySQLDialect.cs
index d7b4be07d63..09a5ba03072 100644
--- a/src/NHibernate/Dialect/MySQLDialect.cs
+++ b/src/NHibernate/Dialect/MySQLDialect.cs
@@ -451,6 +451,9 @@ public override string CreateTemporaryTableString
get { return "create temporary table if not exists"; }
}
+ ///
+ public override string DropTemporaryTableString => "drop temporary table";
+
protected virtual void RegisterCastTypes()
{
// According to the MySql documentation (http://dev.mysql.com/doc/refman/4.1/en/cast-functions.html)
diff --git a/src/NHibernate/Dialect/Oracle8iDialect.cs b/src/NHibernate/Dialect/Oracle8iDialect.cs
index 9bbe3e7dfb9..2843db5e58f 100644
--- a/src/NHibernate/Dialect/Oracle8iDialect.cs
+++ b/src/NHibernate/Dialect/Oracle8iDialect.cs
@@ -532,6 +532,11 @@ public override string GenerateTemporaryTableName(String baseTableName)
return name.Length > 30 ? name.Substring(1, (30) - (1)) : name;
}
+ ///
+ /// Oracle does commit any pending transaction prior to executing any DDL,
+ /// included for temporary tables.
+ public override bool? PerformTemporaryTableDDLInIsolation() => true;
+
public override bool DropTemporaryTableAfterUse()
{
return false;
diff --git a/src/NHibernate/Hql/Ast/ANTLR/Exec/AbstractStatementExecutor.cs b/src/NHibernate/Hql/Ast/ANTLR/Exec/AbstractStatementExecutor.cs
index 9c2f86a4fd0..eb6a46f1265 100644
--- a/src/NHibernate/Hql/Ast/ANTLR/Exec/AbstractStatementExecutor.cs
+++ b/src/NHibernate/Hql/Ast/ANTLR/Exec/AbstractStatementExecutor.cs
@@ -298,7 +298,7 @@ public void DoWork(DbConnection connection, DbTransaction transaction)
{
stmnt = connection.CreateCommand();
stmnt.Transaction = transaction;
- stmnt.CommandText = "drop table " + persister.TemporaryIdTableName;
+ stmnt.CommandText = $"{session.Factory.Dialect.DropTemporaryTableString} {persister.TemporaryIdTableName}";
stmnt.ExecuteNonQuery();
session.Factory.Settings.SqlStatementLogger.LogCommand(stmnt, FormatStyle.Ddl);
}