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) {