From 0c2ce4f6fd1f0bcf3e397529416fc31edeafd657 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 04a313d7159385e2f64c5bc6c16a31fb89e231c7 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 6f013f295622..39ee98bfe8e0 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 @@ -71,6 +71,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 b8221afd9efc..b9b4006efb7d 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 @@ -52,7 +52,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; } @@ -155,7 +155,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 b72810a691fb..5b49b78c3b0e 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 @@ -81,19 +81,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 ad21e44d7ab6..a2ba0f576e7a 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 @@ -86,7 +86,8 @@ public SqmInsertValuesStatement copy(SqmCopyContext context) { valuesList.add( sqmValues.copy( context ) ); } } - return context.registerCopy( + + final SqmInsertValuesStatement sqmInsertValuesStatementCopy = context.registerCopy( this, new SqmInsertValuesStatement<>( nodeBuilder(), @@ -95,10 +96,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 de46df9bc72aecbee750437a5dda2bff01129dc0 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 | 15 ++++++++++++--- 1 file changed, 12 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 a33393ddf6f2..7536000130b1 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 @@ -189,6 +189,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; @@ -6742,9 +6743,17 @@ 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() ) ); + } + + private boolean isParameterValueEqualToOne(SqmParameterInterpretation sqmParameterInterpretation) { + if ( sqmParameterInterpretation.getResolvedExpression() instanceof JdbcParameterImpl jdbcParameter ) { + assert jdbcParameterBindings != null; + final JdbcParameterBinding binding = jdbcParameterBindings.getBinding( jdbcParameter ); + return binding != null && Integer.valueOf( 1 ).equals( binding.getBindValue() ); + } + return false; } private SelectStatement stripToSelectClause(SelectStatement statement) {