HHH-17506 Support ON CONFLICT clause for HQL/Criteria inserts
This commit is contained in:
parent
c931c86896
commit
bb4ed4b000
|
@ -5,7 +5,7 @@
|
|||
# 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.url jdbc:sap://localhost:39015/
|
||||
hibernate.connection.username HIBERNATE_TEST
|
||||
|
|
|
@ -161,7 +161,7 @@ hibernate.connection.url @DB_URL@
|
|||
|
||||
## HANA
|
||||
|
||||
#hibernate.dialect org.hibernate.dialect.HANAColumnStoreDialect
|
||||
#hibernate.dialect org.hibernate.dialect.HANADialect
|
||||
#hibernate.connection.driver_class com.sap.db.jdbc.Driver
|
||||
#hibernate.connection.url jdbc:sap://localhost:30015
|
||||
#hibernate.connection.username HIBERNATE_TEST
|
||||
|
|
|
@ -254,7 +254,7 @@ ext {
|
|||
'connection.init_sql' : ''
|
||||
],
|
||||
hana_cloud : [
|
||||
'db.dialect' : 'org.hibernate.dialect.HANAColumnStoreDialect',
|
||||
'db.dialect' : 'org.hibernate.dialect.HANADialect',
|
||||
'jdbc.driver': 'com.sap.db.jdbc.Driver',
|
||||
'jdbc.user' : 'HIBERNATE_TEST',
|
||||
'jdbc.pass' : 'H1bernate_test',
|
||||
|
@ -263,7 +263,7 @@ ext {
|
|||
'connection.init_sql' : ''
|
||||
],
|
||||
hana_ci : [
|
||||
'db.dialect' : 'org.hibernate.dialect.HANAColumnStoreDialect',
|
||||
'db.dialect' : 'org.hibernate.dialect.HANADialect',
|
||||
'jdbc.driver': 'com.sap.db.jdbc.Driver',
|
||||
'jdbc.user' : 'SYSTEM',
|
||||
'jdbc.pass' : 'H1bernate_test',
|
||||
|
|
|
@ -146,13 +146,14 @@ public class AltibaseSqlAstTranslator<T extends JdbcOperation> extends AbstractS
|
|||
emulateQueryPartTableReferenceColumnAliasing( tableReference );
|
||||
}
|
||||
|
||||
protected String getFromDual() {
|
||||
return " from dual";
|
||||
@Override
|
||||
protected String getDual() {
|
||||
return "dual";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getFromDualForSelectOnly() {
|
||||
return getFromDual();
|
||||
return " from " + getDual();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -82,14 +82,14 @@ public class CUBRIDSqlAstTranslator<T extends JdbcOperation> extends AbstractSql
|
|||
}
|
||||
|
||||
@Override
|
||||
protected String getFromDual() {
|
||||
protected String getDual() {
|
||||
//TODO: is this really needed?
|
||||
//TODO: would "from table({0})" be better?
|
||||
return " from db_root";
|
||||
return "db_root";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getFromDualForSelectOnly() {
|
||||
return getFromDual();
|
||||
return " from " + getDual();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -570,6 +570,10 @@ public class CockroachLegacyDialect extends Dialect {
|
|||
public boolean supportsRecursiveCTE() {
|
||||
return getVersion().isSameOrAfter( 20, 1 );
|
||||
}
|
||||
@Override
|
||||
public boolean supportsConflictClauseForInsertCTE() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNoColumnsInsertString() {
|
||||
|
@ -1165,4 +1169,14 @@ public class CockroachLegacyDialect extends Dialect {
|
|||
// RuntimeModelCreationContext runtimeModelCreationContext) {
|
||||
// return new CteInsertStrategy( rootEntityDescriptor, runtimeModelCreationContext );
|
||||
// }
|
||||
|
||||
@Override
|
||||
public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() {
|
||||
return DmlTargetColumnQualifierSupport.TABLE_ALIAS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsFromClauseInUpdate() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,20 +7,27 @@
|
|||
package org.hibernate.community.dialect;
|
||||
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.sql.ast.Clause;
|
||||
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
||||
import org.hibernate.sql.ast.tree.Statement;
|
||||
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.Literal;
|
||||
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.InArrayPredicate;
|
||||
import org.hibernate.sql.ast.tree.predicate.LikePredicate;
|
||||
import org.hibernate.sql.ast.tree.select.QueryGroup;
|
||||
import org.hibernate.sql.ast.tree.select.QueryPart;
|
||||
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.JdbcOperationQueryInsert;
|
||||
|
||||
/**
|
||||
* A SQL AST translator for Cockroach.
|
||||
|
@ -33,6 +40,56 @@ public class CockroachLegacySqlAstTranslator<T extends JdbcOperation> extends Ab
|
|||
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
|
||||
protected void renderExpressionAsClauseItem(Expression expression) {
|
||||
expression.accept( this );
|
||||
|
|
|
@ -24,6 +24,7 @@ import org.hibernate.dialect.DB2Dialect;
|
|||
import org.hibernate.dialect.DB2StructJdbcType;
|
||||
import org.hibernate.dialect.DatabaseVersion;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.dialect.DmlTargetColumnQualifierSupport;
|
||||
import org.hibernate.dialect.OracleDialect;
|
||||
import org.hibernate.dialect.aggregate.AggregateSupport;
|
||||
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.IdentifierHelperBuilder;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.exception.ConstraintViolationException;
|
||||
import org.hibernate.exception.LockTimeoutException;
|
||||
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.mapping.Column;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
|
@ -94,6 +98,7 @@ import org.hibernate.type.spi.TypeConfiguration;
|
|||
|
||||
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.BLOB;
|
||||
import static org.hibernate.type.SqlTypes.BOOLEAN;
|
||||
|
@ -889,6 +894,20 @@ public class DB2LegacyDialect extends Dialect {
|
|||
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
|
||||
public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() {
|
||||
return (sqlException, message, sql) -> {
|
||||
|
@ -896,6 +915,14 @@ public class DB2LegacyDialect extends Dialect {
|
|||
switch ( errorCode ) {
|
||||
case -952:
|
||||
return new LockTimeoutException( message, sqlException, sql );
|
||||
case -803:
|
||||
return new ConstraintViolationException(
|
||||
message,
|
||||
sqlException,
|
||||
sql,
|
||||
ConstraintViolationException.ConstraintKind.UNIQUE,
|
||||
getViolatedConstraintNameExtractor().extractConstraintName( sqlException )
|
||||
);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
@ -1079,4 +1106,14 @@ public class DB2LegacyDialect extends Dialect {
|
|||
public int rowIdSqlType() {
|
||||
return VARBINARY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() {
|
||||
return DmlTargetColumnQualifierSupport.TABLE_ALIAS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsFromClauseInUpdate() {
|
||||
return getDB2Version().isSameOrAfter( 11 );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,8 +13,10 @@ import org.hibernate.LockMode;
|
|||
import org.hibernate.dialect.DatabaseVersion;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
|
||||
import org.hibernate.query.IllegalQueryOperationException;
|
||||
import org.hibernate.query.sqm.ComparisonOperator;
|
||||
import org.hibernate.query.sqm.FetchClauseType;
|
||||
import org.hibernate.sql.ast.Clause;
|
||||
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
||||
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
||||
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.SqlTuple;
|
||||
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.TableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
|
||||
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.predicate.BooleanExpressionPredicate;
|
||||
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||
|
@ -344,12 +348,40 @@ public class DB2LegacySqlAstTranslator<T extends JdbcOperation> extends Abstract
|
|||
@Override
|
||||
protected void visitInsertStatementOnly(InsertSelectStatement 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 ) {
|
||||
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) {
|
||||
final List<ColumnReference> returningColumns = statement.getReturningColumns();
|
||||
final int size = returningColumns.size();
|
||||
|
@ -497,13 +529,13 @@ public class DB2LegacySqlAstTranslator<T extends JdbcOperation> extends Abstract
|
|||
}
|
||||
|
||||
@Override
|
||||
protected String getFromDual() {
|
||||
return " from sysibm.dual";
|
||||
protected String getDual() {
|
||||
return "sysibm.dual";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getFromDualForSelectOnly() {
|
||||
return getFromDual();
|
||||
return " from " + getDual();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -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.IdentifierHelperBuilder;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.exception.ConstraintViolationException;
|
||||
import org.hibernate.exception.LockTimeoutException;
|
||||
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.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
|
||||
|
@ -706,14 +709,42 @@ public class DerbyLegacyDialect extends Dialect {
|
|||
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
|
||||
public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() {
|
||||
return (sqlException, message, sql) -> {
|
||||
final String sqlState = JdbcExceptionHelper.extractSqlState( sqlException );
|
||||
// final int errorCode = JdbcExceptionHelper.extractErrorCode( sqlException );
|
||||
final String constraintName;
|
||||
|
||||
if ( sqlState != null ) {
|
||||
switch ( sqlState ) {
|
||||
case "23505":
|
||||
// Unique constraint violation
|
||||
constraintName = getViolatedConstraintNameExtractor().extractConstraintName(sqlException);
|
||||
return new ConstraintViolationException(
|
||||
message,
|
||||
sqlException,
|
||||
sql,
|
||||
ConstraintViolationException.ConstraintKind.UNIQUE,
|
||||
constraintName
|
||||
);
|
||||
case "40XL1":
|
||||
case "40XL2":
|
||||
return new LockTimeoutException( message, sqlException, sql );
|
||||
|
|
|
@ -235,13 +235,13 @@ public class DerbyLegacySqlAstTranslator<T extends JdbcOperation> extends Abstra
|
|||
}
|
||||
|
||||
@Override
|
||||
protected String getFromDual() {
|
||||
return " from (values 0) dual";
|
||||
protected String getDual() {
|
||||
return "(values 0)";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getFromDualForSelectOnly() {
|
||||
return getFromDual();
|
||||
return " from " + getDual() + " dual";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -263,13 +263,13 @@ public class FirebirdSqlAstTranslator<T extends JdbcOperation> extends AbstractS
|
|||
}
|
||||
|
||||
@Override
|
||||
protected String getFromDual() {
|
||||
return " from rdb$database";
|
||||
protected String getDual() {
|
||||
return "rdb$database";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getFromDualForSelectOnly() {
|
||||
return getFromDual();
|
||||
return " from " + getDual();
|
||||
}
|
||||
|
||||
private boolean supportsOffsetFetchClause() {
|
||||
|
|
|
@ -776,8 +776,19 @@ public class H2LegacyDialect extends Dialect {
|
|||
public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() {
|
||||
return (sqlException, message, sql) -> {
|
||||
final int errorCode = JdbcExceptionHelper.extractErrorCode( sqlException );
|
||||
final String constraintName;
|
||||
|
||||
switch (errorCode) {
|
||||
case 23505:
|
||||
// Unique constraint violation
|
||||
constraintName = getViolatedConstraintNameExtractor().extractConstraintName(sqlException);
|
||||
return new ConstraintViolationException(
|
||||
message,
|
||||
sqlException,
|
||||
sql,
|
||||
ConstraintViolationException.ConstraintKind.UNIQUE,
|
||||
constraintName
|
||||
);
|
||||
case 40001:
|
||||
// DEADLOCK DETECTED
|
||||
return new LockAcquisitionException(message, sqlException, sql);
|
||||
|
@ -786,7 +797,7 @@ public class H2LegacyDialect extends Dialect {
|
|||
return new PessimisticLockException(message, sqlException, sql);
|
||||
case 90006:
|
||||
// NULL not allowed for column [90006-145]
|
||||
final String constraintName = getViolatedConstraintNameExtractor().extractConstraintName(sqlException);
|
||||
constraintName = getViolatedConstraintNameExtractor().extractConstraintName(sqlException);
|
||||
return new ConstraintViolationException(message, sqlException, sql, constraintName);
|
||||
case 57014:
|
||||
return new QueryTimeoutException( message, sqlException, sql );
|
||||
|
@ -940,4 +951,9 @@ public class H2LegacyDialect extends Dialect {
|
|||
public int rowIdSqlType() {
|
||||
return BIGINT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() {
|
||||
return DmlTargetColumnQualifierSupport.TABLE_ALIAS;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import org.hibernate.LockMode;
|
|||
import org.hibernate.dialect.identity.H2IdentityColumnSupport;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.internal.util.collections.CollectionHelper;
|
||||
import org.hibernate.query.IllegalQueryOperationException;
|
||||
import org.hibernate.query.sqm.ComparisonOperator;
|
||||
import org.hibernate.sql.ast.Clause;
|
||||
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.SqlTupleContainer;
|
||||
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.TableGroup;
|
||||
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.InSubQueryPredicate;
|
||||
import org.hibernate.sql.ast.tree.predicate.LikePredicate;
|
||||
import org.hibernate.sql.ast.tree.select.QueryPart;
|
||||
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.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
|
||||
protected void visitReturningColumns(List<ColumnReference> returningColumns) {
|
||||
// do nothing - this is handled via `#visitReturningInsertStatement`
|
||||
|
@ -289,8 +332,8 @@ public class H2LegacySqlAstTranslator<T extends JdbcOperation> extends AbstractS
|
|||
}
|
||||
|
||||
@Override
|
||||
protected String getFromDual() {
|
||||
return " from dual";
|
||||
protected String getDual() {
|
||||
return "dual";
|
||||
}
|
||||
|
||||
private boolean supportsOffsetFetchClause() {
|
||||
|
|
|
@ -41,6 +41,8 @@ import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder;
|
|||
import org.hibernate.engine.jdbc.env.spi.NameQualifierSupport;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
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.ViolatedConstraintNameExtractor;
|
||||
import org.hibernate.internal.CoreMessageLogger;
|
||||
|
@ -497,6 +499,29 @@ public class HSQLLegacyDialect extends Dialect {
|
|||
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
|
||||
* messages may be localized - therefore use the common, non-locale element " table: "
|
||||
|
@ -856,4 +881,9 @@ public class HSQLLegacyDialect extends Dialect {
|
|||
public UniqueDelegate getUniqueDelegate() {
|
||||
return uniqueDelegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() {
|
||||
return DmlTargetColumnQualifierSupport.TABLE_ALIAS;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,23 +11,32 @@ import java.util.function.Consumer;
|
|||
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
|
||||
import org.hibernate.query.IllegalQueryOperationException;
|
||||
import org.hibernate.query.sqm.BinaryArithmeticOperator;
|
||||
import org.hibernate.query.sqm.ComparisonOperator;
|
||||
import org.hibernate.sql.ast.Clause;
|
||||
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
||||
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
||||
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.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.CaseSearchedExpression;
|
||||
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.Literal;
|
||||
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
||||
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.select.QueryPart;
|
||||
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.type.descriptor.jdbc.ArrayJdbcType;
|
||||
|
||||
|
@ -42,6 +51,44 @@ public class HSQLLegacySqlAstTranslator<T extends JdbcOperation> extends Abstrac
|
|||
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
|
||||
protected void renderExpressionAsClauseItem(Expression expression) {
|
||||
expression.accept( this );
|
||||
|
@ -295,14 +342,9 @@ public class HSQLLegacySqlAstTranslator<T extends JdbcOperation> extends Abstrac
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getFromDual() {
|
||||
return " from (values(0))";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getFromDualForSelectOnly() {
|
||||
return getFromDual();
|
||||
return " from " + getDual();
|
||||
}
|
||||
|
||||
private boolean supportsOffsetFetchClause() {
|
||||
|
|
|
@ -134,13 +134,13 @@ public class InformixSqlAstTranslator<T extends JdbcOperation> extends AbstractS
|
|||
}
|
||||
|
||||
@Override
|
||||
protected String getFromDual() {
|
||||
return " from (select 0 from systables where tabid=1) dual";
|
||||
protected String getDual() {
|
||||
return "(select 0 from systables where tabid=1)";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getFromDualForSelectOnly() {
|
||||
return getFromDual();
|
||||
return " from " + getDual() + " dual";
|
||||
}
|
||||
|
||||
private boolean supportsParameterOffsetFetchExpression() {
|
||||
|
|
|
@ -139,14 +139,14 @@ public class IngresSqlAstTranslator<T extends JdbcOperation> extends AbstractSql
|
|||
}
|
||||
|
||||
@Override
|
||||
protected String getFromDual() {
|
||||
//this is only necessary if the query has a where clause
|
||||
return " from (select 0) dual";
|
||||
protected String getDual() {
|
||||
return "(select 0)";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getFromDualForSelectOnly() {
|
||||
return getFromDual();
|
||||
//this is only necessary if the query has a where clause
|
||||
return " from " + getDual() + " dual";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -6,22 +6,38 @@
|
|||
*/
|
||||
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.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.internal.util.collections.Stack;
|
||||
import org.hibernate.query.sqm.ComparisonOperator;
|
||||
import org.hibernate.sql.ast.Clause;
|
||||
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.delete.DeleteStatement;
|
||||
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.Literal;
|
||||
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.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.LikePredicate;
|
||||
import org.hibernate.sql.ast.tree.select.QueryGroup;
|
||||
import org.hibernate.sql.ast.tree.select.QueryPart;
|
||||
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.JdbcOperationQueryInsert;
|
||||
|
||||
/**
|
||||
* A SQL AST translator for MariaDB.
|
||||
|
@ -37,6 +53,135 @@ public class MariaDBLegacySqlAstTranslator<T extends JdbcOperation> extends Abst
|
|||
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
|
||||
protected boolean supportsWithClause() {
|
||||
return dialect.getVersion().isSameOrAfter( 10, 2 );
|
||||
|
@ -222,13 +367,13 @@ public class MariaDBLegacySqlAstTranslator<T extends JdbcOperation> extends Abst
|
|||
}
|
||||
|
||||
@Override
|
||||
protected String getFromDual() {
|
||||
return " from dual";
|
||||
protected String getDual() {
|
||||
return "dual";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getFromDualForSelectOnly() {
|
||||
return getDialect().getVersion().isBefore( 10, 4 ) ? getFromDual() : "";
|
||||
return getDialect().getVersion().isBefore( 10, 4 ) ? ( " from " + getDual() ) : "";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -94,12 +94,12 @@ public class MaxDBSqlAstTranslator<T extends JdbcOperation> extends AbstractSqlA
|
|||
}
|
||||
|
||||
@Override
|
||||
public String getFromDual() {
|
||||
return " from dual";
|
||||
protected String getDual() {
|
||||
return "dual";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getFromDualForSelectOnly() {
|
||||
return getFromDual();
|
||||
return " from " + getDual();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -82,13 +82,8 @@ public class MimerSQLSqlAstTranslator<T extends JdbcOperation> extends AbstractS
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getFromDual() {
|
||||
return " from (values(0))";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getFromDualForSelectOnly() {
|
||||
return getFromDual();
|
||||
return " from " + getDual();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1395,4 +1395,14 @@ public class MySQLLegacyDialect extends Dialect {
|
|||
return "set foreign_key_checks = 1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() {
|
||||
return DmlTargetColumnQualifierSupport.TABLE_ALIAS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsFromClauseInUpdate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,24 +6,40 @@
|
|||
*/
|
||||
package org.hibernate.community.dialect;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.dialect.DialectDelegateWrapper;
|
||||
import org.hibernate.dialect.DmlTargetColumnQualifierSupport;
|
||||
import org.hibernate.dialect.MySQLSqlAstTranslator;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.internal.util.collections.Stack;
|
||||
import org.hibernate.query.sqm.ComparisonOperator;
|
||||
import org.hibernate.sql.ast.Clause;
|
||||
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.delete.DeleteStatement;
|
||||
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.Literal;
|
||||
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.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.LikePredicate;
|
||||
import org.hibernate.sql.ast.tree.select.QueryGroup;
|
||||
import org.hibernate.sql.ast.tree.select.QueryPart;
|
||||
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.JdbcOperationQueryInsert;
|
||||
|
||||
/**
|
||||
* A SQL AST translator for MySQL.
|
||||
|
@ -36,6 +52,146 @@ public class MySQLLegacySqlAstTranslator<T extends JdbcOperation> extends Abstra
|
|||
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
|
||||
protected void renderExpressionAsClauseItem(Expression expression) {
|
||||
expression.accept( this );
|
||||
|
@ -234,13 +390,13 @@ public class MySQLLegacySqlAstTranslator<T extends JdbcOperation> extends Abstra
|
|||
}
|
||||
|
||||
@Override
|
||||
protected String getFromDual() {
|
||||
return " from dual";
|
||||
protected String getDual() {
|
||||
return "dual";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getFromDualForSelectOnly() {
|
||||
return getDialect().getVersion().isSameOrAfter( 8 ) ? "" : getFromDual();
|
||||
return getDialect().getVersion().isSameOrAfter( 8 ) ? "" : ( " from " + getDual() );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1008,6 +1008,7 @@ public class OracleLegacyDialect extends Dialect {
|
|||
@Override
|
||||
public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() {
|
||||
return (sqlException, message, sql) -> {
|
||||
final String constraintName;
|
||||
// interpreting Oracle exceptions is much much more precise based on their specific vendor codes.
|
||||
switch ( JdbcExceptionHelper.extractErrorCode( sqlException ) ) {
|
||||
|
||||
|
@ -1040,9 +1041,19 @@ public class OracleLegacyDialect extends Dialect {
|
|||
|
||||
// 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:
|
||||
// ORA-01407: cannot update column to NULL
|
||||
final String constraintName = getViolatedConstraintNameExtractor().extractConstraintName( sqlException );
|
||||
constraintName = getViolatedConstraintNameExtractor().extractConstraintName( sqlException );
|
||||
return new ConstraintViolationException( message, sqlException, sql, constraintName );
|
||||
|
||||
default:
|
||||
|
@ -1520,4 +1531,9 @@ public class OracleLegacyDialect extends Dialect {
|
|||
public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() {
|
||||
return DmlTargetColumnQualifierSupport.TABLE_ALIAS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsFromClauseInUpdate() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.from.FromClause;
|
||||
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.TableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.UnionTableGroup;
|
||||
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.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.SortSpecification;
|
||||
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.results.internal.SqlSelectionImpl;
|
||||
import org.hibernate.type.SqlTypes;
|
||||
|
@ -67,6 +70,54 @@ public class OracleLegacySqlAstTranslator<T extends JdbcOperation> extends Abstr
|
|||
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
|
||||
protected boolean needsRecursiveKeywordInWithClause() {
|
||||
return false;
|
||||
|
@ -227,7 +278,14 @@ public class OracleLegacySqlAstTranslator<T extends JdbcOperation> extends Abstr
|
|||
|
||||
@Override
|
||||
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
|
||||
|
@ -617,13 +675,13 @@ public class OracleLegacySqlAstTranslator<T extends JdbcOperation> extends Abstr
|
|||
}
|
||||
|
||||
@Override
|
||||
protected String getFromDual() {
|
||||
return " from dual";
|
||||
protected String getDual() {
|
||||
return "dual";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getFromDualForSelectOnly() {
|
||||
return getFromDual();
|
||||
return " from " + getDual();
|
||||
}
|
||||
|
||||
private boolean supportsOffsetFetchClause() {
|
||||
|
|
|
@ -732,6 +732,11 @@ public class PostgreSQLLegacyDialect extends Dialect {
|
|||
return getVersion().isSameOrAfter( 9, 1 );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsConflictClauseForInsertCTE() {
|
||||
return getVersion().isSameOrAfter( 9, 5 );
|
||||
}
|
||||
|
||||
@Override
|
||||
public SequenceSupport getSequenceSupport() {
|
||||
return getVersion().isBefore( 8, 2 )
|
||||
|
@ -1467,4 +1472,14 @@ public class PostgreSQLLegacyDialect extends Dialect {
|
|||
// The maximum scale for `interval second` is 6 unfortunately
|
||||
return 6;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() {
|
||||
return DmlTargetColumnQualifierSupport.TABLE_ALIAS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsFromClauseInUpdate() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|||
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
|
||||
import org.hibernate.query.sqm.ComparisonOperator;
|
||||
import org.hibernate.query.sqm.FetchClauseType;
|
||||
import org.hibernate.sql.ast.Clause;
|
||||
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
||||
import org.hibernate.sql.ast.tree.Statement;
|
||||
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.Literal;
|
||||
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.LikePredicate;
|
||||
import org.hibernate.sql.ast.tree.predicate.NullnessPredicate;
|
||||
import org.hibernate.sql.ast.tree.select.QueryGroup;
|
||||
import org.hibernate.sql.ast.tree.select.QueryPart;
|
||||
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.JdbcOperationQueryInsert;
|
||||
import org.hibernate.sql.model.internal.TableInsertStandard;
|
||||
import org.hibernate.type.SqlTypes;
|
||||
|
||||
|
@ -45,6 +53,56 @@ public class PostgreSQLLegacySqlAstTranslator<T extends JdbcOperation> extends A
|
|||
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
|
||||
protected void renderExpressionAsClauseItem(Expression expression) {
|
||||
expression.accept( this );
|
||||
|
@ -218,9 +276,7 @@ public class PostgreSQLLegacySqlAstTranslator<T extends JdbcOperation> extends A
|
|||
appendSql( "()" );
|
||||
}
|
||||
else {
|
||||
appendSql( "(select 1" );
|
||||
appendSql( getFromDualForSelectOnly() );
|
||||
appendSql( ')' );
|
||||
appendSql( "(select 1)" );
|
||||
}
|
||||
}
|
||||
else if ( expression instanceof Summarization ) {
|
||||
|
|
|
@ -126,12 +126,12 @@ public class RDMSOS2200SqlAstTranslator<T extends JdbcOperation> extends Abstrac
|
|||
}
|
||||
|
||||
@Override
|
||||
protected String getFromDual() {
|
||||
return " from rdms.rdms_dummy where key_col=1";
|
||||
protected String getDual() {
|
||||
return "rdms.rdms_dummy";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getFromDualForSelectOnly() {
|
||||
return getFromDual();
|
||||
return " from " + getDual() + " where key_col=1";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ import org.hibernate.boot.model.relational.SqlStringGenerationContext;
|
|||
import org.hibernate.dialect.AbstractTransactSQLDialect;
|
||||
import org.hibernate.dialect.DatabaseVersion;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.dialect.DmlTargetColumnQualifierSupport;
|
||||
import org.hibernate.dialect.Replacer;
|
||||
import org.hibernate.dialect.TimeZoneSupport;
|
||||
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.NameQualifierSupport;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.exception.ConstraintViolationException;
|
||||
import org.hibernate.exception.LockTimeoutException;
|
||||
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.mapping.Column;
|
||||
import org.hibernate.query.sqm.CastType;
|
||||
|
@ -85,6 +89,7 @@ import java.util.TimeZone;
|
|||
|
||||
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.produce.function.FunctionParameterType.INTEGER;
|
||||
import static org.hibernate.type.SqlTypes.*;
|
||||
|
@ -723,6 +728,20 @@ public class SQLServerLegacyDialect extends AbstractTransactSQLDialect {
|
|||
public boolean supportsFetchClause(FetchClauseType type) {
|
||||
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
|
||||
public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() {
|
||||
|
@ -740,6 +759,13 @@ public class SQLServerLegacyDialect extends AbstractTransactSQLDialect {
|
|||
case 1222:
|
||||
return new LockTimeoutException( message, sqlException, sql );
|
||||
case 2627:
|
||||
return new ConstraintViolationException(
|
||||
message,
|
||||
sqlException,
|
||||
sql,
|
||||
ConstraintViolationException.ConstraintKind.UNIQUE,
|
||||
getViolatedConstraintNameExtractor().extractConstraintName( sqlException )
|
||||
);
|
||||
case 2601:
|
||||
return new ConstraintViolationException(
|
||||
message,
|
||||
|
|
|
@ -12,15 +12,20 @@ import org.hibernate.LockMode;
|
|||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.dialect.DatabaseVersion;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.internal.util.collections.Stack;
|
||||
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
|
||||
import org.hibernate.query.IllegalQueryOperationException;
|
||||
import org.hibernate.query.sqm.ComparisonOperator;
|
||||
import org.hibernate.query.sqm.FetchClauseType;
|
||||
import org.hibernate.sql.ast.Clause;
|
||||
import org.hibernate.sql.ast.SqlAstJoinType;
|
||||
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
||||
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.delete.DeleteStatement;
|
||||
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.Literal;
|
||||
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.TableReference;
|
||||
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.select.QueryGroup;
|
||||
import org.hibernate.sql.ast.tree.select.QueryPart;
|
||||
import org.hibernate.sql.ast.tree.select.QuerySpec;
|
||||
import org.hibernate.sql.ast.tree.select.SelectClause;
|
||||
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.type.SqlTypes;
|
||||
|
||||
|
@ -54,6 +62,84 @@ public class SQLServerLegacySqlAstTranslator<T extends JdbcOperation> extends Ab
|
|||
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
|
||||
protected boolean needsRecursiveKeywordInWithClause() {
|
||||
return false;
|
||||
|
@ -129,14 +215,7 @@ public class SQLServerLegacySqlAstTranslator<T extends JdbcOperation> extends Ab
|
|||
appendSql( " )" );
|
||||
|
||||
registerAffectedTable( tableReference );
|
||||
final Clause currentClause = getClauseStack().getCurrent();
|
||||
if ( rendersTableReferenceAlias( currentClause ) ) {
|
||||
final String identificationVariable = tableReference.getIdentificationVariable();
|
||||
if ( identificationVariable != null ) {
|
||||
appendSql( ' ' );
|
||||
appendSql( identificationVariable );
|
||||
}
|
||||
}
|
||||
renderTableReferenceIdentificationVariable( tableReference );
|
||||
}
|
||||
else {
|
||||
super.renderNamedTableReference( tableReference, lockMode );
|
||||
|
|
|
@ -628,27 +628,17 @@ public class SybaseASELegacyDialect extends SybaseLegacyDialect {
|
|||
final int errorCode = JdbcExceptionHelper.extractErrorCode( sqle );
|
||||
if ( sqlState != null ) {
|
||||
switch ( sqlState ) {
|
||||
// UNIQUE VIOLATION
|
||||
case "S1000":
|
||||
if ( 2601 == errorCode ) {
|
||||
return extractUsingTemplate( "with unique index '", "'", sqle.getMessage() );
|
||||
}
|
||||
break;
|
||||
case "23000":
|
||||
if ( 546 == errorCode ) {
|
||||
// Foreign key violation
|
||||
return extractUsingTemplate( "constraint name = '", "'", sqle.getMessage() );
|
||||
switch ( errorCode ) {
|
||||
case 2601:
|
||||
// UNIQUE VIOLATION
|
||||
return extractUsingTemplate( "with unique index '", "'", sqle.getMessage() );
|
||||
case 546:
|
||||
// Foreign key violation
|
||||
return extractUsingTemplate( "constraint name = '", "'", sqle.getMessage() );
|
||||
}
|
||||
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;
|
||||
|
@ -659,7 +649,6 @@ public class SybaseASELegacyDialect extends SybaseLegacyDialect {
|
|||
if ( getVersion().isBefore( 15, 7 ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (sqlException, message, sql) -> {
|
||||
final String sqlState = JdbcExceptionHelper.extractSqlState( sqlException );
|
||||
final int errorCode = JdbcExceptionHelper.extractErrorCode( sqlException );
|
||||
|
@ -669,30 +658,44 @@ public class SybaseASELegacyDialect extends SybaseLegacyDialect {
|
|||
case "JZ006":
|
||||
return new LockTimeoutException( message, sqlException, sql );
|
||||
case "S1000":
|
||||
case "23000":
|
||||
switch ( errorCode ) {
|
||||
case 515:
|
||||
// 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:
|
||||
// Unique constraint violation
|
||||
final String constraintName = getViolatedConstraintNameExtractor().extractConstraintName(
|
||||
sqlException );
|
||||
return new ConstraintViolationException( message, sqlException, sql, constraintName );
|
||||
return new ConstraintViolationException(
|
||||
message,
|
||||
sqlException,
|
||||
sql,
|
||||
ConstraintViolationException.ConstraintKind.UNIQUE,
|
||||
getViolatedConstraintNameExtractor().extractConstraintName( sqlException )
|
||||
);
|
||||
}
|
||||
break;
|
||||
case "ZZZZZ":
|
||||
if ( 515 == errorCode ) {
|
||||
// Attempt to insert NULL value into column; column does not allow nulls.
|
||||
final String constraintName = getViolatedConstraintNameExtractor().extractConstraintName(
|
||||
sqlException );
|
||||
return new ConstraintViolationException( message, sqlException, sql, constraintName );
|
||||
}
|
||||
break;
|
||||
case "23000":
|
||||
if ( 546 == errorCode ) {
|
||||
// Foreign key violation
|
||||
final String constraintName = getViolatedConstraintNameExtractor().extractConstraintName(
|
||||
sqlException );
|
||||
return new ConstraintViolationException( message, sqlException, sql, constraintName );
|
||||
return new ConstraintViolationException(
|
||||
message,
|
||||
sqlException,
|
||||
sql,
|
||||
getViolatedConstraintNameExtractor().extractConstraintName( sqlException )
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -11,14 +11,19 @@ import java.util.function.Consumer;
|
|||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.dialect.DmlTargetColumnQualifierSupport;
|
||||
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.sql.ast.Clause;
|
||||
import org.hibernate.sql.ast.SqlAstJoinType;
|
||||
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
||||
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
||||
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.delete.DeleteStatement;
|
||||
import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression;
|
||||
import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression;
|
||||
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.TableGroupJoin;
|
||||
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.predicate.BooleanExpressionPredicate;
|
||||
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.QuerySpec;
|
||||
import org.hibernate.sql.ast.tree.select.SelectClause;
|
||||
import org.hibernate.sql.ast.tree.update.UpdateStatement;
|
||||
import org.hibernate.sql.exec.spi.JdbcOperation;
|
||||
|
||||
/**
|
||||
|
@ -55,6 +64,77 @@ public class SybaseASELegacySqlAstTranslator<T extends JdbcOperation> extends Ab
|
|||
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
|
||||
protected boolean supportsWithClause() {
|
||||
return false;
|
||||
|
@ -130,14 +210,7 @@ public class SybaseASELegacySqlAstTranslator<T extends JdbcOperation> extends Ab
|
|||
appendSql( " )" );
|
||||
|
||||
registerAffectedTable( tableReference );
|
||||
final Clause currentClause = getClauseStack().getCurrent();
|
||||
if ( rendersTableReferenceAlias( currentClause ) ) {
|
||||
final String identificationVariable = tableReference.getIdentificationVariable();
|
||||
if ( identificationVariable != null ) {
|
||||
appendSql( ' ' );
|
||||
appendSql( identificationVariable );
|
||||
}
|
||||
}
|
||||
renderTableReferenceIdentificationVariable( tableReference );
|
||||
}
|
||||
else {
|
||||
super.renderNamedTableReference( tableReference, lockMode );
|
||||
|
@ -252,6 +325,14 @@ public class SybaseASELegacySqlAstTranslator<T extends JdbcOperation> extends Ab
|
|||
visitValuesListEmulateSelectUnion( valuesList );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitValuesTableReference(ValuesTableReference tableReference) {
|
||||
append( '(' );
|
||||
visitValuesListEmulateSelectUnion( tableReference.getValuesList() );
|
||||
append( ')' );
|
||||
renderDerivedTableReference( tableReference );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitOffsetFetchClause(QueryPart queryPart) {
|
||||
assertRowsOnlyFetchClauseType( queryPart );
|
||||
|
@ -386,63 +467,32 @@ public class SybaseASELegacySqlAstTranslator<T extends JdbcOperation> extends Ab
|
|||
}
|
||||
|
||||
@Override
|
||||
public void visitColumnReference(ColumnReference columnReference) {
|
||||
final String dmlTargetTableAlias = getDmlTargetTableAlias();
|
||||
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 ) {
|
||||
columnReference.appendReadExpression( this );
|
||||
}
|
||||
// for now, use the unqualified form
|
||||
else if ( columnReference.isColumnExpressionFormula() ) {
|
||||
// For formulas, we have to replace the qualifier as the alias was already rendered into the formula
|
||||
// This is fine for now as this is only temporary anyway until we render aliases for table references
|
||||
appendSql(
|
||||
columnReference.getColumnExpression()
|
||||
.replaceAll( "(\\b)(" + dmlTargetTableAlias + "\\.)(\\b)", "$1$3" )
|
||||
);
|
||||
}
|
||||
else {
|
||||
columnReference.appendReadExpression(
|
||||
this,
|
||||
getCurrentDmlStatement().getTargetTable().getTableExpression()
|
||||
);
|
||||
}
|
||||
protected String determineColumnReferenceQualifier(ColumnReference columnReference) {
|
||||
final DmlTargetColumnQualifierSupport qualifierSupport = getDialect().getDmlTargetColumnQualifierSupport();
|
||||
final MutationStatement currentDmlStatement;
|
||||
final String dmlAlias;
|
||||
if ( qualifierSupport == DmlTargetColumnQualifierSupport.TABLE_ALIAS
|
||||
|| ( currentDmlStatement = getCurrentDmlStatement() ) == null
|
||||
|| ( dmlAlias = currentDmlStatement.getTargetTable().getIdentificationVariable() ) == null
|
||||
|| !dmlAlias.equals( columnReference.getQualifier() ) ) {
|
||||
return 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 ) {
|
||||
return columnReference.getQualifier();
|
||||
}
|
||||
else if ( columnReference.isColumnExpressionFormula() ) {
|
||||
// For formulas, we have to replace the qualifier as the alias was already rendered into the formula
|
||||
// This is fine for now as this is only temporary anyway until we render aliases for table references
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
columnReference.appendReadExpression( this );
|
||||
}
|
||||
}
|
||||
|
||||
@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 );
|
||||
return getCurrentDmlStatement().getTargetTable().getTableExpression();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -472,8 +522,8 @@ public class SybaseASELegacySqlAstTranslator<T extends JdbcOperation> extends Ab
|
|||
}
|
||||
|
||||
@Override
|
||||
protected String getFromDual() {
|
||||
return " from (select 1) dual(c1)";
|
||||
protected String getDual() {
|
||||
return "(select 1 c1)";
|
||||
}
|
||||
|
||||
private boolean supportsTopClause() {
|
||||
|
|
|
@ -116,14 +116,7 @@ public class SybaseAnywhereSqlAstTranslator<T extends JdbcOperation> extends Abs
|
|||
appendSql( " )" );
|
||||
|
||||
registerAffectedTable( tableReference );
|
||||
final Clause currentClause = getClauseStack().getCurrent();
|
||||
if ( rendersTableReferenceAlias( currentClause ) ) {
|
||||
final String identificationVariable = tableReference.getIdentificationVariable();
|
||||
if ( identificationVariable != null ) {
|
||||
appendSql( ' ' );
|
||||
appendSql( identificationVariable );
|
||||
}
|
||||
}
|
||||
renderTableReferenceIdentificationVariable( tableReference );
|
||||
}
|
||||
else {
|
||||
super.renderNamedTableReference( tableReference, lockMode );
|
||||
|
@ -248,12 +241,12 @@ public class SybaseAnywhereSqlAstTranslator<T extends JdbcOperation> extends Abs
|
|||
}
|
||||
|
||||
@Override
|
||||
protected String getFromDual() {
|
||||
return " from sys.dummy";
|
||||
protected String getDual() {
|
||||
return "sys.dummy";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getFromDualForSelectOnly() {
|
||||
return getFromDual();
|
||||
return " from " + getDual();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import org.hibernate.boot.model.FunctionContributions;
|
|||
import org.hibernate.boot.model.TypeContributions;
|
||||
import org.hibernate.dialect.AbstractTransactSQLDialect;
|
||||
import org.hibernate.dialect.DatabaseVersion;
|
||||
import org.hibernate.dialect.DmlTargetColumnQualifierSupport;
|
||||
import org.hibernate.dialect.NationalizationSupport;
|
||||
import org.hibernate.dialect.SybaseDriverKind;
|
||||
import org.hibernate.dialect.function.CommonFunctionFactory;
|
||||
|
@ -473,4 +474,14 @@ public class SybaseLegacyDialect extends AbstractTransactSQLDialect {
|
|||
// Only the jTDS driver supports named parameters properly
|
||||
return driverKind == SybaseDriverKind.JTDS && super.supportsNamedParameters( databaseMetaData );
|
||||
}
|
||||
|
||||
@Override
|
||||
public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() {
|
||||
return DmlTargetColumnQualifierSupport.TABLE_ALIAS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsFromClauseInUpdate() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,8 @@ import java.util.function.Consumer;
|
|||
|
||||
import org.hibernate.LockMode;
|
||||
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.sql.ast.Clause;
|
||||
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.tree.Statement;
|
||||
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.CaseSearchedExpression;
|
||||
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.from.NamedTableReference;
|
||||
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.QuerySpec;
|
||||
import org.hibernate.sql.ast.tree.update.UpdateStatement;
|
||||
import org.hibernate.sql.exec.spi.JdbcOperation;
|
||||
|
||||
/**
|
||||
|
@ -44,6 +52,46 @@ public class SybaseLegacySqlAstTranslator<T extends JdbcOperation> extends Abstr
|
|||
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
|
||||
protected boolean supportsWithClause() {
|
||||
return false;
|
||||
|
@ -119,14 +167,7 @@ public class SybaseLegacySqlAstTranslator<T extends JdbcOperation> extends Abstr
|
|||
appendSql( " )" );
|
||||
|
||||
registerAffectedTable( tableReference );
|
||||
final Clause currentClause = getClauseStack().getCurrent();
|
||||
if ( rendersTableReferenceAlias( currentClause ) ) {
|
||||
final String identificationVariable = tableReference.getIdentificationVariable();
|
||||
if ( identificationVariable != null ) {
|
||||
appendSql( ' ' );
|
||||
appendSql( identificationVariable );
|
||||
}
|
||||
}
|
||||
renderTableReferenceIdentificationVariable( tableReference );
|
||||
}
|
||||
else {
|
||||
super.renderNamedTableReference( tableReference, lockMode );
|
||||
|
@ -147,6 +188,19 @@ public class SybaseLegacySqlAstTranslator<T extends JdbcOperation> extends Abstr
|
|||
// 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
|
||||
public void visitOffsetFetchClause(QueryPart queryPart) {
|
||||
assertRowsOnlyFetchClauseType( queryPart );
|
||||
|
|
|
@ -158,6 +158,8 @@ BY : [bB] [yY];
|
|||
CASE : [cC] [aA] [sS] [eE];
|
||||
CAST : [cC] [aA] [sS] [tT];
|
||||
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];
|
||||
CROSS : [cC] [rR] [oO] [sS] [sS];
|
||||
CUBE : [cC] [uU] [bB] [eE];
|
||||
|
@ -175,6 +177,7 @@ DELETE : [dD] [eE] [lL] [eE] [tT] [eE];
|
|||
DEPTH : [dD] [eE] [pP] [tT] [hH];
|
||||
DESC : [dD] [eE] [sS] [cC];
|
||||
DISTINCT : [dD] [iI] [sS] [tT] [iI] [nN] [cC] [tT];
|
||||
DO : [dD] [oO];
|
||||
ELEMENT : [eE] [lL] [eE] [mM] [eE] [nN] [tT];
|
||||
ELEMENTS : [eE] [lL] [eE] [mM] [eE] [nN] [tT] [sS];
|
||||
ELSE : [eE] [lL] [sS] [eE];
|
||||
|
@ -246,6 +249,7 @@ NEW : [nN] [eE] [wW];
|
|||
NEXT : [nN] [eE] [xX] [tT];
|
||||
NO : [nN] [oO];
|
||||
NOT : [nN] [oO] [tT];
|
||||
NOTHING : [nN] [oO] [tT] [hH] [iI] [nN] [gG];
|
||||
NULLS : [nN] [uU] [lL] [lL] [sS];
|
||||
OBJECT : [oO] [bB] [jJ] [eE] [cC] [tT];
|
||||
OF : [oO] [fF];
|
||||
|
|
|
@ -83,7 +83,7 @@ assignment
|
|||
* An 'insert' statement
|
||||
*/
|
||||
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
|
||||
;
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
|
@ -1600,6 +1612,8 @@ rollup
|
|||
| CASE
|
||||
| CAST
|
||||
| COLLATE
|
||||
| CONFLICT
|
||||
| CONSTRAINT
|
||||
| COUNT
|
||||
| CROSS
|
||||
| CUBE
|
||||
|
@ -1617,6 +1631,7 @@ rollup
|
|||
| DEPTH
|
||||
| DESC
|
||||
| DISTINCT
|
||||
| DO
|
||||
| ELEMENT
|
||||
| ELEMENTS
|
||||
| ELSE
|
||||
|
@ -1690,6 +1705,7 @@ rollup
|
|||
| NEXT
|
||||
| NO
|
||||
| NOT
|
||||
| NOTHING
|
||||
| NULLS
|
||||
| OBJECT
|
||||
| OF
|
||||
|
|
|
@ -21,6 +21,7 @@ import org.hibernate.dialect.Dialect;
|
|||
import org.hibernate.dialect.H2Dialect;
|
||||
import org.hibernate.dialect.HANACloudColumnStoreDialect;
|
||||
import org.hibernate.dialect.HANAColumnStoreDialect;
|
||||
import org.hibernate.dialect.HANADialect;
|
||||
import org.hibernate.dialect.HANARowStoreDialect;
|
||||
import org.hibernate.dialect.HSQLDialect;
|
||||
import org.hibernate.dialect.MariaDBDialect;
|
||||
|
@ -70,6 +71,8 @@ public class DefaultDialectSelector implements DialectSelector {
|
|||
return findCommunityDialect( name );
|
||||
case "H2":
|
||||
return H2Dialect.class;
|
||||
case "HANA":
|
||||
return HANADialect.class;
|
||||
case "HANACloudColumnStore":
|
||||
return HANACloudColumnStoreDialect.class;
|
||||
case "HANAColumnStore":
|
||||
|
|
|
@ -57,4 +57,12 @@ public interface DialectSpecificSettings {
|
|||
*/
|
||||
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";
|
||||
|
||||
}
|
||||
|
|
|
@ -20,20 +20,20 @@ import java.nio.charset.StandardCharsets;
|
|||
import java.sql.Blob;
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.Clob;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.sql.NClob;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.SQLFeatureNotSupportedException;
|
||||
import java.sql.Statement;
|
||||
import java.sql.Types;
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TimeZone;
|
||||
import java.util.regex.Matcher;
|
||||
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.sequence.HANASequenceSupport;
|
||||
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.StandardConverters;
|
||||
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.ClobImplementer;
|
||||
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.env.spi.IdentifierCaseStrategy;
|
||||
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.SQLGrammarException;
|
||||
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.mapping.Table;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
|
||||
import org.hibernate.procedure.internal.StandardCallableStatementSupport;
|
||||
import org.hibernate.procedure.spi.CallableStatementSupport;
|
||||
import org.hibernate.query.sqm.CastType;
|
||||
import org.hibernate.query.sqm.IntervalType;
|
||||
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.service.ServiceRegistry;
|
||||
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
||||
|
@ -121,6 +126,7 @@ import org.hibernate.type.spi.TypeConfiguration;
|
|||
|
||||
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.BOOLEAN;
|
||||
import static org.hibernate.type.SqlTypes.CHAR;
|
||||
|
@ -160,15 +166,15 @@ import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithM
|
|||
* <p>
|
||||
* 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 Jonathan Bregler
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
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
|
||||
// the LOB when the result set is closed.
|
||||
private static final String MAX_LOB_PREFETCH_SIZE_PARAMETER_NAME = "hibernate.dialect.hana.max_lob_prefetch_size";
|
||||
// Use column or row tables by default
|
||||
public static final String USE_DEFAULT_TABLE_TYPE_COLUMN = "hibernate.dialect.hana.use_default_table_type_column";
|
||||
// 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";
|
||||
// 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)
|
||||
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 TREAT_DOUBLE_TYPED_FIELDS_AS_DECIMAL_DEFAULT_VALUE = Boolean.FALSE;
|
||||
|
||||
private HANANClobJdbcType nClobTypeDescriptor = new HANANClobJdbcType( MAX_LOB_PREFETCH_SIZE_DEFAULT_VALUE );
|
||||
|
||||
private HANABlobType blobTypeDescriptor = new HANABlobType( MAX_LOB_PREFETCH_SIZE_DEFAULT_VALUE );
|
||||
|
||||
private HANAClobJdbcType clobTypeDescriptor;
|
||||
private final int maxLobPrefetchSize;
|
||||
|
||||
private boolean defaultTableTypeColumn;
|
||||
private boolean useLegacyBooleanType = USE_LEGACY_BOOLEAN_TYPE_DEFAULT_VALUE;
|
||||
private boolean useUnicodeStringTypes;
|
||||
private boolean treatDoubleTypedFieldsAsDecimal;
|
||||
|
||||
/*
|
||||
* 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.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
|
||||
|
@ -280,20 +325,6 @@ public abstract class AbstractHANADialect extends Dialect {
|
|||
|
||||
@Override
|
||||
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 );
|
||||
final DdlTypeRegistry ddlTypeRegistry = typeContributions.getTypeConfiguration().getDdlTypeRegistry();
|
||||
|
||||
|
@ -320,24 +351,12 @@ public abstract class AbstractHANADialect extends Dialect {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link HANAServerConfiguration#fromDialectResolutionInfo(DialectResolutionInfo)} instead
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
protected 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 );
|
||||
return HANAServerConfiguration.fromDialectResolutionInfo( info ).getFullVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -434,6 +453,22 @@ public abstract class AbstractHANADialect extends Dialect {
|
|||
|
||||
functionContributions.getFunctionRegistry().register( "timestampadd",
|
||||
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
|
||||
|
@ -526,7 +561,15 @@ public abstract class AbstractHANADialect extends Dialect {
|
|||
final String constraintName = getViolatedConstraintNameExtractor()
|
||||
.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;
|
||||
|
@ -538,6 +581,11 @@ public abstract class AbstractHANADialect extends Dialect {
|
|||
return RowLockStrategy.COLUMN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCreateTableString() {
|
||||
return isDefaultTableTypeColumn() ? "create column table" : "create row table";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAddColumnString() {
|
||||
return "add (";
|
||||
|
@ -621,6 +669,7 @@ public abstract class AbstractHANADialect extends Dialect {
|
|||
@Override
|
||||
protected void registerDefaultKeywords() {
|
||||
super.registerDefaultKeywords();
|
||||
// https://help.sap.com/docs/SAP_HANA_PLATFORM/4fe29514fd584807ac9f2a04f6754767/28bcd6af3eb6437892719f7c27a8a285.html?locale=en-US
|
||||
registerKeyword( "all" );
|
||||
registerKeyword( "alter" );
|
||||
registerKeyword( "as" );
|
||||
|
@ -668,6 +717,7 @@ public abstract class AbstractHANADialect extends Dialect {
|
|||
registerKeyword( "into" );
|
||||
registerKeyword( "is" );
|
||||
registerKeyword( "join" );
|
||||
registerKeyword( "lateral" );
|
||||
registerKeyword( "leading" );
|
||||
registerKeyword( "left" );
|
||||
registerKeyword( "limit" );
|
||||
|
@ -706,6 +756,29 @@ public abstract class AbstractHANADialect extends Dialect {
|
|||
registerKeyword( "where" );
|
||||
registerKeyword( "while" );
|
||||
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
|
||||
|
@ -930,127 +1003,40 @@ public abstract class AbstractHANADialect extends Dialect {
|
|||
public void contributeTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
|
||||
super.contributeTypes( typeContributions, serviceRegistry );
|
||||
|
||||
final ConnectionProvider connectionProvider = serviceRegistry.getService( ConnectionProvider.class );
|
||||
|
||||
int maxLobPrefetchSizeDefault = MAX_LOB_PREFETCH_SIZE_DEFAULT_VALUE;
|
||||
if ( connectionProvider != null ) {
|
||||
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()
|
||||
final TypeConfiguration typeConfiguration = typeContributions.getTypeConfiguration();
|
||||
final JdbcTypeRegistry jdbcTypeRegistry = typeConfiguration.getJdbcTypeRegistry();
|
||||
if ( treatDoubleTypedFieldsAsDecimal ) {
|
||||
typeConfiguration.getBasicTypeRegistry()
|
||||
.register(
|
||||
new BasicTypeImpl<>(
|
||||
DoubleJavaType.INSTANCE,
|
||||
NumericJdbcType.INSTANCE
|
||||
),
|
||||
new BasicTypeImpl<>( DoubleJavaType.INSTANCE, NumericJdbcType.INSTANCE ),
|
||||
Double.class.getName()
|
||||
);
|
||||
typeContributions.getTypeConfiguration().getJdbcToHibernateTypeContributionMap()
|
||||
.computeIfAbsent( Types.FLOAT, code -> new HashSet<>() )
|
||||
.clear();
|
||||
typeContributions.getTypeConfiguration().getJdbcToHibernateTypeContributionMap()
|
||||
.computeIfAbsent( Types.REAL, code -> new HashSet<>() )
|
||||
.clear();
|
||||
typeContributions.getTypeConfiguration().getJdbcToHibernateTypeContributionMap()
|
||||
.computeIfAbsent( Types.DOUBLE, code -> new HashSet<>() )
|
||||
.clear();
|
||||
typeContributions.getTypeConfiguration().getJdbcToHibernateTypeContributionMap()
|
||||
.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
|
||||
);
|
||||
final Map<Integer, Set<String>> jdbcToHibernateTypeContributionMap = typeConfiguration.getJdbcToHibernateTypeContributionMap();
|
||||
jdbcToHibernateTypeContributionMap.computeIfAbsent( Types.FLOAT, code -> new HashSet<>() ).clear();
|
||||
jdbcToHibernateTypeContributionMap.computeIfAbsent( Types.REAL, code -> new HashSet<>() ).clear();
|
||||
jdbcToHibernateTypeContributionMap.computeIfAbsent( Types.DOUBLE, code -> new HashSet<>() ).clear();
|
||||
jdbcToHibernateTypeContributionMap.get( Types.FLOAT ).add( StandardBasicTypes.BIG_DECIMAL.getName() );
|
||||
jdbcToHibernateTypeContributionMap.get( Types.REAL ).add( StandardBasicTypes.BIG_DECIMAL.getName() );
|
||||
jdbcToHibernateTypeContributionMap.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.NCLOB, this.nClobTypeDescriptor );
|
||||
jdbcTypeRegistry.addDescriptor( Types.BLOB, this.blobTypeDescriptor );
|
||||
jdbcTypeRegistry.addDescriptor( Types.CLOB, new HANAClobJdbcType( maxLobPrefetchSize, useUnicodeStringTypes ) );
|
||||
jdbcTypeRegistry.addDescriptor( Types.NCLOB, new HANANClobJdbcType( maxLobPrefetchSize ) );
|
||||
jdbcTypeRegistry.addDescriptor( Types.BLOB, new HANABlobType( maxLobPrefetchSize ) );
|
||||
// tinyint is unsigned on HANA
|
||||
jdbcTypeRegistry.addDescriptor( Types.TINYINT, TinyIntAsSmallIntJdbcType.INSTANCE );
|
||||
if ( isUseUnicodeStringTypes() ) {
|
||||
jdbcTypeRegistry.addDescriptor( Types.VARCHAR, NVarcharJdbcType.INSTANCE );
|
||||
jdbcTypeRegistry.addDescriptor( Types.CHAR, NCharJdbcType.INSTANCE );
|
||||
}
|
||||
if (treatDoubleTypedFieldsAsDecimal) {
|
||||
if ( treatDoubleTypedFieldsAsDecimal ) {
|
||||
jdbcTypeRegistry.addDescriptor( Types.DOUBLE, DecimalJdbcType.INSTANCE );
|
||||
}
|
||||
}
|
||||
|
||||
public JdbcType getBlobTypeDescriptor() {
|
||||
return this.blobTypeDescriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendBooleanValueString(SqlAppender appender, boolean bool) {
|
||||
if ( this.useLegacyBooleanType ) {
|
||||
|
@ -1266,12 +1252,16 @@ public abstract class AbstractHANADialect extends Dialect {
|
|||
}
|
||||
|
||||
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 {
|
||||
|
||||
|
@ -1756,6 +1746,7 @@ public abstract class AbstractHANADialect extends Dialect {
|
|||
public static class HANABlobType implements JdbcType {
|
||||
|
||||
private static final long serialVersionUID = 5874441715643764323L;
|
||||
public static final JdbcType INSTANCE = new HANABlobType( HANAServerConfiguration.MAX_LOB_PREFETCH_SIZE_DEFAULT_VALUE );
|
||||
|
||||
final int maxLobPrefetchSize;
|
||||
|
||||
|
@ -1843,4 +1834,59 @@ public abstract class AbstractHANADialect extends Dialect {
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -572,6 +572,11 @@ public class CockroachDialect extends Dialect {
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsConflictClauseForInsertCTE() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNoColumnsInsertString() {
|
||||
return "default values";
|
||||
|
@ -1160,4 +1165,14 @@ public class CockroachDialect extends Dialect {
|
|||
// RuntimeModelCreationContext runtimeModelCreationContext) {
|
||||
// return new CteInsertStrategy( rootEntityDescriptor, runtimeModelCreationContext );
|
||||
// }
|
||||
|
||||
@Override
|
||||
public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() {
|
||||
return DmlTargetColumnQualifierSupport.TABLE_ALIAS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsFromClauseInUpdate() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,20 +7,27 @@
|
|||
package org.hibernate.dialect;
|
||||
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.sql.ast.Clause;
|
||||
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
||||
import org.hibernate.sql.ast.tree.Statement;
|
||||
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.Literal;
|
||||
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.InArrayPredicate;
|
||||
import org.hibernate.sql.ast.tree.predicate.LikePredicate;
|
||||
import org.hibernate.sql.ast.tree.select.QueryGroup;
|
||||
import org.hibernate.sql.ast.tree.select.QueryPart;
|
||||
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.JdbcOperationQueryInsert;
|
||||
|
||||
/**
|
||||
* A SQL AST translator for Cockroach.
|
||||
|
@ -33,6 +40,56 @@ public class CockroachSqlAstTranslator<T extends JdbcOperation> extends Abstract
|
|||
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
|
||||
protected void renderExpressionAsClauseItem(Expression expression) {
|
||||
expression.accept( this );
|
||||
|
|
|
@ -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.IdentifierHelperBuilder;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.exception.ConstraintViolationException;
|
||||
import org.hibernate.exception.LockTimeoutException;
|
||||
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.mapping.Column;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
|
@ -87,6 +90,7 @@ import org.hibernate.type.spi.TypeConfiguration;
|
|||
|
||||
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.BLOB;
|
||||
import static org.hibernate.type.SqlTypes.BOOLEAN;
|
||||
|
@ -973,6 +977,20 @@ public class DB2Dialect extends Dialect {
|
|||
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
|
||||
public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() {
|
||||
return (sqlException, message, sql) -> {
|
||||
|
@ -980,6 +998,14 @@ public class DB2Dialect extends Dialect {
|
|||
switch ( errorCode ) {
|
||||
case -952:
|
||||
return new LockTimeoutException( message, sqlException, sql );
|
||||
case -803:
|
||||
return new ConstraintViolationException(
|
||||
message,
|
||||
sqlException,
|
||||
sql,
|
||||
ConstraintViolationException.ConstraintKind.UNIQUE,
|
||||
getViolatedConstraintNameExtractor().extractConstraintName( sqlException )
|
||||
);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
@ -1171,4 +1197,14 @@ public class DB2Dialect extends Dialect {
|
|||
public int rowIdSqlType() {
|
||||
return VARBINARY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() {
|
||||
return DmlTargetColumnQualifierSupport.TABLE_ALIAS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsFromClauseInUpdate() {
|
||||
return getDB2Version().isSameOrAfter( 11 );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,8 +12,10 @@ import java.util.function.Consumer;
|
|||
import org.hibernate.LockMode;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
|
||||
import org.hibernate.query.IllegalQueryOperationException;
|
||||
import org.hibernate.query.sqm.ComparisonOperator;
|
||||
import org.hibernate.query.sqm.FetchClauseType;
|
||||
import org.hibernate.sql.ast.Clause;
|
||||
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
||||
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
||||
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.SqlTuple;
|
||||
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.TableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
|
||||
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.predicate.BooleanExpressionPredicate;
|
||||
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||
|
@ -347,12 +351,40 @@ public class DB2SqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAst
|
|||
@Override
|
||||
protected void visitInsertStatementOnly(InsertSelectStatement 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 ) {
|
||||
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) {
|
||||
final List<ColumnReference> returningColumns = statement.getReturningColumns();
|
||||
if ( isEmpty( returningColumns ) ) {
|
||||
|
@ -521,13 +553,13 @@ public class DB2SqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAst
|
|||
}
|
||||
|
||||
@Override
|
||||
protected String getFromDual() {
|
||||
return " from sysibm.dual";
|
||||
protected String getDual() {
|
||||
return "sysibm.dual";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getFromDualForSelectOnly() {
|
||||
return getFromDual();
|
||||
return " from " + getDual();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -135,7 +135,7 @@ public enum Database {
|
|||
HANA {
|
||||
@Override
|
||||
public Dialect createDialect(DialectResolutionInfo info) {
|
||||
return new HANAColumnStoreDialect( info );
|
||||
return new HANADialect( info );
|
||||
}
|
||||
@Override
|
||||
public boolean productNameMatches(String databaseName) {
|
||||
|
|
|
@ -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.IdentifierHelperBuilder;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.exception.ConstraintViolationException;
|
||||
import org.hibernate.exception.LockTimeoutException;
|
||||
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.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
|
||||
|
@ -690,14 +693,42 @@ public class DerbyDialect extends Dialect {
|
|||
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
|
||||
public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() {
|
||||
return (sqlException, message, sql) -> {
|
||||
final String sqlState = JdbcExceptionHelper.extractSqlState( sqlException );
|
||||
// final int errorCode = JdbcExceptionHelper.extractErrorCode( sqlException );
|
||||
final String constraintName;
|
||||
|
||||
if ( sqlState != null ) {
|
||||
switch ( sqlState ) {
|
||||
case "23505":
|
||||
// Unique constraint violation
|
||||
constraintName = getViolatedConstraintNameExtractor().extractConstraintName(sqlException);
|
||||
return new ConstraintViolationException(
|
||||
message,
|
||||
sqlException,
|
||||
sql,
|
||||
ConstraintViolationException.ConstraintKind.UNIQUE,
|
||||
constraintName
|
||||
);
|
||||
case "40XL1":
|
||||
case "40XL2":
|
||||
return new LockTimeoutException( message, sqlException, sql );
|
||||
|
@ -1013,4 +1044,5 @@ public class DerbyDialect extends Dialect {
|
|||
public UniqueDelegate getUniqueDelegate() {
|
||||
return uniqueDelegate;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -233,13 +233,13 @@ public class DerbySqlAstTranslator<T extends JdbcOperation> extends AbstractSqlA
|
|||
}
|
||||
|
||||
@Override
|
||||
protected String getFromDual() {
|
||||
return " from (values 0) dual";
|
||||
protected String getDual() {
|
||||
return "(values 0)";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getFromDualForSelectOnly() {
|
||||
return getFromDual();
|
||||
return " from " + getDual() + " dual";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -4359,6 +4359,17 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun
|
|||
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
|
||||
* {@code VALUES (1), (2), (3)}?
|
||||
|
@ -4380,6 +4391,16 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun
|
|||
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.
|
||||
*
|
||||
|
|
|
@ -721,8 +721,19 @@ public class H2Dialect extends Dialect {
|
|||
public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() {
|
||||
return (sqlException, message, sql) -> {
|
||||
final int errorCode = JdbcExceptionHelper.extractErrorCode( sqlException );
|
||||
final String constraintName;
|
||||
|
||||
switch (errorCode) {
|
||||
case 23505:
|
||||
// Unique constraint violation
|
||||
constraintName = getViolatedConstraintNameExtractor().extractConstraintName(sqlException);
|
||||
return new ConstraintViolationException(
|
||||
message,
|
||||
sqlException,
|
||||
sql,
|
||||
ConstraintViolationException.ConstraintKind.UNIQUE,
|
||||
constraintName
|
||||
);
|
||||
case 40001:
|
||||
// DEADLOCK DETECTED
|
||||
return new LockAcquisitionException(message, sqlException, sql);
|
||||
|
@ -731,7 +742,7 @@ public class H2Dialect extends Dialect {
|
|||
return new PessimisticLockException(message, sqlException, sql);
|
||||
case 90006:
|
||||
// NULL not allowed for column [90006-145]
|
||||
final String constraintName = getViolatedConstraintNameExtractor().extractConstraintName(sqlException);
|
||||
constraintName = getViolatedConstraintNameExtractor().extractConstraintName(sqlException);
|
||||
return new ConstraintViolationException(message, sqlException, sql, constraintName);
|
||||
case 57014:
|
||||
return new QueryTimeoutException( message, sqlException, sql );
|
||||
|
@ -935,4 +946,9 @@ public class H2Dialect extends Dialect {
|
|||
return "?" + position;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() {
|
||||
return DmlTargetColumnQualifierSupport.TABLE_ALIAS;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,9 @@ import java.util.List;
|
|||
import org.hibernate.LockMode;
|
||||
import org.hibernate.dialect.identity.H2IdentityColumnSupport;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.query.IllegalQueryOperationException;
|
||||
import org.hibernate.query.sqm.ComparisonOperator;
|
||||
import org.hibernate.sql.ast.Clause;
|
||||
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
||||
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||
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.SqlTuple;
|
||||
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.TableGroup;
|
||||
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.LikePredicate;
|
||||
import org.hibernate.sql.ast.tree.select.QueryPart;
|
||||
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.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
|
||||
protected void visitReturningColumns(List<ColumnReference> returningColumns) {
|
||||
// do nothing - this is handled via `#visitReturningInsertStatement`
|
||||
|
@ -267,11 +311,10 @@ public class H2SqlAstTranslator<T extends JdbcOperation> extends SqlAstTranslato
|
|||
}
|
||||
|
||||
@Override
|
||||
protected String getFromDual() {
|
||||
return " from dual";
|
||||
protected String getDual() {
|
||||
return "dual";
|
||||
}
|
||||
|
||||
|
||||
private boolean supportsOffsetFetchClause() {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -19,35 +19,14 @@ package org.hibernate.dialect;
|
|||
*
|
||||
* @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 HANACloudColumnStoreDialect() {
|
||||
// No idea how the versioning scheme is here, but since this is deprecated anyway, keep it as is
|
||||
super( 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();
|
||||
super( new HANAServerConfiguration( DatabaseVersion.make( 4 ) ) );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -34,11 +34,14 @@ import static org.hibernate.query.sqm.produce.function.FunctionParameterType.ANY
|
|||
*
|
||||
* @author Andrew Clemons
|
||||
* @author Jonathan Bregler
|
||||
*
|
||||
* @deprecated use {@link HANADialect} instead
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public class HANAColumnStoreDialect extends AbstractHANADialect {
|
||||
|
||||
public HANAColumnStoreDialect(DialectResolutionInfo info) {
|
||||
this( AbstractHANADialect.createVersion( info ) );
|
||||
this( HANAServerConfiguration.fromDialectResolutionInfo( info ) );
|
||||
registerKeywords( info );
|
||||
}
|
||||
|
||||
|
@ -48,129 +51,10 @@ public class HANAColumnStoreDialect extends AbstractHANADialect {
|
|||
}
|
||||
|
||||
public HANAColumnStoreDialect(DatabaseVersion version) {
|
||||
super( version );
|
||||
this( new HANAServerConfiguration( version ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUseUnicodeStringTypes() {
|
||||
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;
|
||||
public HANAColumnStoreDialect(HANAServerConfiguration configuration) {
|
||||
super( configuration, true );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -29,11 +29,14 @@ import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
|
|||
*
|
||||
* @author Andrew Clemons
|
||||
* @author Jonathan Bregler
|
||||
*
|
||||
* @deprecated use {@link HANADialect} instead
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public class HANARowStoreDialect extends AbstractHANADialect {
|
||||
|
||||
public HANARowStoreDialect(DialectResolutionInfo info) {
|
||||
this( AbstractHANADialect.createVersion( info ) );
|
||||
this( HANAServerConfiguration.fromDialectResolutionInfo( info ) );
|
||||
registerKeywords( info );
|
||||
}
|
||||
|
||||
|
@ -43,71 +46,10 @@ public class HANARowStoreDialect extends AbstractHANADialect {
|
|||
}
|
||||
|
||||
public HANARowStoreDialect(DatabaseVersion version) {
|
||||
super( version );
|
||||
this( new HANAServerConfiguration( version ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCreateTableString() {
|
||||
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;
|
||||
public HANARowStoreDialect(HANAServerConfiguration configuration) {
|
||||
super( configuration, false );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
}
|
|
@ -8,24 +8,34 @@ package org.hibernate.dialect;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.MappingException;
|
||||
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.ComparisonOperator;
|
||||
import org.hibernate.sql.ast.Clause;
|
||||
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
||||
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
||||
import org.hibernate.sql.ast.tree.Statement;
|
||||
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.Expression;
|
||||
import org.hibernate.sql.ast.tree.expression.Literal;
|
||||
import org.hibernate.sql.ast.tree.expression.Summarization;
|
||||
import org.hibernate.sql.ast.tree.from.FunctionTableReference;
|
||||
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
||||
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.select.QueryGroup;
|
||||
import org.hibernate.sql.ast.tree.select.QueryPart;
|
||||
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.model.internal.TableInsertStandard;
|
||||
|
||||
|
@ -42,6 +52,83 @@ public class HANASqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAs
|
|||
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) {
|
||||
// HANA only supports the LIMIT + OFFSET syntax but also window functions
|
||||
// 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
|
||||
protected String getFromDual() {
|
||||
return " from sys.dummy";
|
||||
protected String getDual() {
|
||||
return "sys.dummy";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getFromDualForSelectOnly() {
|
||||
return getFromDual();
|
||||
return " from " + getDual();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -165,4 +252,9 @@ public class HANASqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAs
|
|||
protected void visitValuesList(List<Values> valuesList) {
|
||||
visitValuesListEmulateSelectUnion( valuesList );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitValuesTableReference(ValuesTableReference tableReference) {
|
||||
emulateValuesTableReferenceColumnAliasing( tableReference );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.NameQualifierSupport;
|
||||
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.ViolatedConstraintNameExtractor;
|
||||
import org.hibernate.internal.util.JdbcExceptionHelper;
|
||||
|
@ -423,6 +425,29 @@ public class HSQLDialect extends Dialect {
|
|||
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
|
||||
public String getSelectClauseNullString(int sqlType, TypeConfiguration typeConfiguration) {
|
||||
switch ( sqlType ) {
|
||||
|
@ -683,4 +708,9 @@ public class HSQLDialect extends Dialect {
|
|||
public String quoteCollation(String collation) {
|
||||
return '\"' + collation + '\"';
|
||||
}
|
||||
|
||||
@Override
|
||||
public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() {
|
||||
return DmlTargetColumnQualifierSupport.TABLE_ALIAS;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,22 +11,30 @@ import java.util.function.Consumer;
|
|||
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
|
||||
import org.hibernate.query.IllegalQueryOperationException;
|
||||
import org.hibernate.query.sqm.BinaryArithmeticOperator;
|
||||
import org.hibernate.query.sqm.ComparisonOperator;
|
||||
import org.hibernate.sql.ast.Clause;
|
||||
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
||||
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
||||
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.expression.BinaryArithmeticExpression;
|
||||
import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression;
|
||||
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.Literal;
|
||||
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
||||
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.InArrayPredicate;
|
||||
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.type.descriptor.jdbc.ArrayJdbcType;
|
||||
|
||||
|
@ -41,6 +49,44 @@ public class HSQLSqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAs
|
|||
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
|
||||
protected void renderExpressionAsClauseItem(Expression expression) {
|
||||
expression.accept( this );
|
||||
|
@ -283,14 +329,9 @@ public class HSQLSqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAs
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getFromDual() {
|
||||
return " from (values(0))";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getFromDualForSelectOnly() {
|
||||
return getFromDual();
|
||||
return " from " + getDual();
|
||||
}
|
||||
|
||||
private boolean supportsOffsetFetchClause() {
|
||||
|
|
|
@ -6,21 +6,36 @@
|
|||
*/
|
||||
package org.hibernate.dialect;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.internal.util.collections.Stack;
|
||||
import org.hibernate.query.sqm.ComparisonOperator;
|
||||
import org.hibernate.sql.ast.Clause;
|
||||
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.delete.DeleteStatement;
|
||||
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.Literal;
|
||||
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.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.LikePredicate;
|
||||
import org.hibernate.sql.ast.tree.select.QueryGroup;
|
||||
import org.hibernate.sql.ast.tree.select.QueryPart;
|
||||
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.JdbcOperationQueryInsert;
|
||||
|
||||
/**
|
||||
* 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() );
|
||||
}
|
||||
|
||||
@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
|
||||
protected boolean supportsWithClauseInSubquery() {
|
||||
return false;
|
||||
|
@ -216,8 +360,8 @@ public class MariaDBSqlAstTranslator<T extends JdbcOperation> extends AbstractSq
|
|||
}
|
||||
|
||||
@Override
|
||||
protected String getFromDual() {
|
||||
return " from dual";
|
||||
protected String getDual() {
|
||||
return "dual";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1513,4 +1513,14 @@ public class MySQLDialect extends Dialect {
|
|||
public String getEnableConstraintsStatement() {
|
||||
return "set foreign_key_checks = 1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() {
|
||||
return DmlTargetColumnQualifierSupport.TABLE_ALIAS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsFromClauseInUpdate() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,25 +6,39 @@
|
|||
*/
|
||||
package org.hibernate.dialect;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.hibernate.engine.jdbc.Size;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.internal.util.collections.Stack;
|
||||
import org.hibernate.query.sqm.ComparisonOperator;
|
||||
import org.hibernate.sql.ast.Clause;
|
||||
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.delete.DeleteStatement;
|
||||
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.Literal;
|
||||
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.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.LikePredicate;
|
||||
import org.hibernate.sql.ast.tree.select.QueryGroup;
|
||||
import org.hibernate.sql.ast.tree.select.QueryPart;
|
||||
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.JdbcOperationQueryInsert;
|
||||
|
||||
/**
|
||||
* A SQL AST translator for MySQL.
|
||||
|
@ -87,6 +101,146 @@ public class MySQLSqlAstTranslator<T extends JdbcOperation> extends AbstractSqlA
|
|||
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
|
||||
protected void renderExpressionAsClauseItem(Expression expression) {
|
||||
expression.accept( this );
|
||||
|
@ -275,8 +429,8 @@ public class MySQLSqlAstTranslator<T extends JdbcOperation> extends AbstractSqlA
|
|||
}
|
||||
|
||||
@Override
|
||||
protected String getFromDual() {
|
||||
return " from dual";
|
||||
protected String getDual() {
|
||||
return "dual";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1048,6 +1048,7 @@ public class OracleDialect extends Dialect {
|
|||
@Override
|
||||
public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() {
|
||||
return (sqlException, message, sql) -> {
|
||||
final String constraintName;
|
||||
// interpreting Oracle exceptions is much much more precise based on their specific vendor codes.
|
||||
switch ( JdbcExceptionHelper.extractErrorCode( sqlException ) ) {
|
||||
|
||||
|
@ -1080,9 +1081,19 @@ public class OracleDialect extends Dialect {
|
|||
|
||||
// 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:
|
||||
// ORA-01407: cannot update column to NULL
|
||||
final String constraintName = getViolatedConstraintNameExtractor().extractConstraintName( sqlException );
|
||||
constraintName = getViolatedConstraintNameExtractor().extractConstraintName( sqlException );
|
||||
return new ConstraintViolationException( message, sqlException, sql, constraintName );
|
||||
|
||||
default:
|
||||
|
@ -1564,4 +1575,9 @@ public class OracleDialect extends Dialect {
|
|||
public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() {
|
||||
return DmlTargetColumnQualifierSupport.TABLE_ALIAS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsFromClauseInUpdate() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.from.FromClause;
|
||||
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.TableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.UnionTableGroup;
|
||||
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.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.QueryPart;
|
||||
import org.hibernate.sql.ast.tree.select.QuerySpec;
|
||||
import org.hibernate.sql.ast.tree.select.SelectClause;
|
||||
import org.hibernate.sql.ast.tree.select.SortSpecification;
|
||||
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.model.ast.ColumnValueBinding;
|
||||
import org.hibernate.sql.model.internal.OptionalTableUpdate;
|
||||
import org.hibernate.sql.results.internal.SqlSelectionImpl;
|
||||
import org.hibernate.type.SqlTypes;
|
||||
import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
|
||||
/**
|
||||
|
@ -65,6 +68,54 @@ public class OracleSqlAstTranslator<T extends JdbcOperation> extends SqlAstTrans
|
|||
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
|
||||
protected boolean needsRecursiveKeywordInWithClause() {
|
||||
return false;
|
||||
|
@ -171,7 +222,14 @@ public class OracleSqlAstTranslator<T extends JdbcOperation> extends SqlAstTrans
|
|||
|
||||
@Override
|
||||
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
|
||||
|
@ -555,13 +613,13 @@ public class OracleSqlAstTranslator<T extends JdbcOperation> extends SqlAstTrans
|
|||
}
|
||||
|
||||
@Override
|
||||
protected String getFromDual() {
|
||||
return " from dual";
|
||||
protected String getDual() {
|
||||
return "dual";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getFromDualForSelectOnly() {
|
||||
return getDialect().getVersion().isSameOrAfter( 23 ) ? super.getFromDualForSelectOnly() : getFromDual();
|
||||
return getDialect().getVersion().isSameOrAfter( 23 ) ? "" : ( " from " + getDual() );
|
||||
}
|
||||
|
||||
private boolean supportsOffsetFetchClause() {
|
||||
|
|
|
@ -787,6 +787,10 @@ public class PostgreSQLDialect extends Dialect {
|
|||
public boolean supportsNonQueryWithCTE() {
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public boolean supportsConflictClauseForInsertCTE() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SequenceSupport getSequenceSupport() {
|
||||
|
@ -1547,4 +1551,14 @@ public class PostgreSQLDialect extends Dialect {
|
|||
// The maximum scale for `interval second` is 6 unfortunately
|
||||
return 6;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() {
|
||||
return DmlTargetColumnQualifierSupport.TABLE_ALIAS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsFromClauseInUpdate() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|||
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
|
||||
import org.hibernate.query.sqm.ComparisonOperator;
|
||||
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.cte.CteMaterialization;
|
||||
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.Literal;
|
||||
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.InArrayPredicate;
|
||||
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.QueryPart;
|
||||
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.JdbcOperationQueryInsert;
|
||||
import org.hibernate.sql.model.internal.TableInsertStandard;
|
||||
import org.hibernate.type.SqlTypes;
|
||||
|
||||
|
@ -58,6 +66,55 @@ public class PostgreSQLSqlAstTranslator<T extends JdbcOperation> extends SqlAstT
|
|||
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
|
||||
protected void renderExpressionAsClauseItem(Expression expression) {
|
||||
|
|
|
@ -49,6 +49,8 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|||
import org.hibernate.exception.ConstraintViolationException;
|
||||
import org.hibernate.exception.LockTimeoutException;
|
||||
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.mapping.Column;
|
||||
import org.hibernate.persister.entity.mutation.EntityMutationTarget;
|
||||
|
@ -84,6 +86,7 @@ import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
|
|||
|
||||
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.produce.function.FunctionParameterType.INTEGER;
|
||||
import static org.hibernate.type.SqlTypes.BLOB;
|
||||
|
@ -699,6 +702,21 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
|
|||
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
|
||||
public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() {
|
||||
return (sqlException, message, sql) -> {
|
||||
|
@ -712,6 +730,13 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
|
|||
case 1222:
|
||||
return new LockTimeoutException( message, sqlException, sql );
|
||||
case 2627:
|
||||
return new ConstraintViolationException(
|
||||
message,
|
||||
sqlException,
|
||||
sql,
|
||||
ConstraintViolationException.ConstraintKind.UNIQUE,
|
||||
getViolatedConstraintNameExtractor().extractConstraintName( sqlException )
|
||||
);
|
||||
case 2601:
|
||||
return new ConstraintViolationException(
|
||||
message,
|
||||
|
@ -1106,4 +1131,14 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
|
|||
final SQLServerSqlAstTranslator<JdbcOperation> translator = new SQLServerSqlAstTranslator<>( factory, optionalTableUpdate );
|
||||
return translator.createMergeOperation( optionalTableUpdate );
|
||||
}
|
||||
|
||||
@Override
|
||||
public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() {
|
||||
return DmlTargetColumnQualifierSupport.TABLE_ALIAS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsFromClauseInUpdate() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,14 +11,19 @@ import java.util.List;
|
|||
import org.hibernate.LockMode;
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.internal.util.collections.Stack;
|
||||
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
|
||||
import org.hibernate.query.IllegalQueryOperationException;
|
||||
import org.hibernate.query.sqm.ComparisonOperator;
|
||||
import org.hibernate.query.sqm.FetchClauseType;
|
||||
import org.hibernate.sql.ast.Clause;
|
||||
import org.hibernate.sql.ast.SqlAstJoinType;
|
||||
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.delete.DeleteStatement;
|
||||
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.Literal;
|
||||
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.TableReference;
|
||||
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.select.QueryGroup;
|
||||
import org.hibernate.sql.ast.tree.select.QueryPart;
|
||||
import org.hibernate.sql.ast.tree.select.QuerySpec;
|
||||
import org.hibernate.sql.ast.tree.select.SelectClause;
|
||||
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.model.internal.OptionalTableUpdate;
|
||||
import org.hibernate.type.SqlTypes;
|
||||
|
@ -53,6 +61,85 @@ public class SQLServerSqlAstTranslator<T extends JdbcOperation> extends SqlAstTr
|
|||
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
|
||||
protected boolean needsRecursiveKeywordInWithClause() {
|
||||
return false;
|
||||
|
@ -128,14 +215,7 @@ public class SQLServerSqlAstTranslator<T extends JdbcOperation> extends SqlAstTr
|
|||
appendSql( " )" );
|
||||
|
||||
registerAffectedTable( tableReference );
|
||||
final Clause currentClause = getClauseStack().getCurrent();
|
||||
if ( rendersTableReferenceAlias( currentClause ) ) {
|
||||
final String identificationVariable = tableReference.getIdentificationVariable();
|
||||
if ( identificationVariable != null ) {
|
||||
appendSql( ' ' );
|
||||
appendSql( identificationVariable );
|
||||
}
|
||||
}
|
||||
renderTableReferenceIdentificationVariable( tableReference );
|
||||
}
|
||||
else {
|
||||
super.renderNamedTableReference( tableReference, lockMode );
|
||||
|
|
|
@ -633,28 +633,17 @@ public class SybaseASEDialect extends SybaseDialect {
|
|||
final int errorCode = JdbcExceptionHelper.extractErrorCode( sqle );
|
||||
if ( sqlState != null ) {
|
||||
switch ( sqlState ) {
|
||||
// UNIQUE VIOLATION
|
||||
case "S1000":
|
||||
if ( 2601 == errorCode ) {
|
||||
return extractUsingTemplate( "with unique index '", "'", sqle.getMessage() );
|
||||
}
|
||||
break;
|
||||
case "23000":
|
||||
if ( 546 == errorCode ) {
|
||||
// Foreign key violation
|
||||
return extractUsingTemplate( "constraint name = '", "'", sqle.getMessage() );
|
||||
switch ( errorCode ) {
|
||||
case 2601:
|
||||
// UNIQUE VIOLATION
|
||||
return extractUsingTemplate( "with unique index '", "'", sqle.getMessage() );
|
||||
case 546:
|
||||
// Foreign key violation
|
||||
return extractUsingTemplate( "constraint name = '", "'", sqle.getMessage() );
|
||||
}
|
||||
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;
|
||||
|
@ -671,30 +660,44 @@ public class SybaseASEDialect extends SybaseDialect {
|
|||
case "JZ006":
|
||||
return new LockTimeoutException( message, sqlException, sql );
|
||||
case "S1000":
|
||||
case "23000":
|
||||
switch ( errorCode ) {
|
||||
case 515:
|
||||
// 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:
|
||||
// Unique constraint violation
|
||||
final String constraintName = getViolatedConstraintNameExtractor().extractConstraintName(
|
||||
sqlException );
|
||||
return new ConstraintViolationException( message, sqlException, sql, constraintName );
|
||||
return new ConstraintViolationException(
|
||||
message,
|
||||
sqlException,
|
||||
sql,
|
||||
ConstraintViolationException.ConstraintKind.UNIQUE,
|
||||
getViolatedConstraintNameExtractor().extractConstraintName( sqlException )
|
||||
);
|
||||
}
|
||||
break;
|
||||
case "ZZZZZ":
|
||||
if ( 515 == errorCode ) {
|
||||
// Attempt to insert NULL value into column; column does not allow nulls.
|
||||
final String constraintName = getViolatedConstraintNameExtractor().extractConstraintName(
|
||||
sqlException );
|
||||
return new ConstraintViolationException( message, sqlException, sql, constraintName );
|
||||
}
|
||||
break;
|
||||
case "23000":
|
||||
if ( 546 == errorCode ) {
|
||||
// Foreign key violation
|
||||
final String constraintName = getViolatedConstraintNameExtractor().extractConstraintName(
|
||||
sqlException );
|
||||
return new ConstraintViolationException( message, sqlException, sql, constraintName );
|
||||
return new ConstraintViolationException(
|
||||
message,
|
||||
sqlException,
|
||||
sql,
|
||||
getViolatedConstraintNameExtractor().extractConstraintName( sqlException )
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -12,13 +12,17 @@ import java.util.function.Consumer;
|
|||
import org.hibernate.LockMode;
|
||||
import org.hibernate.LockOptions;
|
||||
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.sql.ast.Clause;
|
||||
import org.hibernate.sql.ast.SqlAstJoinType;
|
||||
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
||||
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
||||
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.delete.DeleteStatement;
|
||||
import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression;
|
||||
import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression;
|
||||
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.TableGroupJoin;
|
||||
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.predicate.BooleanExpressionPredicate;
|
||||
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.QuerySpec;
|
||||
import org.hibernate.sql.ast.tree.select.SelectClause;
|
||||
import org.hibernate.sql.ast.tree.update.UpdateStatement;
|
||||
import org.hibernate.sql.exec.spi.JdbcOperation;
|
||||
|
||||
/**
|
||||
|
@ -55,6 +63,77 @@ public class SybaseASESqlAstTranslator<T extends JdbcOperation> extends Abstract
|
|||
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
|
||||
protected boolean supportsWithClause() {
|
||||
return false;
|
||||
|
@ -130,14 +209,7 @@ public class SybaseASESqlAstTranslator<T extends JdbcOperation> extends Abstract
|
|||
appendSql( " )" );
|
||||
|
||||
registerAffectedTable( tableReference );
|
||||
final Clause currentClause = getClauseStack().getCurrent();
|
||||
if ( rendersTableReferenceAlias( currentClause ) ) {
|
||||
final String identificationVariable = tableReference.getIdentificationVariable();
|
||||
if ( identificationVariable != null ) {
|
||||
appendSql( ' ' );
|
||||
appendSql( identificationVariable );
|
||||
}
|
||||
}
|
||||
renderTableReferenceIdentificationVariable( tableReference );
|
||||
}
|
||||
else {
|
||||
super.renderNamedTableReference( tableReference, lockMode );
|
||||
|
@ -250,6 +322,14 @@ public class SybaseASESqlAstTranslator<T extends JdbcOperation> extends Abstract
|
|||
visitValuesListEmulateSelectUnion( valuesList );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitValuesTableReference(ValuesTableReference tableReference) {
|
||||
append( '(' );
|
||||
visitValuesListEmulateSelectUnion( tableReference.getValuesList() );
|
||||
append( ')' );
|
||||
renderDerivedTableReference( tableReference );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitOffsetFetchClause(QueryPart queryPart) {
|
||||
assertRowsOnlyFetchClauseType( queryPart );
|
||||
|
@ -384,63 +464,32 @@ public class SybaseASESqlAstTranslator<T extends JdbcOperation> extends Abstract
|
|||
}
|
||||
|
||||
@Override
|
||||
public void visitColumnReference(ColumnReference columnReference) {
|
||||
final String dmlTargetTableAlias = getDmlTargetTableAlias();
|
||||
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 ) {
|
||||
columnReference.appendReadExpression( this );
|
||||
}
|
||||
// for now, use the unqualified form
|
||||
else if ( columnReference.isColumnExpressionFormula() ) {
|
||||
// For formulas, we have to replace the qualifier as the alias was already rendered into the formula
|
||||
// This is fine for now as this is only temporary anyway until we render aliases for table references
|
||||
appendSql(
|
||||
columnReference.getColumnExpression()
|
||||
.replaceAll( "(\\b)(" + dmlTargetTableAlias + "\\.)(\\b)", "$1$3" )
|
||||
);
|
||||
}
|
||||
else {
|
||||
columnReference.appendReadExpression(
|
||||
this,
|
||||
getCurrentDmlStatement().getTargetTable().getTableExpression()
|
||||
);
|
||||
}
|
||||
protected String determineColumnReferenceQualifier(ColumnReference columnReference) {
|
||||
final DmlTargetColumnQualifierSupport qualifierSupport = getDialect().getDmlTargetColumnQualifierSupport();
|
||||
final MutationStatement currentDmlStatement;
|
||||
final String dmlAlias;
|
||||
if ( qualifierSupport == DmlTargetColumnQualifierSupport.TABLE_ALIAS
|
||||
|| ( currentDmlStatement = getCurrentDmlStatement() ) == null
|
||||
|| ( dmlAlias = currentDmlStatement.getTargetTable().getIdentificationVariable() ) == null
|
||||
|| !dmlAlias.equals( columnReference.getQualifier() ) ) {
|
||||
return 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 ) {
|
||||
return columnReference.getQualifier();
|
||||
}
|
||||
else if ( columnReference.isColumnExpressionFormula() ) {
|
||||
// For formulas, we have to replace the qualifier as the alias was already rendered into the formula
|
||||
// This is fine for now as this is only temporary anyway until we render aliases for table references
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
columnReference.appendReadExpression( this );
|
||||
}
|
||||
}
|
||||
|
||||
@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 );
|
||||
return getCurrentDmlStatement().getTargetTable().getTableExpression();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -465,8 +514,8 @@ public class SybaseASESqlAstTranslator<T extends JdbcOperation> extends Abstract
|
|||
}
|
||||
|
||||
@Override
|
||||
protected String getFromDual() {
|
||||
return " from (select 1) dual(c1)";
|
||||
protected String getDual() {
|
||||
return "(select 1 c1)";
|
||||
}
|
||||
|
||||
private boolean supportsParameterOffsetFetchExpression() {
|
||||
|
|
|
@ -514,4 +514,14 @@ public class SybaseDialect extends AbstractTransactSQLDialect {
|
|||
? AbstractTransactSQLIdentityColumnSupport.INSTANCE
|
||||
: SybaseJconnIdentityColumnSupport.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() {
|
||||
return DmlTargetColumnQualifierSupport.TABLE_ALIAS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsFromClauseInUpdate() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,12 +11,15 @@ import java.util.function.Consumer;
|
|||
|
||||
import org.hibernate.LockMode;
|
||||
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.sql.ast.Clause;
|
||||
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
||||
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
||||
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||
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.CaseSearchedExpression;
|
||||
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.from.NamedTableReference;
|
||||
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.QuerySpec;
|
||||
import org.hibernate.sql.ast.tree.update.UpdateStatement;
|
||||
import org.hibernate.sql.exec.spi.JdbcOperation;
|
||||
|
||||
/**
|
||||
|
@ -43,6 +51,46 @@ public class SybaseSqlAstTranslator<T extends JdbcOperation> extends AbstractSql
|
|||
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
|
||||
protected boolean supportsWithClause() {
|
||||
return false;
|
||||
|
@ -118,14 +166,7 @@ public class SybaseSqlAstTranslator<T extends JdbcOperation> extends AbstractSql
|
|||
appendSql( " )" );
|
||||
|
||||
registerAffectedTable( tableReference );
|
||||
final Clause currentClause = getClauseStack().getCurrent();
|
||||
if ( rendersTableReferenceAlias( currentClause ) ) {
|
||||
final String identificationVariable = tableReference.getIdentificationVariable();
|
||||
if ( identificationVariable != null ) {
|
||||
appendSql( ' ' );
|
||||
appendSql( identificationVariable );
|
||||
}
|
||||
}
|
||||
renderTableReferenceIdentificationVariable( tableReference );
|
||||
}
|
||||
else {
|
||||
super.renderNamedTableReference( tableReference, lockMode );
|
||||
|
@ -146,6 +187,19 @@ public class SybaseSqlAstTranslator<T extends JdbcOperation> extends AbstractSql
|
|||
// 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
|
||||
public void visitOffsetFetchClause(QueryPart queryPart) {
|
||||
assertRowsOnlyFetchClauseType( queryPart );
|
||||
|
|
|
@ -6,23 +6,38 @@
|
|||
*/
|
||||
package org.hibernate.dialect;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.internal.util.collections.Stack;
|
||||
import org.hibernate.query.sqm.ComparisonOperator;
|
||||
import org.hibernate.sql.ast.Clause;
|
||||
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.delete.DeleteStatement;
|
||||
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.Literal;
|
||||
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.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.LikePredicate;
|
||||
import org.hibernate.sql.ast.tree.select.QueryGroup;
|
||||
import org.hibernate.sql.ast.tree.select.QueryPart;
|
||||
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.JdbcOperationQueryInsert;
|
||||
|
||||
/**
|
||||
* A SQL AST translator for TiDB.
|
||||
|
@ -39,6 +54,135 @@ public class TiDBSqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAs
|
|||
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
|
||||
protected void renderExpressionAsClauseItem(Expression expression) {
|
||||
expression.accept( this );
|
||||
|
@ -174,8 +318,8 @@ public class TiDBSqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAs
|
|||
}
|
||||
|
||||
@Override
|
||||
protected String getFromDual() {
|
||||
return " from dual";
|
||||
protected String getDual() {
|
||||
return "dual";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -53,6 +53,7 @@ import org.hibernate.procedure.ProcedureCall;
|
|||
import org.hibernate.query.MutationQuery;
|
||||
import org.hibernate.query.SelectionQuery;
|
||||
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
|
||||
import org.hibernate.query.criteria.JpaCriteriaInsert;
|
||||
import org.hibernate.query.criteria.JpaCriteriaInsertSelect;
|
||||
import org.hibernate.query.spi.QueryImplementor;
|
||||
import org.hibernate.query.spi.QueryProducerImplementor;
|
||||
|
@ -513,6 +514,12 @@ public class SessionDelegatorBaseImpl implements SessionImplementor {
|
|||
return delegate().createMutationQuery( insertSelect );
|
||||
}
|
||||
|
||||
@Override
|
||||
public MutationQuery createMutationQuery(@SuppressWarnings("rawtypes") JpaCriteriaInsert insertSelect) {
|
||||
//noinspection resource
|
||||
return delegate().createMutationQuery( insertSelect );
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> QueryImplementor<T> createQuery(CriteriaQuery<T> criteriaQuery) {
|
||||
return queryDelegate().createQuery( criteriaQuery );
|
||||
|
|
|
@ -40,6 +40,7 @@ import org.hibernate.query.NativeQuery;
|
|||
import org.hibernate.query.Query;
|
||||
import org.hibernate.query.SelectionQuery;
|
||||
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
|
||||
import org.hibernate.query.criteria.JpaCriteriaInsert;
|
||||
import org.hibernate.query.criteria.JpaCriteriaInsertSelect;
|
||||
import org.hibernate.stat.SessionStatistics;
|
||||
|
||||
|
@ -773,6 +774,11 @@ public class SessionLazyDelegator implements Session {
|
|||
return this.lazySession.get().createMutationQuery( insertSelect );
|
||||
}
|
||||
|
||||
@Override
|
||||
public MutationQuery createMutationQuery(@SuppressWarnings("rawtypes") JpaCriteriaInsert insertSelect) {
|
||||
return this.lazySession.get().createMutationQuery( insertSelect );
|
||||
}
|
||||
|
||||
@Override
|
||||
public MutationQuery createNativeMutationQuery(String sqlString) {
|
||||
return this.lazySession.get().createNativeMutationQuery( sqlString );
|
||||
|
|
|
@ -34,6 +34,7 @@ import org.hibernate.procedure.ProcedureCall;
|
|||
import org.hibernate.query.MutationQuery;
|
||||
import org.hibernate.query.SelectionQuery;
|
||||
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
|
||||
import org.hibernate.query.criteria.JpaCriteriaInsert;
|
||||
import org.hibernate.query.criteria.JpaCriteriaInsertSelect;
|
||||
import org.hibernate.query.spi.QueryImplementor;
|
||||
import org.hibernate.query.spi.QueryProducerImplementor;
|
||||
|
@ -101,6 +102,12 @@ public class SharedSessionDelegatorBaseImpl implements SharedSessionContractImpl
|
|||
return delegate().createMutationQuery( insertSelect );
|
||||
}
|
||||
|
||||
@Override
|
||||
public MutationQuery createMutationQuery(@SuppressWarnings("rawtypes") JpaCriteriaInsert insertSelect) {
|
||||
//noinspection resource
|
||||
return delegate().createMutationQuery( insertSelect );
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> QueryImplementor<T> createQuery(CriteriaQuery<T> criteriaQuery) {
|
||||
return queryDelegate().createQuery( criteriaQuery );
|
||||
|
|
|
@ -19,15 +19,26 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
|||
*/
|
||||
public class ConstraintViolationException extends JDBCException {
|
||||
|
||||
private final ConstraintKind kind;
|
||||
private final @Nullable String constraintName;
|
||||
|
||||
public ConstraintViolationException(String message, SQLException root, @Nullable String constraintName) {
|
||||
super( message, root );
|
||||
this.constraintName = constraintName;
|
||||
this( message, root, ConstraintKind.OTHER, 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 );
|
||||
this.kind = kind;
|
||||
this.constraintName = constraintName;
|
||||
}
|
||||
|
||||
|
@ -39,4 +50,16 @@ public class ConstraintViolationException extends JDBCException {
|
|||
public @Nullable String getConstraintName() {
|
||||
return constraintName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the kind of constraint that was violated.
|
||||
*/
|
||||
public ConstraintKind getKind() {
|
||||
return kind;
|
||||
}
|
||||
|
||||
public enum ConstraintKind {
|
||||
UNIQUE,
|
||||
OTHER
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,6 +70,7 @@ import org.hibernate.query.SelectionQuery;
|
|||
import org.hibernate.query.UnknownNamedQueryException;
|
||||
import org.hibernate.query.criteria.CriteriaDefinition;
|
||||
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
|
||||
import org.hibernate.query.criteria.JpaCriteriaInsert;
|
||||
import org.hibernate.query.criteria.JpaCriteriaInsertSelect;
|
||||
import org.hibernate.query.hql.spi.SqmQueryImplementor;
|
||||
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.delete.SqmDeleteStatement;
|
||||
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.SqmQuerySpec;
|
||||
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
|
||||
@SuppressWarnings("UnnecessaryLocalVariable")
|
||||
public ProcedureCall getNamedProcedureCall(String name) {
|
||||
|
|
|
@ -65,7 +65,11 @@ public class PrimaryKey extends Constraint {
|
|||
}
|
||||
|
||||
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;
|
||||
for ( Column column : getColumns() ) {
|
||||
if ( first ) {
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
package org.hibernate.query;
|
||||
|
||||
import org.hibernate.query.criteria.JpaCriteriaInsert;
|
||||
import org.hibernate.query.criteria.JpaCriteriaInsertSelect;
|
||||
|
||||
import jakarta.persistence.criteria.CriteriaDelete;
|
||||
|
@ -323,6 +324,11 @@ public interface QueryProducer {
|
|||
*/
|
||||
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.
|
||||
*
|
||||
|
|
|
@ -123,6 +123,12 @@ public interface HibernateCriteriaBuilder extends CriteriaBuilder {
|
|||
|
||||
<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.
|
||||
*
|
||||
|
@ -693,8 +699,6 @@ public interface HibernateCriteriaBuilder extends CriteriaBuilder {
|
|||
|
||||
<T> JpaExpression<T> value(T value);
|
||||
|
||||
<V, C extends Collection<V>> JpaExpression<Collection<V>> values(C collection);
|
||||
|
||||
@Override
|
||||
<V, M extends Map<?, V>> Expression<Collection<V>> values(M map);
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -8,6 +8,9 @@ package org.hibernate.query.criteria;
|
|||
|
||||
import org.hibernate.Incubating;
|
||||
|
||||
import jakarta.persistence.Tuple;
|
||||
import jakarta.persistence.criteria.CriteriaQuery;
|
||||
|
||||
/**
|
||||
* A representation of SqmInsertSelectStatement at the
|
||||
* {@link org.hibernate.query.criteria} level, even though JPA does
|
||||
|
@ -30,5 +33,10 @@ import org.hibernate.Incubating;
|
|||
* @author Steve Ebersole
|
||||
*/
|
||||
@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);
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
*/
|
||||
package org.hibernate.query.criteria;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.Incubating;
|
||||
|
||||
/**
|
||||
|
@ -13,7 +15,7 @@ import org.hibernate.Incubating;
|
|||
* {@link org.hibernate.query.criteria} level, even though JPA does
|
||||
* 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>
|
||||
* <li>
|
||||
|
@ -30,5 +32,13 @@ import org.hibernate.Incubating;
|
|||
* @author Gavin King
|
||||
*/
|
||||
@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);
|
||||
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -53,6 +53,7 @@ import org.hibernate.query.criteria.JpaSelection;
|
|||
import org.hibernate.query.criteria.JpaSetJoin;
|
||||
import org.hibernate.query.criteria.JpaSimpleCase;
|
||||
import org.hibernate.query.criteria.JpaSubQuery;
|
||||
import org.hibernate.query.criteria.JpaValues;
|
||||
import org.hibernate.query.criteria.JpaWindow;
|
||||
import org.hibernate.query.criteria.JpaWindowFrame;
|
||||
import org.hibernate.query.NullPrecedence;
|
||||
|
@ -148,6 +149,18 @@ public class HibernateCriteriaBuilderDelegate implements HibernateCriteriaBuilde
|
|||
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
|
||||
public <T> JpaCriteriaQuery<T> unionAll(CriteriaQuery<? extends T> query1, CriteriaQuery<?>... queries) {
|
||||
return criteriaBuilder.unionAll( query1, queries );
|
||||
|
@ -746,11 +759,6 @@ public class HibernateCriteriaBuilderDelegate implements HibernateCriteriaBuilde
|
|||
return criteriaBuilder.value( value );
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V, C extends Collection<V>> JpaExpression<Collection<V>> values(C collection) {
|
||||
return criteriaBuilder.values( collection );
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V, M extends Map<?, V>> Expression<Collection<V>> values(M map) {
|
||||
return criteriaBuilder.values( map );
|
||||
|
|
|
@ -24,9 +24,11 @@ import java.time.ZonedDateTime;
|
|||
import java.time.temporal.TemporalAccessor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
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.spi.ParameterDeclarationContext;
|
||||
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.SqmQuery;
|
||||
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.SqmQualifiedJoin;
|
||||
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.SqmInsertStatement;
|
||||
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.SqmSortSpecification;
|
||||
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.spi.NavigablePath;
|
||||
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
||||
import org.hibernate.sql.ast.tree.cte.CteMaterialization;
|
||||
import org.hibernate.sql.ast.tree.cte.CteSearchClauseKind;
|
||||
|
@ -487,8 +495,6 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
|||
processingStateStack.push( processingState );
|
||||
|
||||
try {
|
||||
queryExpressionContext.accept( this );
|
||||
|
||||
final SqmCreationProcessingState stateFieldsProcessingState = new SqmCreationProcessingStateImpl(
|
||||
insertStatement,
|
||||
this
|
||||
|
@ -506,6 +512,9 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
|||
processingStateStack.pop();
|
||||
}
|
||||
|
||||
queryExpressionContext.accept( this );
|
||||
|
||||
insertStatement.onConflict( visitConflictClause( ctx.conflictClause() ) );
|
||||
return insertStatement;
|
||||
}
|
||||
finally {
|
||||
|
@ -526,21 +535,43 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
|||
processingState.getPathRegistry().register( root );
|
||||
|
||||
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() ) {
|
||||
final SqmPath<?> stateField = (SqmPath<?>) visitSimplePath( stateFieldCtx );
|
||||
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;
|
||||
}
|
||||
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
|
||||
public SqmUpdateStatement<R> visitUpdateStatement(HqlParser.UpdateStatementContext ctx) {
|
||||
final boolean versioned = ctx.VERSIONED() != null;
|
||||
|
@ -577,27 +644,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
|||
final HqlParser.SetClauseContext setClauseCtx = ctx.setClause();
|
||||
for ( ParseTree subCtx : setClauseCtx.children ) {
|
||||
if ( subCtx instanceof HqlParser.AssignmentContext ) {
|
||||
final HqlParser.AssignmentContext assignmentContext = (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 );
|
||||
updateStatement.applyAssignment( visitAssignment( (HqlParser.AssignmentContext) subCtx ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
public SqmDeleteStatement<R> visitDeleteStatement(HqlParser.DeleteStatementContext ctx) {
|
||||
final HqlParser.TargetEntityContext dmlTargetContext = ctx.targetEntity();
|
||||
|
|
|
@ -71,26 +71,7 @@ public class SqmPathRegistryImpl implements SqmPathRegistry {
|
|||
if ( sqmPath instanceof SqmFrom<?, ?> ) {
|
||||
final SqmFrom<?, ?> sqmFrom = (SqmFrom<?, ?>) sqmPath;
|
||||
|
||||
final String alias = sqmPath.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,
|
||||
sqmPath
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
registerByAliasOnly( 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
|
||||
public <E> void replace(SqmEntityJoin<E> sqmJoin, SqmRoot<E> sqmRoot) {
|
||||
final String alias = sqmJoin.getExplicitAlias();
|
||||
|
|
|
@ -36,6 +36,13 @@ public interface SqmPathRegistry {
|
|||
*/
|
||||
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}
|
||||
* to treat secondary query roots as cross-joins. Here we will replace the {@code sqmRoot}
|
||||
|
|
|
@ -32,6 +32,7 @@ import org.hibernate.query.criteria.JpaPredicate;
|
|||
import org.hibernate.query.criteria.JpaSearchedCase;
|
||||
import org.hibernate.query.criteria.JpaSelection;
|
||||
import org.hibernate.query.criteria.JpaSimpleCase;
|
||||
import org.hibernate.query.criteria.JpaValues;
|
||||
import org.hibernate.query.criteria.JpaWindow;
|
||||
import org.hibernate.query.spi.QueryEngine;
|
||||
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.insert.SqmInsertSelectStatement;
|
||||
import org.hibernate.query.sqm.tree.insert.SqmInsertValuesStatement;
|
||||
import org.hibernate.query.sqm.tree.insert.SqmValues;
|
||||
import org.hibernate.query.sqm.tree.predicate.SqmInPredicate;
|
||||
import org.hibernate.query.sqm.tree.predicate.SqmPredicate;
|
||||
import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
|
||||
|
@ -522,6 +524,12 @@ public interface NodeBuilder extends HibernateCriteriaBuilder {
|
|||
@Override
|
||||
<T> SqmInsertSelectStatement<T> createCriteriaInsertSelect(Class<T> targetEntity);
|
||||
|
||||
@Override
|
||||
SqmValues values(Expression<?>... expressions);
|
||||
|
||||
@Override
|
||||
SqmValues values(List<? extends Expression<?>> expressions);
|
||||
|
||||
@Override
|
||||
<N extends Number> SqmExpression<N> abs(Expression<N> x);
|
||||
|
||||
|
@ -775,9 +783,6 @@ public interface NodeBuilder extends HibernateCriteriaBuilder {
|
|||
@Override
|
||||
<K, L extends List<?>> SqmExpression<Set<K>> indexes(L list);
|
||||
|
||||
@Override
|
||||
<V, C extends Collection<V>> SqmExpression<Collection<V>> values(C collection);
|
||||
|
||||
@Override
|
||||
<V, M extends Map<?, V>> Expression<Collection<V>> values(M map);
|
||||
|
||||
|
|
|
@ -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.SqmFromClause;
|
||||
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.SqmInsertValuesStatement;
|
||||
import org.hibernate.query.sqm.tree.insert.SqmValues;
|
||||
|
@ -136,6 +137,8 @@ public interface SemanticQueryWalker<T> {
|
|||
|
||||
T visitInsertValuesStatement(SqmInsertValuesStatement<?> statement);
|
||||
|
||||
T visitConflictClause(SqmConflictClause<?> sqmConflictClause);
|
||||
|
||||
T visitDeleteStatement(SqmDeleteStatement<?> statement);
|
||||
|
||||
T visitSelectStatement(SqmSelectStatement<?> statement);
|
||||
|
|
|
@ -827,7 +827,7 @@ public class QuerySqmImpl<R>
|
|||
final NonSelectQueryPlan[] planParts = new NonSelectQueryPlan[valuesList.size()];
|
||||
for ( int i = 0; i < valuesList.size(); i++ ) {
|
||||
final SqmInsertValuesStatement<?> subInsert = insertValues.copyWithoutValues( SqmCopyContext.simpleContext() );
|
||||
subInsert.getValuesList().add( valuesList.get( i ) );
|
||||
subInsert.values( valuesList );
|
||||
planParts[i] = new SimpleInsertQueryPlan( subInsert, domainParameterXref );
|
||||
}
|
||||
|
||||
|
|
|
@ -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.insert.SqmInsertSelectStatement;
|
||||
import org.hibernate.query.sqm.tree.insert.SqmInsertValuesStatement;
|
||||
import org.hibernate.query.sqm.tree.insert.SqmValues;
|
||||
import org.hibernate.query.sqm.tree.predicate.SqmBetweenPredicate;
|
||||
import org.hibernate.query.sqm.tree.predicate.SqmBooleanExpressionPredicate;
|
||||
import org.hibernate.query.sqm.tree.predicate.SqmComparisonPredicate;
|
||||
|
@ -353,6 +354,17 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
|
|||
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
|
||||
public <T> JpaCriteriaQuery<T> union(boolean all, CriteriaQuery<? extends T> query1, CriteriaQuery<?>... 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
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V, C extends Collection<V>> SqmExpression<Collection<V>> values(C collection) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V, M extends Map<?, V>> Expression<Collection<V>> values(M map) {
|
||||
throw new UnsupportedOperationException();
|
||||
return value( map.values() );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -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.SqmQualifiedJoin;
|
||||
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.SqmInsertValuesStatement;
|
||||
import org.hibernate.query.sqm.tree.insert.SqmValues;
|
||||
|
@ -330,6 +332,12 @@ public class SqmTreePrinter implements SemanticQueryWalker<Object> {
|
|||
"into",
|
||||
() -> 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;
|
||||
}
|
||||
|
||||
@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
|
||||
public Object visitSelectStatement(SqmSelectStatement<?> statement) {
|
||||
if ( DEBUG_ENABLED ) {
|
||||
|
|
|
@ -6,62 +6,37 @@
|
|||
*/
|
||||
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.internal.util.collections.Stack;
|
||||
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.QueryParameterBindings;
|
||||
import org.hibernate.query.sqm.internal.DomainParameterXref;
|
||||
import org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter;
|
||||
import org.hibernate.query.sqm.sql.internal.SqlAstProcessingStateImpl;
|
||||
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.insert.SqmInsertStatement;
|
||||
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.SqlAstHelper;
|
||||
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.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.predicate.Predicate;
|
||||
import org.hibernate.sql.ast.tree.update.Assignable;
|
||||
import org.hibernate.sql.ast.tree.update.Assignment;
|
||||
|
||||
/**
|
||||
* Specialized BaseSqmToSqlAstConverter implementation used during conversion
|
||||
* of an SQM mutation query tree representing into the various SQL AST trees
|
||||
* needed to perform that operation.
|
||||
*
|
||||
* @see #visitSetClause(SqmSetClause, Consumer, SqmParameterResolutionConsumer)
|
||||
* @see #visitWhereClause(SqmWhereClause, Consumer, SqmParameterResolutionConsumer)
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class MultiTableSqmMutationConverter extends BaseSqmToSqlAstConverter<Statement> {
|
||||
public interface SqmParameterResolutionConsumer {
|
||||
void accept(SqmParameter<?> sqmParam, MappingModelExpressible<?> mappingType, List<JdbcParameter> jdbcParameters);
|
||||
}
|
||||
|
||||
private final EntityMappingType mutatingEntityDescriptor;
|
||||
private final TableGroup mutatingTableGroup;
|
||||
private Predicate discriminatorPredicate;
|
||||
|
||||
private SqmParameterResolutionConsumer parameterResolutionConsumer;
|
||||
|
||||
public MultiTableSqmMutationConverter(
|
||||
EntityMappingType mutatingEntityDescriptor,
|
||||
SqmStatement<?> statement,
|
||||
|
@ -119,6 +94,7 @@ public class MultiTableSqmMutationConverter extends BaseSqmToSqlAstConverter<Sta
|
|||
sourceAlias,
|
||||
null,
|
||||
() -> (predicate) -> {
|
||||
assert this.discriminatorPredicate == null;
|
||||
this.discriminatorPredicate = predicate;
|
||||
},
|
||||
this
|
||||
|
@ -146,98 +122,9 @@ public class MultiTableSqmMutationConverter extends BaseSqmToSqlAstConverter<Sta
|
|||
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
|
||||
public Predicate visitWhereClause(SqmWhereClause whereClause) {
|
||||
return (Predicate) super.visitWhereClause( whereClause );
|
||||
}
|
||||
|
||||
@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;
|
||||
return SqlAstHelper.combinePredicates( super.visitWhereClause( whereClause ), discriminatorPredicate );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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.MultiTableSqmMutationConverter;
|
||||
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.expression.SqmExpression;
|
||||
import org.hibernate.query.sqm.tree.expression.SqmParameter;
|
||||
|
@ -131,14 +132,7 @@ public abstract class AbstractCteMutationHandler extends AbstractMutationHandler
|
|||
parameterResolutions = new IdentityHashMap<>();
|
||||
}
|
||||
|
||||
//noinspection rawtypes
|
||||
final Map<SqmParameter, MappingModelExpressible> paramTypeResolutions = new LinkedHashMap<>();
|
||||
|
||||
final Predicate restriction = sqmConverter.visitWhereClause(
|
||||
sqmMutationStatement.getWhereClause(),
|
||||
columnReference -> {},
|
||||
(sqmParam, mappingType, jdbcParameters) -> paramTypeResolutions.put( sqmParam, mappingType )
|
||||
);
|
||||
final Predicate restriction = sqmConverter.visitWhereClause( sqmMutationStatement.getWhereClause() );
|
||||
sqmConverter.pruneTableGroupJoins();
|
||||
|
||||
final CteStatement idSelectCte = new CteStatement(
|
||||
|
@ -193,7 +187,12 @@ public abstract class AbstractCteMutationHandler extends AbstractMutationHandler
|
|||
SqmUtil.generateJdbcParamsXref( domainParameterXref, sqmConverter ),
|
||||
factory.getRuntimeMetamodels().getMappingMetamodel(),
|
||||
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()
|
||||
);
|
||||
final LockOptions lockOptions = executionContext.getQueryOptions().getLockOptions();
|
||||
|
|
|
@ -8,14 +8,15 @@ package org.hibernate.query.sqm.mutation.internal.cte;
|
|||
|
||||
import java.util.AbstractMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.hibernate.boot.model.naming.Identifier;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.dialect.temptable.TemporaryTable;
|
||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
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.sqm.BinaryArithmeticOperator;
|
||||
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.SqmJdbcExecutionContextAdapter;
|
||||
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.sql.BaseSqmToSqlAstConverter;
|
||||
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.SqmParameter;
|
||||
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.SqmInsertStatement;
|
||||
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.QueryLiteral;
|
||||
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.QueryPartTableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.StandardTableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
|
||||
import org.hibernate.sql.ast.tree.from.TableReference;
|
||||
import org.hibernate.sql.ast.tree.from.TableReferenceJoin;
|
||||
import org.hibernate.sql.ast.tree.from.UnionTableReference;
|
||||
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.Values;
|
||||
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.QuerySpec;
|
||||
import org.hibernate.sql.ast.tree.select.SelectStatement;
|
||||
import org.hibernate.sql.ast.tree.select.SortSpecification;
|
||||
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.JdbcParameterBindings;
|
||||
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.spi.ListResultsConsumer;
|
||||
import org.hibernate.generator.Generator;
|
||||
import org.hibernate.type.BasicType;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
/**
|
||||
|
@ -191,12 +207,6 @@ public class CteInsertHandler implements InsertHandler {
|
|||
final int size = sqmStatement.getInsertionTargetPaths().size();
|
||||
final List<Map.Entry<List<CteColumn>, Assignment>> targetPathColumns = 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(
|
||||
(assignable, columnReferences) -> {
|
||||
|
@ -211,7 +221,6 @@ public class CteInsertHandler implements InsertHandler {
|
|||
final int end = offset + pathInterpretation.getExpressionType().getJdbcTypeCount();
|
||||
// Find a matching cte table column and set that at the current index
|
||||
final List<CteColumn> columns = cteTable.getCteColumns().subList( offset, end );
|
||||
insertStatement.addTargetColumnReferences( columnReferences );
|
||||
targetPathCteColumns.addAll( columns );
|
||||
targetPathColumns.add(
|
||||
new AbstractMap.SimpleEntry<>(
|
||||
|
@ -245,17 +254,6 @@ public class CteInsertHandler implements InsertHandler {
|
|||
if ( additionalInsertValues.applySelections( querySpec, sessionFactory ) ) {
|
||||
final CteColumn rowNumberColumn = cteTable.getCteColumns()
|
||||
.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.size() - 1,
|
||||
rowNumberColumn
|
||||
|
@ -326,14 +324,6 @@ public class CteInsertHandler implements InsertHandler {
|
|||
// Add the row number to the assignments
|
||||
final CteColumn rowNumberColumn = cteTable.getCteColumns()
|
||||
.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 );
|
||||
}
|
||||
|
||||
|
@ -743,11 +733,13 @@ public class CteInsertHandler implements InsertHandler {
|
|||
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 String[] rootKeyColumns = persister.getKeyColumns( 0 );
|
||||
final List<CteColumn> keyCteColumns = queryCte.getCteTable().getCteColumns().subList( 0, rootKeyColumns.length );
|
||||
for ( int i = 0; i < tableSpan; i++ ) {
|
||||
final String tableExpression = persister.getTableName( i );
|
||||
for ( int tableIndex = 0; tableIndex < tableSpan; tableIndex++ ) {
|
||||
final String tableExpression = persister.getTableName( tableIndex );
|
||||
final TableReference updatingTableReference = updatingTableGroup.getTableReference(
|
||||
updatingTableGroup.getNavigablePath(),
|
||||
tableExpression,
|
||||
|
@ -758,7 +750,7 @@ public class CteInsertHandler implements InsertHandler {
|
|||
updatingTableReference,
|
||||
tableExpression
|
||||
);
|
||||
final String[] keyColumns = persister.getKeyColumns( i );
|
||||
final String[] keyColumns = persister.getKeyColumns( tableIndex );
|
||||
final List<ColumnReference> returningColumnReferences = new ArrayList<>(
|
||||
keyColumns.length + ( assignmentList == null ? 0 : assignmentList.size() )
|
||||
);
|
||||
|
@ -766,7 +758,7 @@ public class CteInsertHandler implements InsertHandler {
|
|||
final QuerySpec insertSelectSpec = new QuerySpec( true );
|
||||
CteStatement finalCteStatement = null;
|
||||
final CteTable dmlResultCte;
|
||||
if ( i == 0 && !assignsId && identifierGenerator.generatedOnExecution() ) {
|
||||
if ( tableIndex == 0 && !assignsId && identifierGenerator.generatedOnExecution() ) {
|
||||
// Special handling for identity generation
|
||||
final String cteTableName = getCteTableName( tableExpression, "base_" );
|
||||
if ( statement.getCteStatement( cteTableName ) != null ) {
|
||||
|
@ -999,11 +991,57 @@ public class CteInsertHandler implements InsertHandler {
|
|||
}
|
||||
}
|
||||
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 ) {
|
||||
statement.addCteStatement( finalCteStatement );
|
||||
}
|
||||
if ( i == 0 && !assignsId && identifierGenerator.generatedOnExecution() ) {
|
||||
if ( tableIndex == 0 && !assignsId && identifierGenerator.generatedOnExecution() ) {
|
||||
// Special handling for identity generation
|
||||
statement.addCteStatement( queryCte );
|
||||
}
|
||||
|
@ -1011,6 +1049,319 @@ public class CteInsertHandler implements InsertHandler {
|
|||
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(
|
||||
TableReference tableReference,
|
||||
String tableExpression) {
|
||||
|
|
|
@ -98,15 +98,10 @@ public class CteUpdateHandler extends AbstractCteMutationHandler implements Upda
|
|||
// visit the set-clause using our special converter, collecting
|
||||
// information about the assignments
|
||||
final SqmSetClause setClause = updateStatement.getSetClause();
|
||||
final List<Assignment> assignments = new ArrayList<>( setClause.getAssignments().size() );
|
||||
|
||||
sqmConverter.visitSetClause(
|
||||
setClause,
|
||||
assignments::add,
|
||||
(sqmParam, mappingType, jdbcParameters) -> {
|
||||
parameterResolutions.put( sqmParam, jdbcParameters );
|
||||
}
|
||||
);
|
||||
final List<Assignment> assignments = sqmConverter.visitSetClause( setClause );
|
||||
for ( Map.Entry<SqmParameter<?>, List<List<JdbcParameter>>> entry : sqmConverter.getJdbcParamsBySqmParam().entrySet() ) {
|
||||
parameterResolutions.put( entry.getKey(), entry.getValue().get( entry.getValue().size() - 1 ) );
|
||||
}
|
||||
sqmConverter.addVersionedAssignment( assignments::add, updateStatement );
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
|
|
@ -11,16 +11,15 @@ import java.util.List;
|
|||
import java.util.function.Consumer;
|
||||
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.ModelPart;
|
||||
import org.hibernate.metamodel.mapping.SelectableConsumer;
|
||||
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
|
||||
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.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.from.TableReference;
|
||||
import org.hibernate.sql.ast.tree.predicate.InListPredicate;
|
||||
|
@ -45,19 +44,42 @@ import org.hibernate.sql.exec.spi.ExecutionContext;
|
|||
* @author Steve Ebersole
|
||||
*/
|
||||
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
|
||||
public InListPredicate produceRestriction(
|
||||
List<?> matchingIdValues,
|
||||
List<Expression> matchingIdValueExpressions,
|
||||
EntityMappingType entityDescriptor,
|
||||
int valueIndex,
|
||||
ModelPart valueModelPart,
|
||||
TableReference mutatingTableReference,
|
||||
Supplier<Consumer<SelectableConsumer>> columnsToMatchVisitationSupplier,
|
||||
ExecutionContext executionContext) {
|
||||
assert matchingIdValues != null;
|
||||
assert ! matchingIdValues.isEmpty();
|
||||
|
||||
final SessionFactoryImplementor sessionFactory = executionContext.getSession().getFactory();
|
||||
assert matchingIdValueExpressions != null;
|
||||
assert ! matchingIdValueExpressions.isEmpty();
|
||||
|
||||
final EntityIdentifierMapping identifierMapping = entityDescriptor.getIdentifierMapping();
|
||||
final int idColumnCount = identifierMapping.getJdbcTypeCount();
|
||||
|
@ -76,45 +98,27 @@ public class InPredicateRestrictionProducer implements MatchingIdRestrictionProd
|
|||
null,
|
||||
basicIdMapping.getJdbcMapping()
|
||||
);
|
||||
predicate = new InListPredicate( inFixture );
|
||||
|
||||
matchingIdValues.forEach(
|
||||
matchingId -> predicate.addExpression( new JdbcLiteral<>( matchingId, basicIdMapping.getJdbcMapping() ) )
|
||||
);
|
||||
predicate = new InListPredicate( inFixture, matchingIdValueExpressions );
|
||||
}
|
||||
else {
|
||||
final List<ColumnReference> columnReferences = new ArrayList<>( idColumnCount );
|
||||
final List<JdbcMapping> jdbcMappings = new ArrayList<>( idColumnCount );
|
||||
identifierMapping.forEachSelectable(
|
||||
(columnIndex, selection) -> {
|
||||
columnReferences.add(
|
||||
new ColumnReference(
|
||||
mutatingTableReference,
|
||||
selection
|
||||
)
|
||||
);
|
||||
jdbcMappings.add( selection.getJdbcMapping() );
|
||||
}
|
||||
);
|
||||
final SelectableConsumer selectableConsumer = (columnIndex, selection) -> {
|
||||
columnReferences.add(
|
||||
new ColumnReference(
|
||||
mutatingTableReference,
|
||||
selection
|
||||
)
|
||||
);
|
||||
};
|
||||
if ( columnsToMatchVisitationSupplier == null ) {
|
||||
identifierMapping.forEachSelectable( selectableConsumer );
|
||||
}
|
||||
else {
|
||||
columnsToMatchVisitationSupplier.get().accept( selectableConsumer );
|
||||
}
|
||||
|
||||
final Expression inFixture = new SqlTuple( columnReferences, identifierMapping );
|
||||
predicate = new InListPredicate( inFixture );
|
||||
|
||||
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 ) );
|
||||
}
|
||||
);
|
||||
predicate = new InListPredicate( inFixture, matchingIdValueExpressions );
|
||||
}
|
||||
|
||||
return predicate;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue