HHH-17506 Support ON CONFLICT clause for HQL/Criteria inserts

This commit is contained in:
Christian Beikov 2023-12-11 20:05:22 +01:00
parent c931c86896
commit bb4ed4b000
146 changed files with 5981 additions and 1580 deletions

View File

@ -5,7 +5,7 @@
# See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>. # See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
# #
hibernate.dialect org.hibernate.dialect.HANAColumnStoreDialect hibernate.dialect org.hibernate.dialect.HANADialect
hibernate.connection.driver_class com.sap.db.jdbc.Driver hibernate.connection.driver_class com.sap.db.jdbc.Driver
hibernate.connection.url jdbc:sap://localhost:39015/ hibernate.connection.url jdbc:sap://localhost:39015/
hibernate.connection.username HIBERNATE_TEST hibernate.connection.username HIBERNATE_TEST

View File

@ -161,7 +161,7 @@ hibernate.connection.url @DB_URL@
## HANA ## HANA
#hibernate.dialect org.hibernate.dialect.HANAColumnStoreDialect #hibernate.dialect org.hibernate.dialect.HANADialect
#hibernate.connection.driver_class com.sap.db.jdbc.Driver #hibernate.connection.driver_class com.sap.db.jdbc.Driver
#hibernate.connection.url jdbc:sap://localhost:30015 #hibernate.connection.url jdbc:sap://localhost:30015
#hibernate.connection.username HIBERNATE_TEST #hibernate.connection.username HIBERNATE_TEST

View File

@ -254,7 +254,7 @@ ext {
'connection.init_sql' : '' 'connection.init_sql' : ''
], ],
hana_cloud : [ hana_cloud : [
'db.dialect' : 'org.hibernate.dialect.HANAColumnStoreDialect', 'db.dialect' : 'org.hibernate.dialect.HANADialect',
'jdbc.driver': 'com.sap.db.jdbc.Driver', 'jdbc.driver': 'com.sap.db.jdbc.Driver',
'jdbc.user' : 'HIBERNATE_TEST', 'jdbc.user' : 'HIBERNATE_TEST',
'jdbc.pass' : 'H1bernate_test', 'jdbc.pass' : 'H1bernate_test',
@ -263,7 +263,7 @@ ext {
'connection.init_sql' : '' 'connection.init_sql' : ''
], ],
hana_ci : [ hana_ci : [
'db.dialect' : 'org.hibernate.dialect.HANAColumnStoreDialect', 'db.dialect' : 'org.hibernate.dialect.HANADialect',
'jdbc.driver': 'com.sap.db.jdbc.Driver', 'jdbc.driver': 'com.sap.db.jdbc.Driver',
'jdbc.user' : 'SYSTEM', 'jdbc.user' : 'SYSTEM',
'jdbc.pass' : 'H1bernate_test', 'jdbc.pass' : 'H1bernate_test',

View File

@ -146,13 +146,14 @@ public class AltibaseSqlAstTranslator<T extends JdbcOperation> extends AbstractS
emulateQueryPartTableReferenceColumnAliasing( tableReference ); emulateQueryPartTableReferenceColumnAliasing( tableReference );
} }
protected String getFromDual() { @Override
return " from dual"; protected String getDual() {
return "dual";
} }
@Override @Override
protected String getFromDualForSelectOnly() { protected String getFromDualForSelectOnly() {
return getFromDual(); return " from " + getDual();
} }
@Override @Override

View File

@ -82,14 +82,14 @@ public class CUBRIDSqlAstTranslator<T extends JdbcOperation> extends AbstractSql
} }
@Override @Override
protected String getFromDual() { protected String getDual() {
//TODO: is this really needed? //TODO: is this really needed?
//TODO: would "from table({0})" be better? //TODO: would "from table({0})" be better?
return " from db_root"; return "db_root";
} }
@Override @Override
protected String getFromDualForSelectOnly() { protected String getFromDualForSelectOnly() {
return getFromDual(); return " from " + getDual();
} }
} }

View File

@ -570,6 +570,10 @@ public class CockroachLegacyDialect extends Dialect {
public boolean supportsRecursiveCTE() { public boolean supportsRecursiveCTE() {
return getVersion().isSameOrAfter( 20, 1 ); return getVersion().isSameOrAfter( 20, 1 );
} }
@Override
public boolean supportsConflictClauseForInsertCTE() {
return true;
}
@Override @Override
public String getNoColumnsInsertString() { public String getNoColumnsInsertString() {
@ -1165,4 +1169,14 @@ public class CockroachLegacyDialect extends Dialect {
// RuntimeModelCreationContext runtimeModelCreationContext) { // RuntimeModelCreationContext runtimeModelCreationContext) {
// return new CteInsertStrategy( rootEntityDescriptor, runtimeModelCreationContext ); // return new CteInsertStrategy( rootEntityDescriptor, runtimeModelCreationContext );
// } // }
@Override
public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() {
return DmlTargetColumnQualifierSupport.TABLE_ALIAS;
}
@Override
public boolean supportsFromClauseInUpdate() {
return true;
}
} }

View File

@ -7,20 +7,27 @@
package org.hibernate.community.dialect; package org.hibernate.community.dialect;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.Statement;
import org.hibernate.sql.ast.tree.cte.CteMaterialization; import org.hibernate.sql.ast.tree.cte.CteMaterialization;
import org.hibernate.sql.ast.tree.cte.CteStatement;
import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.Literal; import org.hibernate.sql.ast.tree.expression.Literal;
import org.hibernate.sql.ast.tree.expression.Summarization; import org.hibernate.sql.ast.tree.expression.Summarization;
import org.hibernate.sql.ast.tree.from.NamedTableReference;
import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.insert.ConflictClause;
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate; import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate;
import org.hibernate.sql.ast.tree.predicate.InArrayPredicate; import org.hibernate.sql.ast.tree.predicate.InArrayPredicate;
import org.hibernate.sql.ast.tree.predicate.LikePredicate; import org.hibernate.sql.ast.tree.predicate.LikePredicate;
import org.hibernate.sql.ast.tree.select.QueryGroup; import org.hibernate.sql.ast.tree.select.QueryGroup;
import org.hibernate.sql.ast.tree.select.QueryPart; import org.hibernate.sql.ast.tree.select.QueryPart;
import org.hibernate.sql.ast.tree.select.QuerySpec; import org.hibernate.sql.ast.tree.select.QuerySpec;
import org.hibernate.sql.ast.tree.update.UpdateStatement;
import org.hibernate.sql.exec.internal.JdbcOperationQueryInsertImpl;
import org.hibernate.sql.exec.spi.JdbcOperation; import org.hibernate.sql.exec.spi.JdbcOperation;
import org.hibernate.sql.exec.spi.JdbcOperationQueryInsert;
/** /**
* A SQL AST translator for Cockroach. * A SQL AST translator for Cockroach.
@ -33,6 +40,56 @@ public class CockroachLegacySqlAstTranslator<T extends JdbcOperation> extends Ab
super( sessionFactory, statement ); super( sessionFactory, statement );
} }
@Override
protected JdbcOperationQueryInsert translateInsert(InsertSelectStatement sqlAst) {
visitInsertStatement( sqlAst );
return new JdbcOperationQueryInsertImpl(
getSql(),
getParameterBinders(),
getAffectedTableNames(),
null
);
}
@Override
protected void renderTableReferenceIdentificationVariable(TableReference tableReference) {
final String identificationVariable = tableReference.getIdentificationVariable();
if ( identificationVariable != null ) {
final Clause currentClause = getClauseStack().getCurrent();
if ( currentClause == Clause.INSERT ) {
// PostgreSQL requires the "as" keyword for inserts
appendSql( " as " );
}
else {
append( WHITESPACE );
}
append( tableReference.getIdentificationVariable() );
}
}
@Override
protected void renderDmlTargetTableExpression(NamedTableReference tableReference) {
super.renderDmlTargetTableExpression( tableReference );
final Statement currentStatement = getStatementStack().getCurrent();
if ( !( currentStatement instanceof UpdateStatement )
|| !hasNonTrivialFromClause( ( (UpdateStatement) currentStatement ).getFromClause() ) ) {
// For UPDATE statements we render a full FROM clause and a join condition to match target table rows,
// but for that to work, we have to omit the alias for the target table reference here
renderTableReferenceIdentificationVariable( tableReference );
}
}
@Override
protected void renderFromClauseAfterUpdateSet(UpdateStatement statement) {
renderFromClauseJoiningDmlTargetReference( statement );
}
@Override
protected void visitConflictClause(ConflictClause conflictClause) {
visitStandardConflictClause( conflictClause );
}
@Override @Override
protected void renderExpressionAsClauseItem(Expression expression) { protected void renderExpressionAsClauseItem(Expression expression) {
expression.accept( this ); expression.accept( this );

View File

@ -24,6 +24,7 @@ import org.hibernate.dialect.DB2Dialect;
import org.hibernate.dialect.DB2StructJdbcType; import org.hibernate.dialect.DB2StructJdbcType;
import org.hibernate.dialect.DatabaseVersion; import org.hibernate.dialect.DatabaseVersion;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.DmlTargetColumnQualifierSupport;
import org.hibernate.dialect.OracleDialect; import org.hibernate.dialect.OracleDialect;
import org.hibernate.dialect.aggregate.AggregateSupport; import org.hibernate.dialect.aggregate.AggregateSupport;
import org.hibernate.dialect.aggregate.DB2AggregateSupport; import org.hibernate.dialect.aggregate.DB2AggregateSupport;
@ -49,8 +50,11 @@ import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
import org.hibernate.engine.jdbc.env.spi.IdentifierHelper; import org.hibernate.engine.jdbc.env.spi.IdentifierHelper;
import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder; import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.exception.ConstraintViolationException;
import org.hibernate.exception.LockTimeoutException; import org.hibernate.exception.LockTimeoutException;
import org.hibernate.exception.spi.SQLExceptionConversionDelegate; import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor;
import org.hibernate.exception.spi.ViolatedConstraintNameExtractor;
import org.hibernate.internal.util.JdbcExceptionHelper; import org.hibernate.internal.util.JdbcExceptionHelper;
import org.hibernate.mapping.Column; import org.hibernate.mapping.Column;
import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityMappingType;
@ -94,6 +98,7 @@ import org.hibernate.type.spi.TypeConfiguration;
import jakarta.persistence.TemporalType; import jakarta.persistence.TemporalType;
import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate;
import static org.hibernate.type.SqlTypes.BINARY; import static org.hibernate.type.SqlTypes.BINARY;
import static org.hibernate.type.SqlTypes.BLOB; import static org.hibernate.type.SqlTypes.BLOB;
import static org.hibernate.type.SqlTypes.BOOLEAN; import static org.hibernate.type.SqlTypes.BOOLEAN;
@ -889,6 +894,20 @@ public class DB2LegacyDialect extends Dialect {
appender.appendSql( '\'' ); appender.appendSql( '\'' );
} }
@Override
public ViolatedConstraintNameExtractor getViolatedConstraintNameExtractor() {
return new TemplatedViolatedConstraintNameExtractor(
sqle -> {
switch ( JdbcExceptionHelper.extractErrorCode( sqle ) ) {
case -803:
return extractUsingTemplate( "SQLERRMC=1;", ",", sqle.getMessage() );
default:
return null;
}
}
);
}
@Override @Override
public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() { public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() {
return (sqlException, message, sql) -> { return (sqlException, message, sql) -> {
@ -896,6 +915,14 @@ public class DB2LegacyDialect extends Dialect {
switch ( errorCode ) { switch ( errorCode ) {
case -952: case -952:
return new LockTimeoutException( message, sqlException, sql ); return new LockTimeoutException( message, sqlException, sql );
case -803:
return new ConstraintViolationException(
message,
sqlException,
sql,
ConstraintViolationException.ConstraintKind.UNIQUE,
getViolatedConstraintNameExtractor().extractConstraintName( sqlException )
);
} }
return null; return null;
}; };
@ -1079,4 +1106,14 @@ public class DB2LegacyDialect extends Dialect {
public int rowIdSqlType() { public int rowIdSqlType() {
return VARBINARY; return VARBINARY;
} }
@Override
public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() {
return DmlTargetColumnQualifierSupport.TABLE_ALIAS;
}
@Override
public boolean supportsFromClauseInUpdate() {
return getDB2Version().isSameOrAfter( 11 );
}
} }

View File

@ -13,8 +13,10 @@ import org.hibernate.LockMode;
import org.hibernate.dialect.DatabaseVersion; import org.hibernate.dialect.DatabaseVersion;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.JdbcMappingContainer; import org.hibernate.metamodel.mapping.JdbcMappingContainer;
import org.hibernate.query.IllegalQueryOperationException;
import org.hibernate.query.sqm.ComparisonOperator; import org.hibernate.query.sqm.ComparisonOperator;
import org.hibernate.query.sqm.FetchClauseType; import org.hibernate.query.sqm.FetchClauseType;
import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.SqlAstNodeRenderingMode; import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.spi.SqlSelection;
@ -28,10 +30,12 @@ import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.Literal; import org.hibernate.sql.ast.tree.expression.Literal;
import org.hibernate.sql.ast.tree.expression.SqlTuple; import org.hibernate.sql.ast.tree.expression.SqlTuple;
import org.hibernate.sql.ast.tree.expression.Summarization; import org.hibernate.sql.ast.tree.expression.Summarization;
import org.hibernate.sql.ast.tree.from.NamedTableReference;
import org.hibernate.sql.ast.tree.from.QueryPartTableReference; import org.hibernate.sql.ast.tree.from.QueryPartTableReference;
import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableGroupJoin; import org.hibernate.sql.ast.tree.from.TableGroupJoin;
import org.hibernate.sql.ast.tree.from.TableReferenceJoin; import org.hibernate.sql.ast.tree.from.TableReferenceJoin;
import org.hibernate.sql.ast.tree.insert.ConflictClause;
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement; import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate; import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate;
import org.hibernate.sql.ast.tree.predicate.Predicate; import org.hibernate.sql.ast.tree.predicate.Predicate;
@ -344,12 +348,40 @@ public class DB2LegacySqlAstTranslator<T extends JdbcOperation> extends Abstract
@Override @Override
protected void visitInsertStatementOnly(InsertSelectStatement statement) { protected void visitInsertStatementOnly(InsertSelectStatement statement) {
final boolean closeWrapper = renderReturningClause( statement ); final boolean closeWrapper = renderReturningClause( statement );
super.visitInsertStatementOnly( statement ); if ( statement.getConflictClause() == null || statement.getConflictClause().isDoNothing() ) {
// Render plain insert statement and possibly run into unique constraint violation
super.visitInsertStatementOnly( statement );
}
else {
visitInsertStatementEmulateMerge( statement );
}
if ( closeWrapper ) { if ( closeWrapper ) {
appendSql( ')' ); appendSql( ')' );
} }
} }
@Override
protected void visitConflictClause(ConflictClause conflictClause) {
if ( conflictClause != null ) {
if ( conflictClause.isDoUpdate() && conflictClause.getConstraintName() != null ) {
throw new IllegalQueryOperationException( "Insert conflict do update clause with constraint name is not supported" );
}
}
}
@Override
protected void renderDmlTargetTableExpression(NamedTableReference tableReference) {
super.renderDmlTargetTableExpression( tableReference );
if ( getClauseStack().getCurrent() != Clause.INSERT ) {
renderTableReferenceIdentificationVariable( tableReference );
}
}
@Override
protected void renderFromClauseAfterUpdateSet(UpdateStatement statement) {
renderFromClauseExcludingDmlTargetReference( statement );
}
protected boolean renderReturningClause(MutationStatement statement) { protected boolean renderReturningClause(MutationStatement statement) {
final List<ColumnReference> returningColumns = statement.getReturningColumns(); final List<ColumnReference> returningColumns = statement.getReturningColumns();
final int size = returningColumns.size(); final int size = returningColumns.size();
@ -497,13 +529,13 @@ public class DB2LegacySqlAstTranslator<T extends JdbcOperation> extends Abstract
} }
@Override @Override
protected String getFromDual() { protected String getDual() {
return " from sysibm.dual"; return "sysibm.dual";
} }
@Override @Override
protected String getFromDualForSelectOnly() { protected String getFromDualForSelectOnly() {
return getFromDual(); return " from " + getDual();
} }
@Override @Override

View File

@ -39,8 +39,11 @@ import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
import org.hibernate.engine.jdbc.env.spi.IdentifierHelper; import org.hibernate.engine.jdbc.env.spi.IdentifierHelper;
import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder; import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.exception.ConstraintViolationException;
import org.hibernate.exception.LockTimeoutException; import org.hibernate.exception.LockTimeoutException;
import org.hibernate.exception.spi.SQLExceptionConversionDelegate; import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor;
import org.hibernate.exception.spi.ViolatedConstraintNameExtractor;
import org.hibernate.internal.util.JdbcExceptionHelper; import org.hibernate.internal.util.JdbcExceptionHelper;
import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.spi.RuntimeModelCreationContext; import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
@ -706,14 +709,42 @@ public class DerbyLegacyDialect extends Dialect {
return false; return false;
} }
@Override
public ViolatedConstraintNameExtractor getViolatedConstraintNameExtractor() {
return new TemplatedViolatedConstraintNameExtractor( sqle -> {
final String sqlState = JdbcExceptionHelper.extractSqlState( sqle );
if ( sqlState != null ) {
switch ( sqlState ) {
case "23505":
return TemplatedViolatedConstraintNameExtractor.extractUsingTemplate(
"'", "'",
sqle.getMessage()
);
}
}
return null;
} );
}
@Override @Override
public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() { public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() {
return (sqlException, message, sql) -> { return (sqlException, message, sql) -> {
final String sqlState = JdbcExceptionHelper.extractSqlState( sqlException ); final String sqlState = JdbcExceptionHelper.extractSqlState( sqlException );
// final int errorCode = JdbcExceptionHelper.extractErrorCode( sqlException ); // final int errorCode = JdbcExceptionHelper.extractErrorCode( sqlException );
final String constraintName;
if ( sqlState != null ) { if ( sqlState != null ) {
switch ( sqlState ) { switch ( sqlState ) {
case "23505":
// Unique constraint violation
constraintName = getViolatedConstraintNameExtractor().extractConstraintName(sqlException);
return new ConstraintViolationException(
message,
sqlException,
sql,
ConstraintViolationException.ConstraintKind.UNIQUE,
constraintName
);
case "40XL1": case "40XL1":
case "40XL2": case "40XL2":
return new LockTimeoutException( message, sqlException, sql ); return new LockTimeoutException( message, sqlException, sql );

View File

@ -235,13 +235,13 @@ public class DerbyLegacySqlAstTranslator<T extends JdbcOperation> extends Abstra
} }
@Override @Override
protected String getFromDual() { protected String getDual() {
return " from (values 0) dual"; return "(values 0)";
} }
@Override @Override
protected String getFromDualForSelectOnly() { protected String getFromDualForSelectOnly() {
return getFromDual(); return " from " + getDual() + " dual";
} }
@Override @Override

View File

@ -263,13 +263,13 @@ public class FirebirdSqlAstTranslator<T extends JdbcOperation> extends AbstractS
} }
@Override @Override
protected String getFromDual() { protected String getDual() {
return " from rdb$database"; return "rdb$database";
} }
@Override @Override
protected String getFromDualForSelectOnly() { protected String getFromDualForSelectOnly() {
return getFromDual(); return " from " + getDual();
} }
private boolean supportsOffsetFetchClause() { private boolean supportsOffsetFetchClause() {

View File

@ -776,8 +776,19 @@ public class H2LegacyDialect extends Dialect {
public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() { public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() {
return (sqlException, message, sql) -> { return (sqlException, message, sql) -> {
final int errorCode = JdbcExceptionHelper.extractErrorCode( sqlException ); final int errorCode = JdbcExceptionHelper.extractErrorCode( sqlException );
final String constraintName;
switch (errorCode) { switch (errorCode) {
case 23505:
// Unique constraint violation
constraintName = getViolatedConstraintNameExtractor().extractConstraintName(sqlException);
return new ConstraintViolationException(
message,
sqlException,
sql,
ConstraintViolationException.ConstraintKind.UNIQUE,
constraintName
);
case 40001: case 40001:
// DEADLOCK DETECTED // DEADLOCK DETECTED
return new LockAcquisitionException(message, sqlException, sql); return new LockAcquisitionException(message, sqlException, sql);
@ -786,7 +797,7 @@ public class H2LegacyDialect extends Dialect {
return new PessimisticLockException(message, sqlException, sql); return new PessimisticLockException(message, sqlException, sql);
case 90006: case 90006:
// NULL not allowed for column [90006-145] // NULL not allowed for column [90006-145]
final String constraintName = getViolatedConstraintNameExtractor().extractConstraintName(sqlException); constraintName = getViolatedConstraintNameExtractor().extractConstraintName(sqlException);
return new ConstraintViolationException(message, sqlException, sql, constraintName); return new ConstraintViolationException(message, sqlException, sql, constraintName);
case 57014: case 57014:
return new QueryTimeoutException( message, sqlException, sql ); return new QueryTimeoutException( message, sqlException, sql );
@ -940,4 +951,9 @@ public class H2LegacyDialect extends Dialect {
public int rowIdSqlType() { public int rowIdSqlType() {
return BIGINT; return BIGINT;
} }
@Override
public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() {
return DmlTargetColumnQualifierSupport.TABLE_ALIAS;
}
} }

View File

@ -12,6 +12,7 @@ import org.hibernate.LockMode;
import org.hibernate.dialect.identity.H2IdentityColumnSupport; import org.hibernate.dialect.identity.H2IdentityColumnSupport;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.query.IllegalQueryOperationException;
import org.hibernate.query.sqm.ComparisonOperator; import org.hibernate.query.sqm.ComparisonOperator;
import org.hibernate.sql.ast.Clause; import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.SqlAstNodeRenderingMode; import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
@ -27,14 +28,18 @@ import org.hibernate.sql.ast.tree.expression.Literal;
import org.hibernate.sql.ast.tree.expression.SqlTuple; import org.hibernate.sql.ast.tree.expression.SqlTuple;
import org.hibernate.sql.ast.tree.expression.SqlTupleContainer; import org.hibernate.sql.ast.tree.expression.SqlTupleContainer;
import org.hibernate.sql.ast.tree.expression.Summarization; import org.hibernate.sql.ast.tree.expression.Summarization;
import org.hibernate.sql.ast.tree.from.NamedTableReference;
import org.hibernate.sql.ast.tree.from.QueryPartTableReference; import org.hibernate.sql.ast.tree.from.QueryPartTableReference;
import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.insert.ConflictClause;
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate; import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate;
import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate; import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate;
import org.hibernate.sql.ast.tree.predicate.LikePredicate; import org.hibernate.sql.ast.tree.predicate.LikePredicate;
import org.hibernate.sql.ast.tree.select.QueryPart; import org.hibernate.sql.ast.tree.select.QueryPart;
import org.hibernate.sql.ast.tree.select.SelectClause; import org.hibernate.sql.ast.tree.select.SelectClause;
import org.hibernate.sql.ast.tree.update.UpdateStatement;
import org.hibernate.sql.exec.spi.JdbcOperation; import org.hibernate.sql.exec.spi.JdbcOperation;
import org.hibernate.sql.model.internal.TableInsertStandard; import org.hibernate.sql.model.internal.TableInsertStandard;
@ -79,6 +84,44 @@ public class H2LegacySqlAstTranslator<T extends JdbcOperation> extends AbstractS
); );
} }
@Override
protected void visitInsertStatementOnly(InsertSelectStatement statement) {
if ( statement.getConflictClause() == null || statement.getConflictClause().isDoNothing() ) {
// Render plain insert statement and possibly run into unique constraint violation
super.visitInsertStatementOnly( statement );
}
else {
visitInsertStatementEmulateMerge( statement );
}
}
@Override
protected void visitUpdateStatementOnly(UpdateStatement statement) {
if ( hasNonTrivialFromClause( statement.getFromClause() ) ) {
visitUpdateStatementEmulateMerge( statement );
}
else {
super.visitUpdateStatementOnly( statement );
}
}
@Override
protected void renderDmlTargetTableExpression(NamedTableReference tableReference) {
super.renderDmlTargetTableExpression( tableReference );
if ( getClauseStack().getCurrent() != Clause.INSERT ) {
renderTableReferenceIdentificationVariable( tableReference );
}
}
@Override
protected void visitConflictClause(ConflictClause conflictClause) {
if ( conflictClause != null ) {
if ( conflictClause.isDoUpdate() && conflictClause.getConstraintName() != null ) {
throw new IllegalQueryOperationException( "Insert conflict do update clause with constraint name is not supported" );
}
}
}
@Override @Override
protected void visitReturningColumns(List<ColumnReference> returningColumns) { protected void visitReturningColumns(List<ColumnReference> returningColumns) {
// do nothing - this is handled via `#visitReturningInsertStatement` // do nothing - this is handled via `#visitReturningInsertStatement`
@ -289,8 +332,8 @@ public class H2LegacySqlAstTranslator<T extends JdbcOperation> extends AbstractS
} }
@Override @Override
protected String getFromDual() { protected String getDual() {
return " from dual"; return "dual";
} }
private boolean supportsOffsetFetchClause() { private boolean supportsOffsetFetchClause() {

View File

@ -41,6 +41,8 @@ import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder;
import org.hibernate.engine.jdbc.env.spi.NameQualifierSupport; import org.hibernate.engine.jdbc.env.spi.NameQualifierSupport;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.event.spi.EventSource; import org.hibernate.event.spi.EventSource;
import org.hibernate.exception.ConstraintViolationException;
import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor; import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor;
import org.hibernate.exception.spi.ViolatedConstraintNameExtractor; import org.hibernate.exception.spi.ViolatedConstraintNameExtractor;
import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.CoreMessageLogger;
@ -497,6 +499,29 @@ public class HSQLLegacyDialect extends Dialect {
return null; return null;
} ); } );
@Override
public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() {
return (sqlException, message, sql) -> {
final int errorCode = JdbcExceptionHelper.extractErrorCode( sqlException );
final String constraintName;
switch ( errorCode ) {
case -104:
// Unique constraint violation
constraintName = getViolatedConstraintNameExtractor().extractConstraintName(sqlException);
return new ConstraintViolationException(
message,
sqlException,
sql,
ConstraintViolationException.ConstraintKind.UNIQUE,
constraintName
);
}
return null;
};
}
/** /**
* HSQLDB 2.0 messages have changed * HSQLDB 2.0 messages have changed
* messages may be localized - therefore use the common, non-locale element " table: " * messages may be localized - therefore use the common, non-locale element " table: "
@ -856,4 +881,9 @@ public class HSQLLegacyDialect extends Dialect {
public UniqueDelegate getUniqueDelegate() { public UniqueDelegate getUniqueDelegate() {
return uniqueDelegate; return uniqueDelegate;
} }
@Override
public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() {
return DmlTargetColumnQualifierSupport.TABLE_ALIAS;
}
} }

View File

@ -11,23 +11,32 @@ import java.util.function.Consumer;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.JdbcMappingContainer; import org.hibernate.metamodel.mapping.JdbcMappingContainer;
import org.hibernate.query.IllegalQueryOperationException;
import org.hibernate.query.sqm.BinaryArithmeticOperator; import org.hibernate.query.sqm.BinaryArithmeticOperator;
import org.hibernate.query.sqm.ComparisonOperator; import org.hibernate.query.sqm.ComparisonOperator;
import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.SqlAstNodeRenderingMode; import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.ast.tree.MutationStatement;
import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.Statement;
import org.hibernate.sql.ast.tree.cte.CteStatement; import org.hibernate.sql.ast.tree.cte.CteStatement;
import org.hibernate.sql.ast.tree.delete.DeleteStatement;
import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression; import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression;
import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression; import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression;
import org.hibernate.sql.ast.tree.expression.CaseSimpleExpression; import org.hibernate.sql.ast.tree.expression.CaseSimpleExpression;
import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.Literal; import org.hibernate.sql.ast.tree.expression.Literal;
import org.hibernate.sql.ast.tree.expression.SqlTuple; import org.hibernate.sql.ast.tree.expression.SqlTuple;
import org.hibernate.sql.ast.tree.expression.Summarization; import org.hibernate.sql.ast.tree.expression.Summarization;
import org.hibernate.sql.ast.tree.from.NamedTableReference;
import org.hibernate.sql.ast.tree.insert.ConflictClause;
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate; import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate;
import org.hibernate.sql.ast.tree.select.QueryPart; import org.hibernate.sql.ast.tree.select.QueryPart;
import org.hibernate.sql.ast.tree.select.QuerySpec; import org.hibernate.sql.ast.tree.select.QuerySpec;
import org.hibernate.sql.ast.tree.update.UpdateStatement;
import org.hibernate.sql.exec.spi.JdbcOperation; import org.hibernate.sql.exec.spi.JdbcOperation;
import org.hibernate.type.descriptor.jdbc.ArrayJdbcType; import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
@ -42,6 +51,44 @@ public class HSQLLegacySqlAstTranslator<T extends JdbcOperation> extends Abstrac
super( sessionFactory, statement ); super( sessionFactory, statement );
} }
@Override
protected void visitInsertStatementOnly(InsertSelectStatement statement) {
if ( statement.getConflictClause() == null || statement.getConflictClause().isDoNothing() ) {
// Render plain insert statement and possibly run into unique constraint violation
super.visitInsertStatementOnly( statement );
}
else {
visitInsertStatementEmulateMerge( statement );
}
}
@Override
protected void visitUpdateStatementOnly(UpdateStatement statement) {
if ( hasNonTrivialFromClause( statement.getFromClause() ) ) {
visitUpdateStatementEmulateMerge( statement );
}
else {
super.visitUpdateStatementOnly( statement );
}
}
@Override
protected void renderDmlTargetTableExpression(NamedTableReference tableReference) {
super.renderDmlTargetTableExpression( tableReference );
if ( getClauseStack().getCurrent() != Clause.INSERT ) {
renderTableReferenceIdentificationVariable( tableReference );
}
}
@Override
protected void visitConflictClause(ConflictClause conflictClause) {
if ( conflictClause != null ) {
if ( conflictClause.isDoUpdate() && conflictClause.getConstraintName() != null ) {
throw new IllegalQueryOperationException( "Insert conflict do update clause with constraint name is not supported" );
}
}
}
@Override @Override
protected void renderExpressionAsClauseItem(Expression expression) { protected void renderExpressionAsClauseItem(Expression expression) {
expression.accept( this ); expression.accept( this );
@ -295,14 +342,9 @@ public class HSQLLegacySqlAstTranslator<T extends JdbcOperation> extends Abstrac
return false; return false;
} }
@Override
protected String getFromDual() {
return " from (values(0))";
}
@Override @Override
protected String getFromDualForSelectOnly() { protected String getFromDualForSelectOnly() {
return getFromDual(); return " from " + getDual();
} }
private boolean supportsOffsetFetchClause() { private boolean supportsOffsetFetchClause() {

View File

@ -134,13 +134,13 @@ public class InformixSqlAstTranslator<T extends JdbcOperation> extends AbstractS
} }
@Override @Override
protected String getFromDual() { protected String getDual() {
return " from (select 0 from systables where tabid=1) dual"; return "(select 0 from systables where tabid=1)";
} }
@Override @Override
protected String getFromDualForSelectOnly() { protected String getFromDualForSelectOnly() {
return getFromDual(); return " from " + getDual() + " dual";
} }
private boolean supportsParameterOffsetFetchExpression() { private boolean supportsParameterOffsetFetchExpression() {

View File

@ -139,14 +139,14 @@ public class IngresSqlAstTranslator<T extends JdbcOperation> extends AbstractSql
} }
@Override @Override
protected String getFromDual() { protected String getDual() {
//this is only necessary if the query has a where clause return "(select 0)";
return " from (select 0) dual";
} }
@Override @Override
protected String getFromDualForSelectOnly() { protected String getFromDualForSelectOnly() {
return getFromDual(); //this is only necessary if the query has a where clause
return " from " + getDual() + " dual";
} }
@Override @Override

View File

@ -6,22 +6,38 @@
*/ */
package org.hibernate.community.dialect; package org.hibernate.community.dialect;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.dialect.DmlTargetColumnQualifierSupport;
import org.hibernate.dialect.MySQLSqlAstTranslator; import org.hibernate.dialect.MySQLSqlAstTranslator;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.collections.Stack;
import org.hibernate.query.sqm.ComparisonOperator; import org.hibernate.query.sqm.ComparisonOperator;
import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
import org.hibernate.sql.ast.tree.MutationStatement;
import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.Statement;
import org.hibernate.sql.ast.tree.delete.DeleteStatement;
import org.hibernate.sql.ast.tree.expression.CastTarget; import org.hibernate.sql.ast.tree.expression.CastTarget;
import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.Literal; import org.hibernate.sql.ast.tree.expression.Literal;
import org.hibernate.sql.ast.tree.expression.Summarization; import org.hibernate.sql.ast.tree.expression.Summarization;
import org.hibernate.sql.ast.tree.from.NamedTableReference;
import org.hibernate.sql.ast.tree.from.QueryPartTableReference; import org.hibernate.sql.ast.tree.from.QueryPartTableReference;
import org.hibernate.sql.ast.tree.insert.ConflictClause;
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate; import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate;
import org.hibernate.sql.ast.tree.predicate.LikePredicate; import org.hibernate.sql.ast.tree.predicate.LikePredicate;
import org.hibernate.sql.ast.tree.select.QueryGroup; import org.hibernate.sql.ast.tree.select.QueryGroup;
import org.hibernate.sql.ast.tree.select.QueryPart; import org.hibernate.sql.ast.tree.select.QueryPart;
import org.hibernate.sql.ast.tree.select.QuerySpec; import org.hibernate.sql.ast.tree.select.QuerySpec;
import org.hibernate.sql.ast.tree.select.SelectStatement;
import org.hibernate.sql.ast.tree.update.UpdateStatement;
import org.hibernate.sql.exec.internal.JdbcOperationQueryInsertImpl;
import org.hibernate.sql.exec.spi.JdbcOperation; import org.hibernate.sql.exec.spi.JdbcOperation;
import org.hibernate.sql.exec.spi.JdbcOperationQueryInsert;
/** /**
* A SQL AST translator for MariaDB. * A SQL AST translator for MariaDB.
@ -37,6 +53,135 @@ public class MariaDBLegacySqlAstTranslator<T extends JdbcOperation> extends Abst
this.dialect = (MariaDBLegacyDialect)super.getDialect(); this.dialect = (MariaDBLegacyDialect)super.getDialect();
} }
@Override
protected void visitInsertSource(InsertSelectStatement statement) {
if ( statement.getSourceSelectStatement() != null ) {
if ( statement.getConflictClause() != null ) {
final List<ColumnReference> targetColumnReferences = statement.getTargetColumns();
final List<String> columnNames = new ArrayList<>( targetColumnReferences.size() );
for ( ColumnReference targetColumnReference : targetColumnReferences ) {
columnNames.add( targetColumnReference.getColumnExpression() );
}
appendSql( "select * from " );
emulateQueryPartTableReferenceColumnAliasing(
new QueryPartTableReference(
new SelectStatement( statement.getSourceSelectStatement() ),
"excluded",
columnNames,
false,
getSessionFactory()
)
);
}
else {
statement.getSourceSelectStatement().accept( this );
}
}
else {
visitValuesList( statement.getValuesList() );
}
}
@Override
public void visitColumnReference(ColumnReference columnReference) {
final Statement currentStatement;
if ( "excluded".equals( columnReference.getQualifier() )
&& ( currentStatement = getStatementStack().getCurrent() ) instanceof InsertSelectStatement
&& ( (InsertSelectStatement) currentStatement ).getSourceSelectStatement() == null ) {
// Accessing the excluded row for an insert-values statement in the conflict clause requires the values qualifier
appendSql( "values(" );
columnReference.appendReadExpression( this, null );
append( ')' );
}
else {
super.visitColumnReference( columnReference );
}
}
@Override
protected void renderDeleteClause(DeleteStatement statement) {
appendSql( "delete" );
final Stack<Clause> clauseStack = getClauseStack();
try {
clauseStack.push( Clause.DELETE );
renderTableReferenceIdentificationVariable( statement.getTargetTable() );
if ( statement.getFromClause().getRoots().isEmpty() ) {
appendSql( " from " );
renderDmlTargetTableExpression( statement.getTargetTable() );
}
else {
visitFromClause( statement.getFromClause() );
}
}
finally {
clauseStack.pop();
}
}
@Override
protected void renderUpdateClause(UpdateStatement updateStatement) {
if ( updateStatement.getFromClause().getRoots().isEmpty() ) {
super.renderUpdateClause( updateStatement );
}
else {
appendSql( "update " );
renderFromClauseSpaces( updateStatement.getFromClause() );
}
}
@Override
protected void renderDmlTargetTableExpression(NamedTableReference tableReference) {
super.renderDmlTargetTableExpression( tableReference );
if ( getClauseStack().getCurrent() != Clause.INSERT ) {
renderTableReferenceIdentificationVariable( tableReference );
}
}
@Override
protected boolean supportsJoinsInDelete() {
return true;
}
@Override
protected JdbcOperationQueryInsert translateInsert(InsertSelectStatement sqlAst) {
visitInsertStatement( sqlAst );
return new JdbcOperationQueryInsertImpl(
getSql(),
getParameterBinders(),
getAffectedTableNames(),
null
);
}
@Override
protected void visitConflictClause(ConflictClause conflictClause) {
visitOnDuplicateKeyConflictClause( conflictClause );
}
@Override
protected String determineColumnReferenceQualifier(ColumnReference columnReference) {
final DmlTargetColumnQualifierSupport qualifierSupport = getDialect().getDmlTargetColumnQualifierSupport();
final MutationStatement currentDmlStatement;
final String dmlAlias;
// Since MariaDB does not support aliasing the insert target table,
// we must detect column reference that are used in the conflict clause
// and use the table expression as qualifier instead
if ( getClauseStack().getCurrent() != Clause.SET
|| !( ( currentDmlStatement = getCurrentDmlStatement() ) instanceof InsertSelectStatement )
|| ( dmlAlias = currentDmlStatement.getTargetTable().getIdentificationVariable() ) == null
|| !dmlAlias.equals( columnReference.getQualifier() ) ) {
return columnReference.getQualifier();
}
// Qualify the column reference with the table expression also when in subqueries
else if ( qualifierSupport != DmlTargetColumnQualifierSupport.NONE || !getQueryPartStack().isEmpty() ) {
return getCurrentDmlStatement().getTargetTable().getTableExpression();
}
else {
return null;
}
}
@Override @Override
protected boolean supportsWithClause() { protected boolean supportsWithClause() {
return dialect.getVersion().isSameOrAfter( 10, 2 ); return dialect.getVersion().isSameOrAfter( 10, 2 );
@ -222,13 +367,13 @@ public class MariaDBLegacySqlAstTranslator<T extends JdbcOperation> extends Abst
} }
@Override @Override
protected String getFromDual() { protected String getDual() {
return " from dual"; return "dual";
} }
@Override @Override
protected String getFromDualForSelectOnly() { protected String getFromDualForSelectOnly() {
return getDialect().getVersion().isBefore( 10, 4 ) ? getFromDual() : ""; return getDialect().getVersion().isBefore( 10, 4 ) ? ( " from " + getDual() ) : "";
} }
@Override @Override

View File

@ -94,12 +94,12 @@ public class MaxDBSqlAstTranslator<T extends JdbcOperation> extends AbstractSqlA
} }
@Override @Override
public String getFromDual() { protected String getDual() {
return " from dual"; return "dual";
} }
@Override @Override
protected String getFromDualForSelectOnly() { protected String getFromDualForSelectOnly() {
return getFromDual(); return " from " + getDual();
} }
} }

View File

@ -82,13 +82,8 @@ public class MimerSQLSqlAstTranslator<T extends JdbcOperation> extends AbstractS
return false; return false;
} }
@Override
protected String getFromDual() {
return " from (values(0))";
}
@Override @Override
protected String getFromDualForSelectOnly() { protected String getFromDualForSelectOnly() {
return getFromDual(); return " from " + getDual();
} }
} }

View File

@ -1395,4 +1395,14 @@ public class MySQLLegacyDialect extends Dialect {
return "set foreign_key_checks = 1"; return "set foreign_key_checks = 1";
} }
@Override
public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() {
return DmlTargetColumnQualifierSupport.TABLE_ALIAS;
}
@Override
public boolean supportsFromClauseInUpdate() {
return true;
}
} }

View File

@ -6,24 +6,40 @@
*/ */
package org.hibernate.community.dialect; package org.hibernate.community.dialect;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.dialect.DialectDelegateWrapper; import org.hibernate.dialect.DialectDelegateWrapper;
import org.hibernate.dialect.DmlTargetColumnQualifierSupport;
import org.hibernate.dialect.MySQLSqlAstTranslator; import org.hibernate.dialect.MySQLSqlAstTranslator;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.collections.Stack;
import org.hibernate.query.sqm.ComparisonOperator; import org.hibernate.query.sqm.ComparisonOperator;
import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
import org.hibernate.sql.ast.tree.MutationStatement;
import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.Statement;
import org.hibernate.sql.ast.tree.delete.DeleteStatement;
import org.hibernate.sql.ast.tree.expression.CastTarget; import org.hibernate.sql.ast.tree.expression.CastTarget;
import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.Literal; import org.hibernate.sql.ast.tree.expression.Literal;
import org.hibernate.sql.ast.tree.expression.Summarization; import org.hibernate.sql.ast.tree.expression.Summarization;
import org.hibernate.sql.ast.tree.from.NamedTableReference;
import org.hibernate.sql.ast.tree.from.QueryPartTableReference; import org.hibernate.sql.ast.tree.from.QueryPartTableReference;
import org.hibernate.sql.ast.tree.from.ValuesTableReference; import org.hibernate.sql.ast.tree.from.ValuesTableReference;
import org.hibernate.sql.ast.tree.insert.ConflictClause;
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate; import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate;
import org.hibernate.sql.ast.tree.predicate.LikePredicate; import org.hibernate.sql.ast.tree.predicate.LikePredicate;
import org.hibernate.sql.ast.tree.select.QueryGroup; import org.hibernate.sql.ast.tree.select.QueryGroup;
import org.hibernate.sql.ast.tree.select.QueryPart; import org.hibernate.sql.ast.tree.select.QueryPart;
import org.hibernate.sql.ast.tree.select.QuerySpec; import org.hibernate.sql.ast.tree.select.QuerySpec;
import org.hibernate.sql.ast.tree.select.SelectStatement;
import org.hibernate.sql.ast.tree.update.UpdateStatement;
import org.hibernate.sql.exec.internal.JdbcOperationQueryInsertImpl;
import org.hibernate.sql.exec.spi.JdbcOperation; import org.hibernate.sql.exec.spi.JdbcOperation;
import org.hibernate.sql.exec.spi.JdbcOperationQueryInsert;
/** /**
* A SQL AST translator for MySQL. * A SQL AST translator for MySQL.
@ -36,6 +52,146 @@ public class MySQLLegacySqlAstTranslator<T extends JdbcOperation> extends Abstra
super( sessionFactory, statement ); super( sessionFactory, statement );
} }
@Override
protected void visitInsertSource(InsertSelectStatement statement) {
if ( statement.getSourceSelectStatement() != null ) {
if ( statement.getConflictClause() != null ) {
final List<ColumnReference> targetColumnReferences = statement.getTargetColumns();
final List<String> columnNames = new ArrayList<>( targetColumnReferences.size() );
for ( ColumnReference targetColumnReference : targetColumnReferences ) {
columnNames.add( targetColumnReference.getColumnExpression() );
}
appendSql( "select * from " );
emulateQueryPartTableReferenceColumnAliasing(
new QueryPartTableReference(
new SelectStatement( statement.getSourceSelectStatement() ),
"excluded",
columnNames,
false,
getSessionFactory()
)
);
}
else {
statement.getSourceSelectStatement().accept( this );
}
}
else {
visitValuesList( statement.getValuesList() );
if ( statement.getConflictClause() != null && getDialect().getMySQLVersion().isSameOrAfter( 8, 0, 19 ) ) {
appendSql( " as excluded" );
char separator = '(';
for ( ColumnReference targetColumn : statement.getTargetColumns() ) {
appendSql( separator );
appendSql( targetColumn.getColumnExpression() );
separator = ',';
}
appendSql( ')' );
}
}
}
@Override
public void visitColumnReference(ColumnReference columnReference) {
final Statement currentStatement;
if ( getDialect().getMySQLVersion().isBefore( 8, 0, 19 )
&& "excluded".equals( columnReference.getQualifier() )
&& ( currentStatement = getStatementStack().getCurrent() ) instanceof InsertSelectStatement
&& ( (InsertSelectStatement) currentStatement ).getSourceSelectStatement() == null ) {
// Accessing the excluded row for an insert-values statement in the conflict clause requires the values qualifier
appendSql( "values(" );
columnReference.appendReadExpression( this, null );
append( ')' );
}
else {
super.visitColumnReference( columnReference );
}
}
@Override
protected void renderDeleteClause(DeleteStatement statement) {
appendSql( "delete" );
final Stack<Clause> clauseStack = getClauseStack();
try {
clauseStack.push( Clause.DELETE );
renderTableReferenceIdentificationVariable( statement.getTargetTable() );
if ( statement.getFromClause().getRoots().isEmpty() ) {
appendSql( " from " );
renderDmlTargetTableExpression( statement.getTargetTable() );
}
else {
visitFromClause( statement.getFromClause() );
}
}
finally {
clauseStack.pop();
}
}
@Override
protected void renderUpdateClause(UpdateStatement updateStatement) {
if ( updateStatement.getFromClause().getRoots().isEmpty() ) {
super.renderUpdateClause( updateStatement );
}
else {
appendSql( "update " );
renderFromClauseSpaces( updateStatement.getFromClause() );
}
}
@Override
protected void renderDmlTargetTableExpression(NamedTableReference tableReference) {
super.renderDmlTargetTableExpression( tableReference );
if ( getClauseStack().getCurrent() != Clause.INSERT ) {
renderTableReferenceIdentificationVariable( tableReference );
}
}
@Override
protected boolean supportsJoinsInDelete() {
return true;
}
@Override
protected JdbcOperationQueryInsert translateInsert(InsertSelectStatement sqlAst) {
visitInsertStatement( sqlAst );
return new JdbcOperationQueryInsertImpl(
getSql(),
getParameterBinders(),
getAffectedTableNames(),
null
);
}
@Override
protected void visitConflictClause(ConflictClause conflictClause) {
visitOnDuplicateKeyConflictClause( conflictClause );
}
@Override
protected String determineColumnReferenceQualifier(ColumnReference columnReference) {
final DmlTargetColumnQualifierSupport qualifierSupport = getDialect().getDmlTargetColumnQualifierSupport();
final MutationStatement currentDmlStatement;
final String dmlAlias;
// Since MySQL does not support aliasing the insert target table,
// we must detect column reference that are used in the conflict clause
// and use the table expression as qualifier instead
if ( getClauseStack().getCurrent() != Clause.SET
|| !( ( currentDmlStatement = getCurrentDmlStatement() ) instanceof InsertSelectStatement )
|| ( dmlAlias = currentDmlStatement.getTargetTable().getIdentificationVariable() ) == null
|| !dmlAlias.equals( columnReference.getQualifier() ) ) {
return columnReference.getQualifier();
}
// Qualify the column reference with the table expression also when in subqueries
else if ( qualifierSupport != DmlTargetColumnQualifierSupport.NONE || !getQueryPartStack().isEmpty() ) {
return getCurrentDmlStatement().getTargetTable().getTableExpression();
}
else {
return null;
}
}
@Override @Override
protected void renderExpressionAsClauseItem(Expression expression) { protected void renderExpressionAsClauseItem(Expression expression) {
expression.accept( this ); expression.accept( this );
@ -234,13 +390,13 @@ public class MySQLLegacySqlAstTranslator<T extends JdbcOperation> extends Abstra
} }
@Override @Override
protected String getFromDual() { protected String getDual() {
return " from dual"; return "dual";
} }
@Override @Override
protected String getFromDualForSelectOnly() { protected String getFromDualForSelectOnly() {
return getDialect().getVersion().isSameOrAfter( 8 ) ? "" : getFromDual(); return getDialect().getVersion().isSameOrAfter( 8 ) ? "" : ( " from " + getDual() );
} }
@Override @Override

View File

@ -1008,6 +1008,7 @@ public class OracleLegacyDialect extends Dialect {
@Override @Override
public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() { public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() {
return (sqlException, message, sql) -> { return (sqlException, message, sql) -> {
final String constraintName;
// interpreting Oracle exceptions is much much more precise based on their specific vendor codes. // interpreting Oracle exceptions is much much more precise based on their specific vendor codes.
switch ( JdbcExceptionHelper.extractErrorCode( sqlException ) ) { switch ( JdbcExceptionHelper.extractErrorCode( sqlException ) ) {
@ -1040,9 +1041,19 @@ public class OracleLegacyDialect extends Dialect {
// data integrity violation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // data integrity violation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
case 1:
// ORA-00001: unique constraint violated
constraintName = getViolatedConstraintNameExtractor().extractConstraintName( sqlException );
return new ConstraintViolationException(
message,
sqlException,
sql,
ConstraintViolationException.ConstraintKind.UNIQUE,
constraintName
);
case 1407: case 1407:
// ORA-01407: cannot update column to NULL // ORA-01407: cannot update column to NULL
final String constraintName = getViolatedConstraintNameExtractor().extractConstraintName( sqlException ); constraintName = getViolatedConstraintNameExtractor().extractConstraintName( sqlException );
return new ConstraintViolationException( message, sqlException, sql, constraintName ); return new ConstraintViolationException( message, sqlException, sql, constraintName );
default: default:
@ -1520,4 +1531,9 @@ public class OracleLegacyDialect extends Dialect {
public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() { public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() {
return DmlTargetColumnQualifierSupport.TABLE_ALIAS; return DmlTargetColumnQualifierSupport.TABLE_ALIAS;
} }
@Override
public boolean supportsFromClauseInUpdate() {
return true;
}
} }

View File

@ -37,10 +37,12 @@ import org.hibernate.sql.ast.tree.expression.SqlTupleContainer;
import org.hibernate.sql.ast.tree.expression.Summarization; import org.hibernate.sql.ast.tree.expression.Summarization;
import org.hibernate.sql.ast.tree.from.FromClause; import org.hibernate.sql.ast.tree.from.FromClause;
import org.hibernate.sql.ast.tree.from.FunctionTableReference; import org.hibernate.sql.ast.tree.from.FunctionTableReference;
import org.hibernate.sql.ast.tree.from.NamedTableReference;
import org.hibernate.sql.ast.tree.from.QueryPartTableReference; import org.hibernate.sql.ast.tree.from.QueryPartTableReference;
import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.UnionTableGroup; import org.hibernate.sql.ast.tree.from.UnionTableGroup;
import org.hibernate.sql.ast.tree.from.ValuesTableReference; import org.hibernate.sql.ast.tree.from.ValuesTableReference;
import org.hibernate.sql.ast.tree.insert.ConflictClause;
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement; import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
import org.hibernate.sql.ast.tree.insert.Values; import org.hibernate.sql.ast.tree.insert.Values;
import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate; import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate;
@ -51,6 +53,7 @@ import org.hibernate.sql.ast.tree.select.QuerySpec;
import org.hibernate.sql.ast.tree.select.SelectClause; import org.hibernate.sql.ast.tree.select.SelectClause;
import org.hibernate.sql.ast.tree.select.SortSpecification; import org.hibernate.sql.ast.tree.select.SortSpecification;
import org.hibernate.sql.ast.tree.update.Assignment; import org.hibernate.sql.ast.tree.update.Assignment;
import org.hibernate.sql.ast.tree.update.UpdateStatement;
import org.hibernate.sql.exec.spi.JdbcOperation; import org.hibernate.sql.exec.spi.JdbcOperation;
import org.hibernate.sql.results.internal.SqlSelectionImpl; import org.hibernate.sql.results.internal.SqlSelectionImpl;
import org.hibernate.type.SqlTypes; import org.hibernate.type.SqlTypes;
@ -67,6 +70,54 @@ public class OracleLegacySqlAstTranslator<T extends JdbcOperation> extends Abstr
super( sessionFactory, statement ); super( sessionFactory, statement );
} }
@Override
protected void visitInsertStatementOnly(InsertSelectStatement statement) {
if ( statement.getConflictClause() == null || statement.getConflictClause().isDoNothing() ) {
// Render plain insert statement and possibly run into unique constraint violation
super.visitInsertStatementOnly( statement );
}
else {
visitInsertStatementEmulateMerge( statement );
}
}
@Override
protected void visitUpdateStatementOnly(UpdateStatement statement) {
if ( hasNonTrivialFromClause( statement.getFromClause() ) ) {
visitUpdateStatementEmulateInlineView( statement );
}
else {
renderUpdateClause( statement );
renderSetClause( statement.getAssignments() );
visitWhereClause( statement.getRestriction() );
visitReturningColumns( statement.getReturningColumns() );
}
}
@Override
protected void renderMergeUpdateClause(List<Assignment> assignments, Predicate wherePredicate) {
appendSql( " then update" );
renderSetClause( assignments );
visitWhereClause( wherePredicate );
}
@Override
protected void renderDmlTargetTableExpression(NamedTableReference tableReference) {
super.renderDmlTargetTableExpression( tableReference );
if ( getClauseStack().getCurrent() != Clause.INSERT ) {
renderTableReferenceIdentificationVariable( tableReference );
}
}
@Override
protected void visitConflictClause(ConflictClause conflictClause) {
if ( conflictClause != null ) {
if ( conflictClause.isDoUpdate() && conflictClause.getConstraintName() != null ) {
throw new IllegalQueryOperationException( "Insert conflict do update clause with constraint name is not supported" );
}
}
}
@Override @Override
protected boolean needsRecursiveKeywordInWithClause() { protected boolean needsRecursiveKeywordInWithClause() {
return false; return false;
@ -227,7 +278,14 @@ public class OracleLegacySqlAstTranslator<T extends JdbcOperation> extends Abstr
@Override @Override
protected void visitValuesList(List<Values> valuesList) { protected void visitValuesList(List<Values> valuesList) {
visitValuesListEmulateSelectUnion( valuesList ); if ( valuesList.size() < 2 ) {
visitValuesListStandard( valuesList );
}
else {
// Oracle doesn't support a multi-values insert
// So we render a select union emulation instead
visitValuesListEmulateSelectUnion( valuesList );
}
} }
@Override @Override
@ -617,13 +675,13 @@ public class OracleLegacySqlAstTranslator<T extends JdbcOperation> extends Abstr
} }
@Override @Override
protected String getFromDual() { protected String getDual() {
return " from dual"; return "dual";
} }
@Override @Override
protected String getFromDualForSelectOnly() { protected String getFromDualForSelectOnly() {
return getFromDual(); return " from " + getDual();
} }
private boolean supportsOffsetFetchClause() { private boolean supportsOffsetFetchClause() {

View File

@ -732,6 +732,11 @@ public class PostgreSQLLegacyDialect extends Dialect {
return getVersion().isSameOrAfter( 9, 1 ); return getVersion().isSameOrAfter( 9, 1 );
} }
@Override
public boolean supportsConflictClauseForInsertCTE() {
return getVersion().isSameOrAfter( 9, 5 );
}
@Override @Override
public SequenceSupport getSequenceSupport() { public SequenceSupport getSequenceSupport() {
return getVersion().isBefore( 8, 2 ) return getVersion().isBefore( 8, 2 )
@ -1467,4 +1472,14 @@ public class PostgreSQLLegacyDialect extends Dialect {
// The maximum scale for `interval second` is 6 unfortunately // The maximum scale for `interval second` is 6 unfortunately
return 6; return 6;
} }
@Override
public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() {
return DmlTargetColumnQualifierSupport.TABLE_ALIAS;
}
@Override
public boolean supportsFromClauseInUpdate() {
return true;
}
} }

View File

@ -10,6 +10,7 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.JdbcMappingContainer; import org.hibernate.metamodel.mapping.JdbcMappingContainer;
import org.hibernate.query.sqm.ComparisonOperator; import org.hibernate.query.sqm.ComparisonOperator;
import org.hibernate.query.sqm.FetchClauseType; import org.hibernate.query.sqm.FetchClauseType;
import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.Statement;
import org.hibernate.sql.ast.tree.cte.CteMaterialization; import org.hibernate.sql.ast.tree.cte.CteMaterialization;
@ -18,13 +19,20 @@ import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression;
import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.Literal; import org.hibernate.sql.ast.tree.expression.Literal;
import org.hibernate.sql.ast.tree.expression.Summarization; import org.hibernate.sql.ast.tree.expression.Summarization;
import org.hibernate.sql.ast.tree.from.NamedTableReference;
import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.insert.ConflictClause;
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate; import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate;
import org.hibernate.sql.ast.tree.predicate.LikePredicate; import org.hibernate.sql.ast.tree.predicate.LikePredicate;
import org.hibernate.sql.ast.tree.predicate.NullnessPredicate; import org.hibernate.sql.ast.tree.predicate.NullnessPredicate;
import org.hibernate.sql.ast.tree.select.QueryGroup; import org.hibernate.sql.ast.tree.select.QueryGroup;
import org.hibernate.sql.ast.tree.select.QueryPart; import org.hibernate.sql.ast.tree.select.QueryPart;
import org.hibernate.sql.ast.tree.select.QuerySpec; import org.hibernate.sql.ast.tree.select.QuerySpec;
import org.hibernate.sql.ast.tree.update.UpdateStatement;
import org.hibernate.sql.exec.internal.JdbcOperationQueryInsertImpl;
import org.hibernate.sql.exec.spi.JdbcOperation; import org.hibernate.sql.exec.spi.JdbcOperation;
import org.hibernate.sql.exec.spi.JdbcOperationQueryInsert;
import org.hibernate.sql.model.internal.TableInsertStandard; import org.hibernate.sql.model.internal.TableInsertStandard;
import org.hibernate.type.SqlTypes; import org.hibernate.type.SqlTypes;
@ -45,6 +53,56 @@ public class PostgreSQLLegacySqlAstTranslator<T extends JdbcOperation> extends A
appendSql( "default values" ); appendSql( "default values" );
} }
@Override
protected JdbcOperationQueryInsert translateInsert(InsertSelectStatement sqlAst) {
visitInsertStatement( sqlAst );
return new JdbcOperationQueryInsertImpl(
getSql(),
getParameterBinders(),
getAffectedTableNames(),
null
);
}
@Override
protected void renderTableReferenceIdentificationVariable(TableReference tableReference) {
final String identificationVariable = tableReference.getIdentificationVariable();
if ( identificationVariable != null ) {
final Clause currentClause = getClauseStack().getCurrent();
if ( currentClause == Clause.INSERT ) {
// PostgreSQL requires the "as" keyword for inserts
appendSql( " as " );
}
else {
append( WHITESPACE );
}
append( tableReference.getIdentificationVariable() );
}
}
@Override
protected void renderDmlTargetTableExpression(NamedTableReference tableReference) {
super.renderDmlTargetTableExpression( tableReference );
final Statement currentStatement = getStatementStack().getCurrent();
if ( !( currentStatement instanceof UpdateStatement )
|| !hasNonTrivialFromClause( ( (UpdateStatement) currentStatement ).getFromClause() ) ) {
// For UPDATE statements we render a full FROM clause and a join condition to match target table rows,
// but for that to work, we have to omit the alias for the target table reference here
renderTableReferenceIdentificationVariable( tableReference );
}
}
@Override
protected void renderFromClauseAfterUpdateSet(UpdateStatement statement) {
renderFromClauseJoiningDmlTargetReference( statement );
}
@Override
protected void visitConflictClause(ConflictClause conflictClause) {
visitStandardConflictClause( conflictClause );
}
@Override @Override
protected void renderExpressionAsClauseItem(Expression expression) { protected void renderExpressionAsClauseItem(Expression expression) {
expression.accept( this ); expression.accept( this );
@ -218,9 +276,7 @@ public class PostgreSQLLegacySqlAstTranslator<T extends JdbcOperation> extends A
appendSql( "()" ); appendSql( "()" );
} }
else { else {
appendSql( "(select 1" ); appendSql( "(select 1)" );
appendSql( getFromDualForSelectOnly() );
appendSql( ')' );
} }
} }
else if ( expression instanceof Summarization ) { else if ( expression instanceof Summarization ) {

View File

@ -126,12 +126,12 @@ public class RDMSOS2200SqlAstTranslator<T extends JdbcOperation> extends Abstrac
} }
@Override @Override
protected String getFromDual() { protected String getDual() {
return " from rdms.rdms_dummy where key_col=1"; return "rdms.rdms_dummy";
} }
@Override @Override
protected String getFromDualForSelectOnly() { protected String getFromDualForSelectOnly() {
return getFromDual(); return " from " + getDual() + " where key_col=1";
} }
} }

View File

@ -16,6 +16,7 @@ import org.hibernate.boot.model.relational.SqlStringGenerationContext;
import org.hibernate.dialect.AbstractTransactSQLDialect; import org.hibernate.dialect.AbstractTransactSQLDialect;
import org.hibernate.dialect.DatabaseVersion; import org.hibernate.dialect.DatabaseVersion;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.DmlTargetColumnQualifierSupport;
import org.hibernate.dialect.Replacer; import org.hibernate.dialect.Replacer;
import org.hibernate.dialect.TimeZoneSupport; import org.hibernate.dialect.TimeZoneSupport;
import org.hibernate.dialect.function.CommonFunctionFactory; import org.hibernate.dialect.function.CommonFunctionFactory;
@ -41,8 +42,11 @@ import org.hibernate.engine.jdbc.env.spi.IdentifierHelper;
import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder; import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder;
import org.hibernate.engine.jdbc.env.spi.NameQualifierSupport; import org.hibernate.engine.jdbc.env.spi.NameQualifierSupport;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.exception.ConstraintViolationException;
import org.hibernate.exception.LockTimeoutException; import org.hibernate.exception.LockTimeoutException;
import org.hibernate.exception.spi.SQLExceptionConversionDelegate; import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor;
import org.hibernate.exception.spi.ViolatedConstraintNameExtractor;
import org.hibernate.internal.util.JdbcExceptionHelper; import org.hibernate.internal.util.JdbcExceptionHelper;
import org.hibernate.mapping.Column; import org.hibernate.mapping.Column;
import org.hibernate.query.sqm.CastType; import org.hibernate.query.sqm.CastType;
@ -85,6 +89,7 @@ import java.util.TimeZone;
import jakarta.persistence.TemporalType; import jakarta.persistence.TemporalType;
import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate;
import static org.hibernate.query.sqm.TemporalUnit.NANOSECOND; import static org.hibernate.query.sqm.TemporalUnit.NANOSECOND;
import static org.hibernate.query.sqm.produce.function.FunctionParameterType.INTEGER; import static org.hibernate.query.sqm.produce.function.FunctionParameterType.INTEGER;
import static org.hibernate.type.SqlTypes.*; import static org.hibernate.type.SqlTypes.*;
@ -723,6 +728,20 @@ public class SQLServerLegacyDialect extends AbstractTransactSQLDialect {
public boolean supportsFetchClause(FetchClauseType type) { public boolean supportsFetchClause(FetchClauseType type) {
return getVersion().isSameOrAfter( 11 ); return getVersion().isSameOrAfter( 11 );
} }
@Override
public ViolatedConstraintNameExtractor getViolatedConstraintNameExtractor() {
return new TemplatedViolatedConstraintNameExtractor(
sqle -> {
switch ( JdbcExceptionHelper.extractErrorCode( sqle ) ) {
case 2627:
case 2601:
return extractUsingTemplate( "'", "'", sqle.getMessage() );
default:
return null;
}
}
);
}
@Override @Override
public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() { public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() {
@ -740,6 +759,13 @@ public class SQLServerLegacyDialect extends AbstractTransactSQLDialect {
case 1222: case 1222:
return new LockTimeoutException( message, sqlException, sql ); return new LockTimeoutException( message, sqlException, sql );
case 2627: case 2627:
return new ConstraintViolationException(
message,
sqlException,
sql,
ConstraintViolationException.ConstraintKind.UNIQUE,
getViolatedConstraintNameExtractor().extractConstraintName( sqlException )
);
case 2601: case 2601:
return new ConstraintViolationException( return new ConstraintViolationException(
message, message,

View File

@ -12,15 +12,20 @@ import org.hibernate.LockMode;
import org.hibernate.LockOptions; import org.hibernate.LockOptions;
import org.hibernate.dialect.DatabaseVersion; import org.hibernate.dialect.DatabaseVersion;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.collections.Stack;
import org.hibernate.metamodel.mapping.JdbcMappingContainer; import org.hibernate.metamodel.mapping.JdbcMappingContainer;
import org.hibernate.query.IllegalQueryOperationException;
import org.hibernate.query.sqm.ComparisonOperator; import org.hibernate.query.sqm.ComparisonOperator;
import org.hibernate.query.sqm.FetchClauseType; import org.hibernate.query.sqm.FetchClauseType;
import org.hibernate.sql.ast.Clause; import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.SqlAstJoinType; import org.hibernate.sql.ast.SqlAstJoinType;
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.ast.tree.MutationStatement;
import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.Statement;
import org.hibernate.sql.ast.tree.delete.DeleteStatement;
import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression; import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression;
import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.Literal; import org.hibernate.sql.ast.tree.expression.Literal;
import org.hibernate.sql.ast.tree.expression.SqlTuple; import org.hibernate.sql.ast.tree.expression.SqlTuple;
@ -30,12 +35,15 @@ import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableGroupJoin; import org.hibernate.sql.ast.tree.from.TableGroupJoin;
import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.from.UnionTableReference; import org.hibernate.sql.ast.tree.from.UnionTableReference;
import org.hibernate.sql.ast.tree.insert.ConflictClause;
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
import org.hibernate.sql.ast.tree.predicate.Predicate; import org.hibernate.sql.ast.tree.predicate.Predicate;
import org.hibernate.sql.ast.tree.select.QueryGroup; import org.hibernate.sql.ast.tree.select.QueryGroup;
import org.hibernate.sql.ast.tree.select.QueryPart; import org.hibernate.sql.ast.tree.select.QueryPart;
import org.hibernate.sql.ast.tree.select.QuerySpec; import org.hibernate.sql.ast.tree.select.QuerySpec;
import org.hibernate.sql.ast.tree.select.SelectClause; import org.hibernate.sql.ast.tree.select.SelectClause;
import org.hibernate.sql.ast.tree.select.SortSpecification; import org.hibernate.sql.ast.tree.select.SortSpecification;
import org.hibernate.sql.ast.tree.update.UpdateStatement;
import org.hibernate.sql.exec.spi.JdbcOperation; import org.hibernate.sql.exec.spi.JdbcOperation;
import org.hibernate.type.SqlTypes; import org.hibernate.type.SqlTypes;
@ -54,6 +62,84 @@ public class SQLServerLegacySqlAstTranslator<T extends JdbcOperation> extends Ab
super( sessionFactory, statement ); super( sessionFactory, statement );
} }
@Override
protected void visitInsertStatementOnly(InsertSelectStatement statement) {
if ( statement.getConflictClause() == null || statement.getConflictClause().isDoNothing() ) {
// Render plain insert statement and possibly run into unique constraint violation
super.visitInsertStatementOnly( statement );
}
else {
visitInsertStatementEmulateMerge( statement );
appendSql( ';' );
}
}
@Override
protected void renderDeleteClause(DeleteStatement statement) {
appendSql( "delete" );
final Stack<Clause> clauseStack = getClauseStack();
try {
clauseStack.push( Clause.DELETE );
renderTableReferenceIdentificationVariable( statement.getTargetTable() );
if ( statement.getFromClause().getRoots().isEmpty() ) {
appendSql( " from " );
renderDmlTargetTableExpression( statement.getTargetTable() );
}
else {
visitFromClause( statement.getFromClause() );
}
}
finally {
clauseStack.pop();
}
}
@Override
protected void renderUpdateClause(UpdateStatement updateStatement) {
appendSql( "update" );
final Stack<Clause> clauseStack = getClauseStack();
try {
clauseStack.push( Clause.UPDATE );
renderTableReferenceIdentificationVariable( updateStatement.getTargetTable() );
}
finally {
clauseStack.pop();
}
}
@Override
protected void renderDmlTargetTableExpression(NamedTableReference tableReference) {
super.renderDmlTargetTableExpression( tableReference );
if ( getClauseStack().getCurrent() != Clause.INSERT ) {
renderTableReferenceIdentificationVariable( tableReference );
}
}
@Override
protected boolean supportsJoinsInDelete() {
return true;
}
@Override
protected void renderFromClauseAfterUpdateSet(UpdateStatement statement) {
if ( statement.getFromClause().getRoots().isEmpty() ) {
appendSql( " from " );
renderDmlTargetTableExpression( statement.getTargetTable() );
}
else {
visitFromClause( statement.getFromClause() );
}
}
@Override
protected void visitConflictClause(ConflictClause conflictClause) {
if ( conflictClause != null ) {
if ( conflictClause.isDoUpdate() && conflictClause.getConstraintName() != null ) {
throw new IllegalQueryOperationException( "Insert conflict do update clause with constraint name is not supported" );
}
}
}
@Override @Override
protected boolean needsRecursiveKeywordInWithClause() { protected boolean needsRecursiveKeywordInWithClause() {
return false; return false;
@ -129,14 +215,7 @@ public class SQLServerLegacySqlAstTranslator<T extends JdbcOperation> extends Ab
appendSql( " )" ); appendSql( " )" );
registerAffectedTable( tableReference ); registerAffectedTable( tableReference );
final Clause currentClause = getClauseStack().getCurrent(); renderTableReferenceIdentificationVariable( tableReference );
if ( rendersTableReferenceAlias( currentClause ) ) {
final String identificationVariable = tableReference.getIdentificationVariable();
if ( identificationVariable != null ) {
appendSql( ' ' );
appendSql( identificationVariable );
}
}
} }
else { else {
super.renderNamedTableReference( tableReference, lockMode ); super.renderNamedTableReference( tableReference, lockMode );

View File

@ -628,27 +628,17 @@ public class SybaseASELegacyDialect extends SybaseLegacyDialect {
final int errorCode = JdbcExceptionHelper.extractErrorCode( sqle ); final int errorCode = JdbcExceptionHelper.extractErrorCode( sqle );
if ( sqlState != null ) { if ( sqlState != null ) {
switch ( sqlState ) { switch ( sqlState ) {
// UNIQUE VIOLATION
case "S1000": case "S1000":
if ( 2601 == errorCode ) {
return extractUsingTemplate( "with unique index '", "'", sqle.getMessage() );
}
break;
case "23000": case "23000":
if ( 546 == errorCode ) { switch ( errorCode ) {
// Foreign key violation case 2601:
return extractUsingTemplate( "constraint name = '", "'", sqle.getMessage() ); // UNIQUE VIOLATION
return extractUsingTemplate( "with unique index '", "'", sqle.getMessage() );
case 546:
// Foreign key violation
return extractUsingTemplate( "constraint name = '", "'", sqle.getMessage() );
} }
break; break;
// // FOREIGN KEY VIOLATION
// case 23503:
// return extractUsingTemplate( "violates foreign key constraint \"","\"", sqle.getMessage() );
// // NOT NULL VIOLATION
// case 23502:
// return extractUsingTemplate( "null value in column \"","\" violates not-null constraint", sqle.getMessage() );
// // TODO: RESTRICT VIOLATION
// case 23001:
// return null;
} }
} }
return null; return null;
@ -659,7 +649,6 @@ public class SybaseASELegacyDialect extends SybaseLegacyDialect {
if ( getVersion().isBefore( 15, 7 ) ) { if ( getVersion().isBefore( 15, 7 ) ) {
return null; return null;
} }
return (sqlException, message, sql) -> { return (sqlException, message, sql) -> {
final String sqlState = JdbcExceptionHelper.extractSqlState( sqlException ); final String sqlState = JdbcExceptionHelper.extractSqlState( sqlException );
final int errorCode = JdbcExceptionHelper.extractErrorCode( sqlException ); final int errorCode = JdbcExceptionHelper.extractErrorCode( sqlException );
@ -669,30 +658,44 @@ public class SybaseASELegacyDialect extends SybaseLegacyDialect {
case "JZ006": case "JZ006":
return new LockTimeoutException( message, sqlException, sql ); return new LockTimeoutException( message, sqlException, sql );
case "S1000": case "S1000":
case "23000":
switch ( errorCode ) { switch ( errorCode ) {
case 515: case 515:
// Attempt to insert NULL value into column; column does not allow nulls. // Attempt to insert NULL value into column; column does not allow nulls.
return new ConstraintViolationException(
message,
sqlException,
sql,
getViolatedConstraintNameExtractor().extractConstraintName( sqlException )
);
case 546:
// Foreign key violation
return new ConstraintViolationException(
message,
sqlException,
sql,
getViolatedConstraintNameExtractor().extractConstraintName( sqlException )
);
case 2601: case 2601:
// Unique constraint violation // Unique constraint violation
final String constraintName = getViolatedConstraintNameExtractor().extractConstraintName( return new ConstraintViolationException(
sqlException ); message,
return new ConstraintViolationException( message, sqlException, sql, constraintName ); sqlException,
sql,
ConstraintViolationException.ConstraintKind.UNIQUE,
getViolatedConstraintNameExtractor().extractConstraintName( sqlException )
);
} }
break; break;
case "ZZZZZ": case "ZZZZZ":
if ( 515 == errorCode ) { if ( 515 == errorCode ) {
// Attempt to insert NULL value into column; column does not allow nulls. // Attempt to insert NULL value into column; column does not allow nulls.
final String constraintName = getViolatedConstraintNameExtractor().extractConstraintName( return new ConstraintViolationException(
sqlException ); message,
return new ConstraintViolationException( message, sqlException, sql, constraintName ); sqlException,
} sql,
break; getViolatedConstraintNameExtractor().extractConstraintName( sqlException )
case "23000": );
if ( 546 == errorCode ) {
// Foreign key violation
final String constraintName = getViolatedConstraintNameExtractor().extractConstraintName(
sqlException );
return new ConstraintViolationException( message, sqlException, sql, constraintName );
} }
break; break;
} }

View File

@ -11,14 +11,19 @@ import java.util.function.Consumer;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.LockOptions; import org.hibernate.LockOptions;
import org.hibernate.dialect.DmlTargetColumnQualifierSupport;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.collections.Stack;
import org.hibernate.query.IllegalQueryOperationException;
import org.hibernate.query.sqm.ComparisonOperator; import org.hibernate.query.sqm.ComparisonOperator;
import org.hibernate.sql.ast.Clause; import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.SqlAstJoinType; import org.hibernate.sql.ast.SqlAstJoinType;
import org.hibernate.sql.ast.SqlAstNodeRenderingMode; import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.ast.tree.MutationStatement;
import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.Statement;
import org.hibernate.sql.ast.tree.delete.DeleteStatement;
import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression; import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression;
import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression; import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression;
import org.hibernate.sql.ast.tree.expression.CaseSimpleExpression; import org.hibernate.sql.ast.tree.expression.CaseSimpleExpression;
@ -33,6 +38,9 @@ import org.hibernate.sql.ast.tree.from.NamedTableReference;
import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableGroupJoin; import org.hibernate.sql.ast.tree.from.TableGroupJoin;
import org.hibernate.sql.ast.tree.from.UnionTableReference; import org.hibernate.sql.ast.tree.from.UnionTableReference;
import org.hibernate.sql.ast.tree.from.ValuesTableReference;
import org.hibernate.sql.ast.tree.insert.ConflictClause;
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
import org.hibernate.sql.ast.tree.insert.Values; import org.hibernate.sql.ast.tree.insert.Values;
import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate; import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate;
import org.hibernate.sql.ast.tree.predicate.Predicate; import org.hibernate.sql.ast.tree.predicate.Predicate;
@ -40,6 +48,7 @@ import org.hibernate.sql.ast.tree.select.QueryGroup;
import org.hibernate.sql.ast.tree.select.QueryPart; import org.hibernate.sql.ast.tree.select.QueryPart;
import org.hibernate.sql.ast.tree.select.QuerySpec; import org.hibernate.sql.ast.tree.select.QuerySpec;
import org.hibernate.sql.ast.tree.select.SelectClause; import org.hibernate.sql.ast.tree.select.SelectClause;
import org.hibernate.sql.ast.tree.update.UpdateStatement;
import org.hibernate.sql.exec.spi.JdbcOperation; import org.hibernate.sql.exec.spi.JdbcOperation;
/** /**
@ -55,6 +64,77 @@ public class SybaseASELegacySqlAstTranslator<T extends JdbcOperation> extends Ab
super( sessionFactory, statement ); super( sessionFactory, statement );
} }
@Override
protected void visitInsertStatementOnly(InsertSelectStatement statement) {
if ( statement.getConflictClause() == null || statement.getConflictClause().isDoNothing() ) {
// Render plain insert statement and possibly run into unique constraint violation
super.visitInsertStatementOnly( statement );
}
else {
visitInsertStatementEmulateMerge( statement );
}
}
@Override
protected void renderDeleteClause(DeleteStatement statement) {
appendSql( "delete " );
final Stack<Clause> clauseStack = getClauseStack();
try {
clauseStack.push( Clause.DELETE );
renderDmlTargetTableExpression( statement.getTargetTable() );
if ( statement.getFromClause().getRoots().isEmpty() ) {
appendSql( " from " );
renderDmlTargetTableExpression( statement.getTargetTable() );
renderTableReferenceIdentificationVariable( statement.getTargetTable() );
}
else {
visitFromClause( statement.getFromClause() );
}
}
finally {
clauseStack.pop();
}
}
@Override
protected void renderUpdateClause(UpdateStatement updateStatement) {
appendSql( "update " );
final Stack<Clause> clauseStack = getClauseStack();
try {
clauseStack.push( Clause.UPDATE );
renderDmlTargetTableExpression( updateStatement.getTargetTable() );
}
finally {
clauseStack.pop();
}
}
@Override
protected boolean supportsJoinsInDelete() {
return true;
}
@Override
protected void renderFromClauseAfterUpdateSet(UpdateStatement statement) {
if ( statement.getFromClause().getRoots().isEmpty() ) {
appendSql( " from " );
renderDmlTargetTableExpression( statement.getTargetTable() );
renderTableReferenceIdentificationVariable( statement.getTargetTable() );
}
else {
visitFromClause( statement.getFromClause() );
}
}
@Override
protected void visitConflictClause(ConflictClause conflictClause) {
if ( conflictClause != null ) {
if ( conflictClause.isDoUpdate() && conflictClause.getConstraintName() != null ) {
throw new IllegalQueryOperationException( "Insert conflict do update clause with constraint name is not supported" );
}
}
}
@Override @Override
protected boolean supportsWithClause() { protected boolean supportsWithClause() {
return false; return false;
@ -130,14 +210,7 @@ public class SybaseASELegacySqlAstTranslator<T extends JdbcOperation> extends Ab
appendSql( " )" ); appendSql( " )" );
registerAffectedTable( tableReference ); registerAffectedTable( tableReference );
final Clause currentClause = getClauseStack().getCurrent(); renderTableReferenceIdentificationVariable( tableReference );
if ( rendersTableReferenceAlias( currentClause ) ) {
final String identificationVariable = tableReference.getIdentificationVariable();
if ( identificationVariable != null ) {
appendSql( ' ' );
appendSql( identificationVariable );
}
}
} }
else { else {
super.renderNamedTableReference( tableReference, lockMode ); super.renderNamedTableReference( tableReference, lockMode );
@ -252,6 +325,14 @@ public class SybaseASELegacySqlAstTranslator<T extends JdbcOperation> extends Ab
visitValuesListEmulateSelectUnion( valuesList ); visitValuesListEmulateSelectUnion( valuesList );
} }
@Override
public void visitValuesTableReference(ValuesTableReference tableReference) {
append( '(' );
visitValuesListEmulateSelectUnion( tableReference.getValuesList() );
append( ')' );
renderDerivedTableReference( tableReference );
}
@Override @Override
public void visitOffsetFetchClause(QueryPart queryPart) { public void visitOffsetFetchClause(QueryPart queryPart) {
assertRowsOnlyFetchClauseType( queryPart ); assertRowsOnlyFetchClauseType( queryPart );
@ -386,63 +467,32 @@ public class SybaseASELegacySqlAstTranslator<T extends JdbcOperation> extends Ab
} }
@Override @Override
public void visitColumnReference(ColumnReference columnReference) { protected String determineColumnReferenceQualifier(ColumnReference columnReference) {
final String dmlTargetTableAlias = getDmlTargetTableAlias(); final DmlTargetColumnQualifierSupport qualifierSupport = getDialect().getDmlTargetColumnQualifierSupport();
if ( dmlTargetTableAlias != null && dmlTargetTableAlias.equals( columnReference.getQualifier() ) ) { final MutationStatement currentDmlStatement;
// Sybase needs a table name prefix final String dmlAlias;
// but not if this is a restricted union table reference subquery if ( qualifierSupport == DmlTargetColumnQualifierSupport.TABLE_ALIAS
final QuerySpec currentQuerySpec = (QuerySpec) getQueryPartStack().getCurrent(); || ( currentDmlStatement = getCurrentDmlStatement() ) == null
final List<TableGroup> roots; || ( dmlAlias = currentDmlStatement.getTargetTable().getIdentificationVariable() ) == null
if ( currentQuerySpec != null && !currentQuerySpec.isRoot() || !dmlAlias.equals( columnReference.getQualifier() ) ) {
&& (roots = currentQuerySpec.getFromClause().getRoots()).size() == 1 return columnReference.getQualifier();
&& roots.get( 0 ).getPrimaryTableReference() instanceof UnionTableReference ) { }
columnReference.appendReadExpression( this ); // Sybase needs a table name prefix
} // but not if this is a restricted union table reference subquery
// for now, use the unqualified form final QuerySpec currentQuerySpec = (QuerySpec) getQueryPartStack().getCurrent();
else if ( columnReference.isColumnExpressionFormula() ) { final List<TableGroup> roots;
// For formulas, we have to replace the qualifier as the alias was already rendered into the formula if ( currentQuerySpec != null && !currentQuerySpec.isRoot()
// This is fine for now as this is only temporary anyway until we render aliases for table references && (roots = currentQuerySpec.getFromClause().getRoots()).size() == 1
appendSql( && roots.get( 0 ).getPrimaryTableReference() instanceof UnionTableReference ) {
columnReference.getColumnExpression() return columnReference.getQualifier();
.replaceAll( "(\\b)(" + dmlTargetTableAlias + "\\.)(\\b)", "$1$3" ) }
); else if ( columnReference.isColumnExpressionFormula() ) {
} // For formulas, we have to replace the qualifier as the alias was already rendered into the formula
else { // This is fine for now as this is only temporary anyway until we render aliases for table references
columnReference.appendReadExpression( return null;
this,
getCurrentDmlStatement().getTargetTable().getTableExpression()
);
}
} }
else { else {
columnReference.appendReadExpression( this ); return getCurrentDmlStatement().getTargetTable().getTableExpression();
}
}
@Override
public void visitAggregateColumnWriteExpression(AggregateColumnWriteExpression aggregateColumnWriteExpression) {
final String dmlTargetTableAlias = getDmlTargetTableAlias();
final ColumnReference columnReference = aggregateColumnWriteExpression.getColumnReference();
if ( dmlTargetTableAlias != null && dmlTargetTableAlias.equals( columnReference.getQualifier() ) ) {
// Sybase needs a table name prefix
// but not if this is a restricted union table reference subquery
final QuerySpec currentQuerySpec = (QuerySpec) getQueryPartStack().getCurrent();
final List<TableGroup> roots;
if ( currentQuerySpec != null && !currentQuerySpec.isRoot()
&& (roots = currentQuerySpec.getFromClause().getRoots()).size() == 1
&& roots.get( 0 ).getPrimaryTableReference() instanceof UnionTableReference ) {
aggregateColumnWriteExpression.appendWriteExpression( this, this );
}
else {
aggregateColumnWriteExpression.appendWriteExpression(
this,
this,
getCurrentDmlStatement().getTargetTable().getTableExpression()
);
}
}
else {
aggregateColumnWriteExpression.appendWriteExpression( this, this );
} }
} }
@ -472,8 +522,8 @@ public class SybaseASELegacySqlAstTranslator<T extends JdbcOperation> extends Ab
} }
@Override @Override
protected String getFromDual() { protected String getDual() {
return " from (select 1) dual(c1)"; return "(select 1 c1)";
} }
private boolean supportsTopClause() { private boolean supportsTopClause() {

View File

@ -116,14 +116,7 @@ public class SybaseAnywhereSqlAstTranslator<T extends JdbcOperation> extends Abs
appendSql( " )" ); appendSql( " )" );
registerAffectedTable( tableReference ); registerAffectedTable( tableReference );
final Clause currentClause = getClauseStack().getCurrent(); renderTableReferenceIdentificationVariable( tableReference );
if ( rendersTableReferenceAlias( currentClause ) ) {
final String identificationVariable = tableReference.getIdentificationVariable();
if ( identificationVariable != null ) {
appendSql( ' ' );
appendSql( identificationVariable );
}
}
} }
else { else {
super.renderNamedTableReference( tableReference, lockMode ); super.renderNamedTableReference( tableReference, lockMode );
@ -248,12 +241,12 @@ public class SybaseAnywhereSqlAstTranslator<T extends JdbcOperation> extends Abs
} }
@Override @Override
protected String getFromDual() { protected String getDual() {
return " from sys.dummy"; return "sys.dummy";
} }
@Override @Override
protected String getFromDualForSelectOnly() { protected String getFromDualForSelectOnly() {
return getFromDual(); return " from " + getDual();
} }
} }

View File

@ -18,6 +18,7 @@ import org.hibernate.boot.model.FunctionContributions;
import org.hibernate.boot.model.TypeContributions; import org.hibernate.boot.model.TypeContributions;
import org.hibernate.dialect.AbstractTransactSQLDialect; import org.hibernate.dialect.AbstractTransactSQLDialect;
import org.hibernate.dialect.DatabaseVersion; import org.hibernate.dialect.DatabaseVersion;
import org.hibernate.dialect.DmlTargetColumnQualifierSupport;
import org.hibernate.dialect.NationalizationSupport; import org.hibernate.dialect.NationalizationSupport;
import org.hibernate.dialect.SybaseDriverKind; import org.hibernate.dialect.SybaseDriverKind;
import org.hibernate.dialect.function.CommonFunctionFactory; import org.hibernate.dialect.function.CommonFunctionFactory;
@ -473,4 +474,14 @@ public class SybaseLegacyDialect extends AbstractTransactSQLDialect {
// Only the jTDS driver supports named parameters properly // Only the jTDS driver supports named parameters properly
return driverKind == SybaseDriverKind.JTDS && super.supportsNamedParameters( databaseMetaData ); return driverKind == SybaseDriverKind.JTDS && super.supportsNamedParameters( databaseMetaData );
} }
@Override
public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() {
return DmlTargetColumnQualifierSupport.TABLE_ALIAS;
}
@Override
public boolean supportsFromClauseInUpdate() {
return true;
}
} }

View File

@ -11,6 +11,8 @@ import java.util.function.Consumer;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.collections.Stack;
import org.hibernate.query.IllegalQueryOperationException;
import org.hibernate.query.sqm.ComparisonOperator; import org.hibernate.query.sqm.ComparisonOperator;
import org.hibernate.sql.ast.Clause; import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.SqlAstNodeRenderingMode; import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
@ -18,6 +20,7 @@ import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.Statement;
import org.hibernate.sql.ast.tree.cte.CteStatement; import org.hibernate.sql.ast.tree.cte.CteStatement;
import org.hibernate.sql.ast.tree.delete.DeleteStatement;
import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression; import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression;
import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression; import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression;
import org.hibernate.sql.ast.tree.expression.CaseSimpleExpression; import org.hibernate.sql.ast.tree.expression.CaseSimpleExpression;
@ -27,8 +30,13 @@ import org.hibernate.sql.ast.tree.expression.SqlTuple;
import org.hibernate.sql.ast.tree.expression.Summarization; import org.hibernate.sql.ast.tree.expression.Summarization;
import org.hibernate.sql.ast.tree.from.NamedTableReference; import org.hibernate.sql.ast.tree.from.NamedTableReference;
import org.hibernate.sql.ast.tree.from.UnionTableReference; import org.hibernate.sql.ast.tree.from.UnionTableReference;
import org.hibernate.sql.ast.tree.from.ValuesTableReference;
import org.hibernate.sql.ast.tree.insert.ConflictClause;
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
import org.hibernate.sql.ast.tree.insert.Values;
import org.hibernate.sql.ast.tree.select.QueryPart; import org.hibernate.sql.ast.tree.select.QueryPart;
import org.hibernate.sql.ast.tree.select.QuerySpec; import org.hibernate.sql.ast.tree.select.QuerySpec;
import org.hibernate.sql.ast.tree.update.UpdateStatement;
import org.hibernate.sql.exec.spi.JdbcOperation; import org.hibernate.sql.exec.spi.JdbcOperation;
/** /**
@ -44,6 +52,46 @@ public class SybaseLegacySqlAstTranslator<T extends JdbcOperation> extends Abstr
super( sessionFactory, statement ); super( sessionFactory, statement );
} }
@Override
protected void visitInsertStatementOnly(InsertSelectStatement statement) {
if ( statement.getConflictClause() == null || statement.getConflictClause().isDoNothing() ) {
// Render plain insert statement and possibly run into unique constraint violation
super.visitInsertStatementOnly( statement );
}
else {
visitInsertStatementEmulateMerge( statement );
appendSql( ';' );
}
}
@Override
protected void renderDeleteClause(DeleteStatement statement) {
appendSql( "delete " );
final Stack<Clause> clauseStack = getClauseStack();
try {
clauseStack.push( Clause.DELETE );
renderDmlTargetTableExpression( statement.getTargetTable() );
}
finally {
clauseStack.pop();
}
visitFromClause( statement.getFromClause() );
}
@Override
protected void renderFromClauseAfterUpdateSet(UpdateStatement statement) {
visitFromClause( statement.getFromClause() );
}
@Override
protected void visitConflictClause(ConflictClause conflictClause) {
if ( conflictClause != null ) {
if ( conflictClause.isDoUpdate() && conflictClause.getConstraintName() != null ) {
throw new IllegalQueryOperationException( "Insert conflict do update clause with constraint name is not supported" );
}
}
}
@Override @Override
protected boolean supportsWithClause() { protected boolean supportsWithClause() {
return false; return false;
@ -119,14 +167,7 @@ public class SybaseLegacySqlAstTranslator<T extends JdbcOperation> extends Abstr
appendSql( " )" ); appendSql( " )" );
registerAffectedTable( tableReference ); registerAffectedTable( tableReference );
final Clause currentClause = getClauseStack().getCurrent(); renderTableReferenceIdentificationVariable( tableReference );
if ( rendersTableReferenceAlias( currentClause ) ) {
final String identificationVariable = tableReference.getIdentificationVariable();
if ( identificationVariable != null ) {
appendSql( ' ' );
appendSql( identificationVariable );
}
}
} }
else { else {
super.renderNamedTableReference( tableReference, lockMode ); super.renderNamedTableReference( tableReference, lockMode );
@ -147,6 +188,19 @@ public class SybaseLegacySqlAstTranslator<T extends JdbcOperation> extends Abstr
// Sybase does not support the FOR UPDATE clause // Sybase does not support the FOR UPDATE clause
} }
@Override
protected void visitValuesList(List<Values> valuesList) {
visitValuesListEmulateSelectUnion( valuesList );
}
@Override
public void visitValuesTableReference(ValuesTableReference tableReference) {
append( '(' );
visitValuesListEmulateSelectUnion( tableReference.getValuesList() );
append( ')' );
renderDerivedTableReference( tableReference );
}
@Override @Override
public void visitOffsetFetchClause(QueryPart queryPart) { public void visitOffsetFetchClause(QueryPart queryPart) {
assertRowsOnlyFetchClauseType( queryPart ); assertRowsOnlyFetchClauseType( queryPart );

View File

@ -158,6 +158,8 @@ BY : [bB] [yY];
CASE : [cC] [aA] [sS] [eE]; CASE : [cC] [aA] [sS] [eE];
CAST : [cC] [aA] [sS] [tT]; CAST : [cC] [aA] [sS] [tT];
COLLATE : [cC] [oO] [lL] [lL] [aA] [tT] [eE]; COLLATE : [cC] [oO] [lL] [lL] [aA] [tT] [eE];
CONFLICT : [cC] [oO] [nN] [fF] [lL] [iI] [cC] [tT];
CONSTRAINT : [cC] [oO] [nN] [sS] [tT] [rR] [aA] [iI] [nN] [tT];
COUNT : [cC] [oO] [uU] [nN] [tT]; COUNT : [cC] [oO] [uU] [nN] [tT];
CROSS : [cC] [rR] [oO] [sS] [sS]; CROSS : [cC] [rR] [oO] [sS] [sS];
CUBE : [cC] [uU] [bB] [eE]; CUBE : [cC] [uU] [bB] [eE];
@ -175,6 +177,7 @@ DELETE : [dD] [eE] [lL] [eE] [tT] [eE];
DEPTH : [dD] [eE] [pP] [tT] [hH]; DEPTH : [dD] [eE] [pP] [tT] [hH];
DESC : [dD] [eE] [sS] [cC]; DESC : [dD] [eE] [sS] [cC];
DISTINCT : [dD] [iI] [sS] [tT] [iI] [nN] [cC] [tT]; DISTINCT : [dD] [iI] [sS] [tT] [iI] [nN] [cC] [tT];
DO : [dD] [oO];
ELEMENT : [eE] [lL] [eE] [mM] [eE] [nN] [tT]; ELEMENT : [eE] [lL] [eE] [mM] [eE] [nN] [tT];
ELEMENTS : [eE] [lL] [eE] [mM] [eE] [nN] [tT] [sS]; ELEMENTS : [eE] [lL] [eE] [mM] [eE] [nN] [tT] [sS];
ELSE : [eE] [lL] [sS] [eE]; ELSE : [eE] [lL] [sS] [eE];
@ -246,6 +249,7 @@ NEW : [nN] [eE] [wW];
NEXT : [nN] [eE] [xX] [tT]; NEXT : [nN] [eE] [xX] [tT];
NO : [nN] [oO]; NO : [nN] [oO];
NOT : [nN] [oO] [tT]; NOT : [nN] [oO] [tT];
NOTHING : [nN] [oO] [tT] [hH] [iI] [nN] [gG];
NULLS : [nN] [uU] [lL] [lL] [sS]; NULLS : [nN] [uU] [lL] [lL] [sS];
OBJECT : [oO] [bB] [jJ] [eE] [cC] [tT]; OBJECT : [oO] [bB] [jJ] [eE] [cC] [tT];
OF : [oO] [fF]; OF : [oO] [fF];

View File

@ -83,7 +83,7 @@ assignment
* An 'insert' statement * An 'insert' statement
*/ */
insertStatement insertStatement
: INSERT INTO? targetEntity targetFields (queryExpression | valuesList) : INSERT INTO? targetEntity targetFields (queryExpression | valuesList) conflictClause?
; ;
/** /**
@ -107,6 +107,18 @@ values
: LEFT_PAREN expressionOrPredicate (COMMA expressionOrPredicate)* RIGHT_PAREN : LEFT_PAREN expressionOrPredicate (COMMA expressionOrPredicate)* RIGHT_PAREN
; ;
/**
* a 'conflict' clause in an 'insert' statement
*/
conflictClause: ON CONFLICT conflictTarget? conflictAction;
conflictTarget
: ON CONSTRAINT identifier
| LEFT_PAREN simplePath (COMMA simplePath)* RIGHT_PAREN;
conflictAction
: DO NOTHING
| DO UPDATE setClause whereClause?
;
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// QUERY SPEC - general structure of root sqm or sub sqm // QUERY SPEC - general structure of root sqm or sub sqm
@ -1600,6 +1612,8 @@ rollup
| CASE | CASE
| CAST | CAST
| COLLATE | COLLATE
| CONFLICT
| CONSTRAINT
| COUNT | COUNT
| CROSS | CROSS
| CUBE | CUBE
@ -1617,6 +1631,7 @@ rollup
| DEPTH | DEPTH
| DESC | DESC
| DISTINCT | DISTINCT
| DO
| ELEMENT | ELEMENT
| ELEMENTS | ELEMENTS
| ELSE | ELSE
@ -1690,6 +1705,7 @@ rollup
| NEXT | NEXT
| NO | NO
| NOT | NOT
| NOTHING
| NULLS | NULLS
| OBJECT | OBJECT
| OF | OF

View File

@ -21,6 +21,7 @@ import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.H2Dialect; import org.hibernate.dialect.H2Dialect;
import org.hibernate.dialect.HANACloudColumnStoreDialect; import org.hibernate.dialect.HANACloudColumnStoreDialect;
import org.hibernate.dialect.HANAColumnStoreDialect; import org.hibernate.dialect.HANAColumnStoreDialect;
import org.hibernate.dialect.HANADialect;
import org.hibernate.dialect.HANARowStoreDialect; import org.hibernate.dialect.HANARowStoreDialect;
import org.hibernate.dialect.HSQLDialect; import org.hibernate.dialect.HSQLDialect;
import org.hibernate.dialect.MariaDBDialect; import org.hibernate.dialect.MariaDBDialect;
@ -70,6 +71,8 @@ public class DefaultDialectSelector implements DialectSelector {
return findCommunityDialect( name ); return findCommunityDialect( name );
case "H2": case "H2":
return H2Dialect.class; return H2Dialect.class;
case "HANA":
return HANADialect.class;
case "HANACloudColumnStore": case "HANACloudColumnStore":
return HANACloudColumnStoreDialect.class; return HANACloudColumnStoreDialect.class;
case "HANAColumnStore": case "HANAColumnStore":

View File

@ -57,4 +57,12 @@ public interface DialectSpecificSettings {
*/ */
public static final String COCKROACH_VERSION_STRING = "hibernate.dialect.cockroach.version_string"; public static final String COCKROACH_VERSION_STRING = "hibernate.dialect.cockroach.version_string";
/**
* Specifies the LOB prefetch size. LOBs larger than this value will be read into memory as the HANA JDBC driver closes
* the LOB when the result set is closed.
*
* @settingDefault {@code 1024}
*/
public static final String HANA_MAX_LOB_PREFETCH_SIZE = "hibernate.dialect.hana.max_lob_prefetch_size";
} }

View File

@ -20,20 +20,20 @@ import java.nio.charset.StandardCharsets;
import java.sql.Blob; import java.sql.Blob;
import java.sql.CallableStatement; import java.sql.CallableStatement;
import java.sql.Clob; import java.sql.Clob;
import java.sql.Connection;
import java.sql.DatabaseMetaData; import java.sql.DatabaseMetaData;
import java.sql.NClob; import java.sql.NClob;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException; import java.sql.SQLFeatureNotSupportedException;
import java.sql.Statement;
import java.sql.Types; import java.sql.Types;
import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAccessor;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date; import java.util.Date;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone; import java.util.TimeZone;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -54,6 +54,8 @@ import org.hibernate.dialect.pagination.LimitHandler;
import org.hibernate.dialect.pagination.LimitOffsetLimitHandler; import org.hibernate.dialect.pagination.LimitOffsetLimitHandler;
import org.hibernate.dialect.sequence.HANASequenceSupport; import org.hibernate.dialect.sequence.HANASequenceSupport;
import org.hibernate.dialect.sequence.SequenceSupport; import org.hibernate.dialect.sequence.SequenceSupport;
import org.hibernate.dialect.temptable.TemporaryTable;
import org.hibernate.dialect.temptable.TemporaryTableKind;
import org.hibernate.engine.config.spi.ConfigurationService; import org.hibernate.engine.config.spi.ConfigurationService;
import org.hibernate.engine.config.spi.StandardConverters; import org.hibernate.engine.config.spi.StandardConverters;
import org.hibernate.engine.jdbc.BinaryStream; import org.hibernate.engine.jdbc.BinaryStream;
@ -61,7 +63,6 @@ import org.hibernate.engine.jdbc.BlobImplementer;
import org.hibernate.engine.jdbc.CharacterStream; import org.hibernate.engine.jdbc.CharacterStream;
import org.hibernate.engine.jdbc.ClobImplementer; import org.hibernate.engine.jdbc.ClobImplementer;
import org.hibernate.engine.jdbc.NClobImplementer; import org.hibernate.engine.jdbc.NClobImplementer;
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo; import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
import org.hibernate.engine.jdbc.env.spi.IdentifierCaseStrategy; import org.hibernate.engine.jdbc.env.spi.IdentifierCaseStrategy;
import org.hibernate.engine.jdbc.env.spi.IdentifierHelper; import org.hibernate.engine.jdbc.env.spi.IdentifierHelper;
@ -73,15 +74,19 @@ import org.hibernate.exception.LockAcquisitionException;
import org.hibernate.exception.LockTimeoutException; import org.hibernate.exception.LockTimeoutException;
import org.hibernate.exception.SQLGrammarException; import org.hibernate.exception.SQLGrammarException;
import org.hibernate.exception.spi.SQLExceptionConversionDelegate; import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.JdbcExceptionHelper; import org.hibernate.internal.util.JdbcExceptionHelper;
import org.hibernate.mapping.Table; import org.hibernate.mapping.Table;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
import org.hibernate.procedure.internal.StandardCallableStatementSupport; import org.hibernate.procedure.internal.StandardCallableStatementSupport;
import org.hibernate.procedure.spi.CallableStatementSupport; import org.hibernate.procedure.spi.CallableStatementSupport;
import org.hibernate.query.sqm.CastType; import org.hibernate.query.sqm.CastType;
import org.hibernate.query.sqm.IntervalType; import org.hibernate.query.sqm.IntervalType;
import org.hibernate.query.sqm.TemporalUnit; import org.hibernate.query.sqm.TemporalUnit;
import org.hibernate.query.sqm.mutation.internal.temptable.GlobalTemporaryTableInsertStrategy;
import org.hibernate.query.sqm.mutation.internal.temptable.GlobalTemporaryTableMutationStrategy;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.query.sqm.produce.function.FunctionParameterType; import org.hibernate.query.sqm.produce.function.FunctionParameterType;
import org.hibernate.service.ServiceRegistry; import org.hibernate.service.ServiceRegistry;
import org.hibernate.sql.ast.SqlAstNodeRenderingMode; import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
@ -121,6 +126,7 @@ import org.hibernate.type.spi.TypeConfiguration;
import jakarta.persistence.TemporalType; import jakarta.persistence.TemporalType;
import static org.hibernate.query.sqm.produce.function.FunctionParameterType.ANY;
import static org.hibernate.type.SqlTypes.BINARY; import static org.hibernate.type.SqlTypes.BINARY;
import static org.hibernate.type.SqlTypes.BOOLEAN; import static org.hibernate.type.SqlTypes.BOOLEAN;
import static org.hibernate.type.SqlTypes.CHAR; import static org.hibernate.type.SqlTypes.CHAR;
@ -160,15 +166,15 @@ import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithM
* <p> * <p>
* Note: This dialect is configured to create foreign keys with {@code on update cascade}. * Note: This dialect is configured to create foreign keys with {@code on update cascade}.
* *
* @deprecated Will be replaced with {@link HANADialect} in the future.
* @author Andrew Clemons * @author Andrew Clemons
* @author Jonathan Bregler * @author Jonathan Bregler
*/ */
@Deprecated(forRemoval = true)
public abstract class AbstractHANADialect extends Dialect { public abstract class AbstractHANADialect extends Dialect {
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( AbstractHANADialect.class );
// Set the LOB prefetch size. LOBs larger than this value will be read into memory as the HANA JDBC driver closes // Use column or row tables by default
// the LOB when the result set is closed. public static final String USE_DEFAULT_TABLE_TYPE_COLUMN = "hibernate.dialect.hana.use_default_table_type_column";
private static final String MAX_LOB_PREFETCH_SIZE_PARAMETER_NAME = "hibernate.dialect.hana.max_lob_prefetch_size";
// Use TINYINT instead of the native BOOLEAN type // Use TINYINT instead of the native BOOLEAN type
private static final String USE_LEGACY_BOOLEAN_TYPE_PARAMETER_NAME = "hibernate.dialect.hana.use_legacy_boolean_type"; private static final String USE_LEGACY_BOOLEAN_TYPE_PARAMETER_NAME = "hibernate.dialect.hana.use_legacy_boolean_type";
// Use unicode (NVARCHAR, NCLOB, etc.) instead of non-unicode (VARCHAR, CLOB) string types // Use unicode (NVARCHAR, NCLOB, etc.) instead of non-unicode (VARCHAR, CLOB) string types
@ -177,18 +183,15 @@ public abstract class AbstractHANADialect extends Dialect {
// JDBC driver (https://service.sap.com/sap/support/notes/2590160) // JDBC driver (https://service.sap.com/sap/support/notes/2590160)
private static final String TREAT_DOUBLE_TYPED_FIELDS_AS_DECIMAL_PARAMETER_NAME = "hibernate.dialect.hana.treat_double_typed_fields_as_decimal"; private static final String TREAT_DOUBLE_TYPED_FIELDS_AS_DECIMAL_PARAMETER_NAME = "hibernate.dialect.hana.treat_double_typed_fields_as_decimal";
private static final int MAX_LOB_PREFETCH_SIZE_DEFAULT_VALUE = 1024;
private static final Boolean USE_LEGACY_BOOLEAN_TYPE_DEFAULT_VALUE = Boolean.FALSE; private static final Boolean USE_LEGACY_BOOLEAN_TYPE_DEFAULT_VALUE = Boolean.FALSE;
private static final Boolean TREAT_DOUBLE_TYPED_FIELDS_AS_DECIMAL_DEFAULT_VALUE = Boolean.FALSE; private static final Boolean TREAT_DOUBLE_TYPED_FIELDS_AS_DECIMAL_DEFAULT_VALUE = Boolean.FALSE;
private HANANClobJdbcType nClobTypeDescriptor = new HANANClobJdbcType( MAX_LOB_PREFETCH_SIZE_DEFAULT_VALUE ); private final int maxLobPrefetchSize;
private HANABlobType blobTypeDescriptor = new HANABlobType( MAX_LOB_PREFETCH_SIZE_DEFAULT_VALUE );
private HANAClobJdbcType clobTypeDescriptor;
private boolean defaultTableTypeColumn;
private boolean useLegacyBooleanType = USE_LEGACY_BOOLEAN_TYPE_DEFAULT_VALUE; private boolean useLegacyBooleanType = USE_LEGACY_BOOLEAN_TYPE_DEFAULT_VALUE;
private boolean useUnicodeStringTypes; private boolean useUnicodeStringTypes;
private boolean treatDoubleTypedFieldsAsDecimal;
/* /*
* Tables named "TYPE" need to be quoted * Tables named "TYPE" need to be quoted
@ -232,14 +235,56 @@ public abstract class AbstractHANADialect extends Dialect {
} }
}; };
public AbstractHANADialect(DatabaseVersion version) {
super( version );
public AbstractHANADialect(DatabaseVersion version) {
this( new HANAServerConfiguration( version ), true );
}
public AbstractHANADialect(HANAServerConfiguration configuration, boolean defaultTableTypeColumn) {
super( configuration.getFullVersion() );
this.defaultTableTypeColumn = defaultTableTypeColumn;
this.maxLobPrefetchSize = configuration.getMaxLobPrefetchSize();
this.useUnicodeStringTypes = useUnicodeStringTypesDefault(); this.useUnicodeStringTypes = useUnicodeStringTypesDefault();
this.clobTypeDescriptor = new HANAClobJdbcType( }
MAX_LOB_PREFETCH_SIZE_DEFAULT_VALUE,
useUnicodeStringTypesDefault() @Override
public void contribute(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
// This is the best hook for consuming dialect configuration that we have for now,
// since this method is called very early in the bootstrap process
final ConfigurationService configurationService = serviceRegistry.getService( ConfigurationService.class );
assert configurationService != null;
this.defaultTableTypeColumn = configurationService.getSetting(
USE_DEFAULT_TABLE_TYPE_COLUMN,
StandardConverters.BOOLEAN,
this.defaultTableTypeColumn
); );
if ( supportsAsciiStringTypes() ) {
this.useUnicodeStringTypes = configurationService.getSetting(
USE_UNICODE_STRING_TYPES_PARAMETER_NAME,
StandardConverters.BOOLEAN,
useUnicodeStringTypesDefault()
);
}
this.useLegacyBooleanType = configurationService.getSetting(
USE_LEGACY_BOOLEAN_TYPE_PARAMETER_NAME,
StandardConverters.BOOLEAN,
USE_LEGACY_BOOLEAN_TYPE_DEFAULT_VALUE
);
this.treatDoubleTypedFieldsAsDecimal = configurationService.getSetting(
TREAT_DOUBLE_TYPED_FIELDS_AS_DECIMAL_PARAMETER_NAME,
StandardConverters.BOOLEAN,
TREAT_DOUBLE_TYPED_FIELDS_AS_DECIMAL_DEFAULT_VALUE
);
super.contribute( typeContributions, serviceRegistry );
}
protected boolean isDefaultTableTypeColumn() {
return defaultTableTypeColumn;
}
protected boolean isCloud() {
return getVersion().isSameOrAfter( 4 );
} }
@Override @Override
@ -280,20 +325,6 @@ public abstract class AbstractHANADialect extends Dialect {
@Override @Override
protected void registerColumnTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) { protected void registerColumnTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
final ConfigurationService configurationService = serviceRegistry.getService( ConfigurationService.class );
if ( supportsAsciiStringTypes() ) {
this.useUnicodeStringTypes = configurationService.getSetting(
USE_UNICODE_STRING_TYPES_PARAMETER_NAME,
StandardConverters.BOOLEAN,
useUnicodeStringTypesDefault()
);
}
this.useLegacyBooleanType = configurationService.getSetting(
USE_LEGACY_BOOLEAN_TYPE_PARAMETER_NAME,
StandardConverters.BOOLEAN,
USE_LEGACY_BOOLEAN_TYPE_DEFAULT_VALUE
);
super.registerColumnTypes( typeContributions, serviceRegistry ); super.registerColumnTypes( typeContributions, serviceRegistry );
final DdlTypeRegistry ddlTypeRegistry = typeContributions.getTypeConfiguration().getDdlTypeRegistry(); final DdlTypeRegistry ddlTypeRegistry = typeContributions.getTypeConfiguration().getDdlTypeRegistry();
@ -320,24 +351,12 @@ public abstract class AbstractHANADialect extends Dialect {
return false; return false;
} }
/**
* @deprecated Use {@link HANAServerConfiguration#fromDialectResolutionInfo(DialectResolutionInfo)} instead
*/
@Deprecated(forRemoval = true)
protected static DatabaseVersion createVersion(DialectResolutionInfo info) { protected static DatabaseVersion createVersion(DialectResolutionInfo info) {
// Parse the version according to https://answers.sap.com/questions/9760991/hana-sps-version-check.html return HANAServerConfiguration.fromDialectResolutionInfo( info ).getFullVersion();
final String versionString = info.getDatabaseVersion();
int majorVersion = 1;
int minorVersion = 0;
int patchLevel = 0;
final String[] components = versionString.split( "\\." );
if ( components.length >= 3 ) {
try {
majorVersion = Integer.parseInt( components[0] );
minorVersion = Integer.parseInt( components[1] );
patchLevel = Integer.parseInt( components[2] );
}
catch (NumberFormatException ex) {
// Ignore
}
}
return DatabaseVersion.make( majorVersion, minorVersion, patchLevel );
} }
@Override @Override
@ -434,6 +453,22 @@ public abstract class AbstractHANADialect extends Dialect {
functionContributions.getFunctionRegistry().register( "timestampadd", functionContributions.getFunctionRegistry().register( "timestampadd",
new IntegralTimestampaddFunction( this, typeConfiguration ) ); new IntegralTimestampaddFunction( this, typeConfiguration ) );
// full-text search functions
functionContributions.getFunctionRegistry().registerNamed(
"score",
typeConfiguration.getBasicTypeRegistry().resolve( StandardBasicTypes.DOUBLE )
);
functionContributions.getFunctionRegistry().registerNamed( "snippets" );
functionContributions.getFunctionRegistry().registerNamed( "highlighted" );
functionContributions.getFunctionRegistry().registerBinaryTernaryPattern(
"contains",
typeConfiguration.getBasicTypeRegistry().resolve( StandardBasicTypes.BOOLEAN ),
"contains(?1,?2)",
"contains(?1,?2,?3)",
ANY, ANY, ANY,
typeConfiguration
);
} }
@Override @Override
@ -526,7 +561,15 @@ public abstract class AbstractHANADialect extends Dialect {
final String constraintName = getViolatedConstraintNameExtractor() final String constraintName = getViolatedConstraintNameExtractor()
.extractConstraintName( sqlException ); .extractConstraintName( sqlException );
return new ConstraintViolationException( message, sqlException, sql, constraintName ); return new ConstraintViolationException(
message,
sqlException,
sql,
errorCode == 301
? ConstraintViolationException.ConstraintKind.UNIQUE
: ConstraintViolationException.ConstraintKind.OTHER,
constraintName
);
} }
return null; return null;
@ -538,6 +581,11 @@ public abstract class AbstractHANADialect extends Dialect {
return RowLockStrategy.COLUMN; return RowLockStrategy.COLUMN;
} }
@Override
public String getCreateTableString() {
return isDefaultTableTypeColumn() ? "create column table" : "create row table";
}
@Override @Override
public String getAddColumnString() { public String getAddColumnString() {
return "add ("; return "add (";
@ -621,6 +669,7 @@ public abstract class AbstractHANADialect extends Dialect {
@Override @Override
protected void registerDefaultKeywords() { protected void registerDefaultKeywords() {
super.registerDefaultKeywords(); super.registerDefaultKeywords();
// https://help.sap.com/docs/SAP_HANA_PLATFORM/4fe29514fd584807ac9f2a04f6754767/28bcd6af3eb6437892719f7c27a8a285.html?locale=en-US
registerKeyword( "all" ); registerKeyword( "all" );
registerKeyword( "alter" ); registerKeyword( "alter" );
registerKeyword( "as" ); registerKeyword( "as" );
@ -668,6 +717,7 @@ public abstract class AbstractHANADialect extends Dialect {
registerKeyword( "into" ); registerKeyword( "into" );
registerKeyword( "is" ); registerKeyword( "is" );
registerKeyword( "join" ); registerKeyword( "join" );
registerKeyword( "lateral" );
registerKeyword( "leading" ); registerKeyword( "leading" );
registerKeyword( "left" ); registerKeyword( "left" );
registerKeyword( "limit" ); registerKeyword( "limit" );
@ -706,6 +756,29 @@ public abstract class AbstractHANADialect extends Dialect {
registerKeyword( "where" ); registerKeyword( "where" );
registerKeyword( "while" ); registerKeyword( "while" );
registerKeyword( "with" ); registerKeyword( "with" );
if ( isCloud() ) {
// https://help.sap.com/docs/hana-cloud-database/sap-hana-cloud-sap-hana-database-sql-reference-guide/reserved-words
registerKeyword( "array" );
registerKeyword( "at" );
registerKeyword( "authorization" );
registerKeyword( "between" );
registerKeyword( "by" );
registerKeyword( "collate" );
registerKeyword( "empty" );
registerKeyword( "filter" );
registerKeyword( "grouping" );
registerKeyword( "no" );
registerKeyword( "not" );
registerKeyword( "of" );
registerKeyword( "over" );
registerKeyword( "recursive" );
registerKeyword( "row" );
registerKeyword( "table" );
registerKeyword( "to" );
registerKeyword( "unnest" );
registerKeyword( "window" );
registerKeyword( "within" );
}
} }
@Override @Override
@ -930,127 +1003,40 @@ public abstract class AbstractHANADialect extends Dialect {
public void contributeTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) { public void contributeTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
super.contributeTypes( typeContributions, serviceRegistry ); super.contributeTypes( typeContributions, serviceRegistry );
final ConnectionProvider connectionProvider = serviceRegistry.getService( ConnectionProvider.class ); final TypeConfiguration typeConfiguration = typeContributions.getTypeConfiguration();
final JdbcTypeRegistry jdbcTypeRegistry = typeConfiguration.getJdbcTypeRegistry();
int maxLobPrefetchSizeDefault = MAX_LOB_PREFETCH_SIZE_DEFAULT_VALUE; if ( treatDoubleTypedFieldsAsDecimal ) {
if ( connectionProvider != null ) { typeConfiguration.getBasicTypeRegistry()
Connection conn = null;
try {
conn = connectionProvider.getConnection();
try ( Statement statement = conn.createStatement() ) {
try ( ResultSet rs = statement.executeQuery(
"SELECT TOP 1 VALUE,MAP(LAYER_NAME,'DEFAULT',1,'SYSTEM',2,'DATABASE',3,4) AS LAYER FROM SYS.M_INIFILE_CONTENTS WHERE FILE_NAME='indexserver.ini' AND SECTION='session' AND KEY='max_lob_prefetch_size' ORDER BY LAYER DESC" ) ) {
// This only works if the current user has the privilege INIFILE ADMIN
if ( rs.next() ) {
maxLobPrefetchSizeDefault = rs.getInt( 1 );
}
}
}
}
catch (Exception e) {
LOG.debug(
"An error occurred while trying to determine the value of the HANA parameter indexserver.ini / session / max_lob_prefetch_size. Using the default value "
+ maxLobPrefetchSizeDefault,
e );
}
finally {
if ( conn != null ) {
try {
connectionProvider.closeConnection( conn );
}
catch (SQLException e) {
// ignore
}
}
}
}
final ConfigurationService configurationService = serviceRegistry.getService( ConfigurationService.class );
int maxLobPrefetchSize = configurationService.getSetting(
MAX_LOB_PREFETCH_SIZE_PARAMETER_NAME,
value -> Integer.valueOf( value.toString() ),
maxLobPrefetchSizeDefault
);
if ( this.nClobTypeDescriptor.getMaxLobPrefetchSize() != maxLobPrefetchSize ) {
this.nClobTypeDescriptor = new HANANClobJdbcType( maxLobPrefetchSize );
}
if ( this.blobTypeDescriptor.getMaxLobPrefetchSize() != maxLobPrefetchSize ) {
this.blobTypeDescriptor = new HANABlobType( maxLobPrefetchSize );
}
if ( supportsAsciiStringTypes() ) {
if ( this.clobTypeDescriptor.getMaxLobPrefetchSize() != maxLobPrefetchSize
|| this.clobTypeDescriptor.isUseUnicodeStringTypes() != this.useUnicodeStringTypes ) {
this.clobTypeDescriptor = new HANAClobJdbcType( maxLobPrefetchSize, this.useUnicodeStringTypes );
}
}
boolean treatDoubleTypedFieldsAsDecimal = configurationService.getSetting(TREAT_DOUBLE_TYPED_FIELDS_AS_DECIMAL_PARAMETER_NAME, StandardConverters.BOOLEAN,
TREAT_DOUBLE_TYPED_FIELDS_AS_DECIMAL_DEFAULT_VALUE);
final JdbcTypeRegistry jdbcTypeRegistry = typeContributions.getTypeConfiguration()
.getJdbcTypeRegistry();
if (treatDoubleTypedFieldsAsDecimal) {
typeContributions.getTypeConfiguration().getBasicTypeRegistry()
.register( .register(
new BasicTypeImpl<>( new BasicTypeImpl<>( DoubleJavaType.INSTANCE, NumericJdbcType.INSTANCE ),
DoubleJavaType.INSTANCE,
NumericJdbcType.INSTANCE
),
Double.class.getName() Double.class.getName()
); );
typeContributions.getTypeConfiguration().getJdbcToHibernateTypeContributionMap() final Map<Integer, Set<String>> jdbcToHibernateTypeContributionMap = typeConfiguration.getJdbcToHibernateTypeContributionMap();
.computeIfAbsent( Types.FLOAT, code -> new HashSet<>() ) jdbcToHibernateTypeContributionMap.computeIfAbsent( Types.FLOAT, code -> new HashSet<>() ).clear();
.clear(); jdbcToHibernateTypeContributionMap.computeIfAbsent( Types.REAL, code -> new HashSet<>() ).clear();
typeContributions.getTypeConfiguration().getJdbcToHibernateTypeContributionMap() jdbcToHibernateTypeContributionMap.computeIfAbsent( Types.DOUBLE, code -> new HashSet<>() ).clear();
.computeIfAbsent( Types.REAL, code -> new HashSet<>() ) jdbcToHibernateTypeContributionMap.get( Types.FLOAT ).add( StandardBasicTypes.BIG_DECIMAL.getName() );
.clear(); jdbcToHibernateTypeContributionMap.get( Types.REAL ).add( StandardBasicTypes.BIG_DECIMAL.getName() );
typeContributions.getTypeConfiguration().getJdbcToHibernateTypeContributionMap() jdbcToHibernateTypeContributionMap.get( Types.DOUBLE ).add( StandardBasicTypes.BIG_DECIMAL.getName() );
.computeIfAbsent( Types.DOUBLE, code -> new HashSet<>() ) jdbcTypeRegistry.addDescriptor( Types.FLOAT, NumericJdbcType.INSTANCE );
.clear(); jdbcTypeRegistry.addDescriptor( Types.REAL, NumericJdbcType.INSTANCE );
typeContributions.getTypeConfiguration().getJdbcToHibernateTypeContributionMap() jdbcTypeRegistry.addDescriptor( Types.DOUBLE, NumericJdbcType.INSTANCE );
.get( Types.FLOAT )
.add( StandardBasicTypes.BIG_DECIMAL.getName() );
typeContributions.getTypeConfiguration().getJdbcToHibernateTypeContributionMap()
.get( Types.REAL )
.add( StandardBasicTypes.BIG_DECIMAL.getName() );
typeContributions.getTypeConfiguration().getJdbcToHibernateTypeContributionMap()
.get( Types.DOUBLE )
.add( StandardBasicTypes.BIG_DECIMAL.getName() );
jdbcTypeRegistry.addDescriptor(
Types.FLOAT,
NumericJdbcType.INSTANCE
);
jdbcTypeRegistry.addDescriptor(
Types.REAL,
NumericJdbcType.INSTANCE
);
jdbcTypeRegistry.addDescriptor(
Types.DOUBLE,
NumericJdbcType.INSTANCE
);
} }
jdbcTypeRegistry.addDescriptor( Types.CLOB, this.clobTypeDescriptor ); jdbcTypeRegistry.addDescriptor( Types.CLOB, new HANAClobJdbcType( maxLobPrefetchSize, useUnicodeStringTypes ) );
jdbcTypeRegistry.addDescriptor( Types.NCLOB, this.nClobTypeDescriptor ); jdbcTypeRegistry.addDescriptor( Types.NCLOB, new HANANClobJdbcType( maxLobPrefetchSize ) );
jdbcTypeRegistry.addDescriptor( Types.BLOB, this.blobTypeDescriptor ); jdbcTypeRegistry.addDescriptor( Types.BLOB, new HANABlobType( maxLobPrefetchSize ) );
// tinyint is unsigned on HANA // tinyint is unsigned on HANA
jdbcTypeRegistry.addDescriptor( Types.TINYINT, TinyIntAsSmallIntJdbcType.INSTANCE ); jdbcTypeRegistry.addDescriptor( Types.TINYINT, TinyIntAsSmallIntJdbcType.INSTANCE );
if ( isUseUnicodeStringTypes() ) { if ( isUseUnicodeStringTypes() ) {
jdbcTypeRegistry.addDescriptor( Types.VARCHAR, NVarcharJdbcType.INSTANCE ); jdbcTypeRegistry.addDescriptor( Types.VARCHAR, NVarcharJdbcType.INSTANCE );
jdbcTypeRegistry.addDescriptor( Types.CHAR, NCharJdbcType.INSTANCE ); jdbcTypeRegistry.addDescriptor( Types.CHAR, NCharJdbcType.INSTANCE );
} }
if (treatDoubleTypedFieldsAsDecimal) { if ( treatDoubleTypedFieldsAsDecimal ) {
jdbcTypeRegistry.addDescriptor( Types.DOUBLE, DecimalJdbcType.INSTANCE ); jdbcTypeRegistry.addDescriptor( Types.DOUBLE, DecimalJdbcType.INSTANCE );
} }
} }
public JdbcType getBlobTypeDescriptor() {
return this.blobTypeDescriptor;
}
@Override @Override
public void appendBooleanValueString(SqlAppender appender, boolean bool) { public void appendBooleanValueString(SqlAppender appender, boolean bool) {
if ( this.useLegacyBooleanType ) { if ( this.useLegacyBooleanType ) {
@ -1266,12 +1252,16 @@ public abstract class AbstractHANADialect extends Dialect {
} }
public boolean isUseUnicodeStringTypes() { public boolean isUseUnicodeStringTypes() {
return this.useUnicodeStringTypes; return this.useUnicodeStringTypes || isDefaultTableTypeColumn() && isCloud();
} }
protected abstract boolean supportsAsciiStringTypes(); protected boolean supportsAsciiStringTypes() {
return !isDefaultTableTypeColumn() || !isCloud();
}
protected abstract Boolean useUnicodeStringTypesDefault(); protected Boolean useUnicodeStringTypesDefault() {
return isDefaultTableTypeColumn() ? isCloud() : Boolean.FALSE;
}
private static class CloseSuppressingReader extends FilterReader { private static class CloseSuppressingReader extends FilterReader {
@ -1756,6 +1746,7 @@ public abstract class AbstractHANADialect extends Dialect {
public static class HANABlobType implements JdbcType { public static class HANABlobType implements JdbcType {
private static final long serialVersionUID = 5874441715643764323L; private static final long serialVersionUID = 5874441715643764323L;
public static final JdbcType INSTANCE = new HANABlobType( HANAServerConfiguration.MAX_LOB_PREFETCH_SIZE_DEFAULT_VALUE );
final int maxLobPrefetchSize; final int maxLobPrefetchSize;
@ -1843,4 +1834,59 @@ public abstract class AbstractHANADialect extends Dialect {
return this.maxLobPrefetchSize; return this.maxLobPrefetchSize;
} }
} }
@Override
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(
EntityMappingType entityDescriptor,
RuntimeModelCreationContext runtimeModelCreationContext) {
return new GlobalTemporaryTableMutationStrategy(
TemporaryTable.createIdTable(
entityDescriptor,
basename -> TemporaryTable.ID_TABLE_PREFIX + basename,
this,
runtimeModelCreationContext
),
runtimeModelCreationContext.getSessionFactory()
);
}
@Override
public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy(
EntityMappingType entityDescriptor,
RuntimeModelCreationContext runtimeModelCreationContext) {
return new GlobalTemporaryTableInsertStrategy(
TemporaryTable.createEntityTable(
entityDescriptor,
name -> TemporaryTable.ENTITY_TABLE_PREFIX + name,
this,
runtimeModelCreationContext
),
runtimeModelCreationContext.getSessionFactory()
);
}
@Override
public TemporaryTableKind getSupportedTemporaryTableKind() {
return TemporaryTableKind.GLOBAL;
}
@Override
public String getTemporaryTableCreateOptions() {
return "on commit delete rows";
}
@Override
public String getTemporaryTableCreateCommand() {
return "create global temporary row table";
}
@Override
public String getTemporaryTableTruncateCommand() {
return "truncate table";
}
@Override
public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() {
return DmlTargetColumnQualifierSupport.TABLE_ALIAS;
}
} }

View File

@ -572,6 +572,11 @@ public class CockroachDialect extends Dialect {
return true; return true;
} }
@Override
public boolean supportsConflictClauseForInsertCTE() {
return true;
}
@Override @Override
public String getNoColumnsInsertString() { public String getNoColumnsInsertString() {
return "default values"; return "default values";
@ -1160,4 +1165,14 @@ public class CockroachDialect extends Dialect {
// RuntimeModelCreationContext runtimeModelCreationContext) { // RuntimeModelCreationContext runtimeModelCreationContext) {
// return new CteInsertStrategy( rootEntityDescriptor, runtimeModelCreationContext ); // return new CteInsertStrategy( rootEntityDescriptor, runtimeModelCreationContext );
// } // }
@Override
public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() {
return DmlTargetColumnQualifierSupport.TABLE_ALIAS;
}
@Override
public boolean supportsFromClauseInUpdate() {
return true;
}
} }

View File

@ -7,20 +7,27 @@
package org.hibernate.dialect; package org.hibernate.dialect;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.Statement;
import org.hibernate.sql.ast.tree.cte.CteMaterialization; import org.hibernate.sql.ast.tree.cte.CteMaterialization;
import org.hibernate.sql.ast.tree.cte.CteStatement;
import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.Literal; import org.hibernate.sql.ast.tree.expression.Literal;
import org.hibernate.sql.ast.tree.expression.Summarization; import org.hibernate.sql.ast.tree.expression.Summarization;
import org.hibernate.sql.ast.tree.from.NamedTableReference;
import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.insert.ConflictClause;
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate; import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate;
import org.hibernate.sql.ast.tree.predicate.InArrayPredicate; import org.hibernate.sql.ast.tree.predicate.InArrayPredicate;
import org.hibernate.sql.ast.tree.predicate.LikePredicate; import org.hibernate.sql.ast.tree.predicate.LikePredicate;
import org.hibernate.sql.ast.tree.select.QueryGroup; import org.hibernate.sql.ast.tree.select.QueryGroup;
import org.hibernate.sql.ast.tree.select.QueryPart; import org.hibernate.sql.ast.tree.select.QueryPart;
import org.hibernate.sql.ast.tree.select.QuerySpec; import org.hibernate.sql.ast.tree.select.QuerySpec;
import org.hibernate.sql.ast.tree.update.UpdateStatement;
import org.hibernate.sql.exec.internal.JdbcOperationQueryInsertImpl;
import org.hibernate.sql.exec.spi.JdbcOperation; import org.hibernate.sql.exec.spi.JdbcOperation;
import org.hibernate.sql.exec.spi.JdbcOperationQueryInsert;
/** /**
* A SQL AST translator for Cockroach. * A SQL AST translator for Cockroach.
@ -33,6 +40,56 @@ public class CockroachSqlAstTranslator<T extends JdbcOperation> extends Abstract
super( sessionFactory, statement ); super( sessionFactory, statement );
} }
@Override
protected JdbcOperationQueryInsert translateInsert(InsertSelectStatement sqlAst) {
visitInsertStatement( sqlAst );
return new JdbcOperationQueryInsertImpl(
getSql(),
getParameterBinders(),
getAffectedTableNames(),
null
);
}
@Override
protected void renderTableReferenceIdentificationVariable(TableReference tableReference) {
final String identificationVariable = tableReference.getIdentificationVariable();
if ( identificationVariable != null ) {
final Clause currentClause = getClauseStack().getCurrent();
if ( currentClause == Clause.INSERT ) {
// PostgreSQL requires the "as" keyword for inserts
appendSql( " as " );
}
else {
append( WHITESPACE );
}
append( tableReference.getIdentificationVariable() );
}
}
@Override
protected void renderDmlTargetTableExpression(NamedTableReference tableReference) {
super.renderDmlTargetTableExpression( tableReference );
final Statement currentStatement = getStatementStack().getCurrent();
if ( !( currentStatement instanceof UpdateStatement )
|| !hasNonTrivialFromClause( ( (UpdateStatement) currentStatement ).getFromClause() ) ) {
// For UPDATE statements we render a full FROM clause and a join condition to match target table rows,
// but for that to work, we have to omit the alias for the target table reference here
renderTableReferenceIdentificationVariable( tableReference );
}
}
@Override
protected void renderFromClauseAfterUpdateSet(UpdateStatement statement) {
renderFromClauseJoiningDmlTargetReference( statement );
}
@Override
protected void visitConflictClause(ConflictClause conflictClause) {
visitStandardConflictClause( conflictClause );
}
@Override @Override
protected void renderExpressionAsClauseItem(Expression expression) { protected void renderExpressionAsClauseItem(Expression expression) {
expression.accept( this ); expression.accept( this );

View File

@ -42,8 +42,11 @@ import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
import org.hibernate.engine.jdbc.env.spi.IdentifierHelper; import org.hibernate.engine.jdbc.env.spi.IdentifierHelper;
import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder; import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.exception.ConstraintViolationException;
import org.hibernate.exception.LockTimeoutException; import org.hibernate.exception.LockTimeoutException;
import org.hibernate.exception.spi.SQLExceptionConversionDelegate; import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor;
import org.hibernate.exception.spi.ViolatedConstraintNameExtractor;
import org.hibernate.internal.util.JdbcExceptionHelper; import org.hibernate.internal.util.JdbcExceptionHelper;
import org.hibernate.mapping.Column; import org.hibernate.mapping.Column;
import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityMappingType;
@ -87,6 +90,7 @@ import org.hibernate.type.spi.TypeConfiguration;
import jakarta.persistence.TemporalType; import jakarta.persistence.TemporalType;
import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate;
import static org.hibernate.type.SqlTypes.BINARY; import static org.hibernate.type.SqlTypes.BINARY;
import static org.hibernate.type.SqlTypes.BLOB; import static org.hibernate.type.SqlTypes.BLOB;
import static org.hibernate.type.SqlTypes.BOOLEAN; import static org.hibernate.type.SqlTypes.BOOLEAN;
@ -973,6 +977,20 @@ public class DB2Dialect extends Dialect {
appender.appendSql( '\'' ); appender.appendSql( '\'' );
} }
@Override
public ViolatedConstraintNameExtractor getViolatedConstraintNameExtractor() {
return new TemplatedViolatedConstraintNameExtractor(
sqle -> {
switch ( JdbcExceptionHelper.extractErrorCode( sqle ) ) {
case -803:
return extractUsingTemplate( "SQLERRMC=1;", ",", sqle.getMessage() );
default:
return null;
}
}
);
}
@Override @Override
public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() { public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() {
return (sqlException, message, sql) -> { return (sqlException, message, sql) -> {
@ -980,6 +998,14 @@ public class DB2Dialect extends Dialect {
switch ( errorCode ) { switch ( errorCode ) {
case -952: case -952:
return new LockTimeoutException( message, sqlException, sql ); return new LockTimeoutException( message, sqlException, sql );
case -803:
return new ConstraintViolationException(
message,
sqlException,
sql,
ConstraintViolationException.ConstraintKind.UNIQUE,
getViolatedConstraintNameExtractor().extractConstraintName( sqlException )
);
} }
return null; return null;
}; };
@ -1171,4 +1197,14 @@ public class DB2Dialect extends Dialect {
public int rowIdSqlType() { public int rowIdSqlType() {
return VARBINARY; return VARBINARY;
} }
@Override
public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() {
return DmlTargetColumnQualifierSupport.TABLE_ALIAS;
}
@Override
public boolean supportsFromClauseInUpdate() {
return getDB2Version().isSameOrAfter( 11 );
}
} }

View File

@ -12,8 +12,10 @@ import java.util.function.Consumer;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.JdbcMappingContainer; import org.hibernate.metamodel.mapping.JdbcMappingContainer;
import org.hibernate.query.IllegalQueryOperationException;
import org.hibernate.query.sqm.ComparisonOperator; import org.hibernate.query.sqm.ComparisonOperator;
import org.hibernate.query.sqm.FetchClauseType; import org.hibernate.query.sqm.FetchClauseType;
import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.SqlAstNodeRenderingMode; import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.spi.SqlSelection;
@ -27,10 +29,12 @@ import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.Literal; import org.hibernate.sql.ast.tree.expression.Literal;
import org.hibernate.sql.ast.tree.expression.SqlTuple; import org.hibernate.sql.ast.tree.expression.SqlTuple;
import org.hibernate.sql.ast.tree.expression.Summarization; import org.hibernate.sql.ast.tree.expression.Summarization;
import org.hibernate.sql.ast.tree.from.NamedTableReference;
import org.hibernate.sql.ast.tree.from.QueryPartTableReference; import org.hibernate.sql.ast.tree.from.QueryPartTableReference;
import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableGroupJoin; import org.hibernate.sql.ast.tree.from.TableGroupJoin;
import org.hibernate.sql.ast.tree.from.TableReferenceJoin; import org.hibernate.sql.ast.tree.from.TableReferenceJoin;
import org.hibernate.sql.ast.tree.insert.ConflictClause;
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement; import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate; import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate;
import org.hibernate.sql.ast.tree.predicate.Predicate; import org.hibernate.sql.ast.tree.predicate.Predicate;
@ -347,12 +351,40 @@ public class DB2SqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAst
@Override @Override
protected void visitInsertStatementOnly(InsertSelectStatement statement) { protected void visitInsertStatementOnly(InsertSelectStatement statement) {
final boolean closeWrapper = renderReturningClause( statement ); final boolean closeWrapper = renderReturningClause( statement );
super.visitInsertStatementOnly( statement ); if ( statement.getConflictClause() == null || statement.getConflictClause().isDoNothing() ) {
// Render plain insert statement and possibly run into unique constraint violation
super.visitInsertStatementOnly( statement );
}
else {
visitInsertStatementEmulateMerge( statement );
}
if ( closeWrapper ) { if ( closeWrapper ) {
appendSql( ')' ); appendSql( ')' );
} }
} }
@Override
protected void visitConflictClause(ConflictClause conflictClause) {
if ( conflictClause != null ) {
if ( conflictClause.isDoUpdate() && conflictClause.getConstraintName() != null ) {
throw new IllegalQueryOperationException( "Insert conflict do update clause with constraint name is not supported" );
}
}
}
@Override
protected void renderDmlTargetTableExpression(NamedTableReference tableReference) {
super.renderDmlTargetTableExpression( tableReference );
if ( getClauseStack().getCurrent() != Clause.INSERT ) {
renderTableReferenceIdentificationVariable( tableReference );
}
}
@Override
protected void renderFromClauseAfterUpdateSet(UpdateStatement statement) {
renderFromClauseExcludingDmlTargetReference( statement );
}
protected boolean renderReturningClause(MutationStatement statement) { protected boolean renderReturningClause(MutationStatement statement) {
final List<ColumnReference> returningColumns = statement.getReturningColumns(); final List<ColumnReference> returningColumns = statement.getReturningColumns();
if ( isEmpty( returningColumns ) ) { if ( isEmpty( returningColumns ) ) {
@ -521,13 +553,13 @@ public class DB2SqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAst
} }
@Override @Override
protected String getFromDual() { protected String getDual() {
return " from sysibm.dual"; return "sysibm.dual";
} }
@Override @Override
protected String getFromDualForSelectOnly() { protected String getFromDualForSelectOnly() {
return getFromDual(); return " from " + getDual();
} }
@Override @Override

View File

@ -135,7 +135,7 @@ public enum Database {
HANA { HANA {
@Override @Override
public Dialect createDialect(DialectResolutionInfo info) { public Dialect createDialect(DialectResolutionInfo info) {
return new HANAColumnStoreDialect( info ); return new HANADialect( info );
} }
@Override @Override
public boolean productNameMatches(String databaseName) { public boolean productNameMatches(String databaseName) {

View File

@ -35,8 +35,11 @@ import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
import org.hibernate.engine.jdbc.env.spi.IdentifierHelper; import org.hibernate.engine.jdbc.env.spi.IdentifierHelper;
import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder; import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.exception.ConstraintViolationException;
import org.hibernate.exception.LockTimeoutException; import org.hibernate.exception.LockTimeoutException;
import org.hibernate.exception.spi.SQLExceptionConversionDelegate; import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor;
import org.hibernate.exception.spi.ViolatedConstraintNameExtractor;
import org.hibernate.internal.util.JdbcExceptionHelper; import org.hibernate.internal.util.JdbcExceptionHelper;
import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.spi.RuntimeModelCreationContext; import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
@ -690,14 +693,42 @@ public class DerbyDialect extends Dialect {
return 512; return 512;
} }
@Override
public ViolatedConstraintNameExtractor getViolatedConstraintNameExtractor() {
return new TemplatedViolatedConstraintNameExtractor( sqle -> {
final String sqlState = JdbcExceptionHelper.extractSqlState( sqle );
if ( sqlState != null ) {
switch ( sqlState ) {
case "23505":
return TemplatedViolatedConstraintNameExtractor.extractUsingTemplate(
"'", "'",
sqle.getMessage()
);
}
}
return null;
} );
}
@Override @Override
public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() { public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() {
return (sqlException, message, sql) -> { return (sqlException, message, sql) -> {
final String sqlState = JdbcExceptionHelper.extractSqlState( sqlException ); final String sqlState = JdbcExceptionHelper.extractSqlState( sqlException );
// final int errorCode = JdbcExceptionHelper.extractErrorCode( sqlException ); // final int errorCode = JdbcExceptionHelper.extractErrorCode( sqlException );
final String constraintName;
if ( sqlState != null ) { if ( sqlState != null ) {
switch ( sqlState ) { switch ( sqlState ) {
case "23505":
// Unique constraint violation
constraintName = getViolatedConstraintNameExtractor().extractConstraintName(sqlException);
return new ConstraintViolationException(
message,
sqlException,
sql,
ConstraintViolationException.ConstraintKind.UNIQUE,
constraintName
);
case "40XL1": case "40XL1":
case "40XL2": case "40XL2":
return new LockTimeoutException( message, sqlException, sql ); return new LockTimeoutException( message, sqlException, sql );
@ -1013,4 +1044,5 @@ public class DerbyDialect extends Dialect {
public UniqueDelegate getUniqueDelegate() { public UniqueDelegate getUniqueDelegate() {
return uniqueDelegate; return uniqueDelegate;
} }
} }

View File

@ -233,13 +233,13 @@ public class DerbySqlAstTranslator<T extends JdbcOperation> extends AbstractSqlA
} }
@Override @Override
protected String getFromDual() { protected String getDual() {
return " from (values 0) dual"; return "(values 0)";
} }
@Override @Override
protected String getFromDualForSelectOnly() { protected String getFromDualForSelectOnly() {
return getFromDual(); return " from " + getDual() + " dual";
} }
@Override @Override

View File

@ -4359,6 +4359,17 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun
return false; return false;
} }
/**
* Does this dialect support the {@code conflict} clause for insert statements
* that appear in a CTE?
*
* @return {@code true} if {@code conflict} clause is supported
* @since 6.5
*/
public boolean supportsConflictClauseForInsertCTE() {
return false;
}
/** /**
* Does this dialect support {@code values} lists of form * Does this dialect support {@code values} lists of form
* {@code VALUES (1), (2), (3)}? * {@code VALUES (1), (2), (3)}?
@ -4380,6 +4391,16 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun
return true; return true;
} }
/**
* Does this dialect support the {@code from} clause for update statements?
*
* @return {@code true} if {@code from} clause is supported
* @since 6.5
*/
public boolean supportsFromClauseInUpdate() {
return false;
}
/** /**
* Does this dialect support {@code SKIP_LOCKED} timeout. * Does this dialect support {@code SKIP_LOCKED} timeout.
* *

View File

@ -721,8 +721,19 @@ public class H2Dialect extends Dialect {
public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() { public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() {
return (sqlException, message, sql) -> { return (sqlException, message, sql) -> {
final int errorCode = JdbcExceptionHelper.extractErrorCode( sqlException ); final int errorCode = JdbcExceptionHelper.extractErrorCode( sqlException );
final String constraintName;
switch (errorCode) { switch (errorCode) {
case 23505:
// Unique constraint violation
constraintName = getViolatedConstraintNameExtractor().extractConstraintName(sqlException);
return new ConstraintViolationException(
message,
sqlException,
sql,
ConstraintViolationException.ConstraintKind.UNIQUE,
constraintName
);
case 40001: case 40001:
// DEADLOCK DETECTED // DEADLOCK DETECTED
return new LockAcquisitionException(message, sqlException, sql); return new LockAcquisitionException(message, sqlException, sql);
@ -731,7 +742,7 @@ public class H2Dialect extends Dialect {
return new PessimisticLockException(message, sqlException, sql); return new PessimisticLockException(message, sqlException, sql);
case 90006: case 90006:
// NULL not allowed for column [90006-145] // NULL not allowed for column [90006-145]
final String constraintName = getViolatedConstraintNameExtractor().extractConstraintName(sqlException); constraintName = getViolatedConstraintNameExtractor().extractConstraintName(sqlException);
return new ConstraintViolationException(message, sqlException, sql, constraintName); return new ConstraintViolationException(message, sqlException, sql, constraintName);
case 57014: case 57014:
return new QueryTimeoutException( message, sqlException, sql ); return new QueryTimeoutException( message, sqlException, sql );
@ -935,4 +946,9 @@ public class H2Dialect extends Dialect {
return "?" + position; return "?" + position;
} }
} }
@Override
public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() {
return DmlTargetColumnQualifierSupport.TABLE_ALIAS;
}
} }

View File

@ -11,7 +11,9 @@ import java.util.List;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.dialect.identity.H2IdentityColumnSupport; import org.hibernate.dialect.identity.H2IdentityColumnSupport;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.query.IllegalQueryOperationException;
import org.hibernate.query.sqm.ComparisonOperator; import org.hibernate.query.sqm.ComparisonOperator;
import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.SqlAstNodeRenderingMode; import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.Statement;
@ -23,13 +25,17 @@ import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.Literal; import org.hibernate.sql.ast.tree.expression.Literal;
import org.hibernate.sql.ast.tree.expression.SqlTuple; import org.hibernate.sql.ast.tree.expression.SqlTuple;
import org.hibernate.sql.ast.tree.expression.Summarization; import org.hibernate.sql.ast.tree.expression.Summarization;
import org.hibernate.sql.ast.tree.from.NamedTableReference;
import org.hibernate.sql.ast.tree.from.QueryPartTableReference; import org.hibernate.sql.ast.tree.from.QueryPartTableReference;
import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.insert.ConflictClause;
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate; import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate;
import org.hibernate.sql.ast.tree.predicate.LikePredicate; import org.hibernate.sql.ast.tree.predicate.LikePredicate;
import org.hibernate.sql.ast.tree.select.QueryPart; import org.hibernate.sql.ast.tree.select.QueryPart;
import org.hibernate.sql.ast.tree.select.SelectClause; import org.hibernate.sql.ast.tree.select.SelectClause;
import org.hibernate.sql.ast.tree.update.UpdateStatement;
import org.hibernate.sql.exec.spi.JdbcOperation; import org.hibernate.sql.exec.spi.JdbcOperation;
import org.hibernate.sql.model.internal.TableInsertStandard; import org.hibernate.sql.model.internal.TableInsertStandard;
@ -80,6 +86,44 @@ public class H2SqlAstTranslator<T extends JdbcOperation> extends SqlAstTranslato
); );
} }
@Override
protected void visitInsertStatementOnly(InsertSelectStatement statement) {
if ( statement.getConflictClause() == null || statement.getConflictClause().isDoNothing() ) {
// Render plain insert statement and possibly run into unique constraint violation
super.visitInsertStatementOnly( statement );
}
else {
visitInsertStatementEmulateMerge( statement );
}
}
@Override
protected void visitUpdateStatementOnly(UpdateStatement statement) {
if ( hasNonTrivialFromClause( statement.getFromClause() ) ) {
visitUpdateStatementEmulateMerge( statement );
}
else {
super.visitUpdateStatementOnly( statement );
}
}
@Override
protected void renderDmlTargetTableExpression(NamedTableReference tableReference) {
super.renderDmlTargetTableExpression( tableReference );
if ( getClauseStack().getCurrent() != Clause.INSERT ) {
renderTableReferenceIdentificationVariable( tableReference );
}
}
@Override
protected void visitConflictClause(ConflictClause conflictClause) {
if ( conflictClause != null ) {
if ( conflictClause.isDoUpdate() && conflictClause.getConstraintName() != null ) {
throw new IllegalQueryOperationException( "Insert conflict do update clause with constraint name is not supported" );
}
}
}
@Override @Override
protected void visitReturningColumns(List<ColumnReference> returningColumns) { protected void visitReturningColumns(List<ColumnReference> returningColumns) {
// do nothing - this is handled via `#visitReturningInsertStatement` // do nothing - this is handled via `#visitReturningInsertStatement`
@ -267,11 +311,10 @@ public class H2SqlAstTranslator<T extends JdbcOperation> extends SqlAstTranslato
} }
@Override @Override
protected String getFromDual() { protected String getDual() {
return " from dual"; return "dual";
} }
private boolean supportsOffsetFetchClause() { private boolean supportsOffsetFetchClause() {
return true; return true;
} }

View File

@ -19,35 +19,14 @@ package org.hibernate.dialect;
* *
* @author Jonathan Bregler * @author Jonathan Bregler
* *
* @deprecated use HANAColumnStoreDialect(400) * @deprecated use {@link HANADialect} with {@code DatabaseVersion.make( 4 )} instead
*/ */
@Deprecated @Deprecated(forRemoval = true)
public class HANACloudColumnStoreDialect extends HANAColumnStoreDialect { public class HANACloudColumnStoreDialect extends HANAColumnStoreDialect {
public HANACloudColumnStoreDialect() { public HANACloudColumnStoreDialect() {
// No idea how the versioning scheme is here, but since this is deprecated anyway, keep it as is // No idea how the versioning scheme is here, but since this is deprecated anyway, keep it as is
super( DatabaseVersion.make( 4 ) ); super( new HANAServerConfiguration( DatabaseVersion.make( 4 ) ) );
}
@Override
public boolean supportsLateral() {
// Couldn't find a reference since when this is supported
return true;
}
@Override
protected boolean supportsAsciiStringTypes() {
return getVersion().isBefore( 4 );
}
@Override
protected Boolean useUnicodeStringTypesDefault() {
return getVersion().isSameOrAfter( 4 );
}
@Override
public boolean isUseUnicodeStringTypes() {
return getVersion().isSameOrAfter( 4 ) || super.isUseUnicodeStringTypes();
} }
} }

View File

@ -34,11 +34,14 @@ import static org.hibernate.query.sqm.produce.function.FunctionParameterType.ANY
* *
* @author Andrew Clemons * @author Andrew Clemons
* @author Jonathan Bregler * @author Jonathan Bregler
*
* @deprecated use {@link HANADialect} instead
*/ */
@Deprecated(forRemoval = true)
public class HANAColumnStoreDialect extends AbstractHANADialect { public class HANAColumnStoreDialect extends AbstractHANADialect {
public HANAColumnStoreDialect(DialectResolutionInfo info) { public HANAColumnStoreDialect(DialectResolutionInfo info) {
this( AbstractHANADialect.createVersion( info ) ); this( HANAServerConfiguration.fromDialectResolutionInfo( info ) );
registerKeywords( info ); registerKeywords( info );
} }
@ -48,129 +51,10 @@ public class HANAColumnStoreDialect extends AbstractHANADialect {
} }
public HANAColumnStoreDialect(DatabaseVersion version) { public HANAColumnStoreDialect(DatabaseVersion version) {
super( version ); this( new HANAServerConfiguration( version ) );
} }
@Override public HANAColumnStoreDialect(HANAServerConfiguration configuration) {
public boolean isUseUnicodeStringTypes() { super( configuration, true );
return getVersion().isSameOrAfter( 4 ) || super.isUseUnicodeStringTypes();
}
@Override
public int getMaxVarcharLength() {
return 5000;
}
@Override
protected void registerDefaultKeywords() {
super.registerDefaultKeywords();
registerKeyword( "array" );
registerKeyword( "at" );
registerKeyword( "authorization" );
registerKeyword( "between" );
registerKeyword( "by" );
registerKeyword( "collate" );
registerKeyword( "empty" );
registerKeyword( "filter" );
registerKeyword( "grouping" );
registerKeyword( "no" );
registerKeyword( "not" );
registerKeyword( "of" );
registerKeyword( "over" );
registerKeyword( "recursive" );
registerKeyword( "row" );
registerKeyword( "table" );
registerKeyword( "to" );
registerKeyword( "window" );
registerKeyword( "within" );
}
@Override
public void initializeFunctionRegistry(FunctionContributions functionContributions) {
super.initializeFunctionRegistry(functionContributions);
final TypeConfiguration typeConfiguration = functionContributions.getTypeConfiguration();
// full-text search functions
functionContributions.getFunctionRegistry().registerNamed(
"score",
typeConfiguration.getBasicTypeRegistry().resolve( StandardBasicTypes.DOUBLE )
);
functionContributions.getFunctionRegistry().registerNamed( "snippets" );
functionContributions.getFunctionRegistry().registerNamed( "highlighted" );
functionContributions.getFunctionRegistry().registerBinaryTernaryPattern(
"contains",
typeConfiguration.getBasicTypeRegistry().resolve( StandardBasicTypes.BOOLEAN ),
"contains(?1,?2)",
"contains(?1,?2,?3)",
ANY, ANY, ANY,
typeConfiguration
);
}
@Override
public String getCreateTableString() {
return "create column table";
}
@Override
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(
EntityMappingType entityDescriptor,
RuntimeModelCreationContext runtimeModelCreationContext) {
return new GlobalTemporaryTableMutationStrategy(
TemporaryTable.createIdTable(
entityDescriptor,
basename -> TemporaryTable.ID_TABLE_PREFIX + basename,
this,
runtimeModelCreationContext
),
runtimeModelCreationContext.getSessionFactory()
);
}
@Override
public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy(
EntityMappingType entityDescriptor,
RuntimeModelCreationContext runtimeModelCreationContext) {
return new GlobalTemporaryTableInsertStrategy(
TemporaryTable.createEntityTable(
entityDescriptor,
name -> TemporaryTable.ENTITY_TABLE_PREFIX + name,
this,
runtimeModelCreationContext
),
runtimeModelCreationContext.getSessionFactory()
);
}
@Override
public TemporaryTableKind getSupportedTemporaryTableKind() {
return TemporaryTableKind.GLOBAL;
}
@Override
public String getTemporaryTableCreateOptions() {
return "on commit delete rows";
}
@Override
public String getTemporaryTableCreateCommand() {
// We use a row table for temporary tables here because HANA doesn't support UPDATE on temporary column tables
return "create global temporary row table";
}
@Override
public String getTemporaryTableTruncateCommand() {
return "truncate table";
}
@Override
protected boolean supportsAsciiStringTypes() {
return true;
}
@Override
protected Boolean useUnicodeStringTypesDefault() {
return true;
} }
} }

View File

@ -0,0 +1,55 @@
/*
* 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.dialect;
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
/**
* An SQL dialect for the SAP HANA Platform and Cloud.
* <p>
* For more information on SAP HANA Cloud, refer to the
* <a href="https://help.sap.com/docs/hana-cloud-database/sap-hana-cloud-sap-hana-database-sql-reference-guide/sap-hana-cloud-sap-hana-database-sql-reference-guide">SAP HANA Cloud SQL Reference Guide</a>.
* For more information on SAP HANA Platform, refer to the
* <a href="https://help.sap.com/docs/SAP_HANA_PLATFORM/4fe29514fd584807ac9f2a04f6754767/b4b0eec1968f41a099c828a4a6c8ca0f.html?locale=en-US">SAP HANA Platform SQL Reference Guide</a>.
* <p>
* Column tables are created by this dialect by default when using the auto-ddl feature.
*
* @author Andrew Clemons
* @author Jonathan Bregler
*/
@SuppressWarnings("removal")
public class HANADialect extends AbstractHANADialect {
private static final DatabaseVersion MINIMUM_VERSION = DatabaseVersion.make( 1, 0, 120 );
public HANADialect(DialectResolutionInfo info) {
this( HANAServerConfiguration.fromDialectResolutionInfo( info ), true );
registerKeywords( info );
}
public HANADialect() {
// SAP HANA 1.0 SPS12 R0 is the default
this( MINIMUM_VERSION );
}
public HANADialect(DatabaseVersion version) {
this( new HANAServerConfiguration( version ), true );
}
public HANADialect(DatabaseVersion version, boolean defaultTableTypeColumn) {
this( new HANAServerConfiguration( version ), defaultTableTypeColumn );
}
public HANADialect(HANAServerConfiguration configuration, boolean defaultTableTypeColumn) {
super( configuration, defaultTableTypeColumn );
}
@Override
protected DatabaseVersion getMinimumSupportedVersion() {
return MINIMUM_VERSION;
}
}

View File

@ -29,11 +29,14 @@ import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
* *
* @author Andrew Clemons * @author Andrew Clemons
* @author Jonathan Bregler * @author Jonathan Bregler
*
* @deprecated use {@link HANADialect} instead
*/ */
@Deprecated(forRemoval = true)
public class HANARowStoreDialect extends AbstractHANADialect { public class HANARowStoreDialect extends AbstractHANADialect {
public HANARowStoreDialect(DialectResolutionInfo info) { public HANARowStoreDialect(DialectResolutionInfo info) {
this( AbstractHANADialect.createVersion( info ) ); this( HANAServerConfiguration.fromDialectResolutionInfo( info ) );
registerKeywords( info ); registerKeywords( info );
} }
@ -43,71 +46,10 @@ public class HANARowStoreDialect extends AbstractHANADialect {
} }
public HANARowStoreDialect(DatabaseVersion version) { public HANARowStoreDialect(DatabaseVersion version) {
super( version ); this( new HANAServerConfiguration( version ) );
} }
@Override public HANARowStoreDialect(HANAServerConfiguration configuration) {
public String getCreateTableString() { super( configuration, false );
return "create row table";
}
@Override
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(
EntityMappingType entityDescriptor,
RuntimeModelCreationContext runtimeModelCreationContext) {
return new GlobalTemporaryTableMutationStrategy(
TemporaryTable.createIdTable(
entityDescriptor,
basename -> TemporaryTable.ID_TABLE_PREFIX + basename,
this,
runtimeModelCreationContext
),
runtimeModelCreationContext.getSessionFactory()
);
}
@Override
public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy(
EntityMappingType entityDescriptor,
RuntimeModelCreationContext runtimeModelCreationContext) {
return new GlobalTemporaryTableInsertStrategy(
TemporaryTable.createEntityTable(
entityDescriptor,
name -> TemporaryTable.ENTITY_TABLE_PREFIX + name,
this,
runtimeModelCreationContext
),
runtimeModelCreationContext.getSessionFactory()
);
}
@Override
public TemporaryTableKind getSupportedTemporaryTableKind() {
return TemporaryTableKind.GLOBAL;
}
@Override
public String getTemporaryTableCreateOptions() {
return "on commit delete rows";
}
@Override
public String getTemporaryTableCreateCommand() {
return "create global temporary row table";
}
@Override
public String getTemporaryTableTruncateCommand() {
return "truncate table";
}
@Override
protected boolean supportsAsciiStringTypes() {
return true;
}
@Override
protected Boolean useUnicodeStringTypesDefault() {
return Boolean.FALSE;
} }
} }

View File

@ -0,0 +1,99 @@
/*
* 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.dialect;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.config.ConfigurationHelper;
import static org.hibernate.cfg.DialectSpecificSettings.HANA_MAX_LOB_PREFETCH_SIZE;
/**
* Utility class that extract some initial configuration from the database for {@link HANADialect}.
*/
public class HANAServerConfiguration {
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( HANAServerConfiguration.class );
public static final int MAX_LOB_PREFETCH_SIZE_DEFAULT_VALUE = 1024;
private final DatabaseVersion fullVersion;
private final int maxLobPrefetchSize;
public HANAServerConfiguration(DatabaseVersion fullVersion) {
this( fullVersion, MAX_LOB_PREFETCH_SIZE_DEFAULT_VALUE );
}
public HANAServerConfiguration(DatabaseVersion fullVersion, int maxLobPrefetchSize) {
this.fullVersion = fullVersion;
this.maxLobPrefetchSize = maxLobPrefetchSize;
}
public DatabaseVersion getFullVersion() {
return fullVersion;
}
public int getMaxLobPrefetchSize() {
return maxLobPrefetchSize;
}
public static HANAServerConfiguration fromDialectResolutionInfo(DialectResolutionInfo info) {
Integer maxLobPrefetchSize = null;
final DatabaseMetaData databaseMetaData = info.getDatabaseMetadata();
if ( databaseMetaData != null ) {
try (final Statement statement = databaseMetaData.getConnection().createStatement()) {
try ( ResultSet rs = statement.executeQuery(
"SELECT TOP 1 VALUE,MAP(LAYER_NAME,'DEFAULT',1,'SYSTEM',2,'DATABASE',3,4) AS LAYER FROM SYS.M_INIFILE_CONTENTS WHERE FILE_NAME='indexserver.ini' AND SECTION='session' AND KEY='max_lob_prefetch_size' ORDER BY LAYER DESC" ) ) {
// This only works if the current user has the privilege INIFILE ADMIN
if ( rs.next() ) {
maxLobPrefetchSize = rs.getInt( 1 );
}
}
}
catch (SQLException e) {
// Ignore
LOG.debug(
"An error occurred while trying to determine the value of the HANA parameter indexserver.ini / session / max_lob_prefetch_size.",
e );
}
}
// default to the dialect-specific configuration settings
if ( maxLobPrefetchSize == null ) {
maxLobPrefetchSize = ConfigurationHelper.getInt(
HANA_MAX_LOB_PREFETCH_SIZE,
info.getConfigurationValues(),
MAX_LOB_PREFETCH_SIZE_DEFAULT_VALUE
);
}
return new HANAServerConfiguration( createVersion( info ), maxLobPrefetchSize );
}
private static DatabaseVersion createVersion(DialectResolutionInfo info) {
// Parse the version according to https://answers.sap.com/questions/9760991/hana-sps-version-check.html
final String versionString = info.getDatabaseVersion();
int majorVersion = 1;
int minorVersion = 0;
int patchLevel = 0;
final String[] components = versionString.split( "\\." );
if ( components.length >= 3 ) {
try {
majorVersion = Integer.parseInt( components[0] );
minorVersion = Integer.parseInt( components[1] );
patchLevel = Integer.parseInt( components[2] );
}
catch (NumberFormatException ex) {
// Ignore
}
}
return DatabaseVersion.make( majorVersion, minorVersion, patchLevel );
}
}

View File

@ -8,24 +8,34 @@ package org.hibernate.dialect;
import java.util.List; import java.util.List;
import org.hibernate.LockMode;
import org.hibernate.MappingException; import org.hibernate.MappingException;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.collections.Stack;
import org.hibernate.query.IllegalQueryOperationException;
import org.hibernate.query.sqm.BinaryArithmeticOperator; import org.hibernate.query.sqm.BinaryArithmeticOperator;
import org.hibernate.query.sqm.ComparisonOperator; import org.hibernate.query.sqm.ComparisonOperator;
import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.SqlAstNodeRenderingMode; import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.Statement;
import org.hibernate.sql.ast.tree.cte.CteStatement; import org.hibernate.sql.ast.tree.cte.CteStatement;
import org.hibernate.sql.ast.tree.delete.DeleteStatement;
import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression; import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression;
import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.Literal; import org.hibernate.sql.ast.tree.expression.Literal;
import org.hibernate.sql.ast.tree.expression.Summarization; import org.hibernate.sql.ast.tree.expression.Summarization;
import org.hibernate.sql.ast.tree.from.FunctionTableReference; import org.hibernate.sql.ast.tree.from.FunctionTableReference;
import org.hibernate.sql.ast.tree.from.NamedTableReference;
import org.hibernate.sql.ast.tree.from.QueryPartTableReference; import org.hibernate.sql.ast.tree.from.QueryPartTableReference;
import org.hibernate.sql.ast.tree.from.ValuesTableReference;
import org.hibernate.sql.ast.tree.insert.ConflictClause;
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
import org.hibernate.sql.ast.tree.insert.Values; import org.hibernate.sql.ast.tree.insert.Values;
import org.hibernate.sql.ast.tree.select.QueryGroup; import org.hibernate.sql.ast.tree.select.QueryGroup;
import org.hibernate.sql.ast.tree.select.QueryPart; import org.hibernate.sql.ast.tree.select.QueryPart;
import org.hibernate.sql.ast.tree.select.QuerySpec; import org.hibernate.sql.ast.tree.select.QuerySpec;
import org.hibernate.sql.ast.tree.update.UpdateStatement;
import org.hibernate.sql.exec.spi.JdbcOperation; import org.hibernate.sql.exec.spi.JdbcOperation;
import org.hibernate.sql.model.internal.TableInsertStandard; import org.hibernate.sql.model.internal.TableInsertStandard;
@ -42,6 +52,83 @@ public class HANASqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAs
super( sessionFactory, statement ); super( sessionFactory, statement );
} }
@SuppressWarnings("removal")
private boolean isHanaCloud() {
return ( (AbstractHANADialect) getDialect() ).isCloud();
}
@Override
protected void visitInsertStatementOnly(InsertSelectStatement statement) {
if ( statement.getConflictClause() == null || statement.getConflictClause().isDoNothing() ) {
// Render plain insert statement and possibly run into unique constraint violation
super.visitInsertStatementOnly( statement );
}
else {
visitInsertStatementEmulateMerge( statement );
}
}
@Override
protected void visitUpdateStatementOnly(UpdateStatement statement) {
// HANA Cloud does not support the FROM clause in UPDATE statements
if ( isHanaCloud() && hasNonTrivialFromClause( statement.getFromClause() ) ) {
visitUpdateStatementEmulateMerge( statement );
}
else {
super.visitUpdateStatementOnly( statement );
}
}
@Override
protected void renderUpdateClause(UpdateStatement updateStatement) {
// HANA Cloud does not support the FROM clause in UPDATE statements
if ( isHanaCloud() ) {
super.renderUpdateClause( updateStatement );
}
else {
appendSql( "update" );
final Stack<Clause> clauseStack = getClauseStack();
try {
clauseStack.push( Clause.UPDATE );
renderTableReferenceIdentificationVariable( updateStatement.getTargetTable() );
}
finally {
clauseStack.pop();
}
}
}
@Override
protected void renderFromClauseAfterUpdateSet(UpdateStatement statement) {
// HANA Cloud does not support the FROM clause in UPDATE statements
if ( !isHanaCloud() ) {
if ( statement.getFromClause().getRoots().isEmpty() ) {
appendSql( " from " );
renderDmlTargetTableExpression( statement.getTargetTable() );
}
else {
visitFromClause( statement.getFromClause() );
}
}
}
@Override
protected void renderDmlTargetTableExpression(NamedTableReference tableReference) {
super.renderDmlTargetTableExpression( tableReference );
if ( getClauseStack().getCurrent() != Clause.INSERT ) {
renderTableReferenceIdentificationVariable( tableReference );
}
}
@Override
protected void visitConflictClause(ConflictClause conflictClause) {
if ( conflictClause != null ) {
if ( conflictClause.isDoUpdate() && conflictClause.getConstraintName() != null ) {
throw new IllegalQueryOperationException( "Insert conflict do update clause with constraint name is not supported" );
}
}
}
protected boolean shouldEmulateFetchClause(QueryPart queryPart) { protected boolean shouldEmulateFetchClause(QueryPart queryPart) {
// HANA only supports the LIMIT + OFFSET syntax but also window functions // HANA only supports the LIMIT + OFFSET syntax but also window functions
// Check if current query part is already row numbering to avoid infinite recursion // Check if current query part is already row numbering to avoid infinite recursion
@ -141,13 +228,13 @@ public class HANASqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAs
} }
@Override @Override
protected String getFromDual() { protected String getDual() {
return " from sys.dummy"; return "sys.dummy";
} }
@Override @Override
protected String getFromDualForSelectOnly() { protected String getFromDualForSelectOnly() {
return getFromDual(); return " from " + getDual();
} }
@Override @Override
@ -165,4 +252,9 @@ public class HANASqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAs
protected void visitValuesList(List<Values> valuesList) { protected void visitValuesList(List<Values> valuesList) {
visitValuesListEmulateSelectUnion( valuesList ); visitValuesListEmulateSelectUnion( valuesList );
} }
@Override
public void visitValuesTableReference(ValuesTableReference tableReference) {
emulateValuesTableReferenceColumnAliasing( tableReference );
}
} }

View File

@ -27,6 +27,8 @@ import org.hibernate.engine.jdbc.env.spi.IdentifierHelper;
import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder; import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder;
import org.hibernate.engine.jdbc.env.spi.NameQualifierSupport; import org.hibernate.engine.jdbc.env.spi.NameQualifierSupport;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.exception.ConstraintViolationException;
import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor; import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor;
import org.hibernate.exception.spi.ViolatedConstraintNameExtractor; import org.hibernate.exception.spi.ViolatedConstraintNameExtractor;
import org.hibernate.internal.util.JdbcExceptionHelper; import org.hibernate.internal.util.JdbcExceptionHelper;
@ -423,6 +425,29 @@ public class HSQLDialect extends Dialect {
return null; return null;
} ); } );
@Override
public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() {
return (sqlException, message, sql) -> {
final int errorCode = JdbcExceptionHelper.extractErrorCode( sqlException );
final String constraintName;
switch ( errorCode ) {
case -104:
// Unique constraint violation
constraintName = getViolatedConstraintNameExtractor().extractConstraintName(sqlException);
return new ConstraintViolationException(
message,
sqlException,
sql,
ConstraintViolationException.ConstraintKind.UNIQUE,
constraintName
);
}
return null;
};
}
@Override @Override
public String getSelectClauseNullString(int sqlType, TypeConfiguration typeConfiguration) { public String getSelectClauseNullString(int sqlType, TypeConfiguration typeConfiguration) {
switch ( sqlType ) { switch ( sqlType ) {
@ -683,4 +708,9 @@ public class HSQLDialect extends Dialect {
public String quoteCollation(String collation) { public String quoteCollation(String collation) {
return '\"' + collation + '\"'; return '\"' + collation + '\"';
} }
@Override
public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() {
return DmlTargetColumnQualifierSupport.TABLE_ALIAS;
}
} }

View File

@ -11,22 +11,30 @@ import java.util.function.Consumer;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.JdbcMappingContainer; import org.hibernate.metamodel.mapping.JdbcMappingContainer;
import org.hibernate.query.IllegalQueryOperationException;
import org.hibernate.query.sqm.BinaryArithmeticOperator; import org.hibernate.query.sqm.BinaryArithmeticOperator;
import org.hibernate.query.sqm.ComparisonOperator; import org.hibernate.query.sqm.ComparisonOperator;
import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.SqlAstNodeRenderingMode; import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.ast.tree.MutationStatement;
import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.Statement;
import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression; import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression;
import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression; import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression;
import org.hibernate.sql.ast.tree.expression.CaseSimpleExpression; import org.hibernate.sql.ast.tree.expression.CaseSimpleExpression;
import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.Literal; import org.hibernate.sql.ast.tree.expression.Literal;
import org.hibernate.sql.ast.tree.expression.SqlTuple; import org.hibernate.sql.ast.tree.expression.SqlTuple;
import org.hibernate.sql.ast.tree.expression.Summarization; import org.hibernate.sql.ast.tree.expression.Summarization;
import org.hibernate.sql.ast.tree.from.NamedTableReference;
import org.hibernate.sql.ast.tree.insert.ConflictClause;
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate; import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate;
import org.hibernate.sql.ast.tree.predicate.InArrayPredicate; import org.hibernate.sql.ast.tree.predicate.InArrayPredicate;
import org.hibernate.sql.ast.tree.select.QueryPart; import org.hibernate.sql.ast.tree.select.QueryPart;
import org.hibernate.sql.ast.tree.update.UpdateStatement;
import org.hibernate.sql.exec.spi.JdbcOperation; import org.hibernate.sql.exec.spi.JdbcOperation;
import org.hibernate.type.descriptor.jdbc.ArrayJdbcType; import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
@ -41,6 +49,44 @@ public class HSQLSqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAs
super( sessionFactory, statement ); super( sessionFactory, statement );
} }
@Override
protected void visitInsertStatementOnly(InsertSelectStatement statement) {
if ( statement.getConflictClause() == null || statement.getConflictClause().isDoNothing() ) {
// Render plain insert statement and possibly run into unique constraint violation
super.visitInsertStatementOnly( statement );
}
else {
visitInsertStatementEmulateMerge( statement );
}
}
@Override
protected void visitUpdateStatementOnly(UpdateStatement statement) {
if ( hasNonTrivialFromClause( statement.getFromClause() ) ) {
visitUpdateStatementEmulateMerge( statement );
}
else {
super.visitUpdateStatementOnly( statement );
}
}
@Override
protected void renderDmlTargetTableExpression(NamedTableReference tableReference) {
super.renderDmlTargetTableExpression( tableReference );
if ( getClauseStack().getCurrent() != Clause.INSERT ) {
renderTableReferenceIdentificationVariable( tableReference );
}
}
@Override
protected void visitConflictClause(ConflictClause conflictClause) {
if ( conflictClause != null ) {
if ( conflictClause.isDoUpdate() && conflictClause.getConstraintName() != null ) {
throw new IllegalQueryOperationException( "Insert conflict do update clause with constraint name is not supported" );
}
}
}
@Override @Override
protected void renderExpressionAsClauseItem(Expression expression) { protected void renderExpressionAsClauseItem(Expression expression) {
expression.accept( this ); expression.accept( this );
@ -283,14 +329,9 @@ public class HSQLSqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAs
return false; return false;
} }
@Override
protected String getFromDual() {
return " from (values(0))";
}
@Override @Override
protected String getFromDualForSelectOnly() { protected String getFromDualForSelectOnly() {
return getFromDual(); return " from " + getDual();
} }
private boolean supportsOffsetFetchClause() { private boolean supportsOffsetFetchClause() {

View File

@ -6,21 +6,36 @@
*/ */
package org.hibernate.dialect; package org.hibernate.dialect;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.collections.Stack;
import org.hibernate.query.sqm.ComparisonOperator; import org.hibernate.query.sqm.ComparisonOperator;
import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
import org.hibernate.sql.ast.tree.MutationStatement;
import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.Statement;
import org.hibernate.sql.ast.tree.delete.DeleteStatement;
import org.hibernate.sql.ast.tree.expression.CastTarget; import org.hibernate.sql.ast.tree.expression.CastTarget;
import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.Literal; import org.hibernate.sql.ast.tree.expression.Literal;
import org.hibernate.sql.ast.tree.expression.Summarization; import org.hibernate.sql.ast.tree.expression.Summarization;
import org.hibernate.sql.ast.tree.from.NamedTableReference;
import org.hibernate.sql.ast.tree.from.QueryPartTableReference; import org.hibernate.sql.ast.tree.from.QueryPartTableReference;
import org.hibernate.sql.ast.tree.insert.ConflictClause;
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate; import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate;
import org.hibernate.sql.ast.tree.predicate.LikePredicate; import org.hibernate.sql.ast.tree.predicate.LikePredicate;
import org.hibernate.sql.ast.tree.select.QueryGroup; import org.hibernate.sql.ast.tree.select.QueryGroup;
import org.hibernate.sql.ast.tree.select.QueryPart; import org.hibernate.sql.ast.tree.select.QueryPart;
import org.hibernate.sql.ast.tree.select.QuerySpec; import org.hibernate.sql.ast.tree.select.QuerySpec;
import org.hibernate.sql.ast.tree.select.SelectStatement;
import org.hibernate.sql.ast.tree.update.UpdateStatement;
import org.hibernate.sql.exec.internal.JdbcOperationQueryInsertImpl;
import org.hibernate.sql.exec.spi.JdbcOperation; import org.hibernate.sql.exec.spi.JdbcOperation;
import org.hibernate.sql.exec.spi.JdbcOperationQueryInsert;
/** /**
* A SQL AST translator for MariaDB. * A SQL AST translator for MariaDB.
@ -36,6 +51,135 @@ public class MariaDBSqlAstTranslator<T extends JdbcOperation> extends AbstractSq
this.dialect = (MariaDBDialect) DialectDelegateWrapper.extractRealDialect( super.getDialect() ); this.dialect = (MariaDBDialect) DialectDelegateWrapper.extractRealDialect( super.getDialect() );
} }
@Override
protected void visitInsertSource(InsertSelectStatement statement) {
if ( statement.getSourceSelectStatement() != null ) {
if ( statement.getConflictClause() != null ) {
final List<ColumnReference> targetColumnReferences = statement.getTargetColumns();
final List<String> columnNames = new ArrayList<>( targetColumnReferences.size() );
for ( ColumnReference targetColumnReference : targetColumnReferences ) {
columnNames.add( targetColumnReference.getColumnExpression() );
}
appendSql( "select * from " );
emulateQueryPartTableReferenceColumnAliasing(
new QueryPartTableReference(
new SelectStatement( statement.getSourceSelectStatement() ),
"excluded",
columnNames,
false,
getSessionFactory()
)
);
}
else {
statement.getSourceSelectStatement().accept( this );
}
}
else {
visitValuesList( statement.getValuesList() );
}
}
@Override
public void visitColumnReference(ColumnReference columnReference) {
final Statement currentStatement;
if ( "excluded".equals( columnReference.getQualifier() )
&& ( currentStatement = getStatementStack().getCurrent() ) instanceof InsertSelectStatement
&& ( (InsertSelectStatement) currentStatement ).getSourceSelectStatement() == null ) {
// Accessing the excluded row for an insert-values statement in the conflict clause requires the values qualifier
appendSql( "values(" );
columnReference.appendReadExpression( this, null );
append( ')' );
}
else {
super.visitColumnReference( columnReference );
}
}
@Override
protected void renderDeleteClause(DeleteStatement statement) {
appendSql( "delete" );
final Stack<Clause> clauseStack = getClauseStack();
try {
clauseStack.push( Clause.DELETE );
renderTableReferenceIdentificationVariable( statement.getTargetTable() );
if ( statement.getFromClause().getRoots().isEmpty() ) {
appendSql( " from " );
renderDmlTargetTableExpression( statement.getTargetTable() );
}
else {
visitFromClause( statement.getFromClause() );
}
}
finally {
clauseStack.pop();
}
}
@Override
protected void renderUpdateClause(UpdateStatement updateStatement) {
if ( updateStatement.getFromClause().getRoots().isEmpty() ) {
super.renderUpdateClause( updateStatement );
}
else {
appendSql( "update " );
renderFromClauseSpaces( updateStatement.getFromClause() );
}
}
@Override
protected void renderDmlTargetTableExpression(NamedTableReference tableReference) {
super.renderDmlTargetTableExpression( tableReference );
if ( getClauseStack().getCurrent() != Clause.INSERT ) {
renderTableReferenceIdentificationVariable( tableReference );
}
}
@Override
protected boolean supportsJoinsInDelete() {
return true;
}
@Override
protected JdbcOperationQueryInsert translateInsert(InsertSelectStatement sqlAst) {
visitInsertStatement( sqlAst );
return new JdbcOperationQueryInsertImpl(
getSql(),
getParameterBinders(),
getAffectedTableNames(),
null
);
}
@Override
protected void visitConflictClause(ConflictClause conflictClause) {
visitOnDuplicateKeyConflictClause( conflictClause );
}
@Override
protected String determineColumnReferenceQualifier(ColumnReference columnReference) {
final DmlTargetColumnQualifierSupport qualifierSupport = getDialect().getDmlTargetColumnQualifierSupport();
final MutationStatement currentDmlStatement;
final String dmlAlias;
// Since MariaDB does not support aliasing the insert target table,
// we must detect column reference that are used in the conflict clause
// and use the table expression as qualifier instead
if ( getClauseStack().getCurrent() != Clause.SET
|| !( ( currentDmlStatement = getCurrentDmlStatement() ) instanceof InsertSelectStatement )
|| ( dmlAlias = currentDmlStatement.getTargetTable().getIdentificationVariable() ) == null
|| !dmlAlias.equals( columnReference.getQualifier() ) ) {
return columnReference.getQualifier();
}
// Qualify the column reference with the table expression also when in subqueries
else if ( qualifierSupport != DmlTargetColumnQualifierSupport.NONE || !getQueryPartStack().isEmpty() ) {
return getCurrentDmlStatement().getTargetTable().getTableExpression();
}
else {
return null;
}
}
@Override @Override
protected boolean supportsWithClauseInSubquery() { protected boolean supportsWithClauseInSubquery() {
return false; return false;
@ -216,8 +360,8 @@ public class MariaDBSqlAstTranslator<T extends JdbcOperation> extends AbstractSq
} }
@Override @Override
protected String getFromDual() { protected String getDual() {
return " from dual"; return "dual";
} }
@Override @Override

View File

@ -1513,4 +1513,14 @@ public class MySQLDialect extends Dialect {
public String getEnableConstraintsStatement() { public String getEnableConstraintsStatement() {
return "set foreign_key_checks = 1"; return "set foreign_key_checks = 1";
} }
@Override
public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() {
return DmlTargetColumnQualifierSupport.TABLE_ALIAS;
}
@Override
public boolean supportsFromClauseInUpdate() {
return true;
}
} }

View File

@ -6,25 +6,39 @@
*/ */
package org.hibernate.dialect; package org.hibernate.dialect;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale; import java.util.Locale;
import org.hibernate.engine.jdbc.Size; import org.hibernate.engine.jdbc.Size;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.collections.Stack;
import org.hibernate.query.sqm.ComparisonOperator; import org.hibernate.query.sqm.ComparisonOperator;
import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
import org.hibernate.sql.ast.tree.MutationStatement;
import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.Statement;
import org.hibernate.sql.ast.tree.delete.DeleteStatement;
import org.hibernate.sql.ast.tree.expression.CastTarget; import org.hibernate.sql.ast.tree.expression.CastTarget;
import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.Literal; import org.hibernate.sql.ast.tree.expression.Literal;
import org.hibernate.sql.ast.tree.expression.Summarization; import org.hibernate.sql.ast.tree.expression.Summarization;
import org.hibernate.sql.ast.tree.from.NamedTableReference;
import org.hibernate.sql.ast.tree.from.QueryPartTableReference; import org.hibernate.sql.ast.tree.from.QueryPartTableReference;
import org.hibernate.sql.ast.tree.from.ValuesTableReference; import org.hibernate.sql.ast.tree.from.ValuesTableReference;
import org.hibernate.sql.ast.tree.insert.ConflictClause;
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate; import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate;
import org.hibernate.sql.ast.tree.predicate.LikePredicate; import org.hibernate.sql.ast.tree.predicate.LikePredicate;
import org.hibernate.sql.ast.tree.select.QueryGroup; import org.hibernate.sql.ast.tree.select.QueryGroup;
import org.hibernate.sql.ast.tree.select.QueryPart; import org.hibernate.sql.ast.tree.select.QueryPart;
import org.hibernate.sql.ast.tree.select.QuerySpec; import org.hibernate.sql.ast.tree.select.QuerySpec;
import org.hibernate.sql.ast.tree.select.SelectStatement;
import org.hibernate.sql.ast.tree.update.UpdateStatement;
import org.hibernate.sql.exec.internal.JdbcOperationQueryInsertImpl;
import org.hibernate.sql.exec.spi.JdbcOperation; import org.hibernate.sql.exec.spi.JdbcOperation;
import org.hibernate.sql.exec.spi.JdbcOperationQueryInsert;
/** /**
* A SQL AST translator for MySQL. * A SQL AST translator for MySQL.
@ -87,6 +101,146 @@ public class MySQLSqlAstTranslator<T extends JdbcOperation> extends AbstractSqlA
return sqlType; return sqlType;
} }
@Override
protected void visitInsertSource(InsertSelectStatement statement) {
if ( statement.getSourceSelectStatement() != null ) {
if ( statement.getConflictClause() != null ) {
final List<ColumnReference> targetColumnReferences = statement.getTargetColumns();
final List<String> columnNames = new ArrayList<>( targetColumnReferences.size() );
for ( ColumnReference targetColumnReference : targetColumnReferences ) {
columnNames.add( targetColumnReference.getColumnExpression() );
}
appendSql( "select * from " );
emulateQueryPartTableReferenceColumnAliasing(
new QueryPartTableReference(
new SelectStatement( statement.getSourceSelectStatement() ),
"excluded",
columnNames,
false,
getSessionFactory()
)
);
}
else {
statement.getSourceSelectStatement().accept( this );
}
}
else {
visitValuesList( statement.getValuesList() );
if ( statement.getConflictClause() != null && getDialect().getMySQLVersion().isSameOrAfter( 8, 0, 19 ) ) {
appendSql( " as excluded" );
char separator = '(';
for ( ColumnReference targetColumn : statement.getTargetColumns() ) {
appendSql( separator );
appendSql( targetColumn.getColumnExpression() );
separator = ',';
}
appendSql( ')' );
}
}
}
@Override
public void visitColumnReference(ColumnReference columnReference) {
final Statement currentStatement;
if ( getDialect().getMySQLVersion().isBefore( 8, 0, 19 )
&& "excluded".equals( columnReference.getQualifier() )
&& ( currentStatement = getStatementStack().getCurrent() ) instanceof InsertSelectStatement
&& ( (InsertSelectStatement) currentStatement ).getSourceSelectStatement() == null ) {
// Accessing the excluded row for an insert-values statement in the conflict clause requires the values qualifier
appendSql( "values(" );
columnReference.appendReadExpression( this, null );
append( ')' );
}
else {
super.visitColumnReference( columnReference );
}
}
@Override
protected void renderDeleteClause(DeleteStatement statement) {
appendSql( "delete" );
final Stack<Clause> clauseStack = getClauseStack();
try {
clauseStack.push( Clause.DELETE );
renderTableReferenceIdentificationVariable( statement.getTargetTable() );
if ( statement.getFromClause().getRoots().isEmpty() ) {
appendSql( " from " );
renderDmlTargetTableExpression( statement.getTargetTable() );
}
else {
visitFromClause( statement.getFromClause() );
}
}
finally {
clauseStack.pop();
}
}
@Override
protected void renderUpdateClause(UpdateStatement updateStatement) {
if ( updateStatement.getFromClause().getRoots().isEmpty() ) {
super.renderUpdateClause( updateStatement );
}
else {
appendSql( "update " );
renderFromClauseSpaces( updateStatement.getFromClause() );
}
}
@Override
protected void renderDmlTargetTableExpression(NamedTableReference tableReference) {
super.renderDmlTargetTableExpression( tableReference );
if ( getClauseStack().getCurrent() != Clause.INSERT ) {
renderTableReferenceIdentificationVariable( tableReference );
}
}
@Override
protected boolean supportsJoinsInDelete() {
return true;
}
@Override
protected JdbcOperationQueryInsert translateInsert(InsertSelectStatement sqlAst) {
visitInsertStatement( sqlAst );
return new JdbcOperationQueryInsertImpl(
getSql(),
getParameterBinders(),
getAffectedTableNames(),
null
);
}
@Override
protected void visitConflictClause(ConflictClause conflictClause) {
visitOnDuplicateKeyConflictClause( conflictClause );
}
@Override
protected String determineColumnReferenceQualifier(ColumnReference columnReference) {
final DmlTargetColumnQualifierSupport qualifierSupport = getDialect().getDmlTargetColumnQualifierSupport();
final MutationStatement currentDmlStatement;
final String dmlAlias;
// Since MySQL does not support aliasing the insert target table,
// we must detect column reference that are used in the conflict clause
// and use the table expression as qualifier instead
if ( getClauseStack().getCurrent() != Clause.SET
|| !( ( currentDmlStatement = getCurrentDmlStatement() ) instanceof InsertSelectStatement )
|| ( dmlAlias = currentDmlStatement.getTargetTable().getIdentificationVariable() ) == null
|| !dmlAlias.equals( columnReference.getQualifier() ) ) {
return columnReference.getQualifier();
}
// Qualify the column reference with the table expression also when in subqueries
else if ( qualifierSupport != DmlTargetColumnQualifierSupport.NONE || !getQueryPartStack().isEmpty() ) {
return getCurrentDmlStatement().getTargetTable().getTableExpression();
}
else {
return null;
}
}
@Override @Override
protected void renderExpressionAsClauseItem(Expression expression) { protected void renderExpressionAsClauseItem(Expression expression) {
expression.accept( this ); expression.accept( this );
@ -275,8 +429,8 @@ public class MySQLSqlAstTranslator<T extends JdbcOperation> extends AbstractSqlA
} }
@Override @Override
protected String getFromDual() { protected String getDual() {
return " from dual"; return "dual";
} }
@Override @Override

View File

@ -1048,6 +1048,7 @@ public class OracleDialect extends Dialect {
@Override @Override
public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() { public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() {
return (sqlException, message, sql) -> { return (sqlException, message, sql) -> {
final String constraintName;
// interpreting Oracle exceptions is much much more precise based on their specific vendor codes. // interpreting Oracle exceptions is much much more precise based on their specific vendor codes.
switch ( JdbcExceptionHelper.extractErrorCode( sqlException ) ) { switch ( JdbcExceptionHelper.extractErrorCode( sqlException ) ) {
@ -1080,9 +1081,19 @@ public class OracleDialect extends Dialect {
// data integrity violation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // data integrity violation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
case 1:
// ORA-00001: unique constraint violated
constraintName = getViolatedConstraintNameExtractor().extractConstraintName( sqlException );
return new ConstraintViolationException(
message,
sqlException,
sql,
ConstraintViolationException.ConstraintKind.UNIQUE,
constraintName
);
case 1407: case 1407:
// ORA-01407: cannot update column to NULL // ORA-01407: cannot update column to NULL
final String constraintName = getViolatedConstraintNameExtractor().extractConstraintName( sqlException ); constraintName = getViolatedConstraintNameExtractor().extractConstraintName( sqlException );
return new ConstraintViolationException( message, sqlException, sql, constraintName ); return new ConstraintViolationException( message, sqlException, sql, constraintName );
default: default:
@ -1564,4 +1575,9 @@ public class OracleDialect extends Dialect {
public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() { public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() {
return DmlTargetColumnQualifierSupport.TABLE_ALIAS; return DmlTargetColumnQualifierSupport.TABLE_ALIAS;
} }
@Override
public boolean supportsFromClauseInUpdate() {
return true;
}
} }

View File

@ -33,25 +33,28 @@ import org.hibernate.sql.ast.tree.expression.SqlTupleContainer;
import org.hibernate.sql.ast.tree.expression.Summarization; import org.hibernate.sql.ast.tree.expression.Summarization;
import org.hibernate.sql.ast.tree.from.FromClause; import org.hibernate.sql.ast.tree.from.FromClause;
import org.hibernate.sql.ast.tree.from.FunctionTableReference; import org.hibernate.sql.ast.tree.from.FunctionTableReference;
import org.hibernate.sql.ast.tree.from.NamedTableReference;
import org.hibernate.sql.ast.tree.from.QueryPartTableReference; import org.hibernate.sql.ast.tree.from.QueryPartTableReference;
import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.UnionTableGroup; import org.hibernate.sql.ast.tree.from.UnionTableGroup;
import org.hibernate.sql.ast.tree.from.ValuesTableReference; import org.hibernate.sql.ast.tree.from.ValuesTableReference;
import org.hibernate.sql.ast.tree.insert.ConflictClause;
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement; import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
import org.hibernate.sql.ast.tree.insert.Values; import org.hibernate.sql.ast.tree.insert.Values;
import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate; import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate;
import org.hibernate.sql.ast.tree.predicate.Predicate;
import org.hibernate.sql.ast.tree.select.QueryGroup; import org.hibernate.sql.ast.tree.select.QueryGroup;
import org.hibernate.sql.ast.tree.select.QueryPart; import org.hibernate.sql.ast.tree.select.QueryPart;
import org.hibernate.sql.ast.tree.select.QuerySpec; import org.hibernate.sql.ast.tree.select.QuerySpec;
import org.hibernate.sql.ast.tree.select.SelectClause; import org.hibernate.sql.ast.tree.select.SelectClause;
import org.hibernate.sql.ast.tree.select.SortSpecification; import org.hibernate.sql.ast.tree.select.SortSpecification;
import org.hibernate.sql.ast.tree.update.Assignment; import org.hibernate.sql.ast.tree.update.Assignment;
import org.hibernate.sql.ast.tree.update.UpdateStatement;
import org.hibernate.sql.exec.spi.JdbcOperation; import org.hibernate.sql.exec.spi.JdbcOperation;
import org.hibernate.sql.model.ast.ColumnValueBinding; import org.hibernate.sql.model.ast.ColumnValueBinding;
import org.hibernate.sql.model.internal.OptionalTableUpdate; import org.hibernate.sql.model.internal.OptionalTableUpdate;
import org.hibernate.sql.results.internal.SqlSelectionImpl; import org.hibernate.sql.results.internal.SqlSelectionImpl;
import org.hibernate.type.SqlTypes; import org.hibernate.type.SqlTypes;
import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.jdbc.JdbcType;
/** /**
@ -65,6 +68,54 @@ public class OracleSqlAstTranslator<T extends JdbcOperation> extends SqlAstTrans
super( sessionFactory, statement ); super( sessionFactory, statement );
} }
@Override
protected void visitInsertStatementOnly(InsertSelectStatement statement) {
if ( statement.getConflictClause() == null || statement.getConflictClause().isDoNothing() ) {
// Render plain insert statement and possibly run into unique constraint violation
super.visitInsertStatementOnly( statement );
}
else {
visitInsertStatementEmulateMerge( statement );
}
}
@Override
protected void visitUpdateStatementOnly(UpdateStatement statement) {
if ( hasNonTrivialFromClause( statement.getFromClause() ) ) {
visitUpdateStatementEmulateInlineView( statement );
}
else {
renderUpdateClause( statement );
renderSetClause( statement.getAssignments() );
visitWhereClause( statement.getRestriction() );
visitReturningColumns( statement.getReturningColumns() );
}
}
@Override
protected void renderMergeUpdateClause(List<Assignment> assignments, Predicate wherePredicate) {
appendSql( " then update" );
renderSetClause( assignments );
visitWhereClause( wherePredicate );
}
@Override
protected void renderDmlTargetTableExpression(NamedTableReference tableReference) {
super.renderDmlTargetTableExpression( tableReference );
if ( getClauseStack().getCurrent() != Clause.INSERT ) {
renderTableReferenceIdentificationVariable( tableReference );
}
}
@Override
protected void visitConflictClause(ConflictClause conflictClause) {
if ( conflictClause != null ) {
if ( conflictClause.isDoUpdate() && conflictClause.getConstraintName() != null ) {
throw new IllegalQueryOperationException( "Insert conflict do update clause with constraint name is not supported" );
}
}
}
@Override @Override
protected boolean needsRecursiveKeywordInWithClause() { protected boolean needsRecursiveKeywordInWithClause() {
return false; return false;
@ -171,7 +222,14 @@ public class OracleSqlAstTranslator<T extends JdbcOperation> extends SqlAstTrans
@Override @Override
protected void visitValuesList(List<Values> valuesList) { protected void visitValuesList(List<Values> valuesList) {
visitValuesListEmulateSelectUnion( valuesList ); if ( valuesList.size() < 2 ) {
visitValuesListStandard( valuesList );
}
else {
// Oracle doesn't support a multi-values insert
// So we render a select union emulation instead
visitValuesListEmulateSelectUnion( valuesList );
}
} }
@Override @Override
@ -555,13 +613,13 @@ public class OracleSqlAstTranslator<T extends JdbcOperation> extends SqlAstTrans
} }
@Override @Override
protected String getFromDual() { protected String getDual() {
return " from dual"; return "dual";
} }
@Override @Override
protected String getFromDualForSelectOnly() { protected String getFromDualForSelectOnly() {
return getDialect().getVersion().isSameOrAfter( 23 ) ? super.getFromDualForSelectOnly() : getFromDual(); return getDialect().getVersion().isSameOrAfter( 23 ) ? "" : ( " from " + getDual() );
} }
private boolean supportsOffsetFetchClause() { private boolean supportsOffsetFetchClause() {

View File

@ -787,6 +787,10 @@ public class PostgreSQLDialect extends Dialect {
public boolean supportsNonQueryWithCTE() { public boolean supportsNonQueryWithCTE() {
return true; return true;
} }
@Override
public boolean supportsConflictClauseForInsertCTE() {
return true;
}
@Override @Override
public SequenceSupport getSequenceSupport() { public SequenceSupport getSequenceSupport() {
@ -1547,4 +1551,14 @@ public class PostgreSQLDialect extends Dialect {
// The maximum scale for `interval second` is 6 unfortunately // The maximum scale for `interval second` is 6 unfortunately
return 6; return 6;
} }
@Override
public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() {
return DmlTargetColumnQualifierSupport.TABLE_ALIAS;
}
@Override
public boolean supportsFromClauseInUpdate() {
return true;
}
} }

View File

@ -10,6 +10,7 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.JdbcMappingContainer; import org.hibernate.metamodel.mapping.JdbcMappingContainer;
import org.hibernate.query.sqm.ComparisonOperator; import org.hibernate.query.sqm.ComparisonOperator;
import org.hibernate.query.sqm.FetchClauseType; import org.hibernate.query.sqm.FetchClauseType;
import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.Statement;
import org.hibernate.sql.ast.tree.cte.CteMaterialization; import org.hibernate.sql.ast.tree.cte.CteMaterialization;
import org.hibernate.sql.ast.tree.cte.CteStatement; import org.hibernate.sql.ast.tree.cte.CteStatement;
@ -17,6 +18,10 @@ import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression;
import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.Literal; import org.hibernate.sql.ast.tree.expression.Literal;
import org.hibernate.sql.ast.tree.expression.Summarization; import org.hibernate.sql.ast.tree.expression.Summarization;
import org.hibernate.sql.ast.tree.from.NamedTableReference;
import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.insert.ConflictClause;
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate; import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate;
import org.hibernate.sql.ast.tree.predicate.InArrayPredicate; import org.hibernate.sql.ast.tree.predicate.InArrayPredicate;
import org.hibernate.sql.ast.tree.predicate.LikePredicate; import org.hibernate.sql.ast.tree.predicate.LikePredicate;
@ -24,7 +29,10 @@ import org.hibernate.sql.ast.tree.predicate.NullnessPredicate;
import org.hibernate.sql.ast.tree.select.QueryGroup; import org.hibernate.sql.ast.tree.select.QueryGroup;
import org.hibernate.sql.ast.tree.select.QueryPart; import org.hibernate.sql.ast.tree.select.QueryPart;
import org.hibernate.sql.ast.tree.select.QuerySpec; import org.hibernate.sql.ast.tree.select.QuerySpec;
import org.hibernate.sql.ast.tree.update.UpdateStatement;
import org.hibernate.sql.exec.internal.JdbcOperationQueryInsertImpl;
import org.hibernate.sql.exec.spi.JdbcOperation; import org.hibernate.sql.exec.spi.JdbcOperation;
import org.hibernate.sql.exec.spi.JdbcOperationQueryInsert;
import org.hibernate.sql.model.internal.TableInsertStandard; import org.hibernate.sql.model.internal.TableInsertStandard;
import org.hibernate.type.SqlTypes; import org.hibernate.type.SqlTypes;
@ -58,6 +66,55 @@ public class PostgreSQLSqlAstTranslator<T extends JdbcOperation> extends SqlAstT
appendSql( "default values" ); appendSql( "default values" );
} }
@Override
protected JdbcOperationQueryInsert translateInsert(InsertSelectStatement sqlAst) {
visitInsertStatement( sqlAst );
return new JdbcOperationQueryInsertImpl(
getSql(),
getParameterBinders(),
getAffectedTableNames(),
null
);
}
@Override
protected void renderTableReferenceIdentificationVariable(TableReference tableReference) {
final String identificationVariable = tableReference.getIdentificationVariable();
if ( identificationVariable != null ) {
final Clause currentClause = getClauseStack().getCurrent();
if ( currentClause == Clause.INSERT ) {
// PostgreSQL requires the "as" keyword for inserts
appendSql( " as " );
}
else {
append( WHITESPACE );
}
append( tableReference.getIdentificationVariable() );
}
}
@Override
protected void renderDmlTargetTableExpression(NamedTableReference tableReference) {
super.renderDmlTargetTableExpression( tableReference );
final Statement currentStatement = getStatementStack().getCurrent();
if ( !( currentStatement instanceof UpdateStatement )
|| !hasNonTrivialFromClause( ( (UpdateStatement) currentStatement ).getFromClause() ) ) {
// For UPDATE statements we render a full FROM clause and a join condition to match target table rows,
// but for that to work, we have to omit the alias for the target table reference here
renderTableReferenceIdentificationVariable( tableReference );
}
}
@Override
protected void renderFromClauseAfterUpdateSet(UpdateStatement statement) {
renderFromClauseJoiningDmlTargetReference( statement );
}
@Override
protected void visitConflictClause(ConflictClause conflictClause) {
visitStandardConflictClause( conflictClause );
}
@Override @Override
protected void renderExpressionAsClauseItem(Expression expression) { protected void renderExpressionAsClauseItem(Expression expression) {

View File

@ -49,6 +49,8 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.exception.ConstraintViolationException; import org.hibernate.exception.ConstraintViolationException;
import org.hibernate.exception.LockTimeoutException; import org.hibernate.exception.LockTimeoutException;
import org.hibernate.exception.spi.SQLExceptionConversionDelegate; import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor;
import org.hibernate.exception.spi.ViolatedConstraintNameExtractor;
import org.hibernate.internal.util.JdbcExceptionHelper; import org.hibernate.internal.util.JdbcExceptionHelper;
import org.hibernate.mapping.Column; import org.hibernate.mapping.Column;
import org.hibernate.persister.entity.mutation.EntityMutationTarget; import org.hibernate.persister.entity.mutation.EntityMutationTarget;
@ -84,6 +86,7 @@ import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
import jakarta.persistence.TemporalType; import jakarta.persistence.TemporalType;
import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate;
import static org.hibernate.query.sqm.TemporalUnit.NANOSECOND; import static org.hibernate.query.sqm.TemporalUnit.NANOSECOND;
import static org.hibernate.query.sqm.produce.function.FunctionParameterType.INTEGER; import static org.hibernate.query.sqm.produce.function.FunctionParameterType.INTEGER;
import static org.hibernate.type.SqlTypes.BLOB; import static org.hibernate.type.SqlTypes.BLOB;
@ -699,6 +702,21 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
return true; return true;
} }
@Override
public ViolatedConstraintNameExtractor getViolatedConstraintNameExtractor() {
return new TemplatedViolatedConstraintNameExtractor(
sqle -> {
switch ( JdbcExceptionHelper.extractErrorCode( sqle ) ) {
case 2627:
case 2601:
return extractUsingTemplate( "'", "'", sqle.getMessage() );
default:
return null;
}
}
);
}
@Override @Override
public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() { public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() {
return (sqlException, message, sql) -> { return (sqlException, message, sql) -> {
@ -712,6 +730,13 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
case 1222: case 1222:
return new LockTimeoutException( message, sqlException, sql ); return new LockTimeoutException( message, sqlException, sql );
case 2627: case 2627:
return new ConstraintViolationException(
message,
sqlException,
sql,
ConstraintViolationException.ConstraintKind.UNIQUE,
getViolatedConstraintNameExtractor().extractConstraintName( sqlException )
);
case 2601: case 2601:
return new ConstraintViolationException( return new ConstraintViolationException(
message, message,
@ -1106,4 +1131,14 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
final SQLServerSqlAstTranslator<JdbcOperation> translator = new SQLServerSqlAstTranslator<>( factory, optionalTableUpdate ); final SQLServerSqlAstTranslator<JdbcOperation> translator = new SQLServerSqlAstTranslator<>( factory, optionalTableUpdate );
return translator.createMergeOperation( optionalTableUpdate ); return translator.createMergeOperation( optionalTableUpdate );
} }
@Override
public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() {
return DmlTargetColumnQualifierSupport.TABLE_ALIAS;
}
@Override
public boolean supportsFromClauseInUpdate() {
return true;
}
} }

View File

@ -11,14 +11,19 @@ import java.util.List;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.LockOptions; import org.hibernate.LockOptions;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.collections.Stack;
import org.hibernate.metamodel.mapping.JdbcMappingContainer; import org.hibernate.metamodel.mapping.JdbcMappingContainer;
import org.hibernate.query.IllegalQueryOperationException;
import org.hibernate.query.sqm.ComparisonOperator; import org.hibernate.query.sqm.ComparisonOperator;
import org.hibernate.query.sqm.FetchClauseType; import org.hibernate.query.sqm.FetchClauseType;
import org.hibernate.sql.ast.Clause; import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.SqlAstJoinType; import org.hibernate.sql.ast.SqlAstJoinType;
import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.ast.tree.MutationStatement;
import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.Statement;
import org.hibernate.sql.ast.tree.delete.DeleteStatement;
import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression; import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression;
import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.Literal; import org.hibernate.sql.ast.tree.expression.Literal;
import org.hibernate.sql.ast.tree.expression.SqlTuple; import org.hibernate.sql.ast.tree.expression.SqlTuple;
@ -28,12 +33,15 @@ import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableGroupJoin; import org.hibernate.sql.ast.tree.from.TableGroupJoin;
import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.from.UnionTableReference; import org.hibernate.sql.ast.tree.from.UnionTableReference;
import org.hibernate.sql.ast.tree.insert.ConflictClause;
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
import org.hibernate.sql.ast.tree.predicate.Predicate; import org.hibernate.sql.ast.tree.predicate.Predicate;
import org.hibernate.sql.ast.tree.select.QueryGroup; import org.hibernate.sql.ast.tree.select.QueryGroup;
import org.hibernate.sql.ast.tree.select.QueryPart; import org.hibernate.sql.ast.tree.select.QueryPart;
import org.hibernate.sql.ast.tree.select.QuerySpec; import org.hibernate.sql.ast.tree.select.QuerySpec;
import org.hibernate.sql.ast.tree.select.SelectClause; import org.hibernate.sql.ast.tree.select.SelectClause;
import org.hibernate.sql.ast.tree.select.SortSpecification; import org.hibernate.sql.ast.tree.select.SortSpecification;
import org.hibernate.sql.ast.tree.update.UpdateStatement;
import org.hibernate.sql.exec.spi.JdbcOperation; import org.hibernate.sql.exec.spi.JdbcOperation;
import org.hibernate.sql.model.internal.OptionalTableUpdate; import org.hibernate.sql.model.internal.OptionalTableUpdate;
import org.hibernate.type.SqlTypes; import org.hibernate.type.SqlTypes;
@ -53,6 +61,85 @@ public class SQLServerSqlAstTranslator<T extends JdbcOperation> extends SqlAstTr
super( sessionFactory, statement ); super( sessionFactory, statement );
} }
@Override
protected void visitInsertStatementOnly(InsertSelectStatement statement) {
if ( statement.getConflictClause() == null || statement.getConflictClause().isDoNothing() ) {
// Render plain insert statement and possibly run into unique constraint violation
super.visitInsertStatementOnly( statement );
}
else {
visitInsertStatementEmulateMerge( statement );
// A merge statement must end with a `;` on SQL Server
appendSql( ';' );
}
}
@Override
protected void renderDeleteClause(DeleteStatement statement) {
appendSql( "delete" );
final Stack<Clause> clauseStack = getClauseStack();
try {
clauseStack.push( Clause.DELETE );
renderTableReferenceIdentificationVariable( statement.getTargetTable() );
if ( statement.getFromClause().getRoots().isEmpty() ) {
appendSql( " from " );
renderDmlTargetTableExpression( statement.getTargetTable() );
}
else {
visitFromClause( statement.getFromClause() );
}
}
finally {
clauseStack.pop();
}
}
@Override
protected void renderUpdateClause(UpdateStatement updateStatement) {
appendSql( "update" );
final Stack<Clause> clauseStack = getClauseStack();
try {
clauseStack.push( Clause.UPDATE );
renderTableReferenceIdentificationVariable( updateStatement.getTargetTable() );
}
finally {
clauseStack.pop();
}
}
@Override
protected void renderDmlTargetTableExpression(NamedTableReference tableReference) {
super.renderDmlTargetTableExpression( tableReference );
if ( getClauseStack().getCurrent() != Clause.INSERT ) {
renderTableReferenceIdentificationVariable( tableReference );
}
}
@Override
protected boolean supportsJoinsInDelete() {
return true;
}
@Override
protected void renderFromClauseAfterUpdateSet(UpdateStatement statement) {
if ( statement.getFromClause().getRoots().isEmpty() ) {
appendSql( " from " );
renderDmlTargetTableExpression( statement.getTargetTable() );
}
else {
visitFromClause( statement.getFromClause() );
}
}
@Override
protected void visitConflictClause(ConflictClause conflictClause) {
if ( conflictClause != null ) {
if ( conflictClause.isDoUpdate() && conflictClause.getConstraintName() != null ) {
throw new IllegalQueryOperationException( "Insert conflict do update clause with constraint name is not supported" );
}
}
}
@Override @Override
protected boolean needsRecursiveKeywordInWithClause() { protected boolean needsRecursiveKeywordInWithClause() {
return false; return false;
@ -128,14 +215,7 @@ public class SQLServerSqlAstTranslator<T extends JdbcOperation> extends SqlAstTr
appendSql( " )" ); appendSql( " )" );
registerAffectedTable( tableReference ); registerAffectedTable( tableReference );
final Clause currentClause = getClauseStack().getCurrent(); renderTableReferenceIdentificationVariable( tableReference );
if ( rendersTableReferenceAlias( currentClause ) ) {
final String identificationVariable = tableReference.getIdentificationVariable();
if ( identificationVariable != null ) {
appendSql( ' ' );
appendSql( identificationVariable );
}
}
} }
else { else {
super.renderNamedTableReference( tableReference, lockMode ); super.renderNamedTableReference( tableReference, lockMode );

View File

@ -633,28 +633,17 @@ public class SybaseASEDialect extends SybaseDialect {
final int errorCode = JdbcExceptionHelper.extractErrorCode( sqle ); final int errorCode = JdbcExceptionHelper.extractErrorCode( sqle );
if ( sqlState != null ) { if ( sqlState != null ) {
switch ( sqlState ) { switch ( sqlState ) {
// UNIQUE VIOLATION
case "S1000": case "S1000":
if ( 2601 == errorCode ) {
return extractUsingTemplate( "with unique index '", "'", sqle.getMessage() );
}
break;
case "23000": case "23000":
if ( 546 == errorCode ) { switch ( errorCode ) {
// Foreign key violation case 2601:
return extractUsingTemplate( "constraint name = '", "'", sqle.getMessage() ); // UNIQUE VIOLATION
return extractUsingTemplate( "with unique index '", "'", sqle.getMessage() );
case 546:
// Foreign key violation
return extractUsingTemplate( "constraint name = '", "'", sqle.getMessage() );
} }
break; break;
// // FOREIGN KEY VIOLATION
// case 23503:
// return extractUsingTemplate( "violates foreign key constraint \"","\"", sqle.getMessage() );
// // NOT NULL VIOLATION
// case 23502:
// return extractUsingTemplate( "null value in column \"","\" violates not-null constraint", sqle.getMessage() );
// // TODO: RESTRICT VIOLATION
// case 23001:
// return null;
// ALL OTHER
} }
} }
return null; return null;
@ -671,30 +660,44 @@ public class SybaseASEDialect extends SybaseDialect {
case "JZ006": case "JZ006":
return new LockTimeoutException( message, sqlException, sql ); return new LockTimeoutException( message, sqlException, sql );
case "S1000": case "S1000":
case "23000":
switch ( errorCode ) { switch ( errorCode ) {
case 515: case 515:
// Attempt to insert NULL value into column; column does not allow nulls. // Attempt to insert NULL value into column; column does not allow nulls.
return new ConstraintViolationException(
message,
sqlException,
sql,
getViolatedConstraintNameExtractor().extractConstraintName( sqlException )
);
case 546:
// Foreign key violation
return new ConstraintViolationException(
message,
sqlException,
sql,
getViolatedConstraintNameExtractor().extractConstraintName( sqlException )
);
case 2601: case 2601:
// Unique constraint violation // Unique constraint violation
final String constraintName = getViolatedConstraintNameExtractor().extractConstraintName( return new ConstraintViolationException(
sqlException ); message,
return new ConstraintViolationException( message, sqlException, sql, constraintName ); sqlException,
sql,
ConstraintViolationException.ConstraintKind.UNIQUE,
getViolatedConstraintNameExtractor().extractConstraintName( sqlException )
);
} }
break; break;
case "ZZZZZ": case "ZZZZZ":
if ( 515 == errorCode ) { if ( 515 == errorCode ) {
// Attempt to insert NULL value into column; column does not allow nulls. // Attempt to insert NULL value into column; column does not allow nulls.
final String constraintName = getViolatedConstraintNameExtractor().extractConstraintName( return new ConstraintViolationException(
sqlException ); message,
return new ConstraintViolationException( message, sqlException, sql, constraintName ); sqlException,
} sql,
break; getViolatedConstraintNameExtractor().extractConstraintName( sqlException )
case "23000": );
if ( 546 == errorCode ) {
// Foreign key violation
final String constraintName = getViolatedConstraintNameExtractor().extractConstraintName(
sqlException );
return new ConstraintViolationException( message, sqlException, sql, constraintName );
} }
break; break;
} }

View File

@ -12,13 +12,17 @@ import java.util.function.Consumer;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.LockOptions; import org.hibernate.LockOptions;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.collections.Stack;
import org.hibernate.query.IllegalQueryOperationException;
import org.hibernate.query.sqm.ComparisonOperator; import org.hibernate.query.sqm.ComparisonOperator;
import org.hibernate.sql.ast.Clause; import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.SqlAstJoinType; import org.hibernate.sql.ast.SqlAstJoinType;
import org.hibernate.sql.ast.SqlAstNodeRenderingMode; import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.ast.tree.MutationStatement;
import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.Statement;
import org.hibernate.sql.ast.tree.delete.DeleteStatement;
import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression; import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression;
import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression; import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression;
import org.hibernate.sql.ast.tree.expression.CaseSimpleExpression; import org.hibernate.sql.ast.tree.expression.CaseSimpleExpression;
@ -33,6 +37,9 @@ import org.hibernate.sql.ast.tree.from.NamedTableReference;
import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableGroupJoin; import org.hibernate.sql.ast.tree.from.TableGroupJoin;
import org.hibernate.sql.ast.tree.from.UnionTableReference; import org.hibernate.sql.ast.tree.from.UnionTableReference;
import org.hibernate.sql.ast.tree.from.ValuesTableReference;
import org.hibernate.sql.ast.tree.insert.ConflictClause;
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
import org.hibernate.sql.ast.tree.insert.Values; import org.hibernate.sql.ast.tree.insert.Values;
import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate; import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate;
import org.hibernate.sql.ast.tree.predicate.Predicate; import org.hibernate.sql.ast.tree.predicate.Predicate;
@ -40,6 +47,7 @@ import org.hibernate.sql.ast.tree.select.QueryGroup;
import org.hibernate.sql.ast.tree.select.QueryPart; import org.hibernate.sql.ast.tree.select.QueryPart;
import org.hibernate.sql.ast.tree.select.QuerySpec; import org.hibernate.sql.ast.tree.select.QuerySpec;
import org.hibernate.sql.ast.tree.select.SelectClause; import org.hibernate.sql.ast.tree.select.SelectClause;
import org.hibernate.sql.ast.tree.update.UpdateStatement;
import org.hibernate.sql.exec.spi.JdbcOperation; import org.hibernate.sql.exec.spi.JdbcOperation;
/** /**
@ -55,6 +63,77 @@ public class SybaseASESqlAstTranslator<T extends JdbcOperation> extends Abstract
super( sessionFactory, statement ); super( sessionFactory, statement );
} }
@Override
protected void visitInsertStatementOnly(InsertSelectStatement statement) {
if ( statement.getConflictClause() == null || statement.getConflictClause().isDoNothing() ) {
// Render plain insert statement and possibly run into unique constraint violation
super.visitInsertStatementOnly( statement );
}
else {
visitInsertStatementEmulateMerge( statement );
}
}
@Override
protected void renderDeleteClause(DeleteStatement statement) {
appendSql( "delete " );
final Stack<Clause> clauseStack = getClauseStack();
try {
clauseStack.push( Clause.DELETE );
renderDmlTargetTableExpression( statement.getTargetTable() );
if ( statement.getFromClause().getRoots().isEmpty() ) {
appendSql( " from " );
renderDmlTargetTableExpression( statement.getTargetTable() );
renderTableReferenceIdentificationVariable( statement.getTargetTable() );
}
else {
visitFromClause( statement.getFromClause() );
}
}
finally {
clauseStack.pop();
}
}
@Override
protected void renderUpdateClause(UpdateStatement updateStatement) {
appendSql( "update " );
final Stack<Clause> clauseStack = getClauseStack();
try {
clauseStack.push( Clause.UPDATE );
renderDmlTargetTableExpression( updateStatement.getTargetTable() );
}
finally {
clauseStack.pop();
}
}
@Override
protected boolean supportsJoinsInDelete() {
return true;
}
@Override
protected void renderFromClauseAfterUpdateSet(UpdateStatement statement) {
if ( statement.getFromClause().getRoots().isEmpty() ) {
appendSql( " from " );
renderDmlTargetTableExpression( statement.getTargetTable() );
renderTableReferenceIdentificationVariable( statement.getTargetTable() );
}
else {
visitFromClause( statement.getFromClause() );
}
}
@Override
protected void visitConflictClause(ConflictClause conflictClause) {
if ( conflictClause != null ) {
if ( conflictClause.isDoUpdate() && conflictClause.getConstraintName() != null ) {
throw new IllegalQueryOperationException( "Insert conflict do update clause with constraint name is not supported" );
}
}
}
@Override @Override
protected boolean supportsWithClause() { protected boolean supportsWithClause() {
return false; return false;
@ -130,14 +209,7 @@ public class SybaseASESqlAstTranslator<T extends JdbcOperation> extends Abstract
appendSql( " )" ); appendSql( " )" );
registerAffectedTable( tableReference ); registerAffectedTable( tableReference );
final Clause currentClause = getClauseStack().getCurrent(); renderTableReferenceIdentificationVariable( tableReference );
if ( rendersTableReferenceAlias( currentClause ) ) {
final String identificationVariable = tableReference.getIdentificationVariable();
if ( identificationVariable != null ) {
appendSql( ' ' );
appendSql( identificationVariable );
}
}
} }
else { else {
super.renderNamedTableReference( tableReference, lockMode ); super.renderNamedTableReference( tableReference, lockMode );
@ -250,6 +322,14 @@ public class SybaseASESqlAstTranslator<T extends JdbcOperation> extends Abstract
visitValuesListEmulateSelectUnion( valuesList ); visitValuesListEmulateSelectUnion( valuesList );
} }
@Override
public void visitValuesTableReference(ValuesTableReference tableReference) {
append( '(' );
visitValuesListEmulateSelectUnion( tableReference.getValuesList() );
append( ')' );
renderDerivedTableReference( tableReference );
}
@Override @Override
public void visitOffsetFetchClause(QueryPart queryPart) { public void visitOffsetFetchClause(QueryPart queryPart) {
assertRowsOnlyFetchClauseType( queryPart ); assertRowsOnlyFetchClauseType( queryPart );
@ -384,63 +464,32 @@ public class SybaseASESqlAstTranslator<T extends JdbcOperation> extends Abstract
} }
@Override @Override
public void visitColumnReference(ColumnReference columnReference) { protected String determineColumnReferenceQualifier(ColumnReference columnReference) {
final String dmlTargetTableAlias = getDmlTargetTableAlias(); final DmlTargetColumnQualifierSupport qualifierSupport = getDialect().getDmlTargetColumnQualifierSupport();
if ( dmlTargetTableAlias != null && dmlTargetTableAlias.equals( columnReference.getQualifier() ) ) { final MutationStatement currentDmlStatement;
// Sybase needs a table name prefix final String dmlAlias;
// but not if this is a restricted union table reference subquery if ( qualifierSupport == DmlTargetColumnQualifierSupport.TABLE_ALIAS
final QuerySpec currentQuerySpec = (QuerySpec) getQueryPartStack().getCurrent(); || ( currentDmlStatement = getCurrentDmlStatement() ) == null
final List<TableGroup> roots; || ( dmlAlias = currentDmlStatement.getTargetTable().getIdentificationVariable() ) == null
if ( currentQuerySpec != null && !currentQuerySpec.isRoot() || !dmlAlias.equals( columnReference.getQualifier() ) ) {
&& (roots = currentQuerySpec.getFromClause().getRoots()).size() == 1 return columnReference.getQualifier();
&& roots.get( 0 ).getPrimaryTableReference() instanceof UnionTableReference ) { }
columnReference.appendReadExpression( this ); // Sybase needs a table name prefix
} // but not if this is a restricted union table reference subquery
// for now, use the unqualified form final QuerySpec currentQuerySpec = (QuerySpec) getQueryPartStack().getCurrent();
else if ( columnReference.isColumnExpressionFormula() ) { final List<TableGroup> roots;
// For formulas, we have to replace the qualifier as the alias was already rendered into the formula if ( currentQuerySpec != null && !currentQuerySpec.isRoot()
// This is fine for now as this is only temporary anyway until we render aliases for table references && (roots = currentQuerySpec.getFromClause().getRoots()).size() == 1
appendSql( && roots.get( 0 ).getPrimaryTableReference() instanceof UnionTableReference ) {
columnReference.getColumnExpression() return columnReference.getQualifier();
.replaceAll( "(\\b)(" + dmlTargetTableAlias + "\\.)(\\b)", "$1$3" ) }
); else if ( columnReference.isColumnExpressionFormula() ) {
} // For formulas, we have to replace the qualifier as the alias was already rendered into the formula
else { // This is fine for now as this is only temporary anyway until we render aliases for table references
columnReference.appendReadExpression( return null;
this,
getCurrentDmlStatement().getTargetTable().getTableExpression()
);
}
} }
else { else {
columnReference.appendReadExpression( this ); return getCurrentDmlStatement().getTargetTable().getTableExpression();
}
}
@Override
public void visitAggregateColumnWriteExpression(AggregateColumnWriteExpression aggregateColumnWriteExpression) {
final String dmlTargetTableAlias = getDmlTargetTableAlias();
final ColumnReference columnReference = aggregateColumnWriteExpression.getColumnReference();
if ( dmlTargetTableAlias != null && dmlTargetTableAlias.equals( columnReference.getQualifier() ) ) {
// Sybase needs a table name prefix
// but not if this is a restricted union table reference subquery
final QuerySpec currentQuerySpec = (QuerySpec) getQueryPartStack().getCurrent();
final List<TableGroup> roots;
if ( currentQuerySpec != null && !currentQuerySpec.isRoot()
&& (roots = currentQuerySpec.getFromClause().getRoots()).size() == 1
&& roots.get( 0 ).getPrimaryTableReference() instanceof UnionTableReference ) {
aggregateColumnWriteExpression.appendWriteExpression( this, this );
}
else {
aggregateColumnWriteExpression.appendWriteExpression(
this,
this,
getCurrentDmlStatement().getTargetTable().getTableExpression()
);
}
}
else {
aggregateColumnWriteExpression.appendWriteExpression( this, this );
} }
} }
@ -465,8 +514,8 @@ public class SybaseASESqlAstTranslator<T extends JdbcOperation> extends Abstract
} }
@Override @Override
protected String getFromDual() { protected String getDual() {
return " from (select 1) dual(c1)"; return "(select 1 c1)";
} }
private boolean supportsParameterOffsetFetchExpression() { private boolean supportsParameterOffsetFetchExpression() {

View File

@ -514,4 +514,14 @@ public class SybaseDialect extends AbstractTransactSQLDialect {
? AbstractTransactSQLIdentityColumnSupport.INSTANCE ? AbstractTransactSQLIdentityColumnSupport.INSTANCE
: SybaseJconnIdentityColumnSupport.INSTANCE; : SybaseJconnIdentityColumnSupport.INSTANCE;
} }
@Override
public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() {
return DmlTargetColumnQualifierSupport.TABLE_ALIAS;
}
@Override
public boolean supportsFromClauseInUpdate() {
return true;
}
} }

View File

@ -11,12 +11,15 @@ import java.util.function.Consumer;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.collections.Stack;
import org.hibernate.query.IllegalQueryOperationException;
import org.hibernate.query.sqm.ComparisonOperator; import org.hibernate.query.sqm.ComparisonOperator;
import org.hibernate.sql.ast.Clause; import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.SqlAstNodeRenderingMode; import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.Statement;
import org.hibernate.sql.ast.tree.delete.DeleteStatement;
import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression; import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression;
import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression; import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression;
import org.hibernate.sql.ast.tree.expression.CaseSimpleExpression; import org.hibernate.sql.ast.tree.expression.CaseSimpleExpression;
@ -26,8 +29,13 @@ import org.hibernate.sql.ast.tree.expression.SqlTuple;
import org.hibernate.sql.ast.tree.expression.Summarization; import org.hibernate.sql.ast.tree.expression.Summarization;
import org.hibernate.sql.ast.tree.from.NamedTableReference; import org.hibernate.sql.ast.tree.from.NamedTableReference;
import org.hibernate.sql.ast.tree.from.UnionTableReference; import org.hibernate.sql.ast.tree.from.UnionTableReference;
import org.hibernate.sql.ast.tree.from.ValuesTableReference;
import org.hibernate.sql.ast.tree.insert.ConflictClause;
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
import org.hibernate.sql.ast.tree.insert.Values;
import org.hibernate.sql.ast.tree.select.QueryPart; import org.hibernate.sql.ast.tree.select.QueryPart;
import org.hibernate.sql.ast.tree.select.QuerySpec; import org.hibernate.sql.ast.tree.select.QuerySpec;
import org.hibernate.sql.ast.tree.update.UpdateStatement;
import org.hibernate.sql.exec.spi.JdbcOperation; import org.hibernate.sql.exec.spi.JdbcOperation;
/** /**
@ -43,6 +51,46 @@ public class SybaseSqlAstTranslator<T extends JdbcOperation> extends AbstractSql
super( sessionFactory, statement ); super( sessionFactory, statement );
} }
@Override
protected void visitInsertStatementOnly(InsertSelectStatement statement) {
if ( statement.getConflictClause() == null || statement.getConflictClause().isDoNothing() ) {
// Render plain insert statement and possibly run into unique constraint violation
super.visitInsertStatementOnly( statement );
}
else {
visitInsertStatementEmulateMerge( statement );
appendSql( ';' );
}
}
@Override
protected void renderDeleteClause(DeleteStatement statement) {
appendSql( "delete " );
final Stack<Clause> clauseStack = getClauseStack();
try {
clauseStack.push( Clause.DELETE );
renderDmlTargetTableExpression( statement.getTargetTable() );
}
finally {
clauseStack.pop();
}
visitFromClause( statement.getFromClause() );
}
@Override
protected void renderFromClauseAfterUpdateSet(UpdateStatement statement) {
visitFromClause( statement.getFromClause() );
}
@Override
protected void visitConflictClause(ConflictClause conflictClause) {
if ( conflictClause != null ) {
if ( conflictClause.isDoUpdate() && conflictClause.getConstraintName() != null ) {
throw new IllegalQueryOperationException( "Insert conflict do update clause with constraint name is not supported" );
}
}
}
@Override @Override
protected boolean supportsWithClause() { protected boolean supportsWithClause() {
return false; return false;
@ -118,14 +166,7 @@ public class SybaseSqlAstTranslator<T extends JdbcOperation> extends AbstractSql
appendSql( " )" ); appendSql( " )" );
registerAffectedTable( tableReference ); registerAffectedTable( tableReference );
final Clause currentClause = getClauseStack().getCurrent(); renderTableReferenceIdentificationVariable( tableReference );
if ( rendersTableReferenceAlias( currentClause ) ) {
final String identificationVariable = tableReference.getIdentificationVariable();
if ( identificationVariable != null ) {
appendSql( ' ' );
appendSql( identificationVariable );
}
}
} }
else { else {
super.renderNamedTableReference( tableReference, lockMode ); super.renderNamedTableReference( tableReference, lockMode );
@ -146,6 +187,19 @@ public class SybaseSqlAstTranslator<T extends JdbcOperation> extends AbstractSql
// Sybase does not support the FOR UPDATE clause // Sybase does not support the FOR UPDATE clause
} }
@Override
protected void visitValuesList(List<Values> valuesList) {
visitValuesListEmulateSelectUnion( valuesList );
}
@Override
public void visitValuesTableReference(ValuesTableReference tableReference) {
append( '(' );
visitValuesListEmulateSelectUnion( tableReference.getValuesList() );
append( ')' );
renderDerivedTableReference( tableReference );
}
@Override @Override
public void visitOffsetFetchClause(QueryPart queryPart) { public void visitOffsetFetchClause(QueryPart queryPart) {
assertRowsOnlyFetchClauseType( queryPart ); assertRowsOnlyFetchClauseType( queryPart );

View File

@ -6,23 +6,38 @@
*/ */
package org.hibernate.dialect; package org.hibernate.dialect;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.LockOptions; import org.hibernate.LockOptions;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.collections.Stack;
import org.hibernate.query.sqm.ComparisonOperator; import org.hibernate.query.sqm.ComparisonOperator;
import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
import org.hibernate.sql.ast.tree.MutationStatement;
import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.Statement;
import org.hibernate.sql.ast.tree.delete.DeleteStatement;
import org.hibernate.sql.ast.tree.expression.CastTarget; import org.hibernate.sql.ast.tree.expression.CastTarget;
import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.Literal; import org.hibernate.sql.ast.tree.expression.Literal;
import org.hibernate.sql.ast.tree.expression.Summarization; import org.hibernate.sql.ast.tree.expression.Summarization;
import org.hibernate.sql.ast.tree.from.NamedTableReference;
import org.hibernate.sql.ast.tree.from.QueryPartTableReference; import org.hibernate.sql.ast.tree.from.QueryPartTableReference;
import org.hibernate.sql.ast.tree.from.ValuesTableReference; import org.hibernate.sql.ast.tree.from.ValuesTableReference;
import org.hibernate.sql.ast.tree.insert.ConflictClause;
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate; import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate;
import org.hibernate.sql.ast.tree.predicate.LikePredicate; import org.hibernate.sql.ast.tree.predicate.LikePredicate;
import org.hibernate.sql.ast.tree.select.QueryGroup; import org.hibernate.sql.ast.tree.select.QueryGroup;
import org.hibernate.sql.ast.tree.select.QueryPart; import org.hibernate.sql.ast.tree.select.QueryPart;
import org.hibernate.sql.ast.tree.select.QuerySpec; import org.hibernate.sql.ast.tree.select.QuerySpec;
import org.hibernate.sql.ast.tree.select.SelectStatement;
import org.hibernate.sql.ast.tree.update.UpdateStatement;
import org.hibernate.sql.exec.internal.JdbcOperationQueryInsertImpl;
import org.hibernate.sql.exec.spi.JdbcOperation; import org.hibernate.sql.exec.spi.JdbcOperation;
import org.hibernate.sql.exec.spi.JdbcOperationQueryInsert;
/** /**
* A SQL AST translator for TiDB. * A SQL AST translator for TiDB.
@ -39,6 +54,135 @@ public class TiDBSqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAs
this.dialect = (TiDBDialect) super.getDialect(); this.dialect = (TiDBDialect) super.getDialect();
} }
@Override
protected void visitInsertSource(InsertSelectStatement statement) {
if ( statement.getSourceSelectStatement() != null ) {
if ( statement.getConflictClause() != null ) {
final List<ColumnReference> targetColumnReferences = statement.getTargetColumns();
final List<String> columnNames = new ArrayList<>( targetColumnReferences.size() );
for ( ColumnReference targetColumnReference : targetColumnReferences ) {
columnNames.add( targetColumnReference.getColumnExpression() );
}
appendSql( "select * from " );
emulateQueryPartTableReferenceColumnAliasing(
new QueryPartTableReference(
new SelectStatement( statement.getSourceSelectStatement() ),
"excluded",
columnNames,
false,
getSessionFactory()
)
);
}
else {
statement.getSourceSelectStatement().accept( this );
}
}
else {
visitValuesList( statement.getValuesList() );
}
}
@Override
public void visitColumnReference(ColumnReference columnReference) {
final Statement currentStatement;
if ( "excluded".equals( columnReference.getQualifier() )
&& ( currentStatement = getStatementStack().getCurrent() ) instanceof InsertSelectStatement
&& ( (InsertSelectStatement) currentStatement ).getSourceSelectStatement() == null ) {
// Accessing the excluded row for an insert-values statement in the conflict clause requires the values qualifier
appendSql( "values(" );
columnReference.appendReadExpression( this, null );
append( ')' );
}
else {
super.visitColumnReference( columnReference );
}
}
@Override
protected void renderDeleteClause(DeleteStatement statement) {
appendSql( "delete" );
final Stack<Clause> clauseStack = getClauseStack();
try {
clauseStack.push( Clause.DELETE );
renderTableReferenceIdentificationVariable( statement.getTargetTable() );
if ( statement.getFromClause().getRoots().isEmpty() ) {
appendSql( " from " );
renderDmlTargetTableExpression( statement.getTargetTable() );
}
else {
visitFromClause( statement.getFromClause() );
}
}
finally {
clauseStack.pop();
}
}
@Override
protected void renderUpdateClause(UpdateStatement updateStatement) {
if ( updateStatement.getFromClause().getRoots().isEmpty() ) {
super.renderUpdateClause( updateStatement );
}
else {
appendSql( "update " );
renderFromClauseSpaces( updateStatement.getFromClause() );
}
}
@Override
protected void renderDmlTargetTableExpression(NamedTableReference tableReference) {
super.renderDmlTargetTableExpression( tableReference );
if ( getClauseStack().getCurrent() != Clause.INSERT ) {
renderTableReferenceIdentificationVariable( tableReference );
}
}
@Override
protected boolean supportsJoinsInDelete() {
return true;
}
@Override
protected JdbcOperationQueryInsert translateInsert(InsertSelectStatement sqlAst) {
visitInsertStatement( sqlAst );
return new JdbcOperationQueryInsertImpl(
getSql(),
getParameterBinders(),
getAffectedTableNames(),
null
);
}
@Override
protected void visitConflictClause(ConflictClause conflictClause) {
visitOnDuplicateKeyConflictClause( conflictClause );
}
@Override
protected String determineColumnReferenceQualifier(ColumnReference columnReference) {
final DmlTargetColumnQualifierSupport qualifierSupport = getDialect().getDmlTargetColumnQualifierSupport();
final MutationStatement currentDmlStatement;
final String dmlAlias;
// Since TiDB does not support aliasing the insert target table,
// we must detect column reference that are used in the conflict clause
// and use the table expression as qualifier instead
if ( getClauseStack().getCurrent() != Clause.SET
|| !( ( currentDmlStatement = getCurrentDmlStatement() ) instanceof InsertSelectStatement )
|| ( dmlAlias = currentDmlStatement.getTargetTable().getIdentificationVariable() ) == null
|| !dmlAlias.equals( columnReference.getQualifier() ) ) {
return columnReference.getQualifier();
}
// Qualify the column reference with the table expression also when in subqueries
else if ( qualifierSupport != DmlTargetColumnQualifierSupport.NONE || !getQueryPartStack().isEmpty() ) {
return getCurrentDmlStatement().getTargetTable().getTableExpression();
}
else {
return null;
}
}
@Override @Override
protected void renderExpressionAsClauseItem(Expression expression) { protected void renderExpressionAsClauseItem(Expression expression) {
expression.accept( this ); expression.accept( this );
@ -174,8 +318,8 @@ public class TiDBSqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAs
} }
@Override @Override
protected String getFromDual() { protected String getDual() {
return " from dual"; return "dual";
} }
@Override @Override

View File

@ -53,6 +53,7 @@ import org.hibernate.procedure.ProcedureCall;
import org.hibernate.query.MutationQuery; import org.hibernate.query.MutationQuery;
import org.hibernate.query.SelectionQuery; import org.hibernate.query.SelectionQuery;
import org.hibernate.query.criteria.HibernateCriteriaBuilder; import org.hibernate.query.criteria.HibernateCriteriaBuilder;
import org.hibernate.query.criteria.JpaCriteriaInsert;
import org.hibernate.query.criteria.JpaCriteriaInsertSelect; import org.hibernate.query.criteria.JpaCriteriaInsertSelect;
import org.hibernate.query.spi.QueryImplementor; import org.hibernate.query.spi.QueryImplementor;
import org.hibernate.query.spi.QueryProducerImplementor; import org.hibernate.query.spi.QueryProducerImplementor;
@ -513,6 +514,12 @@ public class SessionDelegatorBaseImpl implements SessionImplementor {
return delegate().createMutationQuery( insertSelect ); return delegate().createMutationQuery( insertSelect );
} }
@Override
public MutationQuery createMutationQuery(@SuppressWarnings("rawtypes") JpaCriteriaInsert insertSelect) {
//noinspection resource
return delegate().createMutationQuery( insertSelect );
}
@Override @Override
public <T> QueryImplementor<T> createQuery(CriteriaQuery<T> criteriaQuery) { public <T> QueryImplementor<T> createQuery(CriteriaQuery<T> criteriaQuery) {
return queryDelegate().createQuery( criteriaQuery ); return queryDelegate().createQuery( criteriaQuery );

View File

@ -40,6 +40,7 @@ import org.hibernate.query.NativeQuery;
import org.hibernate.query.Query; import org.hibernate.query.Query;
import org.hibernate.query.SelectionQuery; import org.hibernate.query.SelectionQuery;
import org.hibernate.query.criteria.HibernateCriteriaBuilder; import org.hibernate.query.criteria.HibernateCriteriaBuilder;
import org.hibernate.query.criteria.JpaCriteriaInsert;
import org.hibernate.query.criteria.JpaCriteriaInsertSelect; import org.hibernate.query.criteria.JpaCriteriaInsertSelect;
import org.hibernate.stat.SessionStatistics; import org.hibernate.stat.SessionStatistics;
@ -773,6 +774,11 @@ public class SessionLazyDelegator implements Session {
return this.lazySession.get().createMutationQuery( insertSelect ); return this.lazySession.get().createMutationQuery( insertSelect );
} }
@Override
public MutationQuery createMutationQuery(@SuppressWarnings("rawtypes") JpaCriteriaInsert insertSelect) {
return this.lazySession.get().createMutationQuery( insertSelect );
}
@Override @Override
public MutationQuery createNativeMutationQuery(String sqlString) { public MutationQuery createNativeMutationQuery(String sqlString) {
return this.lazySession.get().createNativeMutationQuery( sqlString ); return this.lazySession.get().createNativeMutationQuery( sqlString );

View File

@ -34,6 +34,7 @@ import org.hibernate.procedure.ProcedureCall;
import org.hibernate.query.MutationQuery; import org.hibernate.query.MutationQuery;
import org.hibernate.query.SelectionQuery; import org.hibernate.query.SelectionQuery;
import org.hibernate.query.criteria.HibernateCriteriaBuilder; import org.hibernate.query.criteria.HibernateCriteriaBuilder;
import org.hibernate.query.criteria.JpaCriteriaInsert;
import org.hibernate.query.criteria.JpaCriteriaInsertSelect; import org.hibernate.query.criteria.JpaCriteriaInsertSelect;
import org.hibernate.query.spi.QueryImplementor; import org.hibernate.query.spi.QueryImplementor;
import org.hibernate.query.spi.QueryProducerImplementor; import org.hibernate.query.spi.QueryProducerImplementor;
@ -101,6 +102,12 @@ public class SharedSessionDelegatorBaseImpl implements SharedSessionContractImpl
return delegate().createMutationQuery( insertSelect ); return delegate().createMutationQuery( insertSelect );
} }
@Override
public MutationQuery createMutationQuery(@SuppressWarnings("rawtypes") JpaCriteriaInsert insertSelect) {
//noinspection resource
return delegate().createMutationQuery( insertSelect );
}
@Override @Override
public <T> QueryImplementor<T> createQuery(CriteriaQuery<T> criteriaQuery) { public <T> QueryImplementor<T> createQuery(CriteriaQuery<T> criteriaQuery) {
return queryDelegate().createQuery( criteriaQuery ); return queryDelegate().createQuery( criteriaQuery );

View File

@ -19,15 +19,26 @@ import org.checkerframework.checker.nullness.qual.Nullable;
*/ */
public class ConstraintViolationException extends JDBCException { public class ConstraintViolationException extends JDBCException {
private final ConstraintKind kind;
private final @Nullable String constraintName; private final @Nullable String constraintName;
public ConstraintViolationException(String message, SQLException root, @Nullable String constraintName) { public ConstraintViolationException(String message, SQLException root, @Nullable String constraintName) {
super( message, root ); this( message, root, ConstraintKind.OTHER, constraintName );
this.constraintName = constraintName;
} }
public ConstraintViolationException(String message, SQLException root, String sql, @Nullable String constraintName) { public ConstraintViolationException(String message, SQLException root, String sql, @Nullable String constraintName) {
this( message, root, sql, ConstraintKind.OTHER, constraintName );
}
public ConstraintViolationException(String message, SQLException root, ConstraintKind kind, @Nullable String constraintName) {
super( message, root );
this.kind = kind;
this.constraintName = constraintName;
}
public ConstraintViolationException(String message, SQLException root, String sql, ConstraintKind kind, @Nullable String constraintName) {
super( message, root, sql ); super( message, root, sql );
this.kind = kind;
this.constraintName = constraintName; this.constraintName = constraintName;
} }
@ -39,4 +50,16 @@ public class ConstraintViolationException extends JDBCException {
public @Nullable String getConstraintName() { public @Nullable String getConstraintName() {
return constraintName; return constraintName;
} }
/**
* Returns the kind of constraint that was violated.
*/
public ConstraintKind getKind() {
return kind;
}
public enum ConstraintKind {
UNIQUE,
OTHER
}
} }

View File

@ -70,6 +70,7 @@ import org.hibernate.query.SelectionQuery;
import org.hibernate.query.UnknownNamedQueryException; import org.hibernate.query.UnknownNamedQueryException;
import org.hibernate.query.criteria.CriteriaDefinition; import org.hibernate.query.criteria.CriteriaDefinition;
import org.hibernate.query.criteria.HibernateCriteriaBuilder; import org.hibernate.query.criteria.HibernateCriteriaBuilder;
import org.hibernate.query.criteria.JpaCriteriaInsert;
import org.hibernate.query.criteria.JpaCriteriaInsertSelect; import org.hibernate.query.criteria.JpaCriteriaInsertSelect;
import org.hibernate.query.hql.spi.SqmQueryImplementor; import org.hibernate.query.hql.spi.SqmQueryImplementor;
import org.hibernate.query.named.NamedResultSetMappingMemento; import org.hibernate.query.named.NamedResultSetMappingMemento;
@ -88,6 +89,7 @@ import org.hibernate.query.sqm.tree.SqmDmlStatement;
import org.hibernate.query.sqm.tree.SqmStatement; import org.hibernate.query.sqm.tree.SqmStatement;
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
import org.hibernate.query.sqm.tree.insert.SqmInsertSelectStatement; import org.hibernate.query.sqm.tree.insert.SqmInsertSelectStatement;
import org.hibernate.query.sqm.tree.insert.SqmInsertStatement;
import org.hibernate.query.sqm.tree.select.SqmQueryGroup; import org.hibernate.query.sqm.tree.select.SqmQueryGroup;
import org.hibernate.query.sqm.tree.select.SqmQuerySpec; import org.hibernate.query.sqm.tree.select.SqmQuerySpec;
import org.hibernate.query.sqm.tree.select.SqmSelectStatement; import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
@ -1236,6 +1238,17 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont
} }
} }
@Override
public MutationQuery createMutationQuery(@SuppressWarnings("rawtypes") JpaCriteriaInsert insertSelect) {
checkOpen();
try {
return createCriteriaQuery( (SqmInsertStatement<?>) insertSelect, null );
}
catch ( RuntimeException e ) {
throw getExceptionConverter().convert( e );
}
}
@Override @Override
@SuppressWarnings("UnnecessaryLocalVariable") @SuppressWarnings("UnnecessaryLocalVariable")
public ProcedureCall getNamedProcedureCall(String name) { public ProcedureCall getNamedProcedureCall(String name) {

View File

@ -65,7 +65,11 @@ public class PrimaryKey extends Constraint {
} }
public String sqlConstraintString(Dialect dialect) { public String sqlConstraintString(Dialect dialect) {
StringBuilder buf = new StringBuilder("primary key ("); StringBuilder buf = new StringBuilder();
if ( orderingUniqueKey != null && orderingUniqueKey.isNameExplicit() ) {
buf.append( "constraint " ).append( orderingUniqueKey.getName() ).append( ' ' );
}
buf.append( "primary key (" );
boolean first = true; boolean first = true;
for ( Column column : getColumns() ) { for ( Column column : getColumns() ) {
if ( first ) { if ( first ) {

View File

@ -6,6 +6,7 @@
*/ */
package org.hibernate.query; package org.hibernate.query;
import org.hibernate.query.criteria.JpaCriteriaInsert;
import org.hibernate.query.criteria.JpaCriteriaInsertSelect; import org.hibernate.query.criteria.JpaCriteriaInsertSelect;
import jakarta.persistence.criteria.CriteriaDelete; import jakarta.persistence.criteria.CriteriaDelete;
@ -323,6 +324,11 @@ public interface QueryProducer {
*/ */
MutationQuery createMutationQuery(@SuppressWarnings("rawtypes") JpaCriteriaInsertSelect insertSelect); MutationQuery createMutationQuery(@SuppressWarnings("rawtypes") JpaCriteriaInsertSelect insertSelect);
/**
* Create a {@link MutationQuery} from the given insert criteria tree
*/
MutationQuery createMutationQuery(@SuppressWarnings("rawtypes") JpaCriteriaInsert insertSelect);
/** /**
* Create a {@link NativeQuery} instance for the given native SQL statement. * Create a {@link NativeQuery} instance for the given native SQL statement.
* *

View File

@ -123,6 +123,12 @@ public interface HibernateCriteriaBuilder extends CriteriaBuilder {
<T> JpaCriteriaInsertSelect<T> createCriteriaInsertSelect(Class<T> targetEntity); <T> JpaCriteriaInsertSelect<T> createCriteriaInsertSelect(Class<T> targetEntity);
@Incubating
JpaValues values(Expression<?>... expressions);
@Incubating
JpaValues values(List<? extends Expression<?>> expressions);
/** /**
* Transform the given HQL {@code select} query to an equivalent criteria query. * Transform the given HQL {@code select} query to an equivalent criteria query.
* *
@ -693,8 +699,6 @@ public interface HibernateCriteriaBuilder extends CriteriaBuilder {
<T> JpaExpression<T> value(T value); <T> JpaExpression<T> value(T value);
<V, C extends Collection<V>> JpaExpression<Collection<V>> values(C collection);
@Override @Override
<V, M extends Map<?, V>> Expression<Collection<V>> values(M map); <V, M extends Map<?, V>> Expression<Collection<V>> values(M map);

View File

@ -0,0 +1,107 @@
/*
* 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.criteria;
import java.util.List;
import org.hibernate.Incubating;
import jakarta.persistence.criteria.Path;
import jakarta.persistence.metamodel.SingularAttribute;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
* A conflict clause for insert statements.
*
* @since 6.5
*/
@Incubating
public interface JpaConflictClause<T> {
/**
* The excluded row/object which was not inserted.
*/
JpaRoot<T> getExcludedRoot();
/**
* The unique constraint name for which a constraint violation is allowed.
*/
@Nullable String getConstraintName();
/**
* Sets the unique constraint name for which a constraint violation is allowed.
*
* @throws IllegalStateException when constraint paths have already been defined
*/
JpaConflictClause<T> conflictOnConstraint(@Nullable String constraintName);
/**
* The paths which are part of a unique constraint, for which a constraint violation is allowed.
*/
List<? extends JpaPath<?>> getConstraintPaths();
/**
* Shorthand for calling {@link #conflictOnConstraintPaths(List)} with paths resolved for the given attributes
* against the insert target.
*/
JpaConflictClause<T> conflictOnConstraintAttributes(String... attributes);
/**
* Shorthand for calling {@link #conflictOnConstraintPaths(List)} with paths resolved for the given attributes
* against the insert target.
*/
JpaConflictClause<T> conflictOnConstraintAttributes(SingularAttribute<T, ?>... attributes);
/**
* See {@link #conflictOnConstraintPaths(List)}.
*/
JpaConflictClause<T> conflictOnConstraintPaths(Path<?>... paths);
/**
* Sets the paths which are part of a unique constraint, for which a constraint violation is allowed.
*
* @throws IllegalStateException when a constraint name has already been defined
*/
JpaConflictClause<T> conflictOnConstraintPaths(List<? extends Path<?>> paths);
/**
* The action to do when a conflict due to a unique constraint violation happens.
*/
@Nullable JpaConflictUpdateAction<T> getConflictAction();
/**
* Sets the action to do on a conflict. Setting {@code null} means to do nothing.
*
* @see #createConflictUpdateAction()
*/
JpaConflictClause<T> onConflictDo(@Nullable JpaConflictUpdateAction<T> action);
/**
* Shorthand version for calling {@link #onConflictDo(JpaConflictUpdateAction)} with {@link #createConflictUpdateAction()}
* as argument and returning the update action.
*/
default JpaConflictUpdateAction<T> onConflictDoUpdate() {
final JpaConflictUpdateAction<T> conflictUpdateAction = createConflictUpdateAction();
onConflictDo( conflictUpdateAction );
return conflictUpdateAction;
}
/**
* Shorthand version for calling {@link #onConflictDo(JpaConflictUpdateAction)} with a {@code null} argument.
*/
default JpaConflictClause<T> onConflictDoNothing() {
return onConflictDo( null );
}
/**
* Create a new conflict update action for this insert statement.
*
* @return a new conflict update action
* @see #onConflictDo(JpaConflictUpdateAction)
*/
JpaConflictUpdateAction<T> createConflictUpdateAction();
}

View File

@ -0,0 +1,92 @@
/*
* 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.criteria;
import org.hibernate.Incubating;
import jakarta.persistence.criteria.Expression;
import jakarta.persistence.criteria.Path;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.metamodel.SingularAttribute;
/**
* The update action that should happen on a unique constraint violation for an insert statement.
*
* @since 6.5
*/
@Incubating
public interface JpaConflictUpdateAction<T> {
/**
* Update the value of the specified attribute.
* @param attribute attribute to be updated
* @param value new value
* @return the modified update query
*/
<Y, X extends Y> JpaConflictUpdateAction<T> set(SingularAttribute<? super T, Y> attribute, X value);
/**
* Update the value of the specified attribute.
* @param attribute attribute to be updated
* @param value new value
* @return the modified update query
*/
<Y> JpaConflictUpdateAction<T> set(SingularAttribute<? super T, Y> attribute, Expression<? extends Y> value);
/**
* Update the value of the specified attribute.
* @param attribute attribute to be updated
* @param value new value
* @return the modified update query
*/
<Y, X extends Y> JpaConflictUpdateAction<T> set(Path<Y> attribute, X value);
/**
* Update the value of the specified attribute.
* @param attribute attribute to be updated
* @param value new value
* @return the modified update query
*/
<Y> JpaConflictUpdateAction<T> set(Path<Y> attribute, Expression<? extends Y> value);
/**
* Update the value of the specified attribute.
* @param attributeName name of the attribute to be updated
* @param value new value
* @return the modified update query
*/
JpaConflictUpdateAction<T> set(String attributeName, Object value);
/**
* Modify the update query to restrict the target of the update
* according to the specified boolean expression.
* Replaces the previously added restriction(s), if any.
* @param restriction a simple or compound boolean expression
* @return the modified update query
*/
JpaConflictUpdateAction<T> where(Expression<Boolean> restriction);
/**
* Modify the update query to restrict the target of the update
* according to the conjunction of the specified restriction
* predicates.
* Replaces the previously added restriction(s), if any.
* If no restrictions are specified, any previously added
* restrictions are simply removed.
* @param restrictions zero or more restriction predicates
* @return the modified update query
*/
JpaConflictUpdateAction<T> where(Predicate... restrictions);
/**
* Return the predicate that corresponds to the where clause
* restriction(s), or null if no restrictions have been
* specified.
* @return where clause predicate
*/
JpaPredicate getRestriction();
}

View File

@ -0,0 +1,62 @@
/*
* 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.criteria;
import java.util.List;
import org.hibernate.Incubating;
import jakarta.persistence.criteria.Path;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
* The commonalities between insert-select and insert-values.
*
* @since 6.5
*/
@Incubating
public interface JpaCriteriaInsert<T> extends JpaManipulationCriteria<T> {
/**
* Returns the insertion target paths.
*/
List<? extends JpaPath<?>> getInsertionTargetPaths();
/**
* Sets the insertion target paths.
*/
JpaCriteriaInsert<T> setInsertionTargetPaths(Path<?>... insertionTargetPaths);
/**
* Sets the insertion target paths.
*/
JpaCriteriaInsert<T> setInsertionTargetPaths(List<? extends Path<?>> insertionTargetPaths);
/**
* Sets the conflict clause that defines what happens when an insert violates a unique constraint.
*/
JpaConflictClause<T> onConflict();
/**
* Sets the conflict clause that defines what happens when an insert violates a unique constraint.
*/
JpaCriteriaInsert<T> onConflict(@Nullable JpaConflictClause<T> conflictClause);
/**
* Returns the conflict clause that defines what happens when an insert violates a unique constraint,
* or {@code null} if there is none.
*/
@Nullable JpaConflictClause<T> getConflictClause();
/**
* Create a new conflict clause for this insert statement.
*
* @return a new conflict clause
* @see JpaCriteriaInsert#onConflict(JpaConflictClause)
*/
JpaConflictClause<T> createConflictClause();
}

View File

@ -8,6 +8,9 @@ package org.hibernate.query.criteria;
import org.hibernate.Incubating; import org.hibernate.Incubating;
import jakarta.persistence.Tuple;
import jakarta.persistence.criteria.CriteriaQuery;
/** /**
* A representation of SqmInsertSelectStatement at the * A representation of SqmInsertSelectStatement at the
* {@link org.hibernate.query.criteria} level, even though JPA does * {@link org.hibernate.query.criteria} level, even though JPA does
@ -30,5 +33,10 @@ import org.hibernate.Incubating;
* @author Steve Ebersole * @author Steve Ebersole
*/ */
@Incubating @Incubating
public interface JpaCriteriaInsertSelect<T> extends JpaManipulationCriteria<T> { public interface JpaCriteriaInsertSelect<T> extends JpaCriteriaInsert<T> {
JpaCriteriaInsertSelect<T> select(CriteriaQuery<Tuple> criteriaQuery);
@Override
JpaCriteriaInsertSelect<T> onConflict(JpaConflictClause<T> conflictClause);
} }

View File

@ -6,6 +6,8 @@
*/ */
package org.hibernate.query.criteria; package org.hibernate.query.criteria;
import java.util.List;
import org.hibernate.Incubating; import org.hibernate.Incubating;
/** /**
@ -13,7 +15,7 @@ import org.hibernate.Incubating;
* {@link org.hibernate.query.criteria} level, even though JPA does * {@link org.hibernate.query.criteria} level, even though JPA does
* not define support for insert-values criteria. * not define support for insert-values criteria.
* *
* @see org.hibernate.query.sqm.tree.insert.SqmInsertSelectStatement * @see org.hibernate.query.sqm.tree.insert.SqmInsertValuesStatement
* *
* @apiNote Incubating mainly for 2 purposes:<ul> * @apiNote Incubating mainly for 2 purposes:<ul>
* <li> * <li>
@ -30,5 +32,13 @@ import org.hibernate.Incubating;
* @author Gavin King * @author Gavin King
*/ */
@Incubating @Incubating
public interface JpaCriteriaInsertValues<T> extends JpaManipulationCriteria<T> { public interface JpaCriteriaInsertValues<T> extends JpaCriteriaInsert<T> {
JpaCriteriaInsertValues<T> values(JpaValues... values);
JpaCriteriaInsertValues<T> values(List<? extends JpaValues> values);
@Override
JpaCriteriaInsertValues<T> onConflict(JpaConflictClause<T> conflictClause);
} }

View File

@ -0,0 +1,25 @@
/*
* 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.criteria;
import java.util.List;
import org.hibernate.Incubating;
/**
* A tuple of values.
*
* @since 6.5
*/
@Incubating
public interface JpaValues {
/**
* Returns the expressions of this tuple.
*/
List<? extends JpaExpression<?>> getExpressions();
}

View File

@ -53,6 +53,7 @@ import org.hibernate.query.criteria.JpaSelection;
import org.hibernate.query.criteria.JpaSetJoin; import org.hibernate.query.criteria.JpaSetJoin;
import org.hibernate.query.criteria.JpaSimpleCase; import org.hibernate.query.criteria.JpaSimpleCase;
import org.hibernate.query.criteria.JpaSubQuery; import org.hibernate.query.criteria.JpaSubQuery;
import org.hibernate.query.criteria.JpaValues;
import org.hibernate.query.criteria.JpaWindow; import org.hibernate.query.criteria.JpaWindow;
import org.hibernate.query.criteria.JpaWindowFrame; import org.hibernate.query.criteria.JpaWindowFrame;
import org.hibernate.query.NullPrecedence; import org.hibernate.query.NullPrecedence;
@ -148,6 +149,18 @@ public class HibernateCriteriaBuilderDelegate implements HibernateCriteriaBuilde
return criteriaBuilder.createCriteriaInsertSelect( targetEntity ); return criteriaBuilder.createCriteriaInsertSelect( targetEntity );
} }
@Override
@Incubating
public JpaValues values(Expression<?>... expressions) {
return criteriaBuilder.values( expressions );
}
@Override
@Incubating
public JpaValues values(List<? extends Expression<?>> expressions) {
return criteriaBuilder.values( expressions );
}
@Override @Override
public <T> JpaCriteriaQuery<T> unionAll(CriteriaQuery<? extends T> query1, CriteriaQuery<?>... queries) { public <T> JpaCriteriaQuery<T> unionAll(CriteriaQuery<? extends T> query1, CriteriaQuery<?>... queries) {
return criteriaBuilder.unionAll( query1, queries ); return criteriaBuilder.unionAll( query1, queries );
@ -746,11 +759,6 @@ public class HibernateCriteriaBuilderDelegate implements HibernateCriteriaBuilde
return criteriaBuilder.value( value ); return criteriaBuilder.value( value );
} }
@Override
public <V, C extends Collection<V>> JpaExpression<Collection<V>> values(C collection) {
return criteriaBuilder.values( collection );
}
@Override @Override
public <V, M extends Map<?, V>> Expression<Collection<V>> values(M map) { public <V, M extends Map<?, V>> Expression<Collection<V>> values(M map) {
return criteriaBuilder.values( map ); return criteriaBuilder.values( map );

View File

@ -24,9 +24,11 @@ import java.time.ZonedDateTime;
import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAccessor;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
import java.util.Collections;
import java.util.GregorianCalendar; import java.util.GregorianCalendar;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
@ -105,6 +107,7 @@ import org.hibernate.query.sqm.produce.function.FunctionArgumentException;
import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers; import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers;
import org.hibernate.query.sqm.spi.ParameterDeclarationContext; import org.hibernate.query.sqm.spi.ParameterDeclarationContext;
import org.hibernate.query.sqm.spi.SqmCreationContext; import org.hibernate.query.sqm.spi.SqmCreationContext;
import org.hibernate.query.sqm.tree.AbstractSqmDmlStatement;
import org.hibernate.query.sqm.tree.SqmJoinType; import org.hibernate.query.sqm.tree.SqmJoinType;
import org.hibernate.query.sqm.tree.SqmQuery; import org.hibernate.query.sqm.tree.SqmQuery;
import org.hibernate.query.sqm.tree.SqmStatement; import org.hibernate.query.sqm.tree.SqmStatement;
@ -171,6 +174,8 @@ import org.hibernate.query.sqm.tree.from.SqmFromClause;
import org.hibernate.query.sqm.tree.from.SqmJoin; import org.hibernate.query.sqm.tree.from.SqmJoin;
import org.hibernate.query.sqm.tree.from.SqmQualifiedJoin; import org.hibernate.query.sqm.tree.from.SqmQualifiedJoin;
import org.hibernate.query.sqm.tree.from.SqmRoot; import org.hibernate.query.sqm.tree.from.SqmRoot;
import org.hibernate.query.sqm.tree.insert.SqmConflictClause;
import org.hibernate.query.sqm.tree.insert.SqmConflictUpdateAction;
import org.hibernate.query.sqm.tree.insert.SqmInsertSelectStatement; import org.hibernate.query.sqm.tree.insert.SqmInsertSelectStatement;
import org.hibernate.query.sqm.tree.insert.SqmInsertStatement; import org.hibernate.query.sqm.tree.insert.SqmInsertStatement;
import org.hibernate.query.sqm.tree.insert.SqmInsertValuesStatement; import org.hibernate.query.sqm.tree.insert.SqmInsertValuesStatement;
@ -207,7 +212,10 @@ import org.hibernate.query.sqm.tree.select.SqmSelectableNode;
import org.hibernate.query.sqm.tree.select.SqmSelection; import org.hibernate.query.sqm.tree.select.SqmSelection;
import org.hibernate.query.sqm.tree.select.SqmSortSpecification; import org.hibernate.query.sqm.tree.select.SqmSortSpecification;
import org.hibernate.query.sqm.tree.select.SqmSubQuery; import org.hibernate.query.sqm.tree.select.SqmSubQuery;
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.query.sqm.tree.update.SqmUpdateStatement;
import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.ast.SqlAstNodeRenderingMode; import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
import org.hibernate.sql.ast.tree.cte.CteMaterialization; import org.hibernate.sql.ast.tree.cte.CteMaterialization;
import org.hibernate.sql.ast.tree.cte.CteSearchClauseKind; import org.hibernate.sql.ast.tree.cte.CteSearchClauseKind;
@ -487,8 +495,6 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
processingStateStack.push( processingState ); processingStateStack.push( processingState );
try { try {
queryExpressionContext.accept( this );
final SqmCreationProcessingState stateFieldsProcessingState = new SqmCreationProcessingStateImpl( final SqmCreationProcessingState stateFieldsProcessingState = new SqmCreationProcessingStateImpl(
insertStatement, insertStatement,
this this
@ -506,6 +512,9 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
processingStateStack.pop(); processingStateStack.pop();
} }
queryExpressionContext.accept( this );
insertStatement.onConflict( visitConflictClause( ctx.conflictClause() ) );
return insertStatement; return insertStatement;
} }
finally { finally {
@ -526,21 +535,43 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
processingState.getPathRegistry().register( root ); processingState.getPathRegistry().register( root );
try { try {
final HqlParser.ValuesListContext valuesListContext = ctx.valuesList();
for ( int i = 1; i < valuesListContext.getChildCount(); i += 2 ) {
final ParseTree values = valuesListContext.getChild( i );
final SqmValues sqmValues = new SqmValues();
for ( int j = 1; j < values.getChildCount(); j += 2 ) {
sqmValues.getExpressions().add( (SqmExpression<?>) values.getChild( j ).accept( this ) );
}
insertStatement.getValuesList().add( sqmValues );
}
for ( HqlParser.SimplePathContext stateFieldCtx : targetFieldsSpecContext.simplePath() ) { for ( HqlParser.SimplePathContext stateFieldCtx : targetFieldsSpecContext.simplePath() ) {
final SqmPath<?> stateField = (SqmPath<?>) visitSimplePath( stateFieldCtx ); final SqmPath<?> stateField = (SqmPath<?>) visitSimplePath( stateFieldCtx );
insertStatement.addInsertTargetStateField( stateField ); insertStatement.addInsertTargetStateField( stateField );
} }
final ArrayList<SqmValues> valuesList = new ArrayList<>();
final HqlParser.ValuesListContext valuesListContext = ctx.valuesList();
for ( int i = 1; i < valuesListContext.getChildCount(); i += 2 ) {
final ParseTree values = valuesListContext.getChild( i );
final ArrayList<SqmExpression<?>> valuesExpressions = new ArrayList<>();
final Iterator<SqmPath<?>> iterator = insertStatement.getInsertionTargetPaths().iterator();
for ( int j = 1; j < values.getChildCount(); j += 2 ) {
final SqmPath<?> targetPath = iterator.next();
final Class<?> targetPathJavaType = targetPath.getJavaType();
final boolean isEnum = targetPathJavaType != null && targetPathJavaType.isEnum();
final ParseTree valuesContext = values.getChild( j );
final HqlParser.ExpressionContext expressionContext;
final Map<Class<?>, Enum<?>> possibleEnumValues;
final SqmExpression<?> value;
if ( isEnum && valuesContext.getChild( 0 ) instanceof HqlParser.ExpressionContext
&& ( possibleEnumValues = getPossibleEnumValues( expressionContext = (HqlParser.ExpressionContext) valuesContext.getChild( 0 ) ) ) != null ) {
value = resolveEnumShorthandLiteral(
expressionContext,
possibleEnumValues,
targetPathJavaType
);
}
else {
value = (SqmExpression<?>) valuesContext.accept( this );
}
valuesExpressions.add( value );
}
valuesList.add( new SqmValues( valuesExpressions ) );
}
insertStatement.values( valuesList );
insertStatement.onConflict( visitConflictClause( ctx.conflictClause() ) );
return insertStatement; return insertStatement;
} }
finally { finally {
@ -549,6 +580,42 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
} }
} }
@Override
public SqmConflictClause<R> visitConflictClause(HqlParser.ConflictClauseContext ctx) {
if ( ctx == null ) {
return null;
}
final SqmCreationProcessingState processingState = processingStateStack.getCurrent();
final SqmInsertStatement<R> statement = (SqmInsertStatement<R>) processingState.getProcessingQuery();
final SqmConflictClause<R> conflictClause = new SqmConflictClause<>( statement );
final HqlParser.ConflictTargetContext conflictTargetContext = ctx.conflictTarget();
if ( conflictTargetContext != null ) {
final HqlParser.IdentifierContext identifierCtx = conflictTargetContext.identifier();
if ( identifierCtx != null ) {
conflictClause.conflictOnConstraint( visitIdentifier( identifierCtx ) );
}
else {
final List<SqmPath<?>> constraintAttributes = new ArrayList<>();
for ( HqlParser.SimplePathContext pathContext : conflictTargetContext.simplePath() ) {
constraintAttributes.add( consumeDomainPath( pathContext ) );
}
conflictClause.conflictOnConstraintPaths( constraintAttributes );
}
}
final HqlParser.ConflictActionContext conflictActionContext = ctx.conflictAction();
final HqlParser.SetClauseContext setClauseContext = conflictActionContext.setClause();
if ( setClauseContext != null ) {
processingState.getPathRegistry().registerByAliasOnly( conflictClause.getExcludedRoot() );
final SqmConflictUpdateAction<R> updateAction = conflictClause.onConflictDoUpdate();
for ( HqlParser.AssignmentContext assignmentContext : setClauseContext.assignment() ) {
updateAction.addAssignment( visitAssignment( assignmentContext ) );
}
final SqmPredicate sqmPredicate = visitWhereClause( conflictActionContext.whereClause() );
updateAction.where( sqmPredicate );
}
return conflictClause;
}
@Override @Override
public SqmUpdateStatement<R> visitUpdateStatement(HqlParser.UpdateStatementContext ctx) { public SqmUpdateStatement<R> visitUpdateStatement(HqlParser.UpdateStatementContext ctx) {
final boolean versioned = ctx.VERSIONED() != null; final boolean versioned = ctx.VERSIONED() != null;
@ -577,27 +644,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
final HqlParser.SetClauseContext setClauseCtx = ctx.setClause(); final HqlParser.SetClauseContext setClauseCtx = ctx.setClause();
for ( ParseTree subCtx : setClauseCtx.children ) { for ( ParseTree subCtx : setClauseCtx.children ) {
if ( subCtx instanceof HqlParser.AssignmentContext ) { if ( subCtx instanceof HqlParser.AssignmentContext ) {
final HqlParser.AssignmentContext assignmentContext = (HqlParser.AssignmentContext) subCtx; updateStatement.applyAssignment( visitAssignment( (HqlParser.AssignmentContext) subCtx ) );
//noinspection unchecked
final SqmPath<Object> targetPath = (SqmPath<Object>) consumeDomainPath( assignmentContext.simplePath() );
final Class<?> targetPathJavaType = targetPath.getJavaType();
final boolean isEnum = targetPathJavaType != null && targetPathJavaType.isEnum();
final ParseTree rightSide = assignmentContext.getChild( 2 );
final HqlParser.ExpressionContext expressionContext;
final Map<Class<?>, Enum<?>> possibleEnumValues;
final SqmExpression<?> value;
if ( isEnum && rightSide.getChild( 0 ) instanceof HqlParser.ExpressionContext
&& ( possibleEnumValues = getPossibleEnumValues( expressionContext = (HqlParser.ExpressionContext) rightSide.getChild( 0 ) ) ) != null ) {
value = resolveEnumShorthandLiteral(
expressionContext,
possibleEnumValues,
targetPathJavaType
);
}
else {
value = (SqmExpression<?>) rightSide.accept( this );
}
updateStatement.applyAssignment( targetPath, value );
} }
} }
@ -613,6 +660,30 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
} }
} }
@Override
public SqmAssignment<?> visitAssignment(HqlParser.AssignmentContext ctx) {
//noinspection unchecked
final SqmPath<Object> targetPath = (SqmPath<Object>) consumeDomainPath( ctx.simplePath() );
final Class<?> targetPathJavaType = targetPath.getJavaType();
final boolean isEnum = targetPathJavaType != null && targetPathJavaType.isEnum();
final ParseTree rightSide = ctx.getChild( 2 );
final HqlParser.ExpressionContext expressionContext;
final Map<Class<?>, Enum<?>> possibleEnumValues;
final SqmExpression<?> value;
if ( isEnum && rightSide.getChild( 0 ) instanceof HqlParser.ExpressionContext
&& ( possibleEnumValues = getPossibleEnumValues( expressionContext = (HqlParser.ExpressionContext) rightSide.getChild( 0 ) ) ) != null ) {
value = resolveEnumShorthandLiteral(
expressionContext,
possibleEnumValues,
targetPathJavaType
);
}
else {
value = (SqmExpression<?>) rightSide.accept( this );
}
return new SqmAssignment<>( targetPath, value );
}
@Override @Override
public SqmDeleteStatement<R> visitDeleteStatement(HqlParser.DeleteStatementContext ctx) { public SqmDeleteStatement<R> visitDeleteStatement(HqlParser.DeleteStatementContext ctx) {
final HqlParser.TargetEntityContext dmlTargetContext = ctx.targetEntity(); final HqlParser.TargetEntityContext dmlTargetContext = ctx.targetEntity();

View File

@ -71,26 +71,7 @@ public class SqmPathRegistryImpl implements SqmPathRegistry {
if ( sqmPath instanceof SqmFrom<?, ?> ) { if ( sqmPath instanceof SqmFrom<?, ?> ) {
final SqmFrom<?, ?> sqmFrom = (SqmFrom<?, ?>) sqmPath; final SqmFrom<?, ?> sqmFrom = (SqmFrom<?, ?>) sqmPath;
final String alias = sqmPath.getExplicitAlias(); registerByAliasOnly( sqmFrom );
if ( alias != null ) {
final String aliasToUse = jpaCompliance.isJpaQueryComplianceEnabled()
? alias.toLowerCase( Locale.getDefault() )
: alias;
final SqmFrom<?, ?> previousFrom = sqmFromByAlias.put( aliasToUse, sqmFrom );
if ( previousFrom != null ) {
throw new AliasCollisionException(
String.format(
Locale.ENGLISH,
"Alias [%s] used for multiple from-clause elements : %s, %s",
alias,
previousFrom,
sqmPath
)
);
}
}
final SqmFrom<?, ?> previousFromByPath = sqmFromByPath.put( sqmPath.getNavigablePath(), sqmFrom ); final SqmFrom<?, ?> previousFromByPath = sqmFromByPath.put( sqmPath.getNavigablePath(), sqmFrom );
@ -124,6 +105,30 @@ public class SqmPathRegistryImpl implements SqmPathRegistry {
} }
} }
@Override
public void registerByAliasOnly(SqmFrom<?, ?> sqmFrom) {
final String alias = sqmFrom.getExplicitAlias();
if ( alias != null ) {
final String aliasToUse = jpaCompliance.isJpaQueryComplianceEnabled()
? alias.toLowerCase( Locale.getDefault() )
: alias;
final SqmFrom<?, ?> previousFrom = sqmFromByAlias.put( aliasToUse, sqmFrom );
if ( previousFrom != null ) {
throw new AliasCollisionException(
String.format(
Locale.ENGLISH,
"Alias [%s] used for multiple from-clause elements : %s, %s",
alias,
previousFrom,
sqmFrom
)
);
}
}
}
@Override @Override
public <E> void replace(SqmEntityJoin<E> sqmJoin, SqmRoot<E> sqmRoot) { public <E> void replace(SqmEntityJoin<E> sqmJoin, SqmRoot<E> sqmRoot) {
final String alias = sqmJoin.getExplicitAlias(); final String alias = sqmJoin.getExplicitAlias();

View File

@ -36,6 +36,13 @@ public interface SqmPathRegistry {
*/ */
void register(SqmPath<?> sqmPath); void register(SqmPath<?> sqmPath);
/**
* Register an SqmFrom by alias only.
* Effectively, this makes the from node only resolvable via the alias,
* which means that the from node is ignored in {@link #findFromExposing(String)}.
*/
void registerByAliasOnly(SqmFrom<?, ?> sqmFrom);
/** /**
* Used with {@linkplain JpaCompliance#isJpaQueryComplianceEnabled() JPA compliance} * Used with {@linkplain JpaCompliance#isJpaQueryComplianceEnabled() JPA compliance}
* to treat secondary query roots as cross-joins. Here we will replace the {@code sqmRoot} * to treat secondary query roots as cross-joins. Here we will replace the {@code sqmRoot}

View File

@ -32,6 +32,7 @@ import org.hibernate.query.criteria.JpaPredicate;
import org.hibernate.query.criteria.JpaSearchedCase; import org.hibernate.query.criteria.JpaSearchedCase;
import org.hibernate.query.criteria.JpaSelection; import org.hibernate.query.criteria.JpaSelection;
import org.hibernate.query.criteria.JpaSimpleCase; import org.hibernate.query.criteria.JpaSimpleCase;
import org.hibernate.query.criteria.JpaValues;
import org.hibernate.query.criteria.JpaWindow; import org.hibernate.query.criteria.JpaWindow;
import org.hibernate.query.spi.QueryEngine; import org.hibernate.query.spi.QueryEngine;
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
@ -48,6 +49,7 @@ import org.hibernate.query.sqm.tree.expression.SqmTuple;
import org.hibernate.query.sqm.tree.from.SqmRoot; import org.hibernate.query.sqm.tree.from.SqmRoot;
import org.hibernate.query.sqm.tree.insert.SqmInsertSelectStatement; import org.hibernate.query.sqm.tree.insert.SqmInsertSelectStatement;
import org.hibernate.query.sqm.tree.insert.SqmInsertValuesStatement; import org.hibernate.query.sqm.tree.insert.SqmInsertValuesStatement;
import org.hibernate.query.sqm.tree.insert.SqmValues;
import org.hibernate.query.sqm.tree.predicate.SqmInPredicate; import org.hibernate.query.sqm.tree.predicate.SqmInPredicate;
import org.hibernate.query.sqm.tree.predicate.SqmPredicate; import org.hibernate.query.sqm.tree.predicate.SqmPredicate;
import org.hibernate.query.sqm.tree.select.SqmSelectStatement; import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
@ -522,6 +524,12 @@ public interface NodeBuilder extends HibernateCriteriaBuilder {
@Override @Override
<T> SqmInsertSelectStatement<T> createCriteriaInsertSelect(Class<T> targetEntity); <T> SqmInsertSelectStatement<T> createCriteriaInsertSelect(Class<T> targetEntity);
@Override
SqmValues values(Expression<?>... expressions);
@Override
SqmValues values(List<? extends Expression<?>> expressions);
@Override @Override
<N extends Number> SqmExpression<N> abs(Expression<N> x); <N extends Number> SqmExpression<N> abs(Expression<N> x);
@ -775,9 +783,6 @@ public interface NodeBuilder extends HibernateCriteriaBuilder {
@Override @Override
<K, L extends List<?>> SqmExpression<Set<K>> indexes(L list); <K, L extends List<?>> SqmExpression<Set<K>> indexes(L list);
@Override
<V, C extends Collection<V>> SqmExpression<Collection<V>> values(C collection);
@Override @Override
<V, M extends Map<?, V>> Expression<Collection<V>> values(M map); <V, M extends Map<?, V>> Expression<Collection<V>> values(M map);

View File

@ -87,6 +87,7 @@ import org.hibernate.query.sqm.tree.from.SqmDerivedJoin;
import org.hibernate.query.sqm.tree.from.SqmEntityJoin; import org.hibernate.query.sqm.tree.from.SqmEntityJoin;
import org.hibernate.query.sqm.tree.from.SqmFromClause; import org.hibernate.query.sqm.tree.from.SqmFromClause;
import org.hibernate.query.sqm.tree.from.SqmRoot; import org.hibernate.query.sqm.tree.from.SqmRoot;
import org.hibernate.query.sqm.tree.insert.SqmConflictClause;
import org.hibernate.query.sqm.tree.insert.SqmInsertSelectStatement; import org.hibernate.query.sqm.tree.insert.SqmInsertSelectStatement;
import org.hibernate.query.sqm.tree.insert.SqmInsertValuesStatement; import org.hibernate.query.sqm.tree.insert.SqmInsertValuesStatement;
import org.hibernate.query.sqm.tree.insert.SqmValues; import org.hibernate.query.sqm.tree.insert.SqmValues;
@ -136,6 +137,8 @@ public interface SemanticQueryWalker<T> {
T visitInsertValuesStatement(SqmInsertValuesStatement<?> statement); T visitInsertValuesStatement(SqmInsertValuesStatement<?> statement);
T visitConflictClause(SqmConflictClause<?> sqmConflictClause);
T visitDeleteStatement(SqmDeleteStatement<?> statement); T visitDeleteStatement(SqmDeleteStatement<?> statement);
T visitSelectStatement(SqmSelectStatement<?> statement); T visitSelectStatement(SqmSelectStatement<?> statement);

View File

@ -827,7 +827,7 @@ public class QuerySqmImpl<R>
final NonSelectQueryPlan[] planParts = new NonSelectQueryPlan[valuesList.size()]; final NonSelectQueryPlan[] planParts = new NonSelectQueryPlan[valuesList.size()];
for ( int i = 0; i < valuesList.size(); i++ ) { for ( int i = 0; i < valuesList.size(); i++ ) {
final SqmInsertValuesStatement<?> subInsert = insertValues.copyWithoutValues( SqmCopyContext.simpleContext() ); final SqmInsertValuesStatement<?> subInsert = insertValues.copyWithoutValues( SqmCopyContext.simpleContext() );
subInsert.getValuesList().add( valuesList.get( i ) ); subInsert.values( valuesList );
planParts[i] = new SimpleInsertQueryPlan( subInsert, domainParameterXref ); planParts[i] = new SimpleInsertQueryPlan( subInsert, domainParameterXref );
} }

View File

@ -131,6 +131,7 @@ import org.hibernate.query.sqm.tree.expression.ValueBindJpaCriteriaParameter;
import org.hibernate.query.sqm.tree.from.SqmRoot; import org.hibernate.query.sqm.tree.from.SqmRoot;
import org.hibernate.query.sqm.tree.insert.SqmInsertSelectStatement; import org.hibernate.query.sqm.tree.insert.SqmInsertSelectStatement;
import org.hibernate.query.sqm.tree.insert.SqmInsertValuesStatement; import org.hibernate.query.sqm.tree.insert.SqmInsertValuesStatement;
import org.hibernate.query.sqm.tree.insert.SqmValues;
import org.hibernate.query.sqm.tree.predicate.SqmBetweenPredicate; import org.hibernate.query.sqm.tree.predicate.SqmBetweenPredicate;
import org.hibernate.query.sqm.tree.predicate.SqmBooleanExpressionPredicate; import org.hibernate.query.sqm.tree.predicate.SqmBooleanExpressionPredicate;
import org.hibernate.query.sqm.tree.predicate.SqmComparisonPredicate; import org.hibernate.query.sqm.tree.predicate.SqmComparisonPredicate;
@ -353,6 +354,17 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
return new SqmInsertSelectStatement<>( targetEntity, this ); return new SqmInsertSelectStatement<>( targetEntity, this );
} }
@Override
public SqmValues values(Expression<?>... expressions) {
return values( Arrays.asList( expressions ) );
}
@Override
public SqmValues values(List<? extends Expression<?>> expressions) {
//noinspection unchecked
return new SqmValues( (List<SqmExpression<?>>) expressions );
}
@Override @Override
public <T> JpaCriteriaQuery<T> union(boolean all, CriteriaQuery<? extends T> query1, CriteriaQuery<?>... queries) { public <T> JpaCriteriaQuery<T> union(boolean all, CriteriaQuery<? extends T> query1, CriteriaQuery<?>... queries) {
return setOperation( all ? SetOperator.UNION_ALL : SetOperator.UNION, query1, queries ); return setOperation( all ? SetOperator.UNION_ALL : SetOperator.UNION, query1, queries );
@ -1904,14 +1916,9 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
// || is a literal enum mapped to a PostgreSQL named 'enum' type // || is a literal enum mapped to a PostgreSQL named 'enum' type
} }
@Override
public <V, C extends Collection<V>> SqmExpression<Collection<V>> values(C collection) {
throw new UnsupportedOperationException();
}
@Override @Override
public <V, M extends Map<?, V>> Expression<Collection<V>> values(M map) { public <V, M extends Map<?, V>> Expression<Collection<V>> values(M map) {
throw new UnsupportedOperationException(); return value( map.values() );
} }
@Override @Override

View File

@ -78,6 +78,8 @@ import org.hibernate.query.sqm.tree.from.SqmFrom;
import org.hibernate.query.sqm.tree.from.SqmFromClause; import org.hibernate.query.sqm.tree.from.SqmFromClause;
import org.hibernate.query.sqm.tree.from.SqmQualifiedJoin; import org.hibernate.query.sqm.tree.from.SqmQualifiedJoin;
import org.hibernate.query.sqm.tree.from.SqmRoot; import org.hibernate.query.sqm.tree.from.SqmRoot;
import org.hibernate.query.sqm.tree.insert.SqmConflictClause;
import org.hibernate.query.sqm.tree.insert.SqmConflictUpdateAction;
import org.hibernate.query.sqm.tree.insert.SqmInsertSelectStatement; import org.hibernate.query.sqm.tree.insert.SqmInsertSelectStatement;
import org.hibernate.query.sqm.tree.insert.SqmInsertValuesStatement; import org.hibernate.query.sqm.tree.insert.SqmInsertValuesStatement;
import org.hibernate.query.sqm.tree.insert.SqmValues; import org.hibernate.query.sqm.tree.insert.SqmValues;
@ -330,6 +332,12 @@ public class SqmTreePrinter implements SemanticQueryWalker<Object> {
"into", "into",
() -> statement.getInsertionTargetPaths().forEach( sqmPath -> sqmPath.accept( this ) ) () -> statement.getInsertionTargetPaths().forEach( sqmPath -> sqmPath.accept( this ) )
); );
if ( statement.getConflictClause() != null ) {
processStanza(
"on conflict",
() -> statement.getConflictClause().accept( this )
);
}
} }
); );
} }
@ -337,6 +345,29 @@ public class SqmTreePrinter implements SemanticQueryWalker<Object> {
return null; return null;
} }
@Override
public Object visitConflictClause(SqmConflictClause<?> sqmConflictClause) {
if ( sqmConflictClause.getConstraintName() != null ) {
logWithIndentation( "[constraintName = %s]", sqmConflictClause.getConstraintName() );
}
else {
processStanza(
"constraint attributes",
() -> sqmConflictClause.getConstraintPaths().forEach( sqmPath -> sqmPath.accept( this ) )
);
}
final SqmConflictUpdateAction<?> updateAction = sqmConflictClause.getConflictAction();
if ( updateAction == null ) {
logWithIndentation( "do nothing" );
}
else {
logWithIndentation( "do update " );
visitSetClause( updateAction.getSetClause() );
visitWhereClause( updateAction.getWhereClause() );
}
return null;
}
@Override @Override
public Object visitSelectStatement(SqmSelectStatement<?> statement) { public Object visitSelectStatement(SqmSelectStatement<?> statement) {
if ( DEBUG_ENABLED ) { if ( DEBUG_ENABLED ) {

View File

@ -6,62 +6,37 @@
*/ */
package org.hibernate.query.sqm.mutation.internal; package org.hibernate.query.sqm.mutation.internal;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.internal.util.collections.Stack; import org.hibernate.internal.util.collections.Stack;
import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.MappingModelExpressible;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.query.spi.QueryOptions; import org.hibernate.query.spi.QueryOptions;
import org.hibernate.query.spi.QueryParameterBindings; import org.hibernate.query.spi.QueryParameterBindings;
import org.hibernate.query.sqm.internal.DomainParameterXref; import org.hibernate.query.sqm.internal.DomainParameterXref;
import org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter; import org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter;
import org.hibernate.query.sqm.sql.internal.SqlAstProcessingStateImpl; import org.hibernate.query.sqm.sql.internal.SqlAstProcessingStateImpl;
import org.hibernate.query.sqm.tree.SqmStatement; import org.hibernate.query.sqm.tree.SqmStatement;
import org.hibernate.query.sqm.tree.expression.SqmParameter;
import org.hibernate.query.sqm.tree.from.SqmRoot; import org.hibernate.query.sqm.tree.from.SqmRoot;
import org.hibernate.query.sqm.tree.insert.SqmInsertStatement;
import org.hibernate.query.sqm.tree.predicate.SqmWhereClause; import org.hibernate.query.sqm.tree.predicate.SqmWhereClause;
import org.hibernate.query.sqm.tree.update.SqmSetClause;
import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.spi.SqlAstCreationContext; import org.hibernate.sql.ast.spi.SqlAstCreationContext;
import org.hibernate.sql.ast.spi.SqlAstHelper; import org.hibernate.sql.ast.spi.SqlAstHelper;
import org.hibernate.sql.ast.spi.SqlAstProcessingState; import org.hibernate.sql.ast.spi.SqlAstProcessingState;
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.Statement;
import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.predicate.Predicate; import org.hibernate.sql.ast.tree.predicate.Predicate;
import org.hibernate.sql.ast.tree.update.Assignable;
import org.hibernate.sql.ast.tree.update.Assignment;
/** /**
* Specialized BaseSqmToSqlAstConverter implementation used during conversion * Specialized BaseSqmToSqlAstConverter implementation used during conversion
* of an SQM mutation query tree representing into the various SQL AST trees * of an SQM mutation query tree representing into the various SQL AST trees
* needed to perform that operation. * needed to perform that operation.
* *
* @see #visitSetClause(SqmSetClause, Consumer, SqmParameterResolutionConsumer)
* @see #visitWhereClause(SqmWhereClause, Consumer, SqmParameterResolutionConsumer)
*
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class MultiTableSqmMutationConverter extends BaseSqmToSqlAstConverter<Statement> { public class MultiTableSqmMutationConverter extends BaseSqmToSqlAstConverter<Statement> {
public interface SqmParameterResolutionConsumer {
void accept(SqmParameter<?> sqmParam, MappingModelExpressible<?> mappingType, List<JdbcParameter> jdbcParameters);
}
private final EntityMappingType mutatingEntityDescriptor; private final EntityMappingType mutatingEntityDescriptor;
private final TableGroup mutatingTableGroup; private final TableGroup mutatingTableGroup;
private Predicate discriminatorPredicate; private Predicate discriminatorPredicate;
private SqmParameterResolutionConsumer parameterResolutionConsumer;
public MultiTableSqmMutationConverter( public MultiTableSqmMutationConverter(
EntityMappingType mutatingEntityDescriptor, EntityMappingType mutatingEntityDescriptor,
SqmStatement<?> statement, SqmStatement<?> statement,
@ -119,6 +94,7 @@ public class MultiTableSqmMutationConverter extends BaseSqmToSqlAstConverter<Sta
sourceAlias, sourceAlias,
null, null,
() -> (predicate) -> { () -> (predicate) -> {
assert this.discriminatorPredicate == null;
this.discriminatorPredicate = predicate; this.discriminatorPredicate = predicate;
}, },
this this
@ -146,98 +122,9 @@ public class MultiTableSqmMutationConverter extends BaseSqmToSqlAstConverter<Sta
return super.getProcessingStateStack(); return super.getProcessingStateStack();
} }
/**
* Specialized hook to visit the assignments defined by the update SQM.
*/
public void visitSetClause(
SqmSetClause setClause,
Consumer<Assignment> assignmentConsumer,
SqmParameterResolutionConsumer parameterResolutionConsumer) {
this.parameterResolutionConsumer = parameterResolutionConsumer;
final List<Assignment> assignments = super.visitSetClause( setClause );
for ( Assignment assignment : assignments ) {
assignmentConsumer.accept( assignment );
}
}
public List<Assignment> visitSetClause(SqmSetClause setClause) {
throw new UnsupportedOperationException();
}
public Predicate visitWhereClause(
SqmWhereClause sqmWhereClause,
Consumer<ColumnReference> restrictionColumnReferenceConsumer,
SqmParameterResolutionConsumer parameterResolutionConsumer) {
this.parameterResolutionConsumer = parameterResolutionConsumer;
if ( sqmWhereClause == null || sqmWhereClause.getPredicate() == null ) {
return discriminatorPredicate;
}
final SqlAstProcessingState rootProcessingState = getCurrentProcessingState();
final SqlAstProcessingStateImpl restrictionProcessingState = new SqlAstProcessingStateImpl(
rootProcessingState,
this,
getCurrentClauseStack()::getCurrent
) {
@Override
public SqlExpressionResolver getSqlExpressionResolver() {
return this;
}
@Override
public Expression resolveSqlExpression(
ColumnReferenceKey key, Function<SqlAstProcessingState, Expression> creator) {
final Expression expression = rootProcessingState.getSqlExpressionResolver().resolveSqlExpression(
key,
creator
);
if ( expression instanceof ColumnReference ) {
restrictionColumnReferenceConsumer.accept( (ColumnReference) expression );
}
return expression;
}
};
pushProcessingState( restrictionProcessingState, getFromClauseIndex() );
try {
getCurrentClauseStack().push( Clause.WHERE );
return SqlAstHelper.combinePredicates(
(Predicate) sqmWhereClause.getPredicate().accept( this ),
discriminatorPredicate
);
}
finally {
getCurrentClauseStack().pop();
popProcessingStateStack();
this.parameterResolutionConsumer = null;
}
}
@Override @Override
public Predicate visitWhereClause(SqmWhereClause whereClause) { public Predicate visitWhereClause(SqmWhereClause whereClause) {
return (Predicate) super.visitWhereClause( whereClause ); return SqlAstHelper.combinePredicates( super.visitWhereClause( whereClause ), discriminatorPredicate );
}
@Override
protected Expression consumeSqmParameter(
SqmParameter<?> sqmParameter,
MappingModelExpressible<?> valueMapping,
BiConsumer<Integer, JdbcParameter> jdbcParameterConsumer) {
assert parameterResolutionConsumer != null;
final Expression expression = super.consumeSqmParameter( sqmParameter, valueMapping, jdbcParameterConsumer );
final List<List<JdbcParameter>> jdbcParameters = getJdbcParamsBySqmParam().get( sqmParameter );
final MappingModelExpressible<?> mappingType = getSqmParameterMappingModelExpressibleResolutions().get( sqmParameter );
parameterResolutionConsumer.accept(
sqmParameter,
mappingType,
jdbcParameters.get( jdbcParameters.size() - 1 )
);
return expression;
} }
} }

View File

@ -28,6 +28,7 @@ import org.hibernate.query.sqm.internal.SqmUtil;
import org.hibernate.query.sqm.mutation.internal.MatchingIdSelectionHelper; import org.hibernate.query.sqm.mutation.internal.MatchingIdSelectionHelper;
import org.hibernate.query.sqm.mutation.internal.MultiTableSqmMutationConverter; import org.hibernate.query.sqm.mutation.internal.MultiTableSqmMutationConverter;
import org.hibernate.query.sqm.mutation.spi.AbstractMutationHandler; import org.hibernate.query.sqm.mutation.spi.AbstractMutationHandler;
import org.hibernate.query.sqm.spi.SqmParameterMappingModelResolutionAccess;
import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement; import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement;
import org.hibernate.query.sqm.tree.expression.SqmExpression; import org.hibernate.query.sqm.tree.expression.SqmExpression;
import org.hibernate.query.sqm.tree.expression.SqmParameter; import org.hibernate.query.sqm.tree.expression.SqmParameter;
@ -131,14 +132,7 @@ public abstract class AbstractCteMutationHandler extends AbstractMutationHandler
parameterResolutions = new IdentityHashMap<>(); parameterResolutions = new IdentityHashMap<>();
} }
//noinspection rawtypes final Predicate restriction = sqmConverter.visitWhereClause( sqmMutationStatement.getWhereClause() );
final Map<SqmParameter, MappingModelExpressible> paramTypeResolutions = new LinkedHashMap<>();
final Predicate restriction = sqmConverter.visitWhereClause(
sqmMutationStatement.getWhereClause(),
columnReference -> {},
(sqmParam, mappingType, jdbcParameters) -> paramTypeResolutions.put( sqmParam, mappingType )
);
sqmConverter.pruneTableGroupJoins(); sqmConverter.pruneTableGroupJoins();
final CteStatement idSelectCte = new CteStatement( final CteStatement idSelectCte = new CteStatement(
@ -193,7 +187,12 @@ public abstract class AbstractCteMutationHandler extends AbstractMutationHandler
SqmUtil.generateJdbcParamsXref( domainParameterXref, sqmConverter ), SqmUtil.generateJdbcParamsXref( domainParameterXref, sqmConverter ),
factory.getRuntimeMetamodels().getMappingMetamodel(), factory.getRuntimeMetamodels().getMappingMetamodel(),
navigablePath -> sqmConverter.getMutatingTableGroup(), navigablePath -> sqmConverter.getMutatingTableGroup(),
paramTypeResolutions::get, new SqmParameterMappingModelResolutionAccess() {
@Override @SuppressWarnings("unchecked")
public <T> MappingModelExpressible<T> getResolvedMappingModelType(SqmParameter<T> parameter) {
return (MappingModelExpressible<T>) sqmConverter.getSqmParameterMappingModelExpressibleResolutions().get( parameter );
}
},
executionContext.getSession() executionContext.getSession()
); );
final LockOptions lockOptions = executionContext.getQueryOptions().getLockOptions(); final LockOptions lockOptions = executionContext.getQueryOptions().getLockOptions();

View File

@ -8,14 +8,15 @@ package org.hibernate.query.sqm.mutation.internal.cte;
import java.util.AbstractMap; import java.util.AbstractMap;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.stream.Collectors;
import org.hibernate.boot.model.naming.Identifier; import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.temptable.TemporaryTable;
import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.id.BulkInsertionCapableIdentifierGenerator; import org.hibernate.id.BulkInsertionCapableIdentifierGenerator;
@ -37,6 +38,7 @@ import org.hibernate.query.results.TableGroupImpl;
import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.spi.DomainQueryExecutionContext;
import org.hibernate.query.sqm.BinaryArithmeticOperator; import org.hibernate.query.sqm.BinaryArithmeticOperator;
import org.hibernate.query.sqm.ComparisonOperator; import org.hibernate.query.sqm.ComparisonOperator;
import org.hibernate.query.sqm.SetOperator;
import org.hibernate.query.sqm.internal.DomainParameterXref; import org.hibernate.query.sqm.internal.DomainParameterXref;
import org.hibernate.query.sqm.internal.SqmJdbcExecutionContextAdapter; import org.hibernate.query.sqm.internal.SqmJdbcExecutionContextAdapter;
import org.hibernate.query.sqm.internal.SqmUtil; import org.hibernate.query.sqm.internal.SqmUtil;
@ -46,9 +48,11 @@ import org.hibernate.query.sqm.mutation.internal.SqmInsertStrategyHelper;
import org.hibernate.query.sqm.spi.SqmParameterMappingModelResolutionAccess; import org.hibernate.query.sqm.spi.SqmParameterMappingModelResolutionAccess;
import org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter; import org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter;
import org.hibernate.query.sqm.sql.internal.SqmPathInterpretation; import org.hibernate.query.sqm.sql.internal.SqmPathInterpretation;
import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.query.sqm.tree.expression.SqmExpression; import org.hibernate.query.sqm.tree.expression.SqmExpression;
import org.hibernate.query.sqm.tree.expression.SqmParameter; import org.hibernate.query.sqm.tree.expression.SqmParameter;
import org.hibernate.query.sqm.tree.expression.SqmStar; import org.hibernate.query.sqm.tree.expression.SqmStar;
import org.hibernate.query.sqm.tree.insert.SqmConflictClause;
import org.hibernate.query.sqm.tree.insert.SqmInsertSelectStatement; import org.hibernate.query.sqm.tree.insert.SqmInsertSelectStatement;
import org.hibernate.query.sqm.tree.insert.SqmInsertStatement; import org.hibernate.query.sqm.tree.insert.SqmInsertStatement;
import org.hibernate.query.sqm.tree.insert.SqmInsertValuesStatement; import org.hibernate.query.sqm.tree.insert.SqmInsertValuesStatement;
@ -70,21 +74,32 @@ import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.JdbcParameter; import org.hibernate.sql.ast.tree.expression.JdbcParameter;
import org.hibernate.sql.ast.tree.expression.QueryLiteral; import org.hibernate.sql.ast.tree.expression.QueryLiteral;
import org.hibernate.sql.ast.tree.expression.SelfRenderingSqlFragmentExpression; import org.hibernate.sql.ast.tree.expression.SelfRenderingSqlFragmentExpression;
import org.hibernate.sql.ast.tree.expression.SqlTuple;
import org.hibernate.sql.ast.tree.expression.Star;
import org.hibernate.sql.ast.tree.from.DerivedTableReference;
import org.hibernate.sql.ast.tree.from.FromClause;
import org.hibernate.sql.ast.tree.from.NamedTableReference; import org.hibernate.sql.ast.tree.from.NamedTableReference;
import org.hibernate.sql.ast.tree.from.QueryPartTableGroup;
import org.hibernate.sql.ast.tree.from.StandardTableGroup;
import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableGroupJoin; import org.hibernate.sql.ast.tree.from.TableGroupJoin;
import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.from.TableReferenceJoin; import org.hibernate.sql.ast.tree.from.TableReferenceJoin;
import org.hibernate.sql.ast.tree.from.UnionTableReference; import org.hibernate.sql.ast.tree.from.UnionTableReference;
import org.hibernate.sql.ast.tree.from.ValuesTableGroup; import org.hibernate.sql.ast.tree.from.ValuesTableGroup;
import org.hibernate.sql.ast.tree.insert.ConflictClause;
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement; import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
import org.hibernate.sql.ast.tree.insert.Values; import org.hibernate.sql.ast.tree.insert.Values;
import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate; import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate;
import org.hibernate.sql.ast.tree.predicate.ExistsPredicate;
import org.hibernate.sql.ast.tree.predicate.Predicate;
import org.hibernate.sql.ast.tree.select.QueryGroup;
import org.hibernate.sql.ast.tree.select.QueryPart; import org.hibernate.sql.ast.tree.select.QueryPart;
import org.hibernate.sql.ast.tree.select.QuerySpec; import org.hibernate.sql.ast.tree.select.QuerySpec;
import org.hibernate.sql.ast.tree.select.SelectStatement; import org.hibernate.sql.ast.tree.select.SelectStatement;
import org.hibernate.sql.ast.tree.select.SortSpecification; import org.hibernate.sql.ast.tree.select.SortSpecification;
import org.hibernate.sql.ast.tree.update.Assignment; import org.hibernate.sql.ast.tree.update.Assignment;
import org.hibernate.sql.ast.tree.update.UpdateStatement;
import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect; import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect;
import org.hibernate.sql.exec.spi.JdbcParameterBindings; import org.hibernate.sql.exec.spi.JdbcParameterBindings;
import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.DomainResult;
@ -92,6 +107,7 @@ import org.hibernate.sql.results.graph.basic.BasicResult;
import org.hibernate.sql.results.internal.SqlSelectionImpl; import org.hibernate.sql.results.internal.SqlSelectionImpl;
import org.hibernate.sql.results.spi.ListResultsConsumer; import org.hibernate.sql.results.spi.ListResultsConsumer;
import org.hibernate.generator.Generator; import org.hibernate.generator.Generator;
import org.hibernate.type.BasicType;
import org.hibernate.type.spi.TypeConfiguration; import org.hibernate.type.spi.TypeConfiguration;
/** /**
@ -191,12 +207,6 @@ public class CteInsertHandler implements InsertHandler {
final int size = sqmStatement.getInsertionTargetPaths().size(); final int size = sqmStatement.getInsertionTargetPaths().size();
final List<Map.Entry<List<CteColumn>, Assignment>> targetPathColumns = new ArrayList<>( size ); final List<Map.Entry<List<CteColumn>, Assignment>> targetPathColumns = new ArrayList<>( size );
final List<CteColumn> targetPathCteColumns = new ArrayList<>( size ); final List<CteColumn> targetPathCteColumns = new ArrayList<>( size );
final NamedTableReference entityTableReference = new NamedTableReference(
cteTable.getTableExpression(),
TemporaryTable.DEFAULT_ALIAS,
true
);
final InsertSelectStatement insertStatement = new InsertSelectStatement( entityTableReference );
final BaseSqmToSqlAstConverter.AdditionalInsertValues additionalInsertValues = sqmConverter.visitInsertionTargetPaths( final BaseSqmToSqlAstConverter.AdditionalInsertValues additionalInsertValues = sqmConverter.visitInsertionTargetPaths(
(assignable, columnReferences) -> { (assignable, columnReferences) -> {
@ -211,7 +221,6 @@ public class CteInsertHandler implements InsertHandler {
final int end = offset + pathInterpretation.getExpressionType().getJdbcTypeCount(); final int end = offset + pathInterpretation.getExpressionType().getJdbcTypeCount();
// Find a matching cte table column and set that at the current index // Find a matching cte table column and set that at the current index
final List<CteColumn> columns = cteTable.getCteColumns().subList( offset, end ); final List<CteColumn> columns = cteTable.getCteColumns().subList( offset, end );
insertStatement.addTargetColumnReferences( columnReferences );
targetPathCteColumns.addAll( columns ); targetPathCteColumns.addAll( columns );
targetPathColumns.add( targetPathColumns.add(
new AbstractMap.SimpleEntry<>( new AbstractMap.SimpleEntry<>(
@ -245,17 +254,6 @@ public class CteInsertHandler implements InsertHandler {
if ( additionalInsertValues.applySelections( querySpec, sessionFactory ) ) { if ( additionalInsertValues.applySelections( querySpec, sessionFactory ) ) {
final CteColumn rowNumberColumn = cteTable.getCteColumns() final CteColumn rowNumberColumn = cteTable.getCteColumns()
.get( cteTable.getCteColumns().size() - 1 ); .get( cteTable.getCteColumns().size() - 1 );
final ColumnReference columnReference = new ColumnReference(
(String) null,
rowNumberColumn.getColumnExpression(),
false,
null,
rowNumberColumn.getJdbcMapping()
);
insertStatement.getTargetColumns().set(
insertStatement.getTargetColumns().size() - 1,
columnReference
);
targetPathCteColumns.set( targetPathCteColumns.set(
targetPathCteColumns.size() - 1, targetPathCteColumns.size() - 1,
rowNumberColumn rowNumberColumn
@ -326,14 +324,6 @@ public class CteInsertHandler implements InsertHandler {
// Add the row number to the assignments // Add the row number to the assignments
final CteColumn rowNumberColumn = cteTable.getCteColumns() final CteColumn rowNumberColumn = cteTable.getCteColumns()
.get( cteTable.getCteColumns().size() - 1 ); .get( cteTable.getCteColumns().size() - 1 );
final ColumnReference columnReference = new ColumnReference(
(String) null,
rowNumberColumn.getColumnExpression(),
false,
null,
rowNumberColumn.getJdbcMapping()
);
insertStatement.getTargetColumns().add( columnReference );
targetPathCteColumns.add( rowNumberColumn ); targetPathCteColumns.add( rowNumberColumn );
} }
@ -743,11 +733,13 @@ public class CteInsertHandler implements InsertHandler {
throw new IllegalStateException( "There must be at least a single root table assignment" ); throw new IllegalStateException( "There must be at least a single root table assignment" );
} }
final ConflictClause conflictClause = sqmConverter.visitConflictClause( sqmStatement.getConflictClause() );
final int tableSpan = persister.getTableSpan(); final int tableSpan = persister.getTableSpan();
final String[] rootKeyColumns = persister.getKeyColumns( 0 ); final String[] rootKeyColumns = persister.getKeyColumns( 0 );
final List<CteColumn> keyCteColumns = queryCte.getCteTable().getCteColumns().subList( 0, rootKeyColumns.length ); final List<CteColumn> keyCteColumns = queryCte.getCteTable().getCteColumns().subList( 0, rootKeyColumns.length );
for ( int i = 0; i < tableSpan; i++ ) { for ( int tableIndex = 0; tableIndex < tableSpan; tableIndex++ ) {
final String tableExpression = persister.getTableName( i ); final String tableExpression = persister.getTableName( tableIndex );
final TableReference updatingTableReference = updatingTableGroup.getTableReference( final TableReference updatingTableReference = updatingTableGroup.getTableReference(
updatingTableGroup.getNavigablePath(), updatingTableGroup.getNavigablePath(),
tableExpression, tableExpression,
@ -758,7 +750,7 @@ public class CteInsertHandler implements InsertHandler {
updatingTableReference, updatingTableReference,
tableExpression tableExpression
); );
final String[] keyColumns = persister.getKeyColumns( i ); final String[] keyColumns = persister.getKeyColumns( tableIndex );
final List<ColumnReference> returningColumnReferences = new ArrayList<>( final List<ColumnReference> returningColumnReferences = new ArrayList<>(
keyColumns.length + ( assignmentList == null ? 0 : assignmentList.size() ) keyColumns.length + ( assignmentList == null ? 0 : assignmentList.size() )
); );
@ -766,7 +758,7 @@ public class CteInsertHandler implements InsertHandler {
final QuerySpec insertSelectSpec = new QuerySpec( true ); final QuerySpec insertSelectSpec = new QuerySpec( true );
CteStatement finalCteStatement = null; CteStatement finalCteStatement = null;
final CteTable dmlResultCte; final CteTable dmlResultCte;
if ( i == 0 && !assignsId && identifierGenerator.generatedOnExecution() ) { if ( tableIndex == 0 && !assignsId && identifierGenerator.generatedOnExecution() ) {
// Special handling for identity generation // Special handling for identity generation
final String cteTableName = getCteTableName( tableExpression, "base_" ); final String cteTableName = getCteTableName( tableExpression, "base_" );
if ( statement.getCteStatement( cteTableName ) != null ) { if ( statement.getCteStatement( cteTableName ) != null ) {
@ -999,11 +991,57 @@ public class CteInsertHandler implements InsertHandler {
} }
} }
dmlStatement.setSourceSelectStatement( insertSelectSpec ); dmlStatement.setSourceSelectStatement( insertSelectSpec );
statement.addCteStatement( new CteStatement( dmlResultCte, dmlStatement ) ); if ( conflictClause != null ) {
if ( conflictClause.isDoNothing() && conflictClause.getConstraintColumnNames().isEmpty() ) {
// Conflict clauses that use a constraint name and do nothing can just use the conflict clause as it is
handleConflictClause( dmlResultCte, dmlStatement, queryCte, tableIndex, conflictClause, statement );
}
else {
final List<Assignment> compatibleAssignments = getCompatibleAssignments( dmlStatement, conflictClause );
if ( isIdentifierConflictClause( sqmStatement ) ) {
// If the identifier is used in the SqmInsert, use the key columns of the respective table
handleConflictClause(
dmlResultCte,
dmlStatement,
queryCte,
tableIndex,
new ConflictClause(
conflictClause.getConstraintName(),
Arrays.asList( keyColumns ),
compatibleAssignments,
compatibleAssignments.isEmpty() ? null : conflictClause.getPredicate()
),
statement
);
}
else if ( targetColumnsContainAllConstraintColumns( dmlStatement, conflictClause ) ) {
// Also apply the conflict clause if the insert target columns contain the constraint columns
handleConflictClause(
dmlResultCte,
dmlStatement,
queryCte,
tableIndex,
new ConflictClause(
conflictClause.getConstraintName(),
conflictClause.getConstraintColumnNames(),
compatibleAssignments,
compatibleAssignments.isEmpty() ? null : conflictClause.getPredicate()
),
statement
);
}
else {
statement.addCteStatement( new CteStatement( dmlResultCte, dmlStatement ) );
}
}
}
else {
statement.addCteStatement( new CteStatement( dmlResultCte, dmlStatement ) );
}
if ( finalCteStatement != null ) { if ( finalCteStatement != null ) {
statement.addCteStatement( finalCteStatement ); statement.addCteStatement( finalCteStatement );
} }
if ( i == 0 && !assignsId && identifierGenerator.generatedOnExecution() ) { if ( tableIndex == 0 && !assignsId && identifierGenerator.generatedOnExecution() ) {
// Special handling for identity generation // Special handling for identity generation
statement.addCteStatement( queryCte ); statement.addCteStatement( queryCte );
} }
@ -1011,6 +1049,319 @@ public class CteInsertHandler implements InsertHandler {
return getCteTableName( rootTableName ); return getCteTableName( rootTableName );
} }
private void handleConflictClause(
CteTable dmlResultCte,
InsertSelectStatement insertStatement,
CteStatement queryCte,
int tableIndex,
ConflictClause conflictClause,
CteContainer statement) {
if ( sessionFactory.getJdbcServices().getDialect().supportsConflictClauseForInsertCTE() ) {
insertStatement.setConflictClause( conflictClause );
statement.addCteStatement( new CteStatement( dmlResultCte, insertStatement ) );
}
else {
// Build an exists subquery clause to only insert if no row with a matching constraint column value exists i.e.
// insert into target (c1, c2)
// select e.c1, e.c2 from HTE_target e
// where not exists (select 1 from target excluded where e.c1=excluded.c1 and e.c2=excluded.c2)
final BasicType<Boolean> booleanType = sessionFactory.getNodeBuilder().getBooleanType();
final List<String> constraintColumnNames = conflictClause.getConstraintColumnNames();
final QuerySpec insertQuerySpec = (QuerySpec) insertStatement.getSourceSelectStatement();
final QuerySpec subquery = new QuerySpec( false, 1 );
// This is the table group we use in the subquery to check no row for the given constraint columns exists.
// We name it "excluded" because the predicates we build for this check are reused for the
// check in the update statement below.
// "excluded" is our well known name to refer to data that was not inserted
final TableGroup tableGroup = new StandardTableGroup(
false,
new NavigablePath( "excluded" ),
entityDescriptor,
null,
new NamedTableReference(
insertStatement.getTargetTable().getTableExpression(),
"excluded"
),
null,
sessionFactory
);
subquery.getSelectClause().addSqlSelection(
new SqlSelectionImpl( new QueryLiteral<>( 1, sessionFactory.getNodeBuilder().getIntegerType() ) )
);
subquery.getFromClause().addRoot( tableGroup );
List<String> columnsToMatch;
if ( constraintColumnNames.isEmpty() ) {
// Assume the primary key columns
final AbstractEntityPersister aep = (AbstractEntityPersister) entityDescriptor;
Predicate predicate = buildColumnMatchPredicate(
columnsToMatch = Arrays.asList( aep.getKeyColumns( tableIndex ) ),
insertStatement,
false,
true
);
if ( predicate == null ) {
throw new IllegalArgumentException( "Couldn't infer conflict constraint columns" );
}
subquery.applyPredicate( predicate );
}
else {
columnsToMatch = constraintColumnNames;
subquery.applyPredicate( buildColumnMatchPredicate( constraintColumnNames, insertStatement, true, true ) );
}
insertQuerySpec.applyPredicate( new ExistsPredicate( subquery, true, booleanType ) );
// Emulate the conflict do update clause by creating a separate update CTEs
if ( conflictClause.isDoUpdate() ) {
final TableGroup temporaryTableGroup = insertQuerySpec.getFromClause().getRoots().get( 0 );
final QuerySpec renamingSubquery = new QuerySpec( false, 1 );
final List<String> columnNames = buildCteRenaming(
renamingSubquery,
temporaryTableGroup,
queryCte
);
renamingSubquery.getFromClause().addRoot( temporaryTableGroup );
final QueryPartTableGroup excludedTableGroup = new QueryPartTableGroup(
new NavigablePath( "excluded" ),
null,
new SelectStatement( renamingSubquery ),
"excluded",
columnNames,
false,
false,
sessionFactory
);
final UpdateStatement updateStatement;
if ( sessionFactory.getJdbcServices().getDialect().supportsFromClauseInUpdate() ) {
final FromClause fromClause = new FromClause( 1 );
final TableGroup updateTableGroup = new StandardTableGroup(
false,
new NavigablePath( "updated" ),
entityDescriptor,
null,
insertStatement.getTargetTable(),
null,
sessionFactory
);
fromClause.addRoot( updateTableGroup );
updateStatement = new UpdateStatement(
insertStatement.getTargetTable(),
fromClause,
conflictClause.getAssignments(),
conflictClause.getPredicate(),
insertStatement.getReturningColumns()
);
updateTableGroup.addTableGroupJoin(
new TableGroupJoin(
excludedTableGroup.getNavigablePath(),
SqlAstJoinType.INNER,
excludedTableGroup,
buildColumnMatchPredicate(
columnsToMatch,
insertStatement,
true,
false
)
)
);
}
else {
final List<Assignment> assignments = conflictClause.getAssignments();
final List<ColumnReference> assignmentColumns = new ArrayList<>( assignments.size() );
final QuerySpec updateSubquery = new QuerySpec( false, 1 );
for ( Assignment assignment : assignments ) {
assignmentColumns.add( (ColumnReference) assignment.getAssignable() );
updateSubquery.getSelectClause().addSqlSelection(
new SqlSelectionImpl( assignment.getAssignedValue() )
);
}
updateSubquery.getFromClause().addRoot( excludedTableGroup );
updateSubquery.applyPredicate( buildColumnMatchPredicate(
columnsToMatch,
insertStatement,
true,
false
) );
final QuerySpec matchCteSubquery = new QuerySpec( false, 1 );
matchCteSubquery.getSelectClause().addSqlSelection(
new SqlSelectionImpl( new QueryLiteral<>(
1,
sessionFactory.getNodeBuilder().getIntegerType()
) )
);
matchCteSubquery.getFromClause().addRoot( updateSubquery.getFromClause().getRoots().get( 0 ) );
matchCteSubquery.applyPredicate( updateSubquery.getWhereClauseRestrictions() );
updateStatement = new UpdateStatement(
insertStatement.getTargetTable(),
List.of( new Assignment(
new SqlTuple( assignmentColumns, null ),
new SelectStatement( updateSubquery )
) ),
Predicate.combinePredicates(
new ExistsPredicate( matchCteSubquery, false, booleanType ),
conflictClause.getPredicate()
),
insertStatement.getReturningColumns()
);
}
final CteTable updateCte = dmlResultCte.withName( dmlResultCte.getTableExpression() + "_upd" );
statement.addCteStatement( new CteStatement( updateCte, updateStatement ) );
final CteTable insertCte = dmlResultCte.withName( dmlResultCte.getTableExpression() + "_ins" );
statement.addCteStatement( new CteStatement( insertCte, insertStatement ) );
// Union the update and inserted ids together to be able to determine the effective update count
final List<QueryPart> queryParts = new ArrayList<>( 2 );
final QuerySpec dmlCombinationQ1 = new QuerySpec( false, 1 );
final QuerySpec dmlCombinationQ2 = new QuerySpec( false, 1 );
dmlCombinationQ1.getSelectClause().addSqlSelection( new SqlSelectionImpl( new Star() ) );
dmlCombinationQ2.getSelectClause().addSqlSelection( new SqlSelectionImpl( new Star() ) );
dmlCombinationQ1.getFromClause().addRoot( new CteTableGroup( new NamedTableReference( updateCte.getTableExpression(), "t" ) ) );
dmlCombinationQ2.getFromClause().addRoot( new CteTableGroup( new NamedTableReference( insertCte.getTableExpression(), "t" ) ) );
queryParts.add( dmlCombinationQ1 );
queryParts.add( dmlCombinationQ2 );
final SelectStatement dmlCombinationStatement = new SelectStatement( new QueryGroup( true, SetOperator.UNION_ALL, queryParts ) );
statement.addCteStatement( new CteStatement( dmlResultCte, dmlCombinationStatement ) );
}
else {
statement.addCteStatement( new CteStatement( dmlResultCte, insertStatement ) );
}
}
}
private List<String> buildCteRenaming(
QuerySpec renamingSubquery,
TableGroup temporaryTableGroup,
CteStatement queryCte) {
final List<CteColumn> cteColumns = queryCte.getCteTable().getCteColumns();
for ( CteColumn cteColumn : cteColumns ) {
renamingSubquery.getSelectClause().addSqlSelection(
new SqlSelectionImpl(
new ColumnReference(
temporaryTableGroup.getPrimaryTableReference(),
cteColumn.getColumnExpression(),
cteColumn.getJdbcMapping()
)
)
);
}
final SelectStatement selectStatement = (SelectStatement) queryCte.getCteDefinition();
final QuerySpec querySpec = (QuerySpec) selectStatement.getQueryPart();
final DerivedTableReference tableReference = (DerivedTableReference) querySpec.getFromClause()
.getRoots()
.get( 0 )
.getPrimaryTableReference();
return tableReference.getColumnNames();
}
private Predicate buildColumnMatchPredicate(
List<String> constraintColumnNames,
InsertSelectStatement dmlStatement,
boolean errorIfMissing,
boolean compareAgainstSelectItems) {
final BasicType<Boolean> booleanType = sessionFactory.getNodeBuilder().getBooleanType();
final QuerySpec insertQuerySpec = (QuerySpec) dmlStatement.getSourceSelectStatement();
Predicate predicate = null;
OUTER: for ( String constraintColumnName : constraintColumnNames ) {
final List<ColumnReference> targetColumns = dmlStatement.getTargetColumns();
for ( int i = 0; i < targetColumns.size(); i++ ) {
final ColumnReference columnReference = targetColumns.get( i );
if ( columnReference.getColumnExpression().equals( constraintColumnName ) ) {
if ( compareAgainstSelectItems ) {
predicate = Predicate.combinePredicates(
predicate,
new ComparisonPredicate(
new ColumnReference(
"excluded",
columnReference.getColumnExpression(),
false,
null,
columnReference.getJdbcMapping()
),
ComparisonOperator.EQUAL,
insertQuerySpec.getSelectClause()
.getSqlSelections()
.get( i )
.getExpression(),
booleanType
)
);
}
else {
predicate = Predicate.combinePredicates(
predicate,
new ComparisonPredicate(
columnReference,
ComparisonOperator.EQUAL,
new ColumnReference(
"excluded",
columnReference.getColumnExpression(),
false,
null,
columnReference.getJdbcMapping()
),
booleanType
)
);
}
continue OUTER;
}
}
if ( errorIfMissing ) {
// Should never happen
final List<String> targetColumnNames = targetColumns.stream()
.map( ColumnReference::getColumnExpression )
.collect( Collectors.toList() );
throw new IllegalArgumentException( "Couldn't find conflict constraint column [" + constraintColumnName + "] in insert target columns: " + targetColumnNames );
}
return null;
}
return predicate;
}
private List<Assignment> getCompatibleAssignments(InsertSelectStatement dmlStatement, ConflictClause conflictClause) {
if ( conflictClause.isDoNothing() ) {
return Collections.emptyList();
}
List<Assignment> compatibleAssignments = null;
final List<Assignment> assignments = conflictClause.getAssignments();
for ( Assignment assignment : assignments ) {
for ( ColumnReference targetColumn : dmlStatement.getTargetColumns() ) {
if ( targetColumn.equals( assignment.getAssignable() ) ) {
if ( compatibleAssignments == null ) {
compatibleAssignments = new ArrayList<>( assignments.size() );
}
compatibleAssignments.add( assignment );
break;
}
}
}
return compatibleAssignments == null ? Collections.emptyList() : compatibleAssignments;
}
private boolean isIdentifierConflictClause(SqmInsertStatement<?> sqmStatement) {
final SqmConflictClause<?> conflictClause = sqmStatement.getConflictClause();
assert conflictClause != null;
final List<SqmPath<?>> constraintPaths = conflictClause.getConstraintPaths();
return constraintPaths.size() == 1
&& constraintPaths.get( 0 ).getReferencedPathSource() == sqmStatement.getTarget().getModel().getIdentifierDescriptor();
}
private boolean targetColumnsContainAllConstraintColumns(InsertSelectStatement statement, ConflictClause conflictClause) {
OUTER: for ( String constraintColumnName : conflictClause.getConstraintColumnNames() ) {
for ( ColumnReference targetColumn : statement.getTargetColumns() ) {
if ( targetColumn.getColumnExpression().equals( constraintColumnName ) ) {
continue OUTER;
}
}
return false;
}
return true;
}
protected NamedTableReference resolveUnionTableReference( protected NamedTableReference resolveUnionTableReference(
TableReference tableReference, TableReference tableReference,
String tableExpression) { String tableExpression) {

View File

@ -98,15 +98,10 @@ public class CteUpdateHandler extends AbstractCteMutationHandler implements Upda
// visit the set-clause using our special converter, collecting // visit the set-clause using our special converter, collecting
// information about the assignments // information about the assignments
final SqmSetClause setClause = updateStatement.getSetClause(); final SqmSetClause setClause = updateStatement.getSetClause();
final List<Assignment> assignments = new ArrayList<>( setClause.getAssignments().size() ); final List<Assignment> assignments = sqmConverter.visitSetClause( setClause );
for ( Map.Entry<SqmParameter<?>, List<List<JdbcParameter>>> entry : sqmConverter.getJdbcParamsBySqmParam().entrySet() ) {
sqmConverter.visitSetClause( parameterResolutions.put( entry.getKey(), entry.getValue().get( entry.getValue().size() - 1 ) );
setClause, }
assignments::add,
(sqmParam, mappingType, jdbcParameters) -> {
parameterResolutions.put( sqmParam, jdbcParameters );
}
);
sqmConverter.addVersionedAssignment( assignments::add, updateStatement ); sqmConverter.addVersionedAssignment( assignments::add, updateStatement );
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -11,16 +11,15 @@ import java.util.List;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.metamodel.mapping.BasicValuedMapping;
import org.hibernate.metamodel.mapping.BasicValuedModelPart; import org.hibernate.metamodel.mapping.BasicValuedModelPart;
import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.SelectableConsumer; import org.hibernate.metamodel.mapping.SelectableConsumer;
import org.hibernate.metamodel.mapping.EntityIdentifierMapping; import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.sql.ast.tree.expression.ColumnReference; import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.JdbcLiteral; import org.hibernate.sql.ast.tree.expression.QueryLiteral;
import org.hibernate.sql.ast.tree.expression.SqlTuple; import org.hibernate.sql.ast.tree.expression.SqlTuple;
import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.predicate.InListPredicate; import org.hibernate.sql.ast.tree.predicate.InListPredicate;
@ -45,19 +44,42 @@ import org.hibernate.sql.exec.spi.ExecutionContext;
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class InPredicateRestrictionProducer implements MatchingIdRestrictionProducer { public class InPredicateRestrictionProducer implements MatchingIdRestrictionProducer {
@Override
public List<Expression> produceIdExpressionList(List<Object> idsAndFks, EntityMappingType entityDescriptor) {
final List<Expression> inListExpressions = new ArrayList<>( idsAndFks.size() );
final EntityIdentifierMapping identifierMapping = entityDescriptor.getIdentifierMapping();
if ( identifierMapping instanceof BasicValuedModelPart ) {
final BasicValuedModelPart basicValuedModelPart = (BasicValuedModelPart) identifierMapping;
for ( int i = 0; i < idsAndFks.size(); i++ ) {
inListExpressions.add( new QueryLiteral<>( idsAndFks.get( i ), basicValuedModelPart ) );
}
}
else {
final int jdbcTypeCount = identifierMapping.getJdbcTypeCount();
for ( int i = 0; i < idsAndFks.size(); i++ ) {
final Object[] id = (Object[]) idsAndFks.get( i );
final List<Expression> tupleElements = new ArrayList<>( jdbcTypeCount );
inListExpressions.add( new SqlTuple( tupleElements, identifierMapping ) );
identifierMapping.forEachJdbcType( (index, jdbcMapping) -> {
tupleElements.add( new QueryLiteral<>( id[index], (BasicValuedMapping) jdbcMapping ) );
} );
}
}
return inListExpressions;
}
@Override @Override
public InListPredicate produceRestriction( public InListPredicate produceRestriction(
List<?> matchingIdValues, List<Expression> matchingIdValueExpressions,
EntityMappingType entityDescriptor, EntityMappingType entityDescriptor,
int valueIndex, int valueIndex,
ModelPart valueModelPart, ModelPart valueModelPart,
TableReference mutatingTableReference, TableReference mutatingTableReference,
Supplier<Consumer<SelectableConsumer>> columnsToMatchVisitationSupplier, Supplier<Consumer<SelectableConsumer>> columnsToMatchVisitationSupplier,
ExecutionContext executionContext) { ExecutionContext executionContext) {
assert matchingIdValues != null; assert matchingIdValueExpressions != null;
assert ! matchingIdValues.isEmpty(); assert ! matchingIdValueExpressions.isEmpty();
final SessionFactoryImplementor sessionFactory = executionContext.getSession().getFactory();
final EntityIdentifierMapping identifierMapping = entityDescriptor.getIdentifierMapping(); final EntityIdentifierMapping identifierMapping = entityDescriptor.getIdentifierMapping();
final int idColumnCount = identifierMapping.getJdbcTypeCount(); final int idColumnCount = identifierMapping.getJdbcTypeCount();
@ -76,45 +98,27 @@ public class InPredicateRestrictionProducer implements MatchingIdRestrictionProd
null, null,
basicIdMapping.getJdbcMapping() basicIdMapping.getJdbcMapping()
); );
predicate = new InListPredicate( inFixture ); predicate = new InListPredicate( inFixture, matchingIdValueExpressions );
matchingIdValues.forEach(
matchingId -> predicate.addExpression( new JdbcLiteral<>( matchingId, basicIdMapping.getJdbcMapping() ) )
);
} }
else { else {
final List<ColumnReference> columnReferences = new ArrayList<>( idColumnCount ); final List<ColumnReference> columnReferences = new ArrayList<>( idColumnCount );
final List<JdbcMapping> jdbcMappings = new ArrayList<>( idColumnCount ); final SelectableConsumer selectableConsumer = (columnIndex, selection) -> {
identifierMapping.forEachSelectable( columnReferences.add(
(columnIndex, selection) -> { new ColumnReference(
columnReferences.add( mutatingTableReference,
new ColumnReference( selection
mutatingTableReference, )
selection );
) };
); if ( columnsToMatchVisitationSupplier == null ) {
jdbcMappings.add( selection.getJdbcMapping() ); identifierMapping.forEachSelectable( selectableConsumer );
} }
); else {
columnsToMatchVisitationSupplier.get().accept( selectableConsumer );
}
final Expression inFixture = new SqlTuple( columnReferences, identifierMapping ); final Expression inFixture = new SqlTuple( columnReferences, identifierMapping );
predicate = new InListPredicate( inFixture ); predicate = new InListPredicate( inFixture, matchingIdValueExpressions );
matchingIdValues.forEach(
matchingId -> {
assert matchingId instanceof Object[];
final Object[] matchingIdParts = (Object[]) matchingId;
final List<JdbcLiteral<?>> tupleParts = new ArrayList<>( idColumnCount );
for ( int p = 0; p < matchingIdParts.length; p++ ) {
tupleParts.add(
new JdbcLiteral<>( matchingIdParts[p],jdbcMappings.get( p ) )
);
}
predicate.addExpression( new SqlTuple( tupleParts, identifierMapping ) );
}
);
} }
return predicate; return predicate;

Some files were not shown because too many files have changed in this diff Show More