HHH-13715 - working support for "multi-table" HQL/Criteria UPDATE and DELETE queries

work on generalized CTE handling;
initial work on SQM CTE support
This commit is contained in:
Steve Ebersole 2019-11-12 14:19:23 -06:00
parent dd364ccf5f
commit eddd5b938b
51 changed files with 826 additions and 144 deletions

View File

@ -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;

View File

@ -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
);
}
};
}

View File

@ -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> {
T visitSelectStatement(SqmSelectStatement<?> statement);
T visitCteStatement(SqmCteStatement sqmCteStatement);
T visitCteConsumer(SqmCteConsumer consumer);
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// from-clause / domain paths

View File

@ -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 );
}
}

View File

@ -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<R>
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

View File

@ -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();

View File

@ -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<QueryParameterImplementor<?>, Map<SqmParameter, List<JdbcParameter>>> 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( );
}

View File

@ -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 <T> SqmDeleteStatement<T> createCriteriaDelete(Class<T> targetEntity) {
return new SqmDeleteStatement<>( targetEntity, this );
return new SqmDeleteStatement<>( targetEntity, SqmQuerySource.CRITERIA, this );
}
@Override

View File

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

View File

@ -120,7 +120,7 @@ public class CteBasedMutationStrategy implements SqmMultiTableMutationStrategy {
);
}
this.cteTable = new CteTable( rootDescriptor, runtimeModelCreationContext );
this.cteTable = new CteTable( rootDescriptor, runtimeModelCreationContext.getTypeConfiguration() );
}
@Override

View File

@ -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
);

View File

@ -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<Object> {
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() );

View File

@ -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<SqmCteTableColumn> sqmCteColumns = sqmCteTable.getColumns();
final List<CteColumn> 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) {

View File

@ -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);
}

View File

@ -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<SqmParameter, List<JdbcParameter>> jdbcParamMap;
public SimpleSqmUpdateTranslation(
UpdateStatement sqlAst,
Map<SqmParameter, List<JdbcParameter>> jdbcParamMap) {
this.sqlAst = sqlAst;
this.jdbcParamMap = jdbcParamMap;
}
@Override
public UpdateStatement getSqlAst() {
return sqlAst;
}
@Override
public Map<SqmParameter, List<JdbcParameter>> getJdbcParamsBySqmParam() {
return jdbcParamMap;
}
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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...
}

View File

@ -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;
}
}

View File

@ -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();

View File

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

View File

@ -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<Assignment> visitSetClause(SqmSetClause setClause) {
final List<Assignment> 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 );
}
}

View File

@ -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<E>
implements SqmDmlStatement<E> {
private SqmRoot<E> target;
public AbstractSqmDmlStatement(NodeBuilder nodeBuilder) {
super( nodeBuilder );
public AbstractSqmDmlStatement(SqmQuerySource querySource, NodeBuilder nodeBuilder) {
super( querySource, nodeBuilder );
}
public AbstractSqmDmlStatement(SqmRoot<E> target, NodeBuilder nodeBuilder) {
this( nodeBuilder );
public AbstractSqmDmlStatement(SqmRoot<E> target, SqmQuerySource querySource, NodeBuilder nodeBuilder) {
this( querySource, nodeBuilder );
this.target = target;
}

View File

@ -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<T> extends AbstractSqmNode implements SqmStatement<T>, ParameterCollector {
public AbstractSqmStatement(NodeBuilder builder) {
private final SqmQuerySource querySource;
public AbstractSqmStatement(
SqmQuerySource querySource,
NodeBuilder builder) {
super( builder );
this.querySource = querySource;
}
private Set<SqmParameter<?>> parameters;
@Override
public SqmQuerySource getQuerySource() {
return querySource;
}
@Override
public void addParameter(SqmParameter parameter) {
if ( parameters == null ) {

View File

@ -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<E> extends SqmStatement<E>, JpaManipulationCriteria<E> {
public interface SqmDmlStatement<E> extends SqmStatement<E>, SqmCteConsumer, JpaManipulationCriteria<E> {
/**
* Get the root path that is the target of the DML statement.
*/

View File

@ -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 {
}

View File

@ -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 <U> JpaSubQuery<U> subquery(Class<U> type) {
return new SqmSubQuery<U>(
this,
new SqmQuerySpec<>( nodeBuilder() ),
nodeBuilder()
);
}
@Override
public JpaPredicate getRestriction() {
return null;
}
@Override
public <X> X accept(SemanticQueryWalker<X> walker) {
return walker.visitCteStatement( this );
}
}

View File

@ -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<SqmCteTableColumn> columns;
public SqmCteTable(String cteName, List<SqmCteTableColumn> columns) {
this.cteName = cteName;
this.columns = columns;
}
public String getCteName() {
return cteName;
}
public List<SqmCteTableColumn> getColumns() {
return columns;
}
public void visitColumns(Consumer<SqmCteTableColumn> columnConsumer) {
for ( int i = 0; i < columns.size(); i++ ) {
columnConsumer.accept( columns.get( i ) );
}
}
}

View File

@ -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;
}
}

View File

@ -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<T>
extends AbstractSqmDmlStatement<T>
implements SqmDeleteOrUpdateStatement<T>, JpaCriteriaDelete<T> {
implements SqmDeleteOrUpdateStatement<T>, SqmCteConsumer, JpaCriteriaDelete<T> {
private final SqmQuerySource querySource;
private SqmWhereClause whereClause;
public SqmDeleteStatement(SqmRoot<T> target, NodeBuilder nodeBuilder) {
super( target, nodeBuilder );
public SqmDeleteStatement(SqmRoot<T> target, SqmQuerySource querySource, NodeBuilder nodeBuilder) {
super( target, querySource, nodeBuilder );
this.querySource = SqmQuerySource.HQL;
}
public SqmDeleteStatement(Class<T> targetEntity, NodeBuilder nodeBuilder) {
public SqmDeleteStatement(Class<T> targetEntity, SqmQuerySource querySource, NodeBuilder nodeBuilder) {
super(
new SqmRoot<>(
nodeBuilder.getDomainModel().entity( targetEntity ),
null,
nodeBuilder
),
querySource,
nodeBuilder
);
this.querySource = SqmQuerySource.CRITERIA;

View File

@ -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<T> extends AbstractSqmDmlStatement<T> implements SqmInsertStatement<T> {
private List<SqmPath> insertionTargetPaths;
protected AbstractSqmInsertStatement(NodeBuilder nodeBuilder) {
super( nodeBuilder );
protected AbstractSqmInsertStatement(SqmQuerySource querySource, NodeBuilder nodeBuilder) {
super( querySource, nodeBuilder );
}
protected AbstractSqmInsertStatement(SqmRoot<T> targetRoot, NodeBuilder nodeBuilder) {
super( targetRoot, nodeBuilder );
protected AbstractSqmInsertStatement(SqmRoot<T> targetRoot, SqmQuerySource querySource, NodeBuilder nodeBuilder) {
super( targetRoot, querySource, nodeBuilder );
}
@Override

View File

@ -18,13 +18,10 @@ import org.hibernate.query.sqm.tree.from.SqmRoot;
* @author Steve Ebersole
*/
public class SqmInsertSelectStatement<T> extends AbstractSqmInsertStatement<T> implements JpaCriteriaInsertSelect<T> {
private final SqmQuerySource querySource;
private SqmQuerySpec selectQuerySpec;
public SqmInsertSelectStatement(SqmRoot<T> targetRoot, NodeBuilder nodeBuilder) {
super( targetRoot, nodeBuilder );
querySource = SqmQuerySource.HQL;
super( targetRoot, SqmQuerySource.HQL, nodeBuilder );
}
public SqmInsertSelectStatement(Class<T> targetEntity, NodeBuilder nodeBuilder) {
@ -34,9 +31,9 @@ public class SqmInsertSelectStatement<T> extends AbstractSqmInsertStatement<T> i
null,
nodeBuilder
),
SqmQuerySource.CRITERIA,
nodeBuilder
);
querySource = SqmQuerySource.CRITERIA;
}
public SqmQuerySpec getSelectQuerySpec() {
@ -52,11 +49,6 @@ public class SqmInsertSelectStatement<T> extends AbstractSqmInsertStatement<T> i
return walker.visitInsertSelectStatement( this );
}
@Override
public SqmQuerySource getQuerySource() {
return querySource;
}
@Override
public JpaPredicate getRestriction() {
// insert has no predicate

View File

@ -28,12 +28,9 @@ import org.hibernate.query.sqm.tree.predicate.SqmPredicate;
public abstract class AbstractSqmSelectQuery<T>
extends AbstractSqmNode
implements SqmSelectQuery<T> {
private SqmQuerySpec<T> sqmQuerySpec;
private Class resultType;
@SuppressWarnings("WeakerAccess")
public AbstractSqmSelectQuery(Class<T> resultType, NodeBuilder builder) {
super( builder );
this.sqmQuerySpec = new SqmQuerySpec( builder );
@ -41,14 +38,10 @@ public abstract class AbstractSqmSelectQuery<T>
}
@SuppressWarnings("WeakerAccess")
public AbstractSqmSelectQuery(SqmQuerySpec<T> 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;

View File

@ -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<T> implements SqmNode, SqmFromClauseContainer, SqmWhereClauseContainer, JpaQueryStructure<T> {
public class SqmQuerySpec<T> implements SqmCteConsumer, SqmNode, SqmFromClauseContainer, SqmWhereClauseContainer, JpaQueryStructure<T> {
private final NodeBuilder nodeBuilder;
private SqmFromClause fromClause;

View File

@ -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<T>
extends AbstractSqmDmlStatement<T>
implements SqmDeleteOrUpdateStatement<T>, JpaCriteriaUpdate<T> {
private final SqmQuerySource querySource;
implements SqmDeleteOrUpdateStatement<T>, SqmCteConsumer, JpaCriteriaUpdate<T> {
private SqmSetClause setClause;
private SqmWhereClause whereClause;
@ -43,8 +42,7 @@ public class SqmUpdateStatement<T>
}
public SqmUpdateStatement(SqmRoot<T> target, SqmQuerySource querySource, NodeBuilder nodeBuilder) {
super( target, nodeBuilder );
this.querySource = querySource;
super( target, querySource, nodeBuilder );
}
public SqmUpdateStatement(Class<T> targetEntity, SqmCriteriaNodeBuilder nodeBuilder) {
@ -59,11 +57,6 @@ public class SqmUpdateStatement<T>
);
}
@Override
public SqmQuerySource getQuerySource() {
return querySource;
}
public SqmSetClause getSetClause() {
return setClause;
}

View File

@ -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<JdbcParameterBinder> getParameterBinders() {
return StandardSqlAstInsertSelectTranslator.this.getParameterBinders();
}
@Override
public Set<String> getAffectedTableNames() {
return StandardSqlAstInsertSelectTranslator.this.getAffectedTableNames();
}
};
}
}

View File

@ -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() );
}

View File

@ -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;
}

View File

@ -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 {
}

View File

@ -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;
}
}

View File

@ -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<CteColumn> 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<CteColumn> 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( ')' );

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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<Assignment> assignments;
private final Predicate restriction;

View File

@ -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 {
}

View File

@ -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