HHH-14483 Split insert-values statement if dialect doesn't support values lists
This commit is contained in:
parent
88e07652c9
commit
98e028c51d
|
@ -225,26 +225,7 @@ public class OracleLegacySqlAstTranslator<T extends JdbcOperation> extends Abstr
|
|||
|
||||
@Override
|
||||
protected void visitValuesList(List<Values> valuesList) {
|
||||
if ( valuesList.size() < 2 ) {
|
||||
super.visitValuesList( valuesList );
|
||||
}
|
||||
else {
|
||||
// Oracle doesn't support a multi-values insert
|
||||
// So we render a select union emulation instead
|
||||
String separator = "";
|
||||
final Stack<Clause> clauseStack = getClauseStack();
|
||||
try {
|
||||
clauseStack.push( Clause.VALUES );
|
||||
for ( Values values : valuesList ) {
|
||||
appendSql( separator );
|
||||
renderExpressionsAsSubquery( values.getExpressions() );
|
||||
separator = " union all ";
|
||||
}
|
||||
}
|
||||
finally {
|
||||
clauseStack.pop();
|
||||
}
|
||||
}
|
||||
visitValuesListEmulateSelectUnion( valuesList );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -546,11 +546,6 @@ public class SybaseASELegacyDialect extends SybaseLegacyDialect {
|
|||
return 255;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsValuesListForInsert() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsLockTimeouts() {
|
||||
return false;
|
||||
|
|
|
@ -33,6 +33,7 @@ import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
|||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
|
||||
import org.hibernate.sql.ast.tree.from.UnionTableReference;
|
||||
import org.hibernate.sql.ast.tree.insert.Values;
|
||||
import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate;
|
||||
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||
import org.hibernate.sql.ast.tree.select.QueryGroup;
|
||||
|
@ -253,6 +254,11 @@ public class SybaseASELegacySqlAstTranslator<T extends JdbcOperation> extends Ab
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void visitValuesList(List<Values> valuesList) {
|
||||
visitValuesListEmulateSelectUnion( valuesList );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitOffsetFetchClause(QueryPart queryPart) {
|
||||
assertRowsOnlyFetchClauseType( queryPart );
|
||||
|
|
|
@ -1135,11 +1135,6 @@ public abstract class AbstractHANADialect extends Dialect {
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsValuesListForInsert() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsOrderByInSubquery() {
|
||||
// Seems to work, though I don't know as of which version
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
*/
|
||||
package org.hibernate.dialect;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.query.sqm.BinaryArithmeticOperator;
|
||||
|
@ -20,6 +22,7 @@ import org.hibernate.sql.ast.tree.expression.Literal;
|
|||
import org.hibernate.sql.ast.tree.expression.Summarization;
|
||||
import org.hibernate.sql.ast.tree.from.FunctionTableReference;
|
||||
import org.hibernate.sql.ast.tree.from.QueryPartTableReference;
|
||||
import org.hibernate.sql.ast.tree.insert.Values;
|
||||
import org.hibernate.sql.ast.tree.select.QueryGroup;
|
||||
import org.hibernate.sql.ast.tree.select.QueryPart;
|
||||
import org.hibernate.sql.ast.tree.select.QuerySpec;
|
||||
|
@ -157,4 +160,9 @@ public class HANASqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAs
|
|||
)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void visitValuesList(List<Values> valuesList) {
|
||||
visitValuesListEmulateSelectUnion( valuesList );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.internal.util.collections.Stack;
|
||||
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
|
||||
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
|
@ -170,26 +169,7 @@ public class OracleSqlAstTranslator<T extends JdbcOperation> extends SqlAstTrans
|
|||
|
||||
@Override
|
||||
protected void visitValuesList(List<Values> valuesList) {
|
||||
if ( valuesList.size() < 2 ) {
|
||||
super.visitValuesList( valuesList );
|
||||
}
|
||||
else {
|
||||
// Oracle doesn't support a multi-values insert
|
||||
// So we render a select union emulation instead
|
||||
String separator = "";
|
||||
final Stack<Clause> clauseStack = getClauseStack();
|
||||
try {
|
||||
clauseStack.push( Clause.VALUES );
|
||||
for ( Values values : valuesList ) {
|
||||
appendSql( separator );
|
||||
renderExpressionsAsSubquery( values.getExpressions() );
|
||||
separator = " union all ";
|
||||
}
|
||||
}
|
||||
finally {
|
||||
clauseStack.pop();
|
||||
}
|
||||
}
|
||||
visitValuesListEmulateSelectUnion( valuesList );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -554,11 +554,6 @@ public class SybaseASEDialect extends SybaseDialect {
|
|||
return 255;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsValuesListForInsert() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsLockTimeouts() {
|
||||
return false;
|
||||
|
|
|
@ -33,6 +33,7 @@ import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
|||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
|
||||
import org.hibernate.sql.ast.tree.from.UnionTableReference;
|
||||
import org.hibernate.sql.ast.tree.insert.Values;
|
||||
import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate;
|
||||
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||
import org.hibernate.sql.ast.tree.select.QueryGroup;
|
||||
|
@ -251,6 +252,11 @@ public class SybaseASESqlAstTranslator<T extends JdbcOperation> extends Abstract
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void visitValuesList(List<Values> valuesList) {
|
||||
visitValuesListEmulateSelectUnion( valuesList );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitOffsetFetchClause(QueryPart queryPart) {
|
||||
assertRowsOnlyFetchClauseType( queryPart );
|
||||
|
|
|
@ -817,9 +817,30 @@ public class QuerySqmImpl<R>
|
|||
}
|
||||
}
|
||||
}
|
||||
return !useMultiTableInsert
|
||||
? new SimpleInsertQueryPlan( sqmInsert, domainParameterXref )
|
||||
: new MultiTableInsertQueryPlan( sqmInsert, domainParameterXref, persister.getSqmMultiTableInsertStrategy() );
|
||||
if ( useMultiTableInsert ) {
|
||||
return new MultiTableInsertQueryPlan(
|
||||
sqmInsert,
|
||||
domainParameterXref,
|
||||
persister.getSqmMultiTableInsertStrategy()
|
||||
);
|
||||
}
|
||||
else if ( sqmInsert instanceof SqmInsertValuesStatement<?>
|
||||
&& ( (SqmInsertValuesStatement<R>) sqmInsert ).getValuesList().size() != 1
|
||||
&& !getSessionFactory().getJdbcServices().getDialect().supportsValuesListForInsert() ) {
|
||||
// Split insert-values queries if the dialect doesn't support values lists
|
||||
final SqmInsertValuesStatement<R> insertValues = (SqmInsertValuesStatement<R>) sqmInsert;
|
||||
final List<SqmValues> valuesList = insertValues.getValuesList();
|
||||
final NonSelectQueryPlan[] planParts = new NonSelectQueryPlan[valuesList.size()];
|
||||
for ( int i = 0; i < valuesList.size(); i++ ) {
|
||||
final SqmInsertValuesStatement<?> subInsert = insertValues.copyWithoutValues( SqmCopyContext.simpleContext() );
|
||||
subInsert.getValuesList().add( valuesList.get( i ) );
|
||||
planParts[i] = new SimpleInsertQueryPlan( subInsert, domainParameterXref );
|
||||
}
|
||||
|
||||
return new AggregatedNonSelectQueryPlanImpl( planParts );
|
||||
}
|
||||
|
||||
return new SimpleInsertQueryPlan( sqmInsert, domainParameterXref );
|
||||
}
|
||||
|
||||
protected boolean hasIdentifierAssigned(SqmInsertStatement<?> sqmInsert, EntityPersister entityDescriptor) {
|
||||
|
|
|
@ -69,6 +69,21 @@ public class SqmInsertValuesStatement<T> extends AbstractSqmInsertStatement<T> {
|
|||
);
|
||||
}
|
||||
|
||||
public SqmInsertValuesStatement<T> copyWithoutValues(SqmCopyContext context) {
|
||||
return context.registerCopy(
|
||||
this,
|
||||
new SqmInsertValuesStatement<>(
|
||||
nodeBuilder(),
|
||||
getQuerySource(),
|
||||
copyParameters( context ),
|
||||
copyCteStatements( context ),
|
||||
getTarget().copy( context ),
|
||||
copyInsertionTargetPaths( context ),
|
||||
new ArrayList<>()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public List<SqmValues> getValuesList() {
|
||||
return valuesList;
|
||||
}
|
||||
|
|
|
@ -1169,28 +1169,28 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
|
|||
}
|
||||
|
||||
protected void visitValuesList(List<Values> valuesList) {
|
||||
visitValuesListStandard( valuesList );
|
||||
}
|
||||
|
||||
protected final void visitValuesListStandard(List<Values> valuesList) {
|
||||
if ( valuesList.size() != 1 && !dialect.supportsValuesListForInsert() ) {
|
||||
throw new IllegalQueryOperationException( "Dialect does not support values lists for insert statements" );
|
||||
}
|
||||
appendSql("values");
|
||||
boolean firstTuple = true;
|
||||
final Stack<Clause> clauseStack = getClauseStack();
|
||||
try {
|
||||
clauseStack.push( Clause.VALUES );
|
||||
for ( Values values : valuesList ) {
|
||||
if ( firstTuple ) {
|
||||
firstTuple = false;
|
||||
}
|
||||
else {
|
||||
for ( int i = 0; i < valuesList.size(); i++ ) {
|
||||
if ( i != 0 ) {
|
||||
appendSql( COMMA_SEPARATOR_CHAR );
|
||||
}
|
||||
appendSql( " (" );
|
||||
boolean firstExpr = true;
|
||||
for ( Expression expression : values.getExpressions() ) {
|
||||
if ( firstExpr ) {
|
||||
firstExpr = false;
|
||||
}
|
||||
else {
|
||||
final List<Expression> expressions = valuesList.get( i ).getExpressions();
|
||||
for ( int j = 0; j < expressions.size(); j++ ) {
|
||||
if ( j != 0 ) {
|
||||
appendSql( COMMA_SEPARATOR_CHAR );
|
||||
}
|
||||
expression.accept( this );
|
||||
expressions.get( j ).accept( this );
|
||||
}
|
||||
appendSql( ')' );
|
||||
}
|
||||
|
@ -1200,6 +1200,29 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
|
|||
}
|
||||
}
|
||||
|
||||
protected void visitValuesListEmulateSelectUnion(List<Values> valuesList) {
|
||||
if ( valuesList.size() < 2 ) {
|
||||
visitValuesListStandard( valuesList );
|
||||
}
|
||||
else {
|
||||
// Oracle doesn't support a multi-values insert
|
||||
// So we render a select union emulation instead
|
||||
String separator = "";
|
||||
final Stack<Clause> clauseStack = getClauseStack();
|
||||
try {
|
||||
clauseStack.push( Clause.VALUES );
|
||||
for ( int i = 0; i < valuesList.size(); i++ ) {
|
||||
appendSql( separator );
|
||||
renderExpressionsAsSubquery( valuesList.get( i ).getExpressions() );
|
||||
separator = " union all ";
|
||||
}
|
||||
}
|
||||
finally {
|
||||
clauseStack.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void visitForUpdateClause(QuerySpec querySpec) {
|
||||
if ( querySpec.isRoot() ) {
|
||||
if ( forUpdate != null ) {
|
||||
|
|
|
@ -51,7 +51,6 @@ import org.hibernate.testing.RequiresDialect;
|
|||
import org.hibernate.testing.orm.junit.SkipForDialect;
|
||||
import org.hibernate.testing.DialectChecks;
|
||||
import org.hibernate.testing.RequiresDialectFeature;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
|
@ -252,7 +251,6 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase {
|
|||
}
|
||||
|
||||
@Test
|
||||
@RequiresDialectFeature(DialectChecks.SupportsValuesListForInsert.class)
|
||||
public void hql_multi_insert_example() {
|
||||
doInJPA(this::entityManagerFactory, entityManager -> {
|
||||
//tag::hql-insert-example[]
|
||||
|
|
|
@ -9,9 +9,7 @@ package org.hibernate.orm.test.query.hql;
|
|||
import org.hibernate.testing.orm.domain.StandardDomainModel;
|
||||
import org.hibernate.testing.orm.domain.contacts.Contact;
|
||||
import org.hibernate.testing.orm.domain.contacts.Contact.Name;
|
||||
import org.hibernate.testing.orm.junit.DialectFeatureChecks;
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.RequiresDialectFeature;
|
||||
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
|
@ -91,7 +89,6 @@ public class InsertUpdateTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
@RequiresDialectFeature( feature = DialectFeatureChecks.SupportsValuesListForInsert.class)
|
||||
public void testInsertMultipleValues(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
|
|
Loading…
Reference in New Issue