From d3505ecccc00395a1c3eff44961d14bfaae583ff Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Thu, 3 Apr 2025 13:58:34 +0200 Subject: [PATCH 1/3] HHH-19314 Add test for issue --- ...flictWithCriteriaCopyTreeEnabledTests.java | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/InsertConflictWithCriteriaCopyTreeEnabledTests.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/InsertConflictWithCriteriaCopyTreeEnabledTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/InsertConflictWithCriteriaCopyTreeEnabledTests.java new file mode 100644 index 000000000000..5df94e23a97d --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/InsertConflictWithCriteriaCopyTreeEnabledTests.java @@ -0,0 +1,92 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.query.hql; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Tuple; +import org.hibernate.cfg.QuerySettings; +import org.hibernate.query.criteria.HibernateCriteriaBuilder; +import org.hibernate.query.criteria.JpaCriteriaInsertSelect; +import org.hibernate.query.criteria.JpaCriteriaInsertValues; +import org.hibernate.query.criteria.JpaCriteriaQuery; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.JiraKey; +import org.hibernate.testing.orm.junit.ServiceRegistry; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.hibernate.testing.orm.junit.Setting; +import org.junit.jupiter.api.Test; + + +@DomainModel( + annotatedClasses = { + InsertConflictWithCriteriaCopyTreeEnabledTests.TestEntity.class, + InsertConflictWithCriteriaCopyTreeEnabledTests.AnotherTestEntity.class, + } +) +@ServiceRegistry( + settings = {@Setting(name = QuerySettings.CRITERIA_COPY_TREE, value = "true")} +) +@SessionFactory +@JiraKey("HHH-19314") +public class InsertConflictWithCriteriaCopyTreeEnabledTests { + + @Test + void createCriteriaInsertValuesTest(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + HibernateCriteriaBuilder cb = session.getCriteriaBuilder(); + + JpaCriteriaInsertValues insertIntoItem = cb + .createCriteriaInsertValues( TestEntity.class ); + insertIntoItem.setInsertionTargetPaths( insertIntoItem.getTarget().get( "id" ) ); + insertIntoItem.values( cb.values( cb.value( 1L ) ) ); + insertIntoItem.onConflict().onConflictDoNothing(); + + session.createMutationQuery( insertIntoItem ).executeUpdate(); + } + ); + } + + @Test + void createCriteriaInsertSelectTest(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + HibernateCriteriaBuilder cb = session.getCriteriaBuilder(); + + JpaCriteriaInsertSelect insertIntoItem = cb + .createCriteriaInsertSelect( TestEntity.class ); + insertIntoItem.setInsertionTargetPaths( insertIntoItem.getTarget().get( "id" ) ); + + JpaCriteriaQuery cq = cb.createQuery( Tuple.class ); + cq.select( cb.tuple( cb.literal( 1 ) ) ); + cq.fetch( 1 ); + insertIntoItem.select( cq ); + insertIntoItem.onConflict().onConflictDoNothing(); + + session.createMutationQuery( insertIntoItem ).executeUpdate(); + } + ); + } + + @Entity(name = "TestEntity") + public static class TestEntity { + @Id + private Long id; + + private String name; + + } + + @Entity(name = "AnotherTestEntity") + public static class AnotherTestEntity { + @Id + private Long id; + + private String name; + + } +} From 4d853c1579bb44457585873bf8e28c0052a9034d Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Thu, 3 Apr 2025 14:04:35 +0200 Subject: [PATCH 2/3] HHH-19314 StackOverflowException when using onConflict with createCriteriaInsertValues and createCriteriaInsertSelect --- .../insert/AbstractSqmInsertStatement.java | 4 +++ .../sqm/tree/insert/SqmConflictClause.java | 4 +-- .../tree/insert/SqmInsertSelectStatement.java | 31 ++++++++++++------- .../tree/insert/SqmInsertValuesStatement.java | 11 +++++-- 4 files changed, 34 insertions(+), 16 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/AbstractSqmInsertStatement.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/AbstractSqmInsertStatement.java index 889e7d6add97..125e90f8d8e8 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/AbstractSqmInsertStatement.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/AbstractSqmInsertStatement.java @@ -89,6 +89,10 @@ protected List> copyInsertionTargetPaths(SqmCopyContext context) { } } + void setConflictClause(SqmConflictClause conflictClause) { + this.conflictClause = conflictClause; + } + protected void verifyInsertTypesMatch( List> insertionTargetPaths, List> expressions) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmConflictClause.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmConflictClause.java index 547e03d94d6c..6c3f23056223 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmConflictClause.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmConflictClause.java @@ -54,7 +54,7 @@ private SqmConflictClause( this.insertStatement = insertStatement; this.excludedRoot = excludedRoot; this.constraintName = constraintName; - this.constraintPaths = Collections.unmodifiableList( constraintPaths ); + this.constraintPaths = constraintPaths == null ? null : Collections.unmodifiableList( constraintPaths ); this.updateAction = updateAction; } @@ -157,7 +157,7 @@ public SqmConflictClause copy(SqmCopyContext context) { insertStatement.copy( context ), excludedRoot.copy( context ), constraintName, - copyOf( constraintPaths, context ), + constraintPaths == null ? null : copyOf( constraintPaths, context ), updateAction == null ? null : updateAction.copy( context ) ) ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmInsertSelectStatement.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmInsertSelectStatement.java index 7cbc113f122b..ba65f8c2ca6c 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmInsertSelectStatement.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmInsertSelectStatement.java @@ -77,19 +77,26 @@ public SqmInsertSelectStatement copy(SqmCopyContext context) { if ( existing != null ) { return existing; } - return context.registerCopy( - this, - new SqmInsertSelectStatement<>( - nodeBuilder(), - context.getQuerySource() == null ? getQuerySource() : context.getQuerySource(), - copyParameters( context ), - copyCteStatements( context ), - getTarget().copy( context ), - copyInsertionTargetPaths( context ), - getConflictClause() == null ? null : getConflictClause().copy( context ), - selectQueryPart.copy( context ) - ) + final SqmInsertSelectStatement sqmInsertSelectStatementCopy = new SqmInsertSelectStatement<>( + nodeBuilder(), + context.getQuerySource() == null ? getQuerySource() : context.getQuerySource(), + copyParameters( context ), + copyCteStatements( context ), + getTarget().copy( context ), + null, + null, + selectQueryPart.copy( context ) ); + + context.registerCopy( this, sqmInsertSelectStatementCopy ); + + sqmInsertSelectStatementCopy.setInsertionTargetPaths( copyInsertionTargetPaths( context ) ); + + if ( getConflictClause() != null ) { + sqmInsertSelectStatementCopy.setConflictClause( getConflictClause().copy( context ) ); + } + + return sqmInsertSelectStatementCopy; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmInsertValuesStatement.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmInsertValuesStatement.java index c4bad1806151..8fe1511a7e02 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmInsertValuesStatement.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmInsertValuesStatement.java @@ -82,7 +82,8 @@ public SqmInsertValuesStatement copy(SqmCopyContext context) { valuesList.add( sqmValues.copy( context ) ); } } - return context.registerCopy( + + final SqmInsertValuesStatement sqmInsertValuesStatementCopy = context.registerCopy( this, new SqmInsertValuesStatement<>( nodeBuilder(), @@ -91,10 +92,16 @@ public SqmInsertValuesStatement copy(SqmCopyContext context) { copyCteStatements( context ), getTarget().copy( context ), copyInsertionTargetPaths( context ), - getConflictClause() == null ? null : getConflictClause().copy( context ), + null, valuesList ) ); + + if ( getConflictClause() != null ) { + sqmInsertValuesStatementCopy.setConflictClause( getConflictClause().copy( context ) ); + } + + return sqmInsertValuesStatementCopy; } public SqmInsertValuesStatement copyWithoutValues(SqmCopyContext context) { From 3211520db67b671168c14e72aae336754db678cd Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Thu, 3 Apr 2025 17:03:18 +0200 Subject: [PATCH 3/3] HHH-19314 Fix fetch clause expression value retrieval --- .../sql/ast/spi/AbstractSqlAstTranslator.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java index 98960c77aa7c..24e1aeaf60a9 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java @@ -190,6 +190,7 @@ import org.hibernate.sql.exec.internal.AbstractJdbcParameter; import org.hibernate.sql.exec.internal.JdbcOperationQueryInsertImpl; import org.hibernate.sql.exec.internal.JdbcParameterBindingImpl; +import org.hibernate.sql.exec.internal.JdbcParameterImpl; import org.hibernate.sql.exec.internal.JdbcParametersImpl; import org.hibernate.sql.exec.internal.SqlTypedMappingJdbcParameter; import org.hibernate.sql.exec.spi.ExecutionContext; @@ -6853,9 +6854,12 @@ private int getSortSelectionIndex(QuerySpec querySpec, SortSpecification sortSpe private boolean isFetchFirstRowOnly(QueryPart queryPart) { return queryPart.getFetchClauseType() == FetchClauseType.ROWS_ONLY - && queryPart.getFetchClauseExpression() instanceof QueryLiteral - && Integer.valueOf( 1 ) - .equals( ( (QueryLiteral) queryPart.getFetchClauseExpression() ).getLiteralValue() ); + && queryPart.getFetchClauseExpression() != null + && Integer.valueOf( 1 ).equals( getLiteralValue( queryPart.getFetchClauseExpression() ) ); + } + + public X getLiteralValue(Expression expression) { + return interpretExpression( expression, jdbcParameterBindings ); } private SelectStatement stripToSelectClause(SelectStatement statement) {