diff --git a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java index 35349889ba..7acb21062e 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java @@ -50,6 +50,7 @@ import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.sqm.ParsingException; import org.hibernate.query.sqm.SqmExpressable; import org.hibernate.query.sqm.SqmPathSource; +import org.hibernate.query.sqm.SqmQuerySource; import org.hibernate.query.sqm.SqmTreeCreationLogger; import org.hibernate.query.sqm.StrictJpaComplianceViolation; import org.hibernate.query.sqm.UnknownEntityException; @@ -131,9 +132,7 @@ import org.hibernate.type.descriptor.java.JavaTypeDescriptor; import org.jboss.logging.Logger; -import org.antlr.v4.runtime.BailErrorStrategy; import org.antlr.v4.runtime.Token; -import org.antlr.v4.runtime.atn.PredictionMode; import static java.util.Arrays.asList; import static org.hibernate.query.hql.internal.HqlParser.IDENTIFIER; @@ -339,7 +338,7 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre creationContext.getNodeBuilder() ); - final SqmDeleteStatement deleteStatement = new SqmDeleteStatement<>( root, creationContext.getNodeBuilder() ); + final SqmDeleteStatement deleteStatement = new SqmDeleteStatement<>( root, SqmQuerySource.HQL, creationContext.getNodeBuilder() ); parameterCollector = deleteStatement; diff --git a/hibernate-core/src/main/java/org/hibernate/query/spi/QueryEngine.java b/hibernate-core/src/main/java/org/hibernate/query/spi/QueryEngine.java index 73db7f4b65..c4ba874fba 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/spi/QueryEngine.java +++ b/hibernate-core/src/main/java/org/hibernate/query/spi/QueryEngine.java @@ -34,12 +34,14 @@ import org.hibernate.query.sqm.internal.SqmCriteriaNodeBuilder; import org.hibernate.query.sqm.produce.function.SqmFunctionRegistry; import org.hibernate.query.sqm.spi.SqmCreationContext; import org.hibernate.query.sqm.sql.SimpleSqmDeleteTranslator; +import org.hibernate.query.sqm.sql.SimpleSqmUpdateTranslator; import org.hibernate.query.sqm.sql.SqmInsertSelectTranslator; import org.hibernate.query.sqm.sql.SqmSelectTranslator; import org.hibernate.query.sqm.sql.SqmTranslatorFactory; import org.hibernate.query.sqm.sql.internal.StandardSqmDeleteTranslator; import org.hibernate.query.sqm.sql.internal.StandardSqmInsertSelectTranslator; import org.hibernate.query.sqm.sql.internal.StandardSqmSelectTranslator; +import org.hibernate.query.sqm.sql.internal.StandardSqmUpdateTranslator; import org.hibernate.service.ServiceRegistry; import org.hibernate.sql.ast.spi.SqlAstCreationContext; @@ -193,6 +195,21 @@ public class QueryEngine { domainParameterBindings ); } + + @Override + public SimpleSqmUpdateTranslator createSimpleUpdateTranslator( + QueryOptions queryOptions, + DomainParameterXref domainParameterXref, + QueryParameterBindings queryParameterBindings, + LoadQueryInfluencers loadQueryInfluencers, + SessionFactoryImplementor factory) { + return new StandardSqmUpdateTranslator( + factory, + queryOptions, + domainParameterXref, + queryParameterBindings + ); + } }; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/SemanticQueryWalker.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/SemanticQueryWalker.java index 64ddaabcbb..2edacf1367 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/SemanticQueryWalker.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/SemanticQueryWalker.java @@ -7,6 +7,8 @@ package org.hibernate.query.sqm; import org.hibernate.NotYetImplementedFor6Exception; +import org.hibernate.query.sqm.tree.cte.SqmCteConsumer; +import org.hibernate.query.sqm.tree.cte.SqmCteStatement; import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; import org.hibernate.query.sqm.tree.domain.SqmBasicValuedSimplePath; import org.hibernate.query.sqm.tree.domain.SqmCorrelation; @@ -96,6 +98,10 @@ public interface SemanticQueryWalker { T visitSelectStatement(SqmSelectStatement statement); + T visitCteStatement(SqmCteStatement sqmCteStatement); + + T visitCteConsumer(SqmCteConsumer consumer); + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // from-clause / domain paths diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/DeleteQueryPlanImpl.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/DeleteQueryPlanImpl.java deleted file mode 100644 index e9d0b89f36..0000000000 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/DeleteQueryPlanImpl.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Hibernate, Relational Persistence for Idiomatic Java - * - * License: GNU Lesser General Public License (LGPL), version 2.1 or later - * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html - */ -package org.hibernate.query.sqm.internal; - -import org.hibernate.query.spi.NonSelectQueryPlan; -import org.hibernate.query.sqm.mutation.spi.DeleteHandler; -import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; -import org.hibernate.sql.exec.spi.ExecutionContext; - -/** - * @author Steve Ebersole - */ -public class DeleteQueryPlanImpl implements NonSelectQueryPlan { - private final DeleteHandler deleteHandler; - private final ExecutionContext executionContext; - - public DeleteQueryPlanImpl( - SqmDeleteStatement sqmDeleteStatement, - DeleteHandler deleteHandler, - ExecutionContext executionContext) { - this.deleteHandler = deleteHandler; - this.executionContext = executionContext; - } - - @Override - public int executeUpdate(ExecutionContext executionContext) { - return deleteHandler.execute( this.executionContext ); - } -} diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/QuerySqmImpl.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/QuerySqmImpl.java index 8751c4ad54..e730a6a209 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/QuerySqmImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/QuerySqmImpl.java @@ -47,7 +47,6 @@ import org.hibernate.query.spi.ScrollableResultsImplementor; import org.hibernate.query.spi.SelectQueryPlan; import org.hibernate.query.sqm.SqmExpressable; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; -import org.hibernate.query.sqm.mutation.spi.UpdateHandler; import org.hibernate.query.sqm.tree.SqmDmlStatement; import org.hibernate.query.sqm.tree.SqmStatement; import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; @@ -562,13 +561,19 @@ public class QuerySqmImpl final String entityNameToUpdate = sqmStatement.getTarget().getReferencedPathSource().getHibernateEntityName(); final EntityPersister entityDescriptor = getSessionFactory().getDomainModel().findEntityDescriptor( entityNameToUpdate ); - final UpdateHandler updateHandler = entityDescriptor.getSqmMultiTableMutationStrategy().buildUpdateHandler( - sqmStatement, - domainParameterXref, - this::getSessionFactory - ); - - return new UpdateQueryPlanImpl( sqmStatement, updateHandler, this ); + final SqmMultiTableMutationStrategy multiTableStrategy = entityDescriptor.getSqmMultiTableMutationStrategy(); + if ( multiTableStrategy == null ) { + return new SimpleUpdateQueryPlan( sqmStatement, domainParameterXref ); + } + else { + return new MultiTableUpdateQueryPlan( + multiTableStrategy.buildUpdateHandler( + sqmStatement, + domainParameterXref, + this::getSessionFactory + ) + ); + } } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SimpleDeleteQueryPlan.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SimpleDeleteQueryPlan.java index b96034394d..f37630cbec 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SimpleDeleteQueryPlan.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SimpleDeleteQueryPlan.java @@ -54,8 +54,8 @@ public class SimpleDeleteQueryPlan implements NonSelectQueryPlan { if ( jdbcDelete == null ) { final QueryEngine queryEngine = factory.getQueryEngine(); - final SqmTranslatorFactory converterFactory = queryEngine.getSqmTranslatorFactory(); - final SimpleSqmDeleteTranslator converter = converterFactory.createSimpleDeleteTranslator( + final SqmTranslatorFactory translatorFactory = queryEngine.getSqmTranslatorFactory(); + final SimpleSqmDeleteTranslator translator = translatorFactory.createSimpleDeleteTranslator( executionContext.getQueryOptions(), domainParameterXref, executionContext.getQueryParameterBindings(), @@ -63,7 +63,7 @@ public class SimpleDeleteQueryPlan implements NonSelectQueryPlan { factory ); - final SimpleSqmDeleteTranslation sqmInterpretation = converter.translate( sqmDelete ); + final SimpleSqmDeleteTranslation sqmInterpretation = translator.translate( sqmDelete ); final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment(); final SqlAstTranslatorFactory sqlAstTranslatorFactory = jdbcEnvironment.getSqlAstTranslatorFactory(); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SimpleUpdateQueryPlan.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SimpleUpdateQueryPlan.java index 6ea1bf1663..5c0a096f4f 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SimpleUpdateQueryPlan.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SimpleUpdateQueryPlan.java @@ -6,25 +6,64 @@ */ package org.hibernate.query.sqm.internal; +import java.util.List; +import java.util.Map; + import org.hibernate.NotYetImplementedFor6Exception; +import org.hibernate.engine.jdbc.spi.JdbcServices; +import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.query.spi.NonSelectQueryPlan; +import org.hibernate.query.spi.QueryEngine; +import org.hibernate.query.spi.QueryParameterImplementor; +import org.hibernate.query.sqm.sql.SimpleSqmDeleteTranslation; +import org.hibernate.query.sqm.sql.SimpleSqmDeleteTranslator; +import org.hibernate.query.sqm.sql.SimpleSqmUpdateTranslation; +import org.hibernate.query.sqm.sql.SimpleSqmUpdateTranslator; +import org.hibernate.query.sqm.sql.SqmTranslatorFactory; +import org.hibernate.query.sqm.tree.expression.SqmParameter; import org.hibernate.query.sqm.tree.update.SqmUpdateStatement; import org.hibernate.sql.exec.spi.ExecutionContext; +import org.hibernate.sql.exec.spi.JdbcParameter; +import org.hibernate.sql.exec.spi.JdbcUpdate; /** * @author Steve Ebersole */ public class SimpleUpdateQueryPlan implements NonSelectQueryPlan { - private final SqmUpdateStatement sqmStatement; + private final SqmUpdateStatement sqmUpdate; + private final DomainParameterXref domainParameterXref; - public SimpleUpdateQueryPlan(SqmUpdateStatement sqmStatement) { - this.sqmStatement = sqmStatement; + private JdbcUpdate jdbcUpdate; + private Map, Map>> jdbcParamsXref; - // todo (6.0) : here is where we need to perform the conversion into SQL AST + public SimpleUpdateQueryPlan( + SqmUpdateStatement sqmUpdate, + DomainParameterXref domainParameterXref) { + this.sqmUpdate = sqmUpdate; + this.domainParameterXref = domainParameterXref; } @Override public int executeUpdate(ExecutionContext executionContext) { + final SessionFactoryImplementor factory = executionContext.getSession().getFactory(); + final JdbcServices jdbcServices = factory.getJdbcServices(); + + if ( jdbcUpdate == null ) { + final QueryEngine queryEngine = factory.getQueryEngine(); + + final SqmTranslatorFactory translatorFactory = queryEngine.getSqmTranslatorFactory(); + final SimpleSqmUpdateTranslator translator = translatorFactory.createSimpleUpdateTranslator( + executionContext.getQueryOptions(), + domainParameterXref, + executionContext.getQueryParameterBindings(), + executionContext.getLoadQueryInfluencers(), + factory + ); + + final SimpleSqmUpdateTranslation sqmInterpretation = translator.translate( sqmUpdate ); + + + } throw new NotYetImplementedFor6Exception( ); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCriteriaNodeBuilder.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCriteriaNodeBuilder.java index 50b18ff49d..348f3061b1 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCriteriaNodeBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCriteriaNodeBuilder.java @@ -53,6 +53,7 @@ import org.hibernate.query.internal.QueryHelper; import org.hibernate.query.spi.QueryEngine; import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.SemanticException; +import org.hibernate.query.sqm.SqmQuerySource; import org.hibernate.query.sqm.produce.function.SqmFunctionTemplate; import org.hibernate.query.sqm.function.SqmCastTarget; import org.hibernate.query.sqm.function.SqmDistinct; @@ -174,7 +175,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder { @Override public SqmDeleteStatement createCriteriaDelete(Class targetEntity) { - return new SqmDeleteStatement<>( targetEntity, this ); + return new SqmDeleteStatement<>( targetEntity, SqmQuerySource.CRITERIA, this ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmTreePrinter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmTreePrinter.java index 3bc8caa28e..18503d1be9 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmTreePrinter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmTreePrinter.java @@ -12,6 +12,8 @@ import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.query.QueryLogger; import org.hibernate.query.sqm.SemanticQueryWalker; import org.hibernate.query.sqm.tree.SqmStatement; +import org.hibernate.query.sqm.tree.cte.SqmCteConsumer; +import org.hibernate.query.sqm.tree.cte.SqmCteStatement; import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; import org.hibernate.query.sqm.tree.domain.SqmBasicValuedSimplePath; import org.hibernate.query.sqm.tree.domain.SqmCorrelation; @@ -284,6 +286,20 @@ public class SqmTreePrinter implements SemanticQueryWalker { return null; } + @Override + public Object visitCteStatement(SqmCteStatement sqmCteStatement) { + if ( DEBUG_ENABLED ) { + logIndented( "cte" ); + } + + return null; + } + + @Override + public Object visitCteConsumer(SqmCteConsumer consumer) { + return null; + } + @Override public Object visitUpdateStatement(SqmUpdateStatement statement) { if ( DEBUG_ENABLED ) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteBasedMutationStrategy.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteBasedMutationStrategy.java index 17a38d26c7..1a6cfe8b10 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteBasedMutationStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteBasedMutationStrategy.java @@ -120,7 +120,7 @@ public class CteBasedMutationStrategy implements SqmMultiTableMutationStrategy { ); } - this.cteTable = new CteTable( rootDescriptor, runtimeModelCreationContext ); + this.cteTable = new CteTable( rootDescriptor, runtimeModelCreationContext.getTypeConfiguration() ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteDeleteHandler.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteDeleteHandler.java index ef0d74c5ae..c206ccdad9 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteDeleteHandler.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteDeleteHandler.java @@ -81,6 +81,7 @@ public class CteDeleteHandler extends AbstractCteMutationHandler implements Dele final JdbcParameterBindings jdbcParameterBindings = new JdbcParameterBindingsImpl( getDomainParameterXref().getQueryParameterCount() ); final QuerySpec cteDefinitionQuerySpec = getCteTable().createCteDefinition( ids, + getEntityDescriptor().getIdentifierMapping(), jdbcParameterBindings, executionContext ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/spi/BaseSemanticQueryWalker.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/spi/BaseSemanticQueryWalker.java index a6d7092341..5e12a0f827 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/spi/BaseSemanticQueryWalker.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/spi/BaseSemanticQueryWalker.java @@ -14,6 +14,8 @@ import org.hibernate.query.sqm.function.SqmExtractUnit; import org.hibernate.query.sqm.function.SqmFunction; import org.hibernate.query.sqm.function.SqmStar; import org.hibernate.query.sqm.function.SqmTrimSpecification; +import org.hibernate.query.sqm.tree.cte.SqmCteConsumer; +import org.hibernate.query.sqm.tree.cte.SqmCteStatement; import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; import org.hibernate.query.sqm.tree.domain.SqmBasicValuedSimplePath; import org.hibernate.query.sqm.tree.domain.SqmCorrelation; @@ -141,6 +143,33 @@ public class BaseSemanticQueryWalker implements SemanticQueryWalker { return statement; } + @Override + public Object visitCteStatement(SqmCteStatement sqmCteStatement) { + visitCteConsumer( sqmCteStatement.getCteConsumer() ); + return sqmCteStatement; + } + + @Override + public Object visitCteConsumer(SqmCteConsumer consumer) { + if ( consumer instanceof SqmQuerySpec ) { + return visitQuerySpec( ( (SqmQuerySpec) consumer ) ); + } + + if ( consumer instanceof SqmDeleteStatement ) { + return visitDeleteStatement( ( (SqmDeleteStatement) consumer ) ); + } + + if ( consumer instanceof SqmUpdateStatement ) { + visitUpdateStatement( (SqmUpdateStatement) consumer ); + } + + if ( consumer instanceof SqmInsertSelectStatement ) { + visitInsertSelectStatement( (SqmInsertSelectStatement) consumer ); + } + + throw new UnsupportedOperationException( "Unsupported SqmCteConsumer : " + consumer ); + } + @Override public Object visitQuerySpec(SqmQuerySpec querySpec) { visitFromClause( querySpec.getFromClause() ); 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 67dddc32f8..c15f8e62e2 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 @@ -11,7 +11,6 @@ import java.util.Collections; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.function.Consumer; import java.util.function.Supplier; @@ -46,6 +45,10 @@ import org.hibernate.query.sqm.sql.internal.EmbeddableValuedPathInterpretation; import org.hibernate.query.sqm.sql.internal.SqlAstQuerySpecProcessingStateImpl; import org.hibernate.query.sqm.sql.internal.SqmParameterInterpretation; import org.hibernate.query.sqm.sql.internal.SqmPathInterpretation; +import org.hibernate.query.sqm.tree.cte.SqmCteConsumer; +import org.hibernate.query.sqm.tree.cte.SqmCteStatement; +import org.hibernate.query.sqm.tree.cte.SqmCteTable; +import org.hibernate.query.sqm.tree.cte.SqmCteTableColumn; import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; import org.hibernate.query.sqm.tree.domain.SqmBasicValuedSimplePath; import org.hibernate.query.sqm.tree.domain.SqmEmbeddedValuedSimplePath; @@ -102,6 +105,10 @@ import org.hibernate.sql.ast.spi.SqlAstProcessingState; import org.hibernate.sql.ast.spi.SqlAstQuerySpecProcessingState; import org.hibernate.sql.ast.spi.SqlAstTreeHelper; import org.hibernate.sql.ast.spi.SqlExpressionResolver; +import org.hibernate.sql.ast.tree.cte.CteColumn; +import org.hibernate.sql.ast.tree.cte.CteConsumer; +import org.hibernate.sql.ast.tree.cte.CteStatement; +import org.hibernate.sql.ast.tree.cte.CteTable; import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression; import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression; import org.hibernate.sql.ast.tree.expression.CaseSimpleExpression; @@ -269,6 +276,44 @@ public abstract class BaseSqmToSqlAstConverter throw new AssertionFailure( "SelectStatement not supported" ); } + @Override + public CteStatement visitCteStatement(SqmCteStatement sqmCteStatement) { + final CteTable cteTable = createCteTable( sqmCteStatement ); + + return new CteStatement( + visitQuerySpec( sqmCteStatement.getCteDefinition() ), + sqmCteStatement.getCteLabel(), + cteTable, + visitCteConsumer( sqmCteStatement.getCteConsumer() ) + ); + } + + protected CteTable createCteTable(SqmCteStatement sqmCteStatement) { + final SqmCteTable sqmCteTable = sqmCteStatement.getCteTable(); + final List sqmCteColumns = sqmCteTable.getColumns(); + final List sqlCteColumns = new ArrayList<>( sqmCteColumns.size() ); + + for ( int i = 0; i < sqmCteColumns.size(); i++ ) { + final SqmCteTableColumn sqmCteTableColumn = sqmCteColumns.get( i ); + + sqlCteColumns.add( + new CteColumn( + sqmCteTableColumn.getColumnName(), + sqmCteTableColumn.getType() + ) + ); + } + + return new CteTable( + sqlCteColumns, + getCreationContext().getSessionFactory() + ); + } + + @Override + public CteConsumer visitCteConsumer(SqmCteConsumer consumer) { + return (CteConsumer) super.visitCteConsumer( consumer ); + } @Override public QuerySpec visitQuerySpec(SqmQuerySpec sqmQuerySpec) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SimpleSqmDeleteTranslator.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SimpleSqmDeleteTranslator.java index 1de20ce96a..5e19565d83 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SimpleSqmDeleteTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SimpleSqmDeleteTranslator.java @@ -12,6 +12,6 @@ import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; /** * @author Steve Ebersole */ -public interface SimpleSqmDeleteTranslator extends SqmToSqlAstConverter, JdbcParameterBySqmParameterAccess { +public interface SimpleSqmDeleteTranslator extends SqmTranslator, SqmToSqlAstConverter, JdbcParameterBySqmParameterAccess { SimpleSqmDeleteTranslation translate(SqmDeleteStatement statement); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SimpleSqmUpdateTranslation.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SimpleSqmUpdateTranslation.java new file mode 100644 index 0000000000..382177a4e4 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SimpleSqmUpdateTranslation.java @@ -0,0 +1,40 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.query.sqm.sql; + +import java.util.List; +import java.util.Map; + +import org.hibernate.query.sqm.tree.expression.SqmParameter; +import org.hibernate.sql.ast.tree.delete.DeleteStatement; +import org.hibernate.sql.ast.tree.update.UpdateStatement; +import org.hibernate.sql.exec.spi.JdbcParameter; + +/** + * @author Steve Ebersole + */ +public class SimpleSqmUpdateTranslation implements SqmTranslation { + private final UpdateStatement sqlAst; + private final Map> jdbcParamMap; + + public SimpleSqmUpdateTranslation( + UpdateStatement sqlAst, + Map> jdbcParamMap) { + this.sqlAst = sqlAst; + this.jdbcParamMap = jdbcParamMap; + } + + @Override + public UpdateStatement getSqlAst() { + return sqlAst; + } + + @Override + public Map> getJdbcParamsBySqmParam() { + return jdbcParamMap; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SimpleSqmUpdateTranslator.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SimpleSqmUpdateTranslator.java new file mode 100644 index 0000000000..ae52043c35 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SimpleSqmUpdateTranslator.java @@ -0,0 +1,17 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.query.sqm.sql; + +import org.hibernate.query.sqm.spi.JdbcParameterBySqmParameterAccess; +import org.hibernate.query.sqm.tree.update.SqmUpdateStatement; + +/** + * @author Steve Ebersole + */ +public interface SimpleSqmUpdateTranslator extends SqmTranslator, SqmToSqlAstConverter, JdbcParameterBySqmParameterAccess { + SimpleSqmUpdateTranslation translate(SqmUpdateStatement sqmUpdate); +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmInsertSelectTranslator.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmInsertSelectTranslator.java index 40ae0324e6..c4998d02c2 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmInsertSelectTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmInsertSelectTranslator.java @@ -12,6 +12,6 @@ import org.hibernate.query.sqm.tree.insert.SqmInsertSelectStatement; /** * @author Steve Ebersole */ -public interface SqmInsertSelectTranslator extends SqmToSqlAstConverter, JdbcParameterBySqmParameterAccess { +public interface SqmInsertSelectTranslator extends SqmToSqlAstConverter, JdbcParameterBySqmParameterAccess, SqmTranslator { SqmInsertSelectTranslation translate(SqmInsertSelectStatement statement); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmSelectTranslator.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmSelectTranslator.java index 5d82382b3a..db3908f713 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmSelectTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmSelectTranslator.java @@ -13,7 +13,7 @@ import org.hibernate.query.sqm.tree.select.SqmSelectStatement; /** * @author Steve Ebersole */ -public interface SqmSelectTranslator extends SqmToSqlAstConverter, JdbcParameterBySqmParameterAccess { +public interface SqmSelectTranslator extends SqmToSqlAstConverter, JdbcParameterBySqmParameterAccess, SqmTranslator { SqmSelectTranslation translate(SqmSelectStatement sqmStatement); SqmQuerySpecTranslation translate(SqmQuerySpec sqmQuerySpec); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmTranslator.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmTranslator.java new file mode 100644 index 0000000000..105807fcab --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmTranslator.java @@ -0,0 +1,17 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.query.sqm.sql; + +import org.hibernate.query.sqm.tree.cte.SqmCteStatement; +import org.hibernate.sql.ast.tree.cte.CteStatement; + +/** + * @author Steve Ebersole + */ +public interface SqmTranslator { + CteStatement translate(SqmCteStatement sqmCte); +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmTranslatorFactory.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmTranslatorFactory.java index 6778746cbf..a010221bcc 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmTranslatorFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmTranslatorFactory.java @@ -7,6 +7,7 @@ package org.hibernate.query.sqm.sql; import org.hibernate.engine.spi.LoadQueryInfluencers; +import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.query.spi.QueryOptions; import org.hibernate.query.spi.QueryParameterBindings; import org.hibernate.query.sqm.internal.DomainParameterXref; @@ -38,5 +39,13 @@ public interface SqmTranslatorFactory { LoadQueryInfluencers influencers, SqlAstCreationContext creationContext); + SimpleSqmUpdateTranslator createSimpleUpdateTranslator( + QueryOptions queryOptions, + DomainParameterXref domainParameterXref, + QueryParameterBindings queryParameterBindings, + LoadQueryInfluencers loadQueryInfluencers, + SessionFactoryImplementor factory); + + // todo (6.0) : update, delete, etc converters... } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/StandardSqmDeleteTranslator.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/StandardSqmDeleteTranslator.java index 68547b7841..00576ae2e7 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/StandardSqmDeleteTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/StandardSqmDeleteTranslator.java @@ -17,12 +17,14 @@ import org.hibernate.query.sqm.internal.DomainParameterXref; import org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter; import org.hibernate.query.sqm.sql.SimpleSqmDeleteTranslation; import org.hibernate.query.sqm.sql.SimpleSqmDeleteTranslator; +import org.hibernate.query.sqm.tree.cte.SqmCteStatement; import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; import org.hibernate.query.sqm.tree.predicate.SqmWhereClause; import org.hibernate.sql.ast.Clause; import org.hibernate.sql.ast.JoinType; import org.hibernate.sql.ast.spi.SqlAstCreationContext; import org.hibernate.sql.ast.spi.SqlAstTreeHelper; +import org.hibernate.sql.ast.tree.cte.CteStatement; import org.hibernate.sql.ast.tree.delete.DeleteStatement; import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.predicate.Predicate; @@ -111,4 +113,10 @@ public class StandardSqmDeleteTranslator getProcessingStateStack().pop(); } } + + @Override + public CteStatement translate(SqmCteStatement sqmCte) { + visitCteStatement( sqmCte ); + return null; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/StandardSqmInsertSelectTranslator.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/StandardSqmInsertSelectTranslator.java index 75b819cad5..907543b7f2 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/StandardSqmInsertSelectTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/StandardSqmInsertSelectTranslator.java @@ -16,9 +16,11 @@ import org.hibernate.query.sqm.internal.DomainParameterXref; import org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter; import org.hibernate.query.sqm.sql.SqmInsertSelectTranslation; import org.hibernate.query.sqm.sql.SqmInsertSelectTranslator; +import org.hibernate.query.sqm.tree.cte.SqmCteStatement; import org.hibernate.query.sqm.tree.insert.SqmInsertSelectStatement; import org.hibernate.sql.ast.JoinType; import org.hibernate.sql.ast.spi.SqlAstCreationContext; +import org.hibernate.sql.ast.tree.cte.CteStatement; import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.insert.InsertSelectStatement; @@ -41,6 +43,11 @@ public class StandardSqmInsertSelectTranslator return new SqmInsertSelectTranslation( visitInsertSelectStatement( sqmStatement ), getJdbcParamsBySqmParam() ); } + @Override + public CteStatement translate(SqmCteStatement sqmCte) { + return visitCteStatement( sqmCte ); + } + @Override public InsertSelectStatement visitInsertSelectStatement(SqmInsertSelectStatement sqmStatement) { final InsertSelectStatement insertSelectStatement = new InsertSelectStatement(); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/StandardSqmSelectTranslator.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/StandardSqmSelectTranslator.java index f7dff2ab7f..50f30d3da1 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/StandardSqmSelectTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/StandardSqmSelectTranslator.java @@ -33,6 +33,7 @@ import org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter; import org.hibernate.query.sqm.sql.SqmQuerySpecTranslation; import org.hibernate.query.sqm.sql.SqmSelectTranslation; import org.hibernate.query.sqm.sql.SqmSelectTranslator; +import org.hibernate.query.sqm.tree.cte.SqmCteStatement; import org.hibernate.query.sqm.tree.expression.SqmLiteralEntityType; import org.hibernate.query.sqm.tree.from.SqmAttributeJoin; import org.hibernate.query.sqm.tree.select.SqmDynamicInstantiation; @@ -45,6 +46,7 @@ import org.hibernate.sql.ast.JoinType; import org.hibernate.sql.ast.spi.FromClauseAccess; import org.hibernate.sql.ast.spi.SqlAstCreationContext; import org.hibernate.sql.ast.spi.SqlAstCreationState; +import org.hibernate.sql.ast.tree.cte.CteStatement; import org.hibernate.sql.ast.tree.expression.EntityTypeLiteral; import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.from.TableGroup; @@ -433,6 +435,11 @@ public class StandardSqmSelectTranslator // .getOrMakeJavaDescriptor( namedClass ); } + @Override + public CteStatement translate(SqmCteStatement sqmCte) { + return visitCteStatement( sqmCte ); + } + // @Override // public SqlSelection resolveSqlSelection(Expression expression) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/StandardSqmUpdateTranslator.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/StandardSqmUpdateTranslator.java new file mode 100644 index 0000000000..7f65ef4d08 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/StandardSqmUpdateTranslator.java @@ -0,0 +1,151 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.query.sqm.sql.internal; + +import java.util.ArrayList; +import java.util.List; + +import org.hibernate.HibernateException; +import org.hibernate.LockMode; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.query.NavigablePath; +import org.hibernate.query.spi.QueryOptions; +import org.hibernate.query.spi.QueryParameterBindings; +import org.hibernate.query.sqm.internal.DomainParameterXref; +import org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter; +import org.hibernate.query.sqm.sql.SimpleSqmUpdateTranslation; +import org.hibernate.query.sqm.sql.SimpleSqmUpdateTranslator; +import org.hibernate.query.sqm.tree.cte.SqmCteStatement; +import org.hibernate.query.sqm.tree.predicate.SqmWhereClause; +import org.hibernate.query.sqm.tree.update.SqmAssignment; +import org.hibernate.query.sqm.tree.update.SqmSetClause; +import org.hibernate.query.sqm.tree.update.SqmUpdateStatement; +import org.hibernate.sql.ast.Clause; +import org.hibernate.sql.ast.JoinType; +import org.hibernate.sql.ast.spi.SqlAstCreationContext; +import org.hibernate.sql.ast.spi.SqlAstTreeHelper; +import org.hibernate.sql.ast.tree.cte.CteStatement; +import org.hibernate.sql.ast.tree.expression.ColumnReference; +import org.hibernate.sql.ast.tree.from.TableGroup; +import org.hibernate.sql.ast.tree.predicate.Predicate; +import org.hibernate.sql.ast.tree.update.Assignment; +import org.hibernate.sql.ast.tree.update.UpdateStatement; +import org.hibernate.sql.exec.internal.JdbcParameterImpl; +import org.hibernate.sql.exec.spi.JdbcParameter; + +/** + * @author Steve Ebersole + */ +public class StandardSqmUpdateTranslator + extends BaseSqmToSqlAstConverter + implements SimpleSqmUpdateTranslator { + + + public StandardSqmUpdateTranslator( + SqlAstCreationContext creationContext, + QueryOptions queryOptions, + DomainParameterXref domainParameterXref, + QueryParameterBindings domainParameterBindings) { + super( creationContext, queryOptions, domainParameterXref, domainParameterBindings ); + } + + @Override + public SimpleSqmUpdateTranslation translate(SqmUpdateStatement sqmUpdate) { + final UpdateStatement sqlUpdateAst = visitUpdateStatement( sqmUpdate ); + return new SimpleSqmUpdateTranslation( + sqlUpdateAst, + getJdbcParamsBySqmParam() + ); + } + + @Override + public UpdateStatement visitUpdateStatement(SqmUpdateStatement sqmStatement) { + final String entityName = sqmStatement.getTarget().getEntityName(); + final EntityPersister entityDescriptor = getCreationContext().getDomainModel().getEntityDescriptor( entityName ); + assert entityDescriptor != null; + + getProcessingStateStack().push( + new SqlAstProcessingStateImpl( + null, + this, + getCurrentClauseStack()::getCurrent + ) + ); + + try { + final NavigablePath rootPath = new NavigablePath( entityName ); + final TableGroup rootTableGroup = entityDescriptor.createRootTableGroup( + rootPath, + null, + JoinType.LEFT, + LockMode.WRITE, + stem -> getSqlAliasBaseGenerator().createSqlAliasBase( stem ), + getSqlExpressionResolver(), + () -> predicate -> additionalRestrictions = predicate, + getCreationContext() + ); + getFromClauseIndex().registerTableGroup( rootPath, rootTableGroup ); + + if ( ! rootTableGroup.getTableReferenceJoins().isEmpty() ) { + throw new HibernateException( "Not expecting multiple table references for an SQM DELETE" ); + } + + Predicate suppliedPredicate = null; + final SqmWhereClause whereClause = sqmStatement.getWhereClause(); + if ( whereClause != null && whereClause.getPredicate() != null ) { + getCurrentClauseStack().push( Clause.WHERE ); + try { + suppliedPredicate = (Predicate) whereClause.getPredicate().accept( this ); + } + finally { + getCurrentClauseStack().pop(); + } + } + + return new UpdateStatement( + rootTableGroup.getPrimaryTableReference(), + visitSetClause( sqmStatement.getSetClause() ), + SqlAstTreeHelper.combinePredicates( suppliedPredicate, additionalRestrictions ) + ); + } + finally { + getProcessingStateStack().pop(); + } + } + + @Override + public List visitSetClause(SqmSetClause setClause) { + final List assignments = new ArrayList<>(); + + for ( SqmAssignment sqmAssignment : setClause.getAssignments() ) { + final SqmPathInterpretation assignedPathInterpretation = (SqmPathInterpretation) sqmAssignment.getTargetPath().accept( this ); + assignedPathInterpretation.getExpressionType().visitColumns( + (columnExpression, containingTableExpression, jdbcMapping) -> { + final JdbcParameter jdbcParameter = new JdbcParameterImpl( jdbcMapping ); + assignments.add( + new Assignment( + new ColumnReference( + containingTableExpression, + columnExpression, + jdbcMapping, + getCreationContext().getSessionFactory() + ), + jdbcParameter + ) + ); + } + ); + } + + return assignments; + } + + @Override + public CteStatement translate(SqmCteStatement sqmCte) { + return visitCteStatement( sqmCte ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/AbstractSqmDmlStatement.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/AbstractSqmDmlStatement.java index 5e41de6ad3..a15e13e75b 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/AbstractSqmDmlStatement.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/AbstractSqmDmlStatement.java @@ -7,6 +7,7 @@ package org.hibernate.query.sqm.tree; import org.hibernate.query.sqm.NodeBuilder; +import org.hibernate.query.sqm.SqmQuerySource; import org.hibernate.query.sqm.tree.from.SqmRoot; import org.hibernate.query.sqm.tree.select.SqmSubQuery; @@ -18,12 +19,12 @@ public abstract class AbstractSqmDmlStatement implements SqmDmlStatement { private SqmRoot target; - public AbstractSqmDmlStatement(NodeBuilder nodeBuilder) { - super( nodeBuilder ); + public AbstractSqmDmlStatement(SqmQuerySource querySource, NodeBuilder nodeBuilder) { + super( querySource, nodeBuilder ); } - public AbstractSqmDmlStatement(SqmRoot target, NodeBuilder nodeBuilder) { - this( nodeBuilder ); + public AbstractSqmDmlStatement(SqmRoot target, SqmQuerySource querySource, NodeBuilder nodeBuilder) { + this( querySource, nodeBuilder ); this.target = target; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/AbstractSqmStatement.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/AbstractSqmStatement.java index e929eba737..f27316f573 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/AbstractSqmStatement.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/AbstractSqmStatement.java @@ -13,6 +13,7 @@ import java.util.Set; import java.util.function.Supplier; import org.hibernate.query.sqm.NodeBuilder; +import org.hibernate.query.sqm.SqmQuerySource; import org.hibernate.query.sqm.tree.expression.JpaCriteriaParameter; import org.hibernate.query.sqm.tree.expression.SqmJpaCriteriaParameterWrapper; import org.hibernate.query.sqm.tree.expression.SqmParameter; @@ -22,12 +23,22 @@ import org.hibernate.query.sqm.internal.ParameterCollector; * @author Steve Ebersole */ public abstract class AbstractSqmStatement extends AbstractSqmNode implements SqmStatement, ParameterCollector { - public AbstractSqmStatement(NodeBuilder builder) { + private final SqmQuerySource querySource; + + public AbstractSqmStatement( + SqmQuerySource querySource, + NodeBuilder builder) { super( builder ); + this.querySource = querySource; } private Set> parameters; + @Override + public SqmQuerySource getQuerySource() { + return querySource; + } + @Override public void addParameter(SqmParameter parameter) { if ( parameters == null ) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/SqmDmlStatement.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/SqmDmlStatement.java index fc95eed986..27649c5973 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/SqmDmlStatement.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/SqmDmlStatement.java @@ -7,6 +7,7 @@ package org.hibernate.query.sqm.tree; import org.hibernate.query.criteria.JpaManipulationCriteria; +import org.hibernate.query.sqm.tree.cte.SqmCteConsumer; import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; import org.hibernate.query.sqm.tree.from.SqmRoot; @@ -18,7 +19,7 @@ import org.hibernate.query.sqm.tree.from.SqmRoot; * * @author Steve Ebersole */ -public interface SqmDmlStatement extends SqmStatement, JpaManipulationCriteria { +public interface SqmDmlStatement extends SqmStatement, SqmCteConsumer, JpaManipulationCriteria { /** * Get the root path that is the target of the DML statement. */ diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/cte/SqmCteConsumer.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/cte/SqmCteConsumer.java new file mode 100644 index 0000000000..4af6c6c0b3 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/cte/SqmCteConsumer.java @@ -0,0 +1,15 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.query.sqm.tree.cte; + +import org.hibernate.query.sqm.tree.SqmNode; + +/** + * @author Steve Ebersole + */ +public interface SqmCteConsumer extends SqmNode { +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/cte/SqmCteStatement.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/cte/SqmCteStatement.java new file mode 100644 index 0000000000..857f1e52ca --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/cte/SqmCteStatement.java @@ -0,0 +1,76 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.query.sqm.tree.cte; + +import org.hibernate.query.criteria.JpaPredicate; +import org.hibernate.query.criteria.JpaSubQuery; +import org.hibernate.query.sqm.NodeBuilder; +import org.hibernate.query.sqm.SemanticQueryWalker; +import org.hibernate.query.sqm.SqmQuerySource; +import org.hibernate.query.sqm.tree.AbstractSqmStatement; +import org.hibernate.query.sqm.tree.SqmStatement; +import org.hibernate.query.sqm.tree.select.SqmQuerySpec; +import org.hibernate.query.sqm.tree.select.SqmSubQuery; + +/** + * @author Steve Ebersole + */ +public class SqmCteStatement extends AbstractSqmStatement implements SqmStatement { + private final SqmCteTable cteTable; + private final String cteLabel; + private final SqmQuerySpec cteDefinition; + private final SqmCteConsumer cteConsumer; + + public SqmCteStatement( + SqmCteTable cteTable, + String cteLabel, + SqmQuerySpec cteDefinition, + SqmCteConsumer cteConsumer, + SqmQuerySource querySource, + NodeBuilder nodeBuilder) { + super( querySource, nodeBuilder ); + this.cteTable = cteTable; + this.cteLabel = cteLabel; + this.cteDefinition = cteDefinition; + this.cteConsumer = cteConsumer; + } + + public SqmCteTable getCteTable() { + return cteTable; + } + + public String getCteLabel() { + return cteLabel; + } + + public SqmQuerySpec getCteDefinition() { + return cteDefinition; + } + + public SqmCteConsumer getCteConsumer() { + return cteConsumer; + } + + @Override + public JpaSubQuery subquery(Class type) { + return new SqmSubQuery( + this, + new SqmQuerySpec<>( nodeBuilder() ), + nodeBuilder() + ); + } + + @Override + public JpaPredicate getRestriction() { + return null; + } + + @Override + public X accept(SemanticQueryWalker walker) { + return walker.visitCteStatement( this ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/cte/SqmCteTable.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/cte/SqmCteTable.java new file mode 100644 index 0000000000..914e72f9cf --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/cte/SqmCteTable.java @@ -0,0 +1,37 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.query.sqm.tree.cte; + +import java.util.List; +import java.util.function.Consumer; + +/** + * @author Steve Ebersole + */ +public class SqmCteTable { + private final String cteName; + private final List columns; + + public SqmCteTable(String cteName, List columns) { + this.cteName = cteName; + this.columns = columns; + } + + public String getCteName() { + return cteName; + } + + public List getColumns() { + return columns; + } + + public void visitColumns(Consumer columnConsumer) { + for ( int i = 0; i < columns.size(); i++ ) { + columnConsumer.accept( columns.get( i ) ); + } + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/cte/SqmCteTableColumn.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/cte/SqmCteTableColumn.java new file mode 100644 index 0000000000..3221957c18 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/cte/SqmCteTableColumn.java @@ -0,0 +1,46 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.query.sqm.tree.cte; + +import org.hibernate.type.BasicType; + +/** + * @author Steve Ebersole + */ +public class SqmCteTableColumn { + private final SqmCteTable cteTable; + private final String columnName; + private final BasicType typeExpressable; + private final boolean allowNulls; + + public SqmCteTableColumn( + SqmCteTable cteTable, + String columnName, + BasicType typeExpressable, + boolean allowNulls) { + this.cteTable = cteTable; + this.columnName = columnName; + this.typeExpressable = typeExpressable; + this.allowNulls = allowNulls; + } + + public SqmCteTable getCteTable() { + return cteTable; + } + + public String getColumnName() { + return columnName; + } + + public BasicType getType() { + return typeExpressable; + } + + public boolean isAllowNulls() { + return allowNulls; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/delete/SqmDeleteStatement.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/delete/SqmDeleteStatement.java index 3c81fcd726..7ea85c30fb 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/delete/SqmDeleteStatement.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/delete/SqmDeleteStatement.java @@ -15,38 +15,39 @@ import org.hibernate.metamodel.model.domain.EntityDomainType; import org.hibernate.query.criteria.JpaCriteriaDelete; import org.hibernate.query.criteria.JpaPredicate; import org.hibernate.query.sqm.NodeBuilder; -import org.hibernate.query.sqm.SqmQuerySource; import org.hibernate.query.sqm.SemanticQueryWalker; +import org.hibernate.query.sqm.SqmQuerySource; import org.hibernate.query.sqm.tree.AbstractSqmDmlStatement; import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement; +import org.hibernate.query.sqm.tree.cte.SqmCteConsumer; import org.hibernate.query.sqm.tree.from.SqmRoot; import org.hibernate.query.sqm.tree.predicate.SqmPredicate; import org.hibernate.query.sqm.tree.predicate.SqmWhereClause; -import org.hibernate.sql.ast.spi.SqlAstTreeHelper; /** * @author Steve Ebersole */ public class SqmDeleteStatement extends AbstractSqmDmlStatement - implements SqmDeleteOrUpdateStatement, JpaCriteriaDelete { + implements SqmDeleteOrUpdateStatement, SqmCteConsumer, JpaCriteriaDelete { private final SqmQuerySource querySource; private SqmWhereClause whereClause; - public SqmDeleteStatement(SqmRoot target, NodeBuilder nodeBuilder) { - super( target, nodeBuilder ); + public SqmDeleteStatement(SqmRoot target, SqmQuerySource querySource, NodeBuilder nodeBuilder) { + super( target, querySource, nodeBuilder ); this.querySource = SqmQuerySource.HQL; } - public SqmDeleteStatement(Class targetEntity, NodeBuilder nodeBuilder) { + public SqmDeleteStatement(Class targetEntity, SqmQuerySource querySource, NodeBuilder nodeBuilder) { super( new SqmRoot<>( nodeBuilder.getDomainModel().entity( targetEntity ), null, nodeBuilder ), + querySource, nodeBuilder ); this.querySource = SqmQuerySource.CRITERIA; 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 c9391f0d01..ac6593cb38 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 @@ -12,6 +12,7 @@ import java.util.List; import java.util.function.Consumer; import org.hibernate.query.sqm.NodeBuilder; +import org.hibernate.query.sqm.SqmQuerySource; import org.hibernate.query.sqm.tree.AbstractSqmDmlStatement; import org.hibernate.query.sqm.tree.domain.SqmPath; import org.hibernate.query.sqm.tree.from.SqmRoot; @@ -24,12 +25,12 @@ import org.hibernate.query.sqm.tree.from.SqmRoot; public abstract class AbstractSqmInsertStatement extends AbstractSqmDmlStatement implements SqmInsertStatement { private List insertionTargetPaths; - protected AbstractSqmInsertStatement(NodeBuilder nodeBuilder) { - super( nodeBuilder ); + protected AbstractSqmInsertStatement(SqmQuerySource querySource, NodeBuilder nodeBuilder) { + super( querySource, nodeBuilder ); } - protected AbstractSqmInsertStatement(SqmRoot targetRoot, NodeBuilder nodeBuilder) { - super( targetRoot, nodeBuilder ); + protected AbstractSqmInsertStatement(SqmRoot targetRoot, SqmQuerySource querySource, NodeBuilder nodeBuilder) { + super( targetRoot, querySource, nodeBuilder ); } @Override 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 6164723cec..e03f79f792 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 @@ -18,13 +18,10 @@ import org.hibernate.query.sqm.tree.from.SqmRoot; * @author Steve Ebersole */ public class SqmInsertSelectStatement extends AbstractSqmInsertStatement implements JpaCriteriaInsertSelect { - private final SqmQuerySource querySource; - private SqmQuerySpec selectQuerySpec; public SqmInsertSelectStatement(SqmRoot targetRoot, NodeBuilder nodeBuilder) { - super( targetRoot, nodeBuilder ); - querySource = SqmQuerySource.HQL; + super( targetRoot, SqmQuerySource.HQL, nodeBuilder ); } public SqmInsertSelectStatement(Class targetEntity, NodeBuilder nodeBuilder) { @@ -34,9 +31,9 @@ public class SqmInsertSelectStatement extends AbstractSqmInsertStatement i null, nodeBuilder ), + SqmQuerySource.CRITERIA, nodeBuilder ); - querySource = SqmQuerySource.CRITERIA; } public SqmQuerySpec getSelectQuerySpec() { @@ -52,11 +49,6 @@ public class SqmInsertSelectStatement extends AbstractSqmInsertStatement i return walker.visitInsertSelectStatement( this ); } - @Override - public SqmQuerySource getQuerySource() { - return querySource; - } - @Override public JpaPredicate getRestriction() { // insert has no predicate diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/AbstractSqmSelectQuery.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/AbstractSqmSelectQuery.java index 0b55761fb2..cbc0ca3f93 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/AbstractSqmSelectQuery.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/AbstractSqmSelectQuery.java @@ -28,12 +28,9 @@ import org.hibernate.query.sqm.tree.predicate.SqmPredicate; public abstract class AbstractSqmSelectQuery extends AbstractSqmNode implements SqmSelectQuery { - private SqmQuerySpec sqmQuerySpec; private Class resultType; - - @SuppressWarnings("WeakerAccess") public AbstractSqmSelectQuery(Class resultType, NodeBuilder builder) { super( builder ); this.sqmQuerySpec = new SqmQuerySpec( builder ); @@ -41,14 +38,10 @@ public abstract class AbstractSqmSelectQuery } - @SuppressWarnings("WeakerAccess") public AbstractSqmSelectQuery(SqmQuerySpec sqmQuerySpec, NodeBuilder builder) { - super( builder ); - this.sqmQuerySpec = sqmQuerySpec; - this.resultType = sqmQuerySpec.getSelectClause().getJavaType(); + this( (Class) sqmQuerySpec.getSelectClause().getJavaType(), builder ); } - @Override public Class getResultType() { return resultType; diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmQuerySpec.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmQuerySpec.java index 70f805a7e3..681320f477 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmQuerySpec.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmQuerySpec.java @@ -22,6 +22,7 @@ import org.hibernate.query.criteria.JpaRoot; import org.hibernate.query.criteria.JpaSelection; import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.sqm.tree.SqmNode; +import org.hibernate.query.sqm.tree.cte.SqmCteConsumer; import org.hibernate.query.sqm.tree.expression.SqmExpression; import org.hibernate.query.sqm.tree.from.SqmFromClause; import org.hibernate.query.sqm.tree.from.SqmFromClauseContainer; @@ -36,7 +37,7 @@ import org.hibernate.type.StandardBasicTypes; * * @author Steve Ebersole */ -public class SqmQuerySpec implements SqmNode, SqmFromClauseContainer, SqmWhereClauseContainer, JpaQueryStructure { +public class SqmQuerySpec implements SqmCteConsumer, SqmNode, SqmFromClauseContainer, SqmWhereClauseContainer, JpaQueryStructure { private final NodeBuilder nodeBuilder; private SqmFromClause fromClause; diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/update/SqmUpdateStatement.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/update/SqmUpdateStatement.java index 2505e21078..97ced350ce 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/update/SqmUpdateStatement.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/update/SqmUpdateStatement.java @@ -18,11 +18,12 @@ import org.hibernate.metamodel.model.domain.EntityDomainType; import org.hibernate.query.criteria.JpaCriteriaUpdate; import org.hibernate.query.criteria.JpaPredicate; import org.hibernate.query.sqm.NodeBuilder; -import org.hibernate.query.sqm.internal.SqmCriteriaNodeBuilder; -import org.hibernate.query.sqm.SqmQuerySource; import org.hibernate.query.sqm.SemanticQueryWalker; +import org.hibernate.query.sqm.SqmQuerySource; +import org.hibernate.query.sqm.internal.SqmCriteriaNodeBuilder; import org.hibernate.query.sqm.tree.AbstractSqmDmlStatement; import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement; +import org.hibernate.query.sqm.tree.cte.SqmCteConsumer; import org.hibernate.query.sqm.tree.from.SqmRoot; import org.hibernate.query.sqm.tree.predicate.SqmPredicate; import org.hibernate.query.sqm.tree.predicate.SqmWhereClause; @@ -32,9 +33,7 @@ import org.hibernate.query.sqm.tree.predicate.SqmWhereClause; */ public class SqmUpdateStatement extends AbstractSqmDmlStatement - implements SqmDeleteOrUpdateStatement, JpaCriteriaUpdate { - private final SqmQuerySource querySource; - + implements SqmDeleteOrUpdateStatement, SqmCteConsumer, JpaCriteriaUpdate { private SqmSetClause setClause; private SqmWhereClause whereClause; @@ -43,8 +42,7 @@ public class SqmUpdateStatement } public SqmUpdateStatement(SqmRoot target, SqmQuerySource querySource, NodeBuilder nodeBuilder) { - super( target, nodeBuilder ); - this.querySource = querySource; + super( target, querySource, nodeBuilder ); } public SqmUpdateStatement(Class targetEntity, SqmCriteriaNodeBuilder nodeBuilder) { @@ -59,11 +57,6 @@ public class SqmUpdateStatement ); } - @Override - public SqmQuerySource getQuerySource() { - return querySource; - } - public SqmSetClause getSetClause() { return setClause; } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/StandardSqlAstInsertSelectTranslator.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/StandardSqlAstInsertSelectTranslator.java index 9578e4e7d8..87a7ef3b38 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/StandardSqlAstInsertSelectTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/StandardSqlAstInsertSelectTranslator.java @@ -9,14 +9,14 @@ package org.hibernate.sql.ast.spi; import java.util.List; import java.util.Set; -import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.sql.ast.SqlAstInsertSelectTranslator; +import org.hibernate.sql.ast.tree.cte.CteColumn; import org.hibernate.sql.ast.tree.cte.CteStatement; +import org.hibernate.sql.ast.tree.delete.DeleteStatement; import org.hibernate.sql.ast.tree.expression.ColumnReference; import org.hibernate.sql.ast.tree.insert.InsertSelectStatement; import org.hibernate.sql.exec.spi.JdbcInsert; -import org.hibernate.sql.exec.spi.JdbcOperation; import org.hibernate.sql.exec.spi.JdbcParameterBinder; /** @@ -81,7 +81,46 @@ public class StandardSqlAstInsertSelectTranslator } @Override - public JdbcOperation translate(CteStatement cteStatement) { - throw new NotYetImplementedFor6Exception( getClass() ); + public JdbcInsert translate(CteStatement sqlAst) { + assert sqlAst.getCteConsumer() instanceof DeleteStatement; + + appendSql( "with " ); + appendSql( sqlAst.getCteLabel() ); + + appendSql( " (" ); + + String separator = ""; + + for ( int i = 0; i < sqlAst.getCteTable().getCteColumns().size(); i++ ) { + final CteColumn cteColumn = sqlAst.getCteTable().getCteColumns().get( i ); + appendSql( separator ); + appendSql( cteColumn.getColumnExpression() ); + separator = ", "; + } + + appendSql( ") as (" ); + + visitQuerySpec( sqlAst.getCteDefinition() ); + + appendSql( ") " ); + + translate( (InsertSelectStatement) sqlAst.getCteConsumer() ); + + return new JdbcInsert() { + @Override + public String getSql() { + return StandardSqlAstInsertSelectTranslator.this.getSql(); + } + + @Override + public List getParameterBinders() { + return StandardSqlAstInsertSelectTranslator.this.getParameterBinders(); + } + + @Override + public Set getAffectedTableNames() { + return StandardSqlAstInsertSelectTranslator.this.getAffectedTableNames(); + } + }; } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/StandardSqlAstSelectTranslator.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/StandardSqlAstSelectTranslator.java index 65f7fc533b..c4bf3a7e19 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/StandardSqlAstSelectTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/StandardSqlAstSelectTranslator.java @@ -13,10 +13,10 @@ import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.sql.ast.SqlAstSelectTranslator; import org.hibernate.sql.ast.SqlTreePrinter; import org.hibernate.sql.ast.tree.SqlAstTreeLogger; +import org.hibernate.sql.ast.tree.cte.CteColumn; import org.hibernate.sql.ast.tree.cte.CteStatement; import org.hibernate.sql.ast.tree.select.QuerySpec; import org.hibernate.sql.ast.tree.select.SelectStatement; -import org.hibernate.sql.exec.spi.JdbcOperation; import org.hibernate.sql.exec.spi.JdbcSelect; import org.hibernate.sql.results.internal.JdbcValuesMappingProducerStandard; @@ -36,7 +36,31 @@ public class StandardSqlAstSelectTranslator } @Override - public JdbcSelect translate(CteStatement cteStatement) { + public JdbcSelect translate(CteStatement sqlAst) { + assert sqlAst.getCteConsumer() instanceof QuerySpec; + + appendSql( "with " ); + appendSql( sqlAst.getCteLabel() ); + + appendSql( " (" ); + + String separator = ""; + + for ( int i = 0; i < sqlAst.getCteTable().getCteColumns().size(); i++ ) { + final CteColumn cteColumn = sqlAst.getCteTable().getCteColumns().get( i ); + appendSql( separator ); + appendSql( cteColumn.getColumnExpression() ); + separator = ", "; + } + + appendSql( ") as (" ); + + visitQuerySpec( sqlAst.getCteDefinition() ); + + appendSql( ") " ); + + translate( (QuerySpec) sqlAst.getCteConsumer() ); + throw new NotYetImplementedFor6Exception( getClass() ); } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteColumn.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteColumn.java index 7c0a5407fb..cfad015064 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteColumn.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteColumn.java @@ -7,26 +7,21 @@ package org.hibernate.sql.ast.tree.cte; import org.hibernate.metamodel.mapping.JdbcMapping; -import org.hibernate.sql.ast.tree.cte.CteTable; /** + * Information about a column in the CTE table + * * @author Steve Ebersole */ public class CteColumn { - private final CteTable cteTable; private final String columnExpression; private final JdbcMapping jdbcMapping; - public CteColumn(CteTable cteTable, String columnExpression, JdbcMapping jdbcMapping) { - this.cteTable = cteTable; + public CteColumn(String columnExpression, JdbcMapping jdbcMapping) { this.columnExpression = columnExpression; this.jdbcMapping = jdbcMapping; } - public CteTable getCteTable() { - return cteTable; - } - public String getColumnExpression() { return columnExpression; } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteConsumer.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteConsumer.java new file mode 100644 index 0000000000..4a6fc1dfa1 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteConsumer.java @@ -0,0 +1,16 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.sql.ast.tree.cte; + +/** + * The consumer part of a CTE statement - the select or insert or delete or update that uses + * the CTE + * + * @author Steve Ebersole + */ +public interface CteConsumer { +} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteStatement.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteStatement.java index 80991c7845..677b82abde 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteStatement.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteStatement.java @@ -18,9 +18,9 @@ public class CteStatement implements Statement { private final String cteLabel; private final CteTable cteTable; private final QuerySpec cteDefinition; - private final Statement cteConsumer; + private final CteConsumer cteConsumer; - public CteStatement(QuerySpec cteDefinition, String cteLabel, CteTable cteTable, Statement cteConsumer) { + public CteStatement(QuerySpec cteDefinition, String cteLabel, CteTable cteTable, CteConsumer cteConsumer) { this.cteDefinition = cteDefinition; this.cteLabel = cteLabel; this.cteTable = cteTable; @@ -39,7 +39,7 @@ public class CteStatement implements Statement { return cteDefinition; } - public Statement getCteConsumer() { + public CteConsumer getCteConsumer() { return cteConsumer; } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteTable.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteTable.java index 736d10f42f..e405bc16b8 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteTable.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteTable.java @@ -13,9 +13,9 @@ import java.util.List; import org.hibernate.LockMode; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.internal.util.StringHelper; +import org.hibernate.metamodel.mapping.Bindable; import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.JdbcMapping; -import org.hibernate.metamodel.spi.RuntimeModelCreationContext; import org.hibernate.query.NavigablePath; import org.hibernate.query.sqm.mutation.internal.cte.CteBasedMutationStrategy; import org.hibernate.sql.ast.Clause; @@ -29,28 +29,26 @@ import org.hibernate.sql.exec.spi.JdbcParameter; import org.hibernate.sql.exec.spi.JdbcParameterBinding; import org.hibernate.sql.exec.spi.JdbcParameterBindings; import org.hibernate.sql.results.internal.SqlSelectionImpl; +import org.hibernate.type.spi.TypeConfiguration; /** - * Describes the CTE and exposes ways to consume it + * Describes the table definition for the CTE - its name amd its columns * * @author Steve Ebersole */ public class CteTable { - private final EntityMappingType entityDescriptor; private final SessionFactoryImplementor sessionFactory; private final List cteColumns; - public CteTable(EntityMappingType entityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) { - this.entityDescriptor = entityDescriptor; + public CteTable(EntityMappingType entityDescriptor, TypeConfiguration typeConfiguration) { this.sessionFactory = entityDescriptor.getEntityPersister().getFactory(); - final int numberOfColumns = entityDescriptor.getIdentifierMapping().getJdbcTypeCount( runtimeModelCreationContext.getTypeConfiguration() ); + final int numberOfColumns = entityDescriptor.getIdentifierMapping().getJdbcTypeCount( typeConfiguration ); cteColumns = new ArrayList<>( numberOfColumns ); entityDescriptor.getIdentifierMapping().visitColumns( (columnExpression, containingTableExpression, jdbcMapping) -> cteColumns.add( new CteColumn( - this, "cte_" + columnExpression, jdbcMapping ) @@ -58,6 +56,11 @@ public class CteTable { ); } + public CteTable(List cteColumns, SessionFactoryImplementor sessionFactory) { + this.cteColumns = cteColumns; + this.sessionFactory = sessionFactory; + } + public String getTableExpression() { return CteBasedMutationStrategy.TABLE_NAME; } @@ -68,12 +71,14 @@ public class CteTable { public QuerySpec createCteDefinition( List matchingIds, + Bindable bindable, JdbcParameterBindings jdbcParameterBindings, ExecutionContext executionContext) { final QuerySpec querySpec = new QuerySpec( false ); final TableReference tableValueConstructorReference = createCteDefinitionTableValueCtor( matchingIds, + bindable, jdbcParameterBindings, executionContext ); @@ -96,7 +101,8 @@ public class CteTable { } private TableReference createCteDefinitionTableValueCtor( - List matchingIds, + List matchingValues, + Bindable bindable, JdbcParameterBindings jdbcParameterBindings, ExecutionContext executionContext) { // use `DerivedTable` as the TableValueConstructor @@ -106,17 +112,14 @@ public class CteTable { final StringBuilder tableValueCtorExpressionBuffer = new StringBuilder( "values(" ); String rowSeparator = ""; - int idProcessedCount = 0; - for ( Object matchingId : matchingIds ) { + for ( Object matchingId : matchingValues ) { tableValueCtorExpressionBuffer.append( rowSeparator ); tableValueCtorExpressionBuffer.append( '(' ); StringHelper.repeat( "?", numberOfColumns, ",", tableValueCtorExpressionBuffer ); tableValueCtorExpressionBuffer.append( ')' ); - final int currentIdPosition = idProcessedCount; - - entityDescriptor.getIdentifierMapping().visitJdbcValues( + bindable.visitJdbcValues( matchingId, Clause.IRRELEVANT, (value, type) -> { @@ -141,7 +144,6 @@ public class CteTable { ); rowSeparator = ", "; - idProcessedCount++; } tableValueCtorExpressionBuffer.append( ')' ); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteTableGroup.java index 45cdd8c1b4..0e012d9593 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteTableGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteTableGroup.java @@ -32,7 +32,7 @@ public class CteTableGroup implements TableGroup { private final TableReference cteTableReference; public CteTableGroup(TableReference cteTableReference) { - this.navigablePath = new NavigablePath( CteBasedMutationStrategy.SHORT_NAME ); + this.navigablePath = new NavigablePath( CteBasedMutationStrategy.TABLE_NAME ); this.cteTableReference = cteTableReference; } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/package-info.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/package-info.java new file mode 100644 index 0000000000..2d3c6980d7 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/package-info.java @@ -0,0 +1,44 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ + +/** + * @asciidoc + * + * Support for SQL CTE statements (WITH). The general syntax for a CTE statement is: + * + * ```` + * cte:: WITH {cte-label} OPEN_PAREN cteColumns CLOSE_PAREN AS cteDefinition consumer + * + * cteColumns:: cteColumn (, cteColumn) + * + * todo (6.0) : should this include not-null, etc? + * cteColumn:: ... + * + * cteDefinition:: querySpec + * + * todo (6.0) : imo it would be better to have a specific contract `CteConsumer` for things that can occur here, which are: + * * select - `QuerySpec` + * * delete - `DeleteStatement` + * * update - `UpdateStatement` + * * insert-select - `InsertSelectStatement + * + * consumer:: querySpec | deleteStatement | updateStatement | insertSelectStatement + * + * for example, a delete consumer might look like: + * + * with cte_name ( col1, col2, col3 ) as ( + * select some_val1, some_val, some_v3 + * from some_place + * ) + * delete from some_table + * where (some_table_col1, some_table_col2, some_table_col3) in ( + * select col1, col2, col3 + * from cte_name + * ) + * ```` + */ +package org.hibernate.sql.ast.tree.cte; diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/delete/DeleteStatement.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/delete/DeleteStatement.java index d909bfa787..b8f0a335b8 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/delete/DeleteStatement.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/delete/DeleteStatement.java @@ -8,6 +8,7 @@ package org.hibernate.sql.ast.tree.delete; import org.hibernate.sql.ast.spi.SqlAstHelper; import org.hibernate.sql.ast.tree.MutationStatement; +import org.hibernate.sql.ast.tree.cte.CteConsumer; import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.predicate.Junction; import org.hibernate.sql.ast.tree.predicate.Predicate; @@ -15,7 +16,7 @@ import org.hibernate.sql.ast.tree.predicate.Predicate; /** * @author Steve Ebersole */ -public class DeleteStatement implements MutationStatement { +public class DeleteStatement implements MutationStatement, CteConsumer { private final TableReference targetTable; private final Predicate restriction; diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/insert/InsertSelectStatement.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/insert/InsertSelectStatement.java index 083b7a9725..e93370e5f8 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/insert/InsertSelectStatement.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/insert/InsertSelectStatement.java @@ -11,6 +11,7 @@ import java.util.Collections; import java.util.List; import org.hibernate.sql.ast.tree.MutationStatement; +import org.hibernate.sql.ast.tree.cte.CteConsumer; import org.hibernate.sql.ast.tree.expression.ColumnReference; import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.select.QuerySpec; @@ -20,7 +21,7 @@ import org.jboss.logging.Logger; /** * @author Steve Ebersole */ -public class InsertSelectStatement implements MutationStatement { +public class InsertSelectStatement implements MutationStatement, CteConsumer { private static final Logger log = Logger.getLogger( InsertSelectStatement.class ); private TableReference targetTable; diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/select/QuerySpec.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/select/QuerySpec.java index a97cb0fe33..4ac006f7f3 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/select/QuerySpec.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/select/QuerySpec.java @@ -13,6 +13,7 @@ import java.util.function.Consumer; import org.hibernate.sql.ast.spi.SqlAstTreeHelper; import org.hibernate.sql.ast.spi.SqlAstWalker; import org.hibernate.sql.ast.tree.SqlAstNode; +import org.hibernate.sql.ast.tree.cte.CteConsumer; import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.from.FromClause; import org.hibernate.sql.ast.tree.predicate.Predicate; @@ -21,7 +22,7 @@ import org.hibernate.sql.ast.tree.predicate.PredicateContainer; /** * @author Steve Ebersole */ -public class QuerySpec implements SqlAstNode, PredicateContainer { +public class QuerySpec implements SqlAstNode, PredicateContainer, CteConsumer { private final boolean isRoot; private final FromClause fromClause; diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/update/UpdateStatement.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/update/UpdateStatement.java index 875fe4c0a2..9dc9fe6f7c 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/update/UpdateStatement.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/update/UpdateStatement.java @@ -7,19 +7,18 @@ package org.hibernate.sql.ast.tree.update; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import org.hibernate.sql.ast.spi.SqlAstTreeHelper; import org.hibernate.sql.ast.tree.MutationStatement; +import org.hibernate.sql.ast.tree.cte.CteConsumer; import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.predicate.Predicate; -import org.hibernate.sql.ast.tree.predicate.PredicateContainer; /** * @author Steve Ebersole */ -public class UpdateStatement implements MutationStatement { +public class UpdateStatement implements MutationStatement, CteConsumer { private final TableReference targetTable; private final List assignments; private final Predicate restriction; diff --git a/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcUpdate.java b/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcUpdate.java new file mode 100644 index 0000000000..2e8d3cedbd --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcUpdate.java @@ -0,0 +1,13 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.sql.exec.spi; + +/** + * @author Steve Ebersole + */ +public interface JdbcUpdate extends JdbcMutation { +} diff --git a/hibernate-core/src/test/resources/log4j.properties b/hibernate-core/src/test/resources/log4j.properties index 48c1268212..74500c4dba 100644 --- a/hibernate-core/src/test/resources/log4j.properties +++ b/hibernate-core/src/test/resources/log4j.properties @@ -21,8 +21,6 @@ log4j.logger.org.hibernate.orm.graph=debug log4j.logger.org.hibernate.orm.query.sqm=debug log4j.logger.org.hibernate.orm.query.hql=debug -log4j.logger.org.hibernate.query.sqm.mutation.internal.idtable.TableBasedDeleteHandler=trace - log4j.logger.org.hibernate.tool.hbm2ddl=trace log4j.logger.org.hibernate.testing.cache=debug