Add support for HQL 'insert ... values ...'

This commit is contained in:
gavinking 2020-02-16 01:25:30 +01:00 committed by Steve Ebersole
parent 10f333943d
commit 95ff568b3d
25 changed files with 420 additions and 171 deletions

View File

@ -269,6 +269,7 @@ TYPE : [tT] [yY] [pP] [eE];
UPDATE : [uU] [pP] [dD] [aA] [tT] [eE];
UPPER : [uU] [pP] [pP] [eE] [rR];
VALUE : [vV] [aA] [lL] [uU] [eE];
VALUES : [vV] [aA] [lL] [uU] [eE] [sS];
WEEK : [wW] [eE] [eE] [kK];
WHEN : [wW] [hH] [eE] [nN];
WHERE : [wW] [hH] [eE] [rR] [eE];

View File

@ -56,13 +56,21 @@ assignment
;
insertStatement
: INSERT INTO? rootEntity targetFieldsSpec querySpec
: INSERT INTO? rootEntity targetFieldsSpec (querySpec | valuesList)
;
targetFieldsSpec
: LEFT_PAREN dotIdentifierSequence (COMMA dotIdentifierSequence)* RIGHT_PAREN
;
valuesList
: VALUES values (COMMA values)*
;
values
: LEFT_PAREN expression (COMMA expression)* RIGHT_PAREN
;
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// QUERY SPEC - general structure of root sqm or sub sqm

View File

@ -128,6 +128,9 @@ import org.hibernate.query.sqm.tree.from.SqmJoin;
import org.hibernate.query.sqm.tree.from.SqmQualifiedJoin;
import org.hibernate.query.sqm.tree.from.SqmRoot;
import org.hibernate.query.sqm.tree.insert.SqmInsertSelectStatement;
import org.hibernate.query.sqm.tree.insert.SqmInsertStatement;
import org.hibernate.query.sqm.tree.insert.SqmInsertValuesStatement;
import org.hibernate.query.sqm.tree.insert.SqmValues;
import org.hibernate.query.sqm.tree.predicate.SqmAndPredicate;
import org.hibernate.query.sqm.tree.predicate.SqmBetweenPredicate;
import org.hibernate.query.sqm.tree.predicate.SqmComparisonPredicate;
@ -303,7 +306,7 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
}
@Override
public SqmInsertSelectStatement visitInsertStatement(HqlParser.InsertStatementContext ctx) {
public SqmInsertStatement visitInsertStatement(HqlParser.InsertStatementContext ctx) {
final SqmRoot<?> root = new SqmRoot<>(
visitEntityName( ctx.rootEntity().entityName() ),
@ -311,7 +314,7 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
creationContext.getNodeBuilder()
);
// for now we only support the INSERT-SELECT form
if ( ctx.querySpec()!=null ) {
final SqmInsertSelectStatement<?> insertStatement = new SqmInsertSelectStatement<>( root, creationContext.getNodeBuilder() );
parameterCollector = insertStatement;
final SqmDmlCreationProcessingState processingState = new SqmDmlCreationProcessingState(
@ -336,6 +339,41 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
finally {
processingStateStack.pop();
}
}
else {
final SqmInsertValuesStatement<?> insertStatement = new SqmInsertValuesStatement<>( root, creationContext.getNodeBuilder() );
parameterCollector = insertStatement;
final SqmDmlCreationProcessingState processingState = new SqmDmlCreationProcessingState(
insertStatement,
this
);
processingStateStack.push( processingState );
processingState.getPathRegistry().register( root );
try {
for ( HqlParser.ValuesContext values : ctx.valuesList().values() ) {
SqmValues sqmValues = new SqmValues();
for ( HqlParser.ExpressionContext expressionContext : values.expression() ) {
sqmValues.getExpressions().add( (SqmExpression) expressionContext.accept( this ) );
}
insertStatement.getValuesList().add( sqmValues );
}
for ( HqlParser.DotIdentifierSequenceContext stateFieldCtx : ctx.targetFieldsSpec().dotIdentifierSequence() ) {
final SqmPath stateField = (SqmPath) visitDotIdentifierSequence( stateFieldCtx );
// todo : validate each resolved stateField...
insertStatement.addInsertTargetStateField( stateField );
}
return insertStatement;
}
finally {
processingStateStack.pop();
}
}
}
@Override

View File

@ -58,6 +58,8 @@ import org.hibernate.query.sqm.tree.from.SqmEntityJoin;
import org.hibernate.query.sqm.tree.from.SqmFromClause;
import org.hibernate.query.sqm.tree.from.SqmRoot;
import org.hibernate.query.sqm.tree.insert.SqmInsertSelectStatement;
import org.hibernate.query.sqm.tree.insert.SqmInsertValuesStatement;
import org.hibernate.query.sqm.tree.insert.SqmValues;
import org.hibernate.query.sqm.tree.predicate.SqmAndPredicate;
import org.hibernate.query.sqm.tree.predicate.SqmBetweenPredicate;
import org.hibernate.query.sqm.tree.predicate.SqmBooleanExpressionPredicate;
@ -102,6 +104,8 @@ public interface SemanticQueryWalker<T> {
T visitInsertSelectStatement(SqmInsertSelectStatement<?> statement);
T visitInsertValuesStatement(SqmInsertValuesStatement<?> statement);
T visitDeleteStatement(SqmDeleteStatement<?> statement);
T visitSelectStatement(SqmSelectStatement<?> statement);
@ -156,6 +160,8 @@ public interface SemanticQueryWalker<T> {
T visitSelection(SqmSelection selection);
T visitValues(SqmValues values);
T visitGroupByClause(SqmGroupByClause clause);
T visitGrouping(SqmGroupByClause.SqmGrouping grouping);

View File

@ -573,12 +573,12 @@ public class QuerySqmImpl<R>
private NonSelectQueryPlan buildInsertQueryPlan() {
final SqmInsertStatement sqmInsert = (SqmInsertStatement) getSqmStatement();
final String entityNameToUpdate = sqmInsert.getTarget().getReferencedPathSource().getHibernateEntityName();
final EntityPersister entityDescriptor = getSessionFactory().getDomainModel().findEntityDescriptor( entityNameToUpdate );
// final String entityNameToUpdate = sqmInsert.getTarget().getReferencedPathSource().getHibernateEntityName();
// final EntityPersister entityDescriptor = getSessionFactory().getDomainModel().findEntityDescriptor( entityNameToUpdate );
// final SqmMultiTableMutationStrategy multiTableStrategy = entityDescriptor.getSqmMultiTableMutationStrategy();
// if ( multiTableStrategy == null ) {
return new SimpleInsertQueryPlan( (SqmInsertSelectStatement) sqmInsert, domainParameterXref );
return new SimpleInsertQueryPlan( sqmInsert, domainParameterXref );
// }
// else {
//TODO:

View File

@ -12,11 +12,11 @@ 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.SqmInsertSelectTranslation;
import org.hibernate.query.sqm.sql.SqmInsertSelectTranslator;
import org.hibernate.query.sqm.sql.SqmInsertTranslation;
import org.hibernate.query.sqm.sql.SqmInsertTranslator;
import org.hibernate.query.sqm.sql.SqmTranslatorFactory;
import org.hibernate.query.sqm.tree.expression.SqmParameter;
import org.hibernate.query.sqm.tree.insert.SqmInsertSelectStatement;
import org.hibernate.query.sqm.tree.insert.SqmInsertStatement;
import org.hibernate.sql.ast.SqlAstInsertSelectTranslator;
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
import org.hibernate.sql.ast.spi.FromClauseAccess;
@ -32,7 +32,7 @@ import java.util.Map;
* @author Gavin King
*/
public class SimpleInsertQueryPlan implements NonSelectQueryPlan {
private final SqmInsertSelectStatement sqmInsert;
private final SqmInsertStatement sqmInsert;
private final DomainParameterXref domainParameterXref;
private JdbcInsert jdbcInsert;
@ -40,7 +40,7 @@ public class SimpleInsertQueryPlan implements NonSelectQueryPlan {
private Map<QueryParameterImplementor<?>, Map<SqmParameter, List<JdbcParameter>>> jdbcParamsXref;
public SimpleInsertQueryPlan(
SqmInsertSelectStatement sqmInsert,
SqmInsertStatement sqmInsert,
DomainParameterXref domainParameterXref) {
this.sqmInsert = sqmInsert;
this.domainParameterXref = domainParameterXref;
@ -55,7 +55,7 @@ public class SimpleInsertQueryPlan implements NonSelectQueryPlan {
final QueryEngine queryEngine = factory.getQueryEngine();
final SqmTranslatorFactory translatorFactory = queryEngine.getSqmTranslatorFactory();
final SqmInsertSelectTranslator translator = translatorFactory.createInsertSelectTranslator(
final SqmInsertTranslator translator = translatorFactory.createInsertTranslator(
executionContext.getQueryOptions(),
domainParameterXref,
executionContext.getQueryParameterBindings(),
@ -63,7 +63,7 @@ public class SimpleInsertQueryPlan implements NonSelectQueryPlan {
factory
);
final SqmInsertSelectTranslation sqmInterpretation = translator.translate(sqmInsert);
final SqmInsertTranslation sqmInterpretation = translator.translate(sqmInsert);
tableGroupAccess = translator.getFromClauseAccess();

View File

@ -64,6 +64,8 @@ import org.hibernate.query.sqm.tree.from.SqmFrom;
import org.hibernate.query.sqm.tree.from.SqmFromClause;
import org.hibernate.query.sqm.tree.from.SqmRoot;
import org.hibernate.query.sqm.tree.insert.SqmInsertSelectStatement;
import org.hibernate.query.sqm.tree.insert.SqmInsertValuesStatement;
import org.hibernate.query.sqm.tree.insert.SqmValues;
import org.hibernate.query.sqm.tree.predicate.SqmAndPredicate;
import org.hibernate.query.sqm.tree.predicate.SqmBetweenPredicate;
import org.hibernate.query.sqm.tree.predicate.SqmBooleanExpressionPredicate;
@ -93,6 +95,7 @@ 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.tree.insert.Values;
import org.jboss.logging.Logger;
/**
@ -297,6 +300,24 @@ public class SqmTreePrinter implements SemanticQueryWalker<Object> {
return null;
}
@Override
public Object visitInsertValuesStatement(SqmInsertValuesStatement<?> statement) {
if ( DEBUG_ENABLED ) {
processStanza(
"insert",
() -> {
logWithIndentation( "[target = %s]", statement.getTarget().getNavigablePath().getFullPath() );
processStanza(
"into",
() -> statement.getInsertionTargetPaths().forEach( sqmPath -> sqmPath.accept( this ) )
);
}
);
}
return null;
}
@Override
public Object visitSelectStatement(SqmSelectStatement<?> statement) {
if ( DEBUG_ENABLED ) {
@ -578,6 +599,11 @@ public class SqmTreePrinter implements SemanticQueryWalker<Object> {
return null;
}
@Override
public Object visitValues(SqmValues values) {
return null;
}
@Override
public Object visitPositionalParameterExpression(SqmPositionalParameter expression) {
logWithIndentation( "?%s", expression.getPosition() );

View File

@ -31,7 +31,7 @@ import org.hibernate.sql.ast.tree.expression.QueryLiteral;
import org.hibernate.sql.ast.tree.from.StandardTableGroup;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
import org.hibernate.sql.ast.tree.insert.InsertStatement;
import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate;
import org.hibernate.sql.ast.tree.predicate.Predicate;
import org.hibernate.sql.ast.tree.select.QuerySpec;
@ -69,7 +69,7 @@ public final class ExecuteWithIdTableHelper {
assert mutatingTableGroup.getModelPart() instanceof EntityMappingType;
final EntityMappingType mutatingEntityDescriptor = (EntityMappingType) mutatingTableGroup.getModelPart();
final InsertSelectStatement idTableInsert = new InsertSelectStatement();
final InsertStatement idTableInsert = new InsertStatement();
final TableReference idTableReference = new TableReference( idTable.getTableExpression(), null, false, factory );
idTableInsert.setTargetTable( idTableReference );

View File

@ -60,6 +60,8 @@ import org.hibernate.query.sqm.tree.from.SqmEntityJoin;
import org.hibernate.query.sqm.tree.from.SqmFromClause;
import org.hibernate.query.sqm.tree.from.SqmRoot;
import org.hibernate.query.sqm.tree.insert.SqmInsertSelectStatement;
import org.hibernate.query.sqm.tree.insert.SqmInsertValuesStatement;
import org.hibernate.query.sqm.tree.insert.SqmValues;
import org.hibernate.query.sqm.tree.predicate.SqmAndPredicate;
import org.hibernate.query.sqm.tree.predicate.SqmBetweenPredicate;
import org.hibernate.query.sqm.tree.predicate.SqmBooleanExpressionPredicate;
@ -145,6 +147,18 @@ public abstract class BaseSemanticQueryWalker implements SemanticQueryWalker<Obj
return statement;
}
@Override
public Object visitInsertValuesStatement(SqmInsertValuesStatement<?> statement) {
visitRootPath( statement.getTarget() );
for ( SqmPath<?> stateField : statement.getInsertionTargetPaths() ) {
stateField.accept( this );
}
for ( SqmValues sqmValues : statement.getValuesList() ) {
visitValues( sqmValues );
}
return statement;
}
@Override
public Object visitDeleteStatement(SqmDeleteStatement<?> statement) {
visitRootPath( statement.getTarget() );
@ -290,6 +304,14 @@ public abstract class BaseSemanticQueryWalker implements SemanticQueryWalker<Obj
return selection;
}
@Override
public Object visitValues(SqmValues values) {
for ( SqmExpression expression : values.getExpressions() ) {
expression.accept( this );
}
return values;
}
@Override
public Object visitWhereClause(SqmWhereClause whereClause) {
if ( whereClause == null ) {

View File

@ -103,6 +103,7 @@ import org.hibernate.query.sqm.tree.from.SqmFromClause;
import org.hibernate.query.sqm.tree.from.SqmJoin;
import org.hibernate.query.sqm.tree.from.SqmRoot;
import org.hibernate.query.sqm.tree.insert.SqmInsertSelectStatement;
import org.hibernate.query.sqm.tree.insert.SqmInsertValuesStatement;
import org.hibernate.query.sqm.tree.predicate.SqmAndPredicate;
import org.hibernate.query.sqm.tree.predicate.SqmBetweenPredicate;
import org.hibernate.query.sqm.tree.predicate.SqmComparisonPredicate;
@ -322,6 +323,11 @@ public abstract class BaseSqmToSqlAstConverter
throw new AssertionFailure( "InsertStatement not supported" );
}
@Override
public Object visitInsertValuesStatement(SqmInsertValuesStatement statement) {
throw new AssertionFailure( "InsertStatement not supported" );
}
@Override
public SelectStatement visitSelectStatement(SqmSelectStatement statement) {
throw new AssertionFailure( "SelectStatement not supported" );

View File

@ -10,24 +10,24 @@ import java.util.List;
import java.util.Map;
import org.hibernate.query.sqm.tree.expression.SqmParameter;
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
import org.hibernate.sql.ast.tree.insert.InsertStatement;
/**
* @author Steve Ebersole
*/
public class SqmInsertSelectTranslation {
private final InsertSelectStatement sqlAst;
public class SqmInsertTranslation {
private final InsertStatement sqlAst;
private final Map<SqmParameter, List<JdbcParameter>> jdbcParamMap;
public SqmInsertSelectTranslation(
InsertSelectStatement sqlAst,
public SqmInsertTranslation(
InsertStatement sqlAst,
Map<SqmParameter, List<JdbcParameter>> jdbcParamMap) {
this.sqlAst = sqlAst;
this.jdbcParamMap = jdbcParamMap;
}
public InsertSelectStatement getSqlAst() {
public InsertStatement getSqlAst() {
return sqlAst;
}

View File

@ -7,11 +7,12 @@
package org.hibernate.query.sqm.sql;
import org.hibernate.query.sqm.spi.JdbcParameterBySqmParameterAccess;
import org.hibernate.query.sqm.tree.insert.SqmInsertSelectStatement;
import org.hibernate.query.sqm.tree.insert.SqmInsertStatement;
/**
* @author Steve Ebersole
*/
public interface SqmInsertSelectTranslator extends SqmToSqlAstConverter, JdbcParameterBySqmParameterAccess, SqmTranslator {
SqmInsertSelectTranslation translate(SqmInsertSelectStatement statement);
public interface SqmInsertTranslator extends SqmToSqlAstConverter, JdbcParameterBySqmParameterAccess, SqmTranslator {
SqmInsertTranslation translate(SqmInsertStatement statement);
}

View File

@ -32,7 +32,7 @@ public interface SqmTranslatorFactory {
LoadQueryInfluencers influencers,
SqlAstCreationContext creationContext);
SqmInsertSelectTranslator createInsertSelectTranslator(
SqmInsertTranslator createInsertTranslator(
QueryOptions queryOptions,
DomainParameterXref domainParameterXref,
QueryParameterBindings domainParameterBindings,

View File

@ -12,7 +12,7 @@ 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.internal.StandardSqmDeleteTranslator;
import org.hibernate.query.sqm.sql.internal.StandardSqmInsertSelectTranslator;
import org.hibernate.query.sqm.sql.internal.StandardSqmInsertTranslator;
import org.hibernate.query.sqm.sql.internal.StandardSqmSelectTranslator;
import org.hibernate.query.sqm.sql.internal.StandardSqmUpdateTranslator;
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
@ -55,13 +55,13 @@ public class StandardSqmTranslatorFactory implements SqmTranslatorFactory {
}
@Override
public SqmInsertSelectTranslator createInsertSelectTranslator(
public SqmInsertTranslator createInsertTranslator(
QueryOptions queryOptions,
DomainParameterXref domainParameterXref,
QueryParameterBindings domainParameterBindings,
LoadQueryInfluencers influencers,
SqlAstCreationContext creationContext) {
return new StandardSqmInsertSelectTranslator(
return new StandardSqmInsertTranslator(
creationContext,
queryOptions,
domainParameterXref,

View File

@ -15,36 +15,45 @@ 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.SqmInsertSelectTranslation;
import org.hibernate.query.sqm.sql.SqmInsertSelectTranslator;
import org.hibernate.query.sqm.sql.SqmInsertTranslation;
import org.hibernate.query.sqm.sql.SqmInsertTranslator;
import org.hibernate.query.sqm.tree.cte.SqmCteStatement;
import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.query.sqm.tree.expression.SqmExpression;
import org.hibernate.query.sqm.tree.insert.SqmInsertSelectStatement;
import org.hibernate.query.sqm.tree.insert.SqmInsertStatement;
import org.hibernate.query.sqm.tree.insert.SqmInsertValuesStatement;
import org.hibernate.query.sqm.tree.insert.SqmValues;
import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
import org.hibernate.query.sqm.tree.select.SqmSelection;
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.Expression;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
import org.hibernate.sql.ast.tree.insert.InsertStatement;
import org.hibernate.sql.ast.tree.insert.Values;
import org.hibernate.sql.ast.tree.select.QuerySpec;
import org.hibernate.sql.ast.tree.select.SelectStatement;
import org.hibernate.sql.ast.tree.update.Assignable;
import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.sql.results.graph.Fetch;
import org.hibernate.sql.results.graph.FetchParent;
import java.util.Collections;
import java.util.List;
/**
* @author Steve Ebersole
*/
public class StandardSqmInsertSelectTranslator
public class StandardSqmInsertTranslator
extends BaseSqmToSqlAstConverter
implements SqmInsertSelectTranslator, DomainResultCreationState {
implements SqmInsertTranslator, DomainResultCreationState {
private final List<DomainResult> domainResults = CollectionHelper.arrayList( 10 );
public StandardSqmInsertSelectTranslator(
public StandardSqmInsertTranslator(
SqlAstCreationContext creationContext,
QueryOptions queryOptions,
DomainParameterXref domainParameterXref,
@ -53,8 +62,15 @@ public class StandardSqmInsertSelectTranslator
}
@Override
public SqmInsertSelectTranslation translate(SqmInsertSelectStatement sqmStatement) {
return new SqmInsertSelectTranslation( visitInsertSelectStatement( sqmStatement ), getJdbcParamsBySqmParam() );
public SqmInsertTranslation translate(SqmInsertStatement sqmStatement) {
InsertStatement sqlAst;
if ( sqmStatement instanceof SqmInsertSelectStatement ) {
sqlAst = visitInsertSelectStatement( (SqmInsertSelectStatement) sqmStatement );
}
else {
sqlAst = visitInsertValuesStatement( (SqmInsertValuesStatement) sqmStatement );
}
return new SqmInsertTranslation( sqlAst, getJdbcParamsBySqmParam() );
}
@Override
@ -63,8 +79,8 @@ public class StandardSqmInsertSelectTranslator
}
@Override
public InsertSelectStatement visitInsertSelectStatement(SqmInsertSelectStatement sqmStatement) {
final InsertSelectStatement insertSelectStatement = new InsertSelectStatement();
public InsertStatement visitInsertSelectStatement(SqmInsertSelectStatement sqmStatement) {
final InsertStatement insertStatement = new InsertStatement();
final String entityName = sqmStatement.getTarget().getEntityName();
final EntityPersister entityDescriptor = getCreationContext().getDomainModel().getEntityDescriptor( entityName );
@ -98,19 +114,75 @@ public class StandardSqmInsertSelectTranslator
getFromClauseIndex().registerTableGroup( rootPath, rootTableGroup );
insertSelectStatement.setTargetTable( rootTableGroup.getPrimaryTableReference() );
insertStatement.setTargetTable( rootTableGroup.getPrimaryTableReference() );
List<SqmPath> targetPaths = sqmStatement.getInsertionTargetPaths();
for (SqmPath target : targetPaths) {
Assignable assignable = (Assignable) target.accept(this);
insertSelectStatement.addTargetColumnReferences( assignable.getColumnReferences() );
insertStatement.addTargetColumnReferences( assignable.getColumnReferences() );
}
insertSelectStatement.setSourceSelectStatement(
insertStatement.setSourceSelectStatement(
visitQuerySpec( sqmStatement.getSelectQuerySpec() )
);
return insertSelectStatement;
return insertStatement;
}
finally {
getProcessingStateStack().pop();
}
}
@Override
public InsertStatement visitInsertValuesStatement(SqmInsertValuesStatement sqmStatement) {
final InsertStatement insertValuesStatement = new InsertStatement();
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 = sqmStatement.getTarget().getNavigablePath();
final TableGroup rootTableGroup = entityDescriptor.createRootTableGroup(
rootPath,
sqmStatement.getTarget().getExplicitAlias(),
false,
LockMode.WRITE,
stem -> getSqlAliasBaseGenerator().createSqlAliasBase( stem ),
getSqlExpressionResolver(),
() -> predicate -> additionalRestrictions = predicate,
getCreationContext()
);
if ( ! rootTableGroup.getTableReferenceJoins().isEmpty()
|| ! rootTableGroup.getTableGroupJoins().isEmpty() ) {
throw new HibernateException( "Not expecting multiple table references for an SQM INSERT-SELECT" );
}
getFromClauseIndex().registerTableGroup( rootPath, rootTableGroup );
insertValuesStatement.setTargetTable( rootTableGroup.getPrimaryTableReference() );
List<SqmPath> targetPaths = sqmStatement.getInsertionTargetPaths();
for (SqmPath target : targetPaths) {
Assignable assignable = (Assignable) target.accept(this);
insertValuesStatement.addTargetColumnReferences( assignable.getColumnReferences() );
}
List<SqmValues> valuesList = sqmStatement.getValuesList();
for ( SqmValues sqmValues : valuesList ) {
insertValuesStatement.getValuesList().add( visitValues( sqmValues ) );
}
return insertValuesStatement;
}
finally {
getProcessingStateStack().pop();
@ -141,6 +213,15 @@ public class StandardSqmInsertSelectTranslator
return null;
}
@Override
public Values visitValues(SqmValues sqmValues) {
Values values = new Values();
for ( SqmExpression expression : sqmValues.getExpressions() ) {
values.getExpressions().add( (Expression) expression.accept( this ) );
}
return values;
}
@Override
public SelectStatement visitSelectStatement(SqmSelectStatement statement) {
final QuerySpec querySpec = visitQuerySpec( statement.getQuerySpec() );
@ -152,4 +233,9 @@ public class StandardSqmInsertSelectTranslator
public SqlAstCreationState getSqlAstCreationState() {
return this;
}
@Override
public List<Fetch> visitFetches(FetchParent fetchParent) {
return Collections.emptyList();
}
}

View File

@ -0,0 +1,41 @@
/*
* 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.insert;
import org.hibernate.query.criteria.JpaPredicate;
import org.hibernate.query.sqm.NodeBuilder;
import org.hibernate.query.sqm.SemanticQueryWalker;
import org.hibernate.query.sqm.SqmQuerySource;
import org.hibernate.query.sqm.tree.from.SqmRoot;
import java.util.ArrayList;
import java.util.List;
/**
* @author Gavin King
*/
public class SqmInsertValuesStatement<T> extends AbstractSqmInsertStatement<T> {
private List<SqmValues> valuesList = new ArrayList<>();
public SqmInsertValuesStatement(SqmRoot<T> targetRoot, NodeBuilder nodeBuilder) {
super( targetRoot, SqmQuerySource.HQL, nodeBuilder );
}
public List<SqmValues> getValuesList() {
return valuesList;
}
@Override
public <X> X accept(SemanticQueryWalker<X> walker) {
return walker.visitInsertValuesStatement( this );
}
@Override
public JpaPredicate getRestriction() {
return null;
}
}

View File

@ -0,0 +1,23 @@
/*
* 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.insert;
import org.hibernate.query.sqm.tree.expression.SqmExpression;
import java.util.ArrayList;
import java.util.List;
/**
* @author Gavin King
*/
public class SqmValues {
private List<SqmExpression> expressions = new ArrayList<>();
public List<SqmExpression> getExpressions() {
return expressions;
}
}

View File

@ -11,13 +11,14 @@ import java.util.List;
import org.hibernate.HibernateException;
import org.hibernate.dialect.Dialect;
import org.hibernate.sql.ast.tree.insert.InsertStatement;
/**
* Implementation of InsertSelect.
*
* @author Steve Ebersole
*
* @deprecated (since 6.0) Use {@link org.hibernate.sql.ast.tree.insert.InsertSelectStatement} instead
* @deprecated (since 6.0) Use {@link InsertStatement} instead
*/
@Deprecated
public class InsertSelect {

View File

@ -6,12 +6,12 @@
*/
package org.hibernate.sql.ast;
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
import org.hibernate.sql.ast.tree.insert.InsertStatement;
import org.hibernate.sql.exec.spi.JdbcInsert;
/**
* @author Steve Ebersole
*/
public interface SqlAstInsertSelectTranslator extends SqlAstTranslator {
JdbcInsert translate(InsertSelectStatement sqlAst);
JdbcInsert translate(InsertStatement sqlAst);
}

View File

@ -42,7 +42,7 @@ import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.from.TableReferenceJoin;
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
import org.hibernate.sql.ast.tree.insert.InsertStatement;
import org.hibernate.sql.ast.tree.predicate.BetweenPredicate;
import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate;
import org.hibernate.sql.ast.tree.predicate.ExistsPredicate;
@ -142,19 +142,19 @@ public class SqlTreePrinter implements SqlAstWalker {
}
);
}
else if ( sqlAstStatement instanceof InsertSelectStatement ) {
final InsertSelectStatement insertSelectStatement = (InsertSelectStatement) sqlAstStatement;
else if ( sqlAstStatement instanceof InsertStatement) {
final InsertStatement insertStatement = (InsertStatement) sqlAstStatement;
logNode(
"insert-select-statement",
() -> {
logNode(
"target",
() -> logNode( insertSelectStatement.getTargetTable().toString() )
() -> logNode( insertStatement.getTargetTable().toString() )
);
logNode(
"into",
() -> {
for ( ColumnReference spec : insertSelectStatement.getTargetColumnReferences() ) {
for ( ColumnReference spec : insertStatement.getTargetColumnReferences() ) {
logNode(
"target-column",
() -> spec.accept( this )
@ -164,7 +164,7 @@ public class SqlTreePrinter implements SqlAstWalker {
);
logNode(
"select",
() -> visitQuerySpec( insertSelectStatement.getSourceSelectStatement() )
() -> visitQuerySpec( insertStatement.getSourceSelectStatement() )
);
}
);

View File

@ -13,9 +13,10 @@ 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.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.insert.InsertStatement;
import org.hibernate.sql.ast.tree.insert.Values;
import org.hibernate.sql.exec.spi.JdbcInsert;
import org.hibernate.sql.exec.spi.JdbcParameterBinder;
@ -30,7 +31,7 @@ public class StandardSqlAstInsertSelectTranslator
}
@Override
public JdbcInsert translate(InsertSelectStatement sqlAst) {
public JdbcInsert translate(InsertStatement sqlAst) {
appendSql( "insert into " );
appendSql( sqlAst.getTargetTable().getTableExpression() );
@ -57,7 +58,33 @@ public class StandardSqlAstInsertSelectTranslator
appendSql( ") " );
if ( sqlAst.getSourceSelectStatement()!=null ) {
visitQuerySpec( sqlAst.getSourceSelectStatement() );
}
else {
appendSql("values");
boolean firstTuple = true;
for ( Values values : sqlAst.getValuesList() ) {
if (firstTuple) {
firstTuple = false;
}
else {
appendSql(", ");
}
appendSql(" (");
boolean firstExpr = true;
for ( Expression expression : values.getExpressions() ) {
if (firstExpr) {
firstExpr = false;
}
else {
appendSql(", ");
}
expression.accept( this );
}
appendSql(")");
}
}
return new JdbcInsert() {
@Override
@ -82,7 +109,7 @@ public class StandardSqlAstInsertSelectTranslator
@Override
public JdbcInsert translate(CteStatement sqlAst) {
assert sqlAst.getCteConsumer() instanceof DeleteStatement;
assert sqlAst.getCteConsumer() instanceof InsertStatement;
appendSql( "with " );
appendSql( sqlAst.getCteLabel() );
@ -104,7 +131,7 @@ public class StandardSqlAstInsertSelectTranslator
appendSql( ") " );
translate( (InsertSelectStatement) sqlAst.getCteConsumer() );
translate( (InsertStatement) sqlAst.getCteConsumer() );
return new JdbcInsert() {
@Override

View File

@ -24,7 +24,7 @@
* * select - `QuerySpec`
* * delete - `DeleteStatement`
* * update - `UpdateStatement`
* * insert-select - `InsertSelectStatement
* * insert-select - `InsertStatement
*
* consumer:: querySpec | deleteStatement | updateStatement | insertSelectStatement
*

View File

@ -1,70 +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.sql.ast.tree.insert;
import java.util.ArrayList;
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;
import org.jboss.logging.Logger;
/**
* @author Steve Ebersole
*/
public class InsertSelectStatement implements MutationStatement, CteConsumer {
private static final Logger log = Logger.getLogger( InsertSelectStatement.class );
private TableReference targetTable;
private List<ColumnReference> targetColumnReferences;
private QuerySpec sourceSelectStatement = new QuerySpec( true );
public TableReference getTargetTable() {
return targetTable;
}
public void setTargetTable(TableReference targetTable) {
log.tracef( "Setting INSERT-SELECT target table [%s]", targetTable );
if ( this.targetTable != null ) {
log.debugf( "INSERT-SELECT target table has been set multiple times" );
}
this.targetTable = targetTable;
}
public List<ColumnReference> getTargetColumnReferences() {
return targetColumnReferences == null ? Collections.emptyList() : targetColumnReferences;
}
public void addTargetColumnReferences(ColumnReference... references) {
if ( targetColumnReferences == null ) {
targetColumnReferences = new ArrayList<>();
}
Collections.addAll( this.targetColumnReferences, references );
}
public void addTargetColumnReferences(List<ColumnReference> references) {
if ( targetColumnReferences == null ) {
targetColumnReferences = new ArrayList<>();
}
this.targetColumnReferences.addAll( references );
}
public QuerySpec getSourceSelectStatement() {
return sourceSelectStatement;
}
public void setSourceSelectStatement(QuerySpec sourceSelectStatement) {
this.sourceSelectStatement = sourceSelectStatement;
}
}

View File

@ -7,60 +7,73 @@
package org.hibernate.sql.ast.tree.insert;
import java.util.ArrayList;
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.expression.Expression;
import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.select.QuerySpec;
import org.jboss.logging.Logger;
/**
* @author Steve Ebersole
*/
public class InsertStatement implements MutationStatement {
public class InsertStatement implements MutationStatement, CteConsumer {
private static final Logger log = Logger.getLogger( InsertStatement.class );
private TableReference targetTable;
private List<ColumnReference> targetColumnReferences;
private List<Expression> values;
public InsertStatement(TableReference targetTable) {
this.targetTable = targetTable;
}
private QuerySpec sourceSelectStatement;
private List<Values> valuesList = new ArrayList<>();
public TableReference getTargetTable() {
return targetTable;
}
public void setTargetTable(TableReference targetTable) {
log.tracef( "Setting INSERT target table [%s]", targetTable );
if ( this.targetTable != null ) {
log.debugf( "INSERT target table has been set multiple times" );
}
this.targetTable = targetTable;
}
public List<ColumnReference> getTargetColumnReferences() {
return targetColumnReferences;
return targetColumnReferences == null ? Collections.emptyList() : targetColumnReferences;
}
public void setTargetColumnReferences(List<ColumnReference> targetColumnReferences) {
this.targetColumnReferences = targetColumnReferences;
}
public void addTargetColumnReference(ColumnReference columnReference) {
public void addTargetColumnReferences(ColumnReference... references) {
if ( targetColumnReferences == null ) {
targetColumnReferences = new ArrayList<>();
}
targetColumnReferences.add( columnReference );
Collections.addAll( this.targetColumnReferences, references );
}
public List<Expression> getValues() {
return values;
public void addTargetColumnReferences(List<ColumnReference> references) {
if ( targetColumnReferences == null ) {
targetColumnReferences = new ArrayList<>();
}
public void setValues(List<Expression> values) {
this.values = values;
this.targetColumnReferences.addAll( references );
}
public void addValue(Expression expression) {
if ( values == null ) {
values = new ArrayList<>();
public QuerySpec getSourceSelectStatement() {
return sourceSelectStatement;
}
values.add( expression );
public void setSourceSelectStatement(QuerySpec sourceSelectStatement) {
this.sourceSelectStatement = sourceSelectStatement;
}
public List<Values> getValuesList() {
return valuesList;
}
public void setValuesList(List<Values> valuesList) {
this.valuesList = valuesList;
}
}

View File

@ -0,0 +1,20 @@
/*
* 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.insert;
import org.hibernate.sql.ast.tree.expression.Expression;
import java.util.ArrayList;
import java.util.List;
public class Values {
private List<Expression> expressions = new ArrayList<>();
public List<Expression> getExpressions() {
return expressions;
}
}