From 11984a06079363411262bc73f2bd5bd54a39b614 Mon Sep 17 00:00:00 2001
From: Mihai Codrean <961778+mihaicodrean@users.noreply.github.com>
Date: Wed, 14 Feb 2024 14:52:34 +0200
Subject: [PATCH 1/5] Add a failing test for insert from select with multiple
associations
---
.../BulkManipulation/HQLBulkOperations.cs | 15 +++++++++-
.../BulkManipulation/Course.cs | 8 +++++
.../BulkManipulation/Enrolment.cs | 12 ++++++++
.../BulkManipulation/Enrolment.hbm.xml | 29 +++++++++++++++++++
.../BulkManipulation/HQLBulkOperations.cs | 15 +++++++++-
.../BulkManipulation/Student.cs | 8 +++++
6 files changed, 85 insertions(+), 2 deletions(-)
create mode 100644 src/NHibernate.Test/BulkManipulation/Course.cs
create mode 100644 src/NHibernate.Test/BulkManipulation/Enrolment.cs
create mode 100644 src/NHibernate.Test/BulkManipulation/Enrolment.hbm.xml
create mode 100644 src/NHibernate.Test/BulkManipulation/Student.cs
diff --git a/src/NHibernate.Test/Async/BulkManipulation/HQLBulkOperations.cs b/src/NHibernate.Test/Async/BulkManipulation/HQLBulkOperations.cs
index 4dd0de91f8e..a61602ecafb 100644
--- a/src/NHibernate.Test/Async/BulkManipulation/HQLBulkOperations.cs
+++ b/src/NHibernate.Test/Async/BulkManipulation/HQLBulkOperations.cs
@@ -43,5 +43,18 @@ public async Task SimpleDeleteAsync()
await (tx.CommitAsync());
}
}
+
+ [Test, KnownBug("#3489")]
+ public async Task InsertFromSelectWithMultipleAssociationsAsync()
+ {
+ using var s = OpenSession();
+ using var tx = s.BeginTransaction();
+
+ await (s.CreateQuery("insert into Enrolment (Course, Student)" +
+ " select e.Course, e.Student from Enrolment e")
+ .ExecuteUpdateAsync());
+
+ await (tx.CommitAsync());
+ }
}
-}
\ No newline at end of file
+}
diff --git a/src/NHibernate.Test/BulkManipulation/Course.cs b/src/NHibernate.Test/BulkManipulation/Course.cs
new file mode 100644
index 00000000000..0ceb699b8f6
--- /dev/null
+++ b/src/NHibernate.Test/BulkManipulation/Course.cs
@@ -0,0 +1,8 @@
+namespace NHibernate.Test.BulkManipulation
+{
+ public class Course
+ {
+ public virtual long CourseId { get; set; }
+ public virtual string Description { get; set; }
+ }
+}
diff --git a/src/NHibernate.Test/BulkManipulation/Enrolment.cs b/src/NHibernate.Test/BulkManipulation/Enrolment.cs
new file mode 100644
index 00000000000..547ecd52640
--- /dev/null
+++ b/src/NHibernate.Test/BulkManipulation/Enrolment.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace NHibernate.Test.BulkManipulation
+{
+ [Serializable]
+ public class Enrolment
+ {
+ public virtual long EnrolmentId { get; set; }
+ public virtual Student Student { get; set; }
+ public virtual Course Course { get; set; }
+ }
+}
diff --git a/src/NHibernate.Test/BulkManipulation/Enrolment.hbm.xml b/src/NHibernate.Test/BulkManipulation/Enrolment.hbm.xml
new file mode 100644
index 00000000000..84e6a0b293d
--- /dev/null
+++ b/src/NHibernate.Test/BulkManipulation/Enrolment.hbm.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/NHibernate.Test/BulkManipulation/HQLBulkOperations.cs b/src/NHibernate.Test/BulkManipulation/HQLBulkOperations.cs
index 66883da2301..c072377f88d 100644
--- a/src/NHibernate.Test/BulkManipulation/HQLBulkOperations.cs
+++ b/src/NHibernate.Test/BulkManipulation/HQLBulkOperations.cs
@@ -32,5 +32,18 @@ public void SimpleDelete()
tx.Commit();
}
}
+
+ [Test, KnownBug("#3489")]
+ public void InsertFromSelectWithMultipleAssociations()
+ {
+ using var s = OpenSession();
+ using var tx = s.BeginTransaction();
+
+ s.CreateQuery("insert into Enrolment (Course, Student)" +
+ " select e.Course, e.Student from Enrolment e")
+ .ExecuteUpdate();
+
+ tx.Commit();
+ }
}
-}
\ No newline at end of file
+}
diff --git a/src/NHibernate.Test/BulkManipulation/Student.cs b/src/NHibernate.Test/BulkManipulation/Student.cs
new file mode 100644
index 00000000000..6dd9468adf3
--- /dev/null
+++ b/src/NHibernate.Test/BulkManipulation/Student.cs
@@ -0,0 +1,8 @@
+namespace NHibernate.Test.BulkManipulation
+{
+ public class Student
+ {
+ public virtual long StudentId { get; set; }
+ public virtual string Name { get; set; }
+ }
+}
From 9265093bfce8eb8362d80a4a157ead27cb98f601 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, 17 Mar 2024 22:20:07 +0100
Subject: [PATCH 2/5] Fix bulk insert from children of same parent
fix #3489
---
.../Async/BulkManipulation/HQLBulkOperations.cs | 2 +-
src/NHibernate.Test/BulkManipulation/HQLBulkOperations.cs | 2 +-
src/NHibernate/Hql/Ast/ANTLR/Tree/SelectClause.cs | 8 ++++++--
3 files changed, 8 insertions(+), 4 deletions(-)
diff --git a/src/NHibernate.Test/Async/BulkManipulation/HQLBulkOperations.cs b/src/NHibernate.Test/Async/BulkManipulation/HQLBulkOperations.cs
index a61602ecafb..d70f8f1efda 100644
--- a/src/NHibernate.Test/Async/BulkManipulation/HQLBulkOperations.cs
+++ b/src/NHibernate.Test/Async/BulkManipulation/HQLBulkOperations.cs
@@ -44,7 +44,7 @@ public async Task SimpleDeleteAsync()
}
}
- [Test, KnownBug("#3489")]
+ [Test]
public async Task InsertFromSelectWithMultipleAssociationsAsync()
{
using var s = OpenSession();
diff --git a/src/NHibernate.Test/BulkManipulation/HQLBulkOperations.cs b/src/NHibernate.Test/BulkManipulation/HQLBulkOperations.cs
index c072377f88d..deb32864c80 100644
--- a/src/NHibernate.Test/BulkManipulation/HQLBulkOperations.cs
+++ b/src/NHibernate.Test/BulkManipulation/HQLBulkOperations.cs
@@ -33,7 +33,7 @@ public void SimpleDelete()
}
}
- [Test, KnownBug("#3489")]
+ [Test]
public void InsertFromSelectWithMultipleAssociations()
{
using var s = OpenSession();
diff --git a/src/NHibernate/Hql/Ast/ANTLR/Tree/SelectClause.cs b/src/NHibernate/Hql/Ast/ANTLR/Tree/SelectClause.cs
index 6119f5642e7..287b2c3c3ab 100644
--- a/src/NHibernate/Hql/Ast/ANTLR/Tree/SelectClause.cs
+++ b/src/NHibernate/Hql/Ast/ANTLR/Tree/SelectClause.cs
@@ -493,10 +493,14 @@ private void RenderNonScalarIdentifiers(
continue;
}
- var node = (IASTNode) e;
- if (processedElements.Add(fromElement))
+ var isNewFrom = processedElements.Add(fromElement);
+ if (isNewFrom)
{
combinedFromElements.Add(fromElement);
+ }
+ var node = (IASTNode) e;
+ if (isNewFrom || node.Type == SqlGenerator.DOT)
+ {
RenderNonScalarIdentifiers(fromElement, inheritedExpressions.ContainsKey(e) ? null : e, appender);
}
else if (!inheritedExpressions.ContainsKey(e) && node.Parent != null)
From c12312fe565f506b032317f6638c60707c4c7572 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?=
<12201973+fredericdelaporte@users.noreply.github.com>
Date: Wed, 20 Mar 2024 01:01:52 +0100
Subject: [PATCH 3/5] Improve the fix
---
.../BulkManipulation/HQLBulkOperations.cs | 3 +++
.../Hql/Ast/ANTLR/Tree/SelectClause.cs | 19 +++++--------------
2 files changed, 8 insertions(+), 14 deletions(-)
diff --git a/src/NHibernate.Test/BulkManipulation/HQLBulkOperations.cs b/src/NHibernate.Test/BulkManipulation/HQLBulkOperations.cs
index deb32864c80..4338c5148b5 100644
--- a/src/NHibernate.Test/BulkManipulation/HQLBulkOperations.cs
+++ b/src/NHibernate.Test/BulkManipulation/HQLBulkOperations.cs
@@ -36,6 +36,9 @@ public void SimpleDelete()
[Test]
public void InsertFromSelectWithMultipleAssociations()
{
+ Assume.That(TestDialect.NativeGeneratorSupportsBulkInsertion,
+ "The dialect does not support a native generator compatible with bulk insertion.");
+
using var s = OpenSession();
using var tx = s.BeginTransaction();
diff --git a/src/NHibernate/Hql/Ast/ANTLR/Tree/SelectClause.cs b/src/NHibernate/Hql/Ast/ANTLR/Tree/SelectClause.cs
index 287b2c3c3ab..079f43fd1fd 100644
--- a/src/NHibernate/Hql/Ast/ANTLR/Tree/SelectClause.cs
+++ b/src/NHibernate/Hql/Ast/ANTLR/Tree/SelectClause.cs
@@ -443,23 +443,22 @@ private void RenderNonScalarSelects(
IList fetchedFromElements)
{
var appender = new ASTAppender(ASTFactory, this);
- var combinedFromElements = new List();
var processedElements = new HashSet();
- RenderNonScalarIdentifiers(appender, processedElements, combinedFromElements, inheritedExpressions);
+ RenderNonScalarIdentifiers(appender, processedElements, inheritedExpressions);
if (Walker.IsShallowQuery)
{
return;
}
// Append fetched elements
- RenderFetchedNonScalarIdentifiers(appender, fetchedFromElements, processedElements, combinedFromElements);
+ RenderFetchedNonScalarIdentifiers(appender, fetchedFromElements, processedElements);
if (currentFromClause.IsScalarSubQuery)
{
return;
}
// Generate the property select tokens.
- foreach (var fromElement in combinedFromElements)
+ foreach (var fromElement in processedElements)
{
RenderNonScalarProperties(appender, fromElement);
}
@@ -482,7 +481,6 @@ private void RenderNonScalarSelects(
private void RenderNonScalarIdentifiers(
ASTAppender appender,
HashSet processedElements,
- List combinedFromElements,
Dictionary inheritedExpressions)
{
foreach (var e in NonScalarExpressions)
@@ -493,13 +491,8 @@ private void RenderNonScalarIdentifiers(
continue;
}
- var isNewFrom = processedElements.Add(fromElement);
- if (isNewFrom)
- {
- combinedFromElements.Add(fromElement);
- }
var node = (IASTNode) e;
- if (isNewFrom || node.Type == SqlGenerator.DOT)
+ if (Walker.IsShallowQuery && node.Type == SqlGenerator.DOT || processedElements.Add(fromElement))
{
RenderNonScalarIdentifiers(fromElement, inheritedExpressions.ContainsKey(e) ? null : e, appender);
}
@@ -513,8 +506,7 @@ private void RenderNonScalarIdentifiers(
private void RenderFetchedNonScalarIdentifiers(
ASTAppender appender,
IList fetchedFromElements,
- HashSet processedElements,
- List combinedFromElements)
+ HashSet processedElements)
{
foreach (var fetchedFromElement in fetchedFromElements)
{
@@ -524,7 +516,6 @@ private void RenderFetchedNonScalarIdentifiers(
}
fetchedFromElement.EntitySuffix = Walker.GetEntitySuffix(fetchedFromElement);
- combinedFromElements.Add(fetchedFromElement);
var fragment = fetchedFromElement.GetIdentifierSelectFragment(fetchedFromElement.EntitySuffix);
if (fragment == null)
{
From daed9520587ac49c1cb1a0001a0fb7c9cc6832d4 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
Date: Wed, 20 Mar 2024 00:03:50 +0000
Subject: [PATCH 4/5] Generate async files
---
.../Async/BulkManipulation/HQLBulkOperations.cs | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/NHibernate.Test/Async/BulkManipulation/HQLBulkOperations.cs b/src/NHibernate.Test/Async/BulkManipulation/HQLBulkOperations.cs
index d70f8f1efda..2bc13bba995 100644
--- a/src/NHibernate.Test/Async/BulkManipulation/HQLBulkOperations.cs
+++ b/src/NHibernate.Test/Async/BulkManipulation/HQLBulkOperations.cs
@@ -47,6 +47,9 @@ public async Task SimpleDeleteAsync()
[Test]
public async Task InsertFromSelectWithMultipleAssociationsAsync()
{
+ Assume.That(TestDialect.NativeGeneratorSupportsBulkInsertion,
+ "The dialect does not support a native generator compatible with bulk insertion.");
+
using var s = OpenSession();
using var tx = s.BeginTransaction();
From de0787e686bf6ebc4a219eec7714c6ff82922486 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?=
<12201973+fredericdelaporte@users.noreply.github.com>
Date: Wed, 20 Mar 2024 01:15:51 +0100
Subject: [PATCH 5/5] Revert an unrelated optimization
---
src/NHibernate/Hql/Ast/ANTLR/Tree/SelectClause.cs | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/src/NHibernate/Hql/Ast/ANTLR/Tree/SelectClause.cs b/src/NHibernate/Hql/Ast/ANTLR/Tree/SelectClause.cs
index 079f43fd1fd..00993891bba 100644
--- a/src/NHibernate/Hql/Ast/ANTLR/Tree/SelectClause.cs
+++ b/src/NHibernate/Hql/Ast/ANTLR/Tree/SelectClause.cs
@@ -443,22 +443,23 @@ private void RenderNonScalarSelects(
IList fetchedFromElements)
{
var appender = new ASTAppender(ASTFactory, this);
+ var combinedFromElements = new List();
var processedElements = new HashSet();
- RenderNonScalarIdentifiers(appender, processedElements, inheritedExpressions);
+ RenderNonScalarIdentifiers(appender, processedElements, combinedFromElements, inheritedExpressions);
if (Walker.IsShallowQuery)
{
return;
}
// Append fetched elements
- RenderFetchedNonScalarIdentifiers(appender, fetchedFromElements, processedElements);
+ RenderFetchedNonScalarIdentifiers(appender, fetchedFromElements, processedElements, combinedFromElements);
if (currentFromClause.IsScalarSubQuery)
{
return;
}
// Generate the property select tokens.
- foreach (var fromElement in processedElements)
+ foreach (var fromElement in combinedFromElements)
{
RenderNonScalarProperties(appender, fromElement);
}
@@ -481,6 +482,7 @@ private void RenderNonScalarSelects(
private void RenderNonScalarIdentifiers(
ASTAppender appender,
HashSet processedElements,
+ List combinedFromElements,
Dictionary inheritedExpressions)
{
foreach (var e in NonScalarExpressions)
@@ -494,6 +496,7 @@ private void RenderNonScalarIdentifiers(
var node = (IASTNode) e;
if (Walker.IsShallowQuery && node.Type == SqlGenerator.DOT || processedElements.Add(fromElement))
{
+ combinedFromElements.Add(fromElement);
RenderNonScalarIdentifiers(fromElement, inheritedExpressions.ContainsKey(e) ? null : e, appender);
}
else if (!inheritedExpressions.ContainsKey(e) && node.Parent != null)
@@ -506,7 +509,8 @@ private void RenderNonScalarIdentifiers(
private void RenderFetchedNonScalarIdentifiers(
ASTAppender appender,
IList fetchedFromElements,
- HashSet processedElements)
+ HashSet processedElements,
+ List combinedFromElements)
{
foreach (var fetchedFromElement in fetchedFromElements)
{
@@ -516,6 +520,7 @@ private void RenderFetchedNonScalarIdentifiers(
}
fetchedFromElement.EntitySuffix = Walker.GetEntitySuffix(fetchedFromElement);
+ combinedFromElements.Add(fetchedFromElement);
var fragment = fetchedFromElement.GetIdentifierSelectFragment(fetchedFromElement.EntitySuffix);
if (fragment == null)
{