From 8d5c7eb4e93fe5675f18483c4136fec13605a24f Mon Sep 17 00:00:00 2001 From: Jan Schatteman Date: Mon, 27 May 2024 22:26:18 +0200 Subject: [PATCH 1/3] HHH-18069 - Add test Signed-off-by: Jan Schatteman --- .../hql/set/UnionOfPartitionResultsTest.java | 139 ++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/set/UnionOfPartitionResultsTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/set/UnionOfPartitionResultsTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/set/UnionOfPartitionResultsTest.java new file mode 100644 index 000000000000..5894908cf34e --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/set/UnionOfPartitionResultsTest.java @@ -0,0 +1,139 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.query.hql.set; + +import java.time.LocalDate; + +import org.hibernate.query.Query; + +import org.hibernate.testing.orm.junit.DialectFeatureChecks; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.FailureExpected; +import org.hibernate.testing.orm.junit.RequiresDialectFeature; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; + +/** + * @author Jan Schatteman + */ +@DomainModel ( + annotatedClasses = { UnionOfPartitionResultsTest.Apple.class, UnionOfPartitionResultsTest.Pie.class } +) +@SessionFactory +public class UnionOfPartitionResultsTest { + + @Test + @FailureExpected + @RequiresDialectFeature(feature = DialectFeatureChecks.SupportsUnion.class) + public void testUnionOfPartitionResults(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + String q = + "SELECT new CurrentApple(id, bakedPie.id, dir) " + + "FROM (" + + "(" + + "SELECT id id, bakedPie bakedPie, bakedOn bakedOn, MAX(bakedOn) OVER (PARTITION BY bakedPie.id) mbo, -1 dir " + + "FROM Apple c " + + "WHERE bakedPie.id IN (1,2,3,4) AND bakedOn <= :now" + + ") UNION ALL (" + + "SELECT id id, bakedPie bakedPie, bakedOn bakedOn, MIN(bakedOn) OVER (PARTITION BY bakedPie.id) mbo, 1 dir " + + "FROM Apple c " + + "WHERE bakedPie.id IN (1,2,3,4) AND bakedOn > :now" + + ")" + + ") " + + "WHERE bakedOn = mbo ORDER BY dir"; + + Query query = session.createQuery( q, CurrentApple.class ); + query.setParameter( "now", LocalDate.now()); + + query.getSingleResult(); + } + ); + } + + public static class CurrentApple { + private final int id; + private final int pieId; + private final int dir; + + public CurrentApple(int id, int pieId, int dir) { + this.id = id; + this.pieId = pieId; + this.dir = dir; + } + public int getDir() { + return dir; + } + public int getId() { + return id; + } + public int getPieId() { + return pieId; + } + } + + @Entity(name = "Apple") + public static class Apple { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + private LocalDate bakedOn; + @ManyToOne + private Pie bakedPie; + + public Integer getId() { + return id; + } + public Apple setId(Integer id) { + this.id = id; + return this; + } + public LocalDate getBakedOn() { + return bakedOn; + } + public Apple setBakedOn(LocalDate bakedOn) { + this.bakedOn = bakedOn; + return this; + } + public Pie getBakedPie() { + return bakedPie; + } + public Apple setBakedPie(Pie bakedPie) { + this.bakedPie = bakedPie; + return this; + } + } + + @Entity(name = "Pie") + public static class Pie { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + private String taste; + + public Integer getId() { + return id; + } + public Pie setId(Integer id) { + this.id = id; + return this; + } + public String getTaste() { + return taste; + } + public Pie setTaste(String taste) { + this.taste = taste; + return this; + } + } + +} From a12582f0653a5fce8c0e70fe7c290b9786c903a0 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Wed, 27 Nov 2024 11:49:21 +0100 Subject: [PATCH 2/3] HHH-18069 - Add test --- .../hql/set/UnionOfPartitionResultsTest.java | 104 +++++++++++++++--- 1 file changed, 90 insertions(+), 14 deletions(-) diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/set/UnionOfPartitionResultsTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/set/UnionOfPartitionResultsTest.java index 5894908cf34e..ff9896246bb6 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/set/UnionOfPartitionResultsTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/set/UnionOfPartitionResultsTest.java @@ -10,7 +10,7 @@ import org.hibernate.testing.orm.junit.DialectFeatureChecks; import org.hibernate.testing.orm.junit.DomainModel; -import org.hibernate.testing.orm.junit.FailureExpected; +import org.hibernate.testing.orm.junit.JiraKey; import org.hibernate.testing.orm.junit.RequiresDialectFeature; import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; @@ -25,41 +25,106 @@ /** * @author Jan Schatteman */ -@DomainModel ( - annotatedClasses = { UnionOfPartitionResultsTest.Apple.class, UnionOfPartitionResultsTest.Pie.class } +@DomainModel( + annotatedClasses = {UnionOfPartitionResultsTest.Apple.class, UnionOfPartitionResultsTest.Pie.class} ) @SessionFactory +@JiraKey( "HHH-18069" ) public class UnionOfPartitionResultsTest { @Test - @FailureExpected @RequiresDialectFeature(feature = DialectFeatureChecks.SupportsUnion.class) + public void testSubqueryWithUnion(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + String q = "SELECT id " + + "FROM " + + "( " + + "( SELECT id id, bakedPie bakedPie " + + "\tFROM Apple c " + + ") " + + "\tUNION ALL " + + "( SELECT id id, bakedPie bakedPie " + + "\tFROM Apple a " + + ") " + + ")"; + + Query query = session.createQuery( q ); + + query.list(); + } + ); + } + + @Test + public void testSubquery(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + String q = "SELECT id " + + "FROM " + + "( " + + "\tSELECT id id, bakedPie bakedPie " + + "\tFROM Apple c " + + ")"; + + Query query = session.createQuery( q ); + + query.list(); + } + ); + } + + @Test + @RequiresDialectFeature(feature = DialectFeatureChecks.SupportsUnion.class) + public void testUnionQuery(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + String q = "( SELECT id id, bakedPie bakedPie " + + "\tFROM Apple c " + + ") " + + "\tUNION ALL " + + "( SELECT id id, bakedPie bakedPie " + + "\tFROM Apple c " + + ") "; + + Query query = session.createQuery( q ); + + query.list(); + } + ); + } + + + @Test + @RequiresDialectFeature(feature = DialectFeatureChecks.SupportsUnion.class) + @RequiresDialectFeature(feature = DialectFeatureChecks.SupportPartitionBy.class) public void testUnionOfPartitionResults(SessionFactoryScope scope) { scope.inTransaction( session -> { String q = "SELECT new CurrentApple(id, bakedPie.id, dir) " + "FROM (" + - "(" + - "SELECT id id, bakedPie bakedPie, bakedOn bakedOn, MAX(bakedOn) OVER (PARTITION BY bakedPie.id) mbo, -1 dir " + - "FROM Apple c " + - "WHERE bakedPie.id IN (1,2,3,4) AND bakedOn <= :now" + - ") UNION ALL (" + - "SELECT id id, bakedPie bakedPie, bakedOn bakedOn, MIN(bakedOn) OVER (PARTITION BY bakedPie.id) mbo, 1 dir " + - "FROM Apple c " + - "WHERE bakedPie.id IN (1,2,3,4) AND bakedOn > :now" + - ")" + + "(" + + "SELECT id id, bakedPie bakedPie, bakedOn bakedOn, MAX(bakedOn) OVER (PARTITION BY bakedPie.id) mbo, -1 dir " + + "FROM Apple c " + + "WHERE bakedPie.id IN (1,2,3,4) AND bakedOn <= :now" + + ") UNION ALL (" + + "SELECT id id, bakedPie bakedPie, bakedOn bakedOn, MIN(bakedOn) OVER (PARTITION BY bakedPie.id) mbo, 1 dir " + + "FROM Apple c " + + "WHERE bakedPie.id IN (1,2,3,4) AND bakedOn > :now" + + ")" + ") " + "WHERE bakedOn = mbo ORDER BY dir"; Query query = session.createQuery( q, CurrentApple.class ); query.setParameter( "now", LocalDate.now()); - query.getSingleResult(); + query.list(); } ); } + public static class CurrentApple { private final int id; private final int pieId; @@ -70,12 +135,15 @@ public CurrentApple(int id, int pieId, int dir) { this.pieId = pieId; this.dir = dir; } + public int getDir() { return dir; } + public int getId() { return id; } + public int getPieId() { return pieId; } @@ -93,20 +161,25 @@ public static class Apple { public Integer getId() { return id; } + public Apple setId(Integer id) { this.id = id; return this; } + public LocalDate getBakedOn() { return bakedOn; } + public Apple setBakedOn(LocalDate bakedOn) { this.bakedOn = bakedOn; return this; } + public Pie getBakedPie() { return bakedPie; } + public Apple setBakedPie(Pie bakedPie) { this.bakedPie = bakedPie; return this; @@ -123,13 +196,16 @@ public static class Pie { public Integer getId() { return id; } + public Pie setId(Integer id) { this.id = id; return this; } + public String getTaste() { return taste; } + public Pie setTaste(String taste) { this.taste = taste; return this; From 2602ba7985473e49aa178a9de49feb74aca7a597 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Wed, 27 Nov 2024 11:50:01 +0100 Subject: [PATCH 3/3] HHH-18069 NullPointerException when unioning partition results --- .../hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java index c35cd0a9fb63..60c28bae449e 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java @@ -1990,9 +1990,13 @@ public QueryGroup visitQueryGroup(SqmQueryGroup queryGroup) { sqmQueryPartStack.push( queryGroup ); pushProcessingState( processingState ); + FromClauseIndex firstQueryPartIndex = null; + SqlAstProcessingState firstPoppedProcessingState = null; try { newQueryParts.add( visitQueryPart( queryParts.get( 0 ) ) ); + firstQueryPartIndex = lastPoppedFromClauseIndex; + firstPoppedProcessingState = lastPoppedProcessingState; collector.setSqmAliasedNodeCollector( (SqmAliasedNodeCollector) lastPoppedProcessingState.getSqlExpressionResolver() ); @@ -2010,6 +2014,8 @@ public QueryGroup visitQueryGroup(SqmQueryGroup queryGroup) { finally { popProcessingStateStack(); sqmQueryPartStack.pop(); + lastPoppedFromClauseIndex = firstQueryPartIndex; + lastPoppedProcessingState = firstPoppedProcessingState; } }