fix constraint name extraction and 'on constraint' on MySQL

This commit is contained in:
Gavin King 2024-03-04 20:20:03 +01:00
parent a84ba5c8c9
commit 115ddffdbc
24 changed files with 64 additions and 38 deletions

View File

@ -383,7 +383,7 @@ public class DB2LegacySqlAstTranslator<T extends JdbcOperation> extends Abstract
protected void visitConflictClause(ConflictClause conflictClause) { protected void visitConflictClause(ConflictClause conflictClause) {
if ( conflictClause != null ) { if ( conflictClause != null ) {
if ( conflictClause.isDoUpdate() && conflictClause.getConstraintName() != null ) { if ( conflictClause.isDoUpdate() && conflictClause.getConstraintName() != null ) {
throw new IllegalQueryOperationException( "Insert conflict do update clause with constraint name is not supported" ); throw new IllegalQueryOperationException( "Insert conflict 'do update' clause with constraint name is not supported" );
} }
} }
} }

View File

@ -178,7 +178,7 @@ public class H2LegacySqlAstTranslator<T extends JdbcOperation> extends AbstractS
protected void visitConflictClause(ConflictClause conflictClause) { protected void visitConflictClause(ConflictClause conflictClause) {
if ( conflictClause != null ) { if ( conflictClause != null ) {
if ( conflictClause.isDoUpdate() && conflictClause.getConstraintName() != null ) { if ( conflictClause.isDoUpdate() && conflictClause.getConstraintName() != null ) {
throw new IllegalQueryOperationException( "Insert conflict do update clause with constraint name is not supported" ); throw new IllegalQueryOperationException( "Insert conflict 'do update' clause with constraint name is not supported" );
} }
} }
} }

View File

@ -84,7 +84,7 @@ public class HSQLLegacySqlAstTranslator<T extends JdbcOperation> extends Abstrac
protected void visitConflictClause(ConflictClause conflictClause) { protected void visitConflictClause(ConflictClause conflictClause) {
if ( conflictClause != null ) { if ( conflictClause != null ) {
if ( conflictClause.isDoUpdate() && conflictClause.getConstraintName() != null ) { if ( conflictClause.isDoUpdate() && conflictClause.getConstraintName() != null ) {
throw new IllegalQueryOperationException( "Insert conflict do update clause with constraint name is not supported" ); throw new IllegalQueryOperationException( "Insert conflict 'do update' clause with constraint name is not supported" );
} }
} }
} }

View File

@ -150,7 +150,7 @@ public class MariaDBLegacySqlAstTranslator<T extends JdbcOperation> extends Abst
getSql(), getSql(),
getParameterBinders(), getParameterBinders(),
getAffectedTableNames(), getAffectedTableNames(),
null getUniqueConstraintNameThatMayFail(sqlAst)
); );
} }

View File

@ -15,8 +15,6 @@ import org.hibernate.dialect.MySQLSqlAstTranslator;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.collections.Stack; import org.hibernate.internal.util.collections.Stack;
import org.hibernate.query.sqm.ComparisonOperator; import org.hibernate.query.sqm.ComparisonOperator;
import org.hibernate.query.sqm.function.AbstractSqmSelfRenderingFunctionDescriptor;
import org.hibernate.query.sqm.function.SelfRenderingFunctionSqlAstExpression;
import org.hibernate.sql.ast.Clause; import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
import org.hibernate.sql.ast.tree.MutationStatement; import org.hibernate.sql.ast.tree.MutationStatement;
@ -162,7 +160,7 @@ public class MySQLLegacySqlAstTranslator<T extends JdbcOperation> extends Abstra
getSql(), getSql(),
getParameterBinders(), getParameterBinders(),
getAffectedTableNames(), getAffectedTableNames(),
null getUniqueConstraintNameThatMayFail(sqlAst)
); );
} }

View File

@ -113,7 +113,7 @@ public class OracleLegacySqlAstTranslator<T extends JdbcOperation> extends Abstr
protected void visitConflictClause(ConflictClause conflictClause) { protected void visitConflictClause(ConflictClause conflictClause) {
if ( conflictClause != null ) { if ( conflictClause != null ) {
if ( conflictClause.isDoUpdate() && conflictClause.getConstraintName() != null ) { if ( conflictClause.isDoUpdate() && conflictClause.getConstraintName() != null ) {
throw new IllegalQueryOperationException( "Insert conflict do update clause with constraint name is not supported" ); throw new IllegalQueryOperationException( "Insert conflict 'do update' clause with constraint name is not supported" );
} }
} }
} }

View File

@ -135,7 +135,7 @@ public class SQLServerLegacySqlAstTranslator<T extends JdbcOperation> extends Ab
protected void visitConflictClause(ConflictClause conflictClause) { protected void visitConflictClause(ConflictClause conflictClause) {
if ( conflictClause != null ) { if ( conflictClause != null ) {
if ( conflictClause.isDoUpdate() && conflictClause.getConstraintName() != null ) { if ( conflictClause.isDoUpdate() && conflictClause.getConstraintName() != null ) {
throw new IllegalQueryOperationException( "Insert conflict do update clause with constraint name is not supported" ); throw new IllegalQueryOperationException( "Insert conflict 'do update' clause with constraint name is not supported" );
} }
} }
} }

View File

@ -130,7 +130,7 @@ public class SybaseASELegacySqlAstTranslator<T extends JdbcOperation> extends Ab
protected void visitConflictClause(ConflictClause conflictClause) { protected void visitConflictClause(ConflictClause conflictClause) {
if ( conflictClause != null ) { if ( conflictClause != null ) {
if ( conflictClause.isDoUpdate() && conflictClause.getConstraintName() != null ) { if ( conflictClause.isDoUpdate() && conflictClause.getConstraintName() != null ) {
throw new IllegalQueryOperationException( "Insert conflict do update clause with constraint name is not supported" ); throw new IllegalQueryOperationException( "Insert conflict 'do update' clause with constraint name is not supported" );
} }
} }
} }

View File

@ -87,7 +87,7 @@ public class SybaseLegacySqlAstTranslator<T extends JdbcOperation> extends Abstr
protected void visitConflictClause(ConflictClause conflictClause) { protected void visitConflictClause(ConflictClause conflictClause) {
if ( conflictClause != null ) { if ( conflictClause != null ) {
if ( conflictClause.isDoUpdate() && conflictClause.getConstraintName() != null ) { if ( conflictClause.isDoUpdate() && conflictClause.getConstraintName() != null ) {
throw new IllegalQueryOperationException( "Insert conflict do update clause with constraint name is not supported" ); throw new IllegalQueryOperationException( "Insert conflict 'do update' clause with constraint name is not supported" );
} }
} }
} }

View File

@ -382,7 +382,7 @@ public class DB2SqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAst
protected void visitConflictClause(ConflictClause conflictClause) { protected void visitConflictClause(ConflictClause conflictClause) {
if ( conflictClause != null ) { if ( conflictClause != null ) {
if ( conflictClause.isDoUpdate() && conflictClause.getConstraintName() != null ) { if ( conflictClause.isDoUpdate() && conflictClause.getConstraintName() != null ) {
throw new IllegalQueryOperationException( "Insert conflict do update clause with constraint name is not supported" ); throw new IllegalQueryOperationException( "Insert conflict 'do update' clause with constraint name is not supported" );
} }
} }
} }

View File

@ -174,7 +174,7 @@ public class H2SqlAstTranslator<T extends JdbcOperation> extends SqlAstTranslato
protected void visitConflictClause(ConflictClause conflictClause) { protected void visitConflictClause(ConflictClause conflictClause) {
if ( conflictClause != null ) { if ( conflictClause != null ) {
if ( conflictClause.isDoUpdate() && conflictClause.getConstraintName() != null ) { if ( conflictClause.isDoUpdate() && conflictClause.getConstraintName() != null ) {
throw new IllegalQueryOperationException( "Insert conflict do update clause with constraint name is not supported" ); throw new IllegalQueryOperationException( "Insert conflict 'do update' clause with constraint name is not supported" );
} }
} }
} }

View File

@ -135,7 +135,7 @@ public class HANASqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAs
protected void visitConflictClause(ConflictClause conflictClause) { protected void visitConflictClause(ConflictClause conflictClause) {
if ( conflictClause != null ) { if ( conflictClause != null ) {
if ( conflictClause.isDoUpdate() && conflictClause.getConstraintName() != null ) { if ( conflictClause.isDoUpdate() && conflictClause.getConstraintName() != null ) {
throw new IllegalQueryOperationException( "Insert conflict do update clause with constraint name is not supported" ); throw new IllegalQueryOperationException( "Insert conflict 'do update' clause with constraint name is not supported" );
} }
} }
} }

View File

@ -80,7 +80,7 @@ public class HSQLSqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAs
protected void visitConflictClause(ConflictClause conflictClause) { protected void visitConflictClause(ConflictClause conflictClause) {
if ( conflictClause != null ) { if ( conflictClause != null ) {
if ( conflictClause.isDoUpdate() && conflictClause.getConstraintName() != null ) { if ( conflictClause.isDoUpdate() && conflictClause.getConstraintName() != null ) {
throw new IllegalQueryOperationException( "Insert conflict do update clause with constraint name is not supported" ); throw new IllegalQueryOperationException( "Insert conflict 'do update' clause with constraint name is not supported" );
} }
} }
} }

View File

@ -163,7 +163,7 @@ public class MariaDBSqlAstTranslator<T extends JdbcOperation> extends AbstractSq
getSql(), getSql(),
getParameterBinders(), getParameterBinders(),
getAffectedTableNames(), getAffectedTableNames(),
null getUniqueConstraintNameThatMayFail(sqlAst)
); );
} }

View File

@ -39,6 +39,7 @@ import org.hibernate.engine.jdbc.env.spi.IdentifierHelper;
import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder; import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder;
import org.hibernate.engine.jdbc.env.spi.NameQualifierSupport; import org.hibernate.engine.jdbc.env.spi.NameQualifierSupport;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.exception.ConstraintViolationException;
import org.hibernate.exception.LockAcquisitionException; import org.hibernate.exception.LockAcquisitionException;
import org.hibernate.exception.LockTimeoutException; import org.hibernate.exception.LockTimeoutException;
import org.hibernate.exception.spi.SQLExceptionConversionDelegate; import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
@ -1200,6 +1201,16 @@ public class MySQLDialect extends Dialect {
case 1207: case 1207:
case 1206: case 1206:
return new LockAcquisitionException( message, sqlException, sql ); return new LockAcquisitionException( message, sqlException, sql );
case 1062:
// Unique constraint violation
String constraintName = getViolatedConstraintNameExtractor().extractConstraintName(sqlException);
return new ConstraintViolationException(
message,
sqlException,
sql,
ConstraintViolationException.ConstraintKind.UNIQUE,
constraintName
);
} }
final String sqlState = JdbcExceptionHelper.extractSqlState( sqlException ); final String sqlState = JdbcExceptionHelper.extractSqlState( sqlException );

View File

@ -224,7 +224,7 @@ public class MySQLSqlAstTranslator<T extends JdbcOperation> extends AbstractSqlA
getSql(), getSql(),
getParameterBinders(), getParameterBinders(),
getAffectedTableNames(), getAffectedTableNames(),
null getUniqueConstraintNameThatMayFail(sqlAst)
); );
} }

View File

@ -112,7 +112,7 @@ public class OracleSqlAstTranslator<T extends JdbcOperation> extends SqlAstTrans
protected void visitConflictClause(ConflictClause conflictClause) { protected void visitConflictClause(ConflictClause conflictClause) {
if ( conflictClause != null ) { if ( conflictClause != null ) {
if ( conflictClause.isDoUpdate() && conflictClause.getConstraintName() != null ) { if ( conflictClause.isDoUpdate() && conflictClause.getConstraintName() != null ) {
throw new IllegalQueryOperationException( "Insert conflict do update clause with constraint name is not supported" ); throw new IllegalQueryOperationException( "Insert conflict 'do update' clause with constraint name is not supported" );
} }
} }
} }

View File

@ -708,11 +708,12 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
case 2627: case 2627:
case 2601: case 2601:
String message = sqle.getMessage(); String message = sqle.getMessage();
int i = message.indexOf("unique index "); if ( message.contains("unique index ") ) {
if (i>0) { return extractUsingTemplate( "unique index '", "'", message);
message = message.substring(i); }
else {
return extractUsingTemplate( "'", "'", message);
} }
return extractUsingTemplate( "'", "'", message);
default: default:
return null; return null;
} }

View File

@ -133,7 +133,7 @@ public class SQLServerSqlAstTranslator<T extends JdbcOperation> extends SqlAstTr
protected void visitConflictClause(ConflictClause conflictClause) { protected void visitConflictClause(ConflictClause conflictClause) {
if ( conflictClause != null ) { if ( conflictClause != null ) {
if ( conflictClause.isDoUpdate() && conflictClause.getConstraintName() != null ) { if ( conflictClause.isDoUpdate() && conflictClause.getConstraintName() != null ) {
throw new IllegalQueryOperationException( "Insert conflict do update clause with constraint name is not supported" ); throw new IllegalQueryOperationException( "Insert conflict 'do update' clause with constraint name is not supported" );
} }
} }
} }

View File

@ -115,7 +115,7 @@ public class SybaseASESqlAstTranslator<T extends JdbcOperation> extends Abstract
protected void visitConflictClause(ConflictClause conflictClause) { protected void visitConflictClause(ConflictClause conflictClause) {
if ( conflictClause != null ) { if ( conflictClause != null ) {
if ( conflictClause.isDoUpdate() && conflictClause.getConstraintName() != null ) { if ( conflictClause.isDoUpdate() && conflictClause.getConstraintName() != null ) {
throw new IllegalQueryOperationException( "Insert conflict do update clause with constraint name is not supported" ); throw new IllegalQueryOperationException( "Insert conflict 'do update' clause with constraint name is not supported" );
} }
} }
} }

View File

@ -86,7 +86,7 @@ public class SybaseSqlAstTranslator<T extends JdbcOperation> extends AbstractSql
protected void visitConflictClause(ConflictClause conflictClause) { protected void visitConflictClause(ConflictClause conflictClause) {
if ( conflictClause != null ) { if ( conflictClause != null ) {
if ( conflictClause.isDoUpdate() && conflictClause.getConstraintName() != null ) { if ( conflictClause.isDoUpdate() && conflictClause.getConstraintName() != null ) {
throw new IllegalQueryOperationException( "Insert conflict do update clause with constraint name is not supported" ); throw new IllegalQueryOperationException( "Insert conflict 'do update' clause with constraint name is not supported" );
} }
} }
} }

View File

@ -166,7 +166,7 @@ public class TiDBSqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAs
getSql(), getSql(),
getParameterBinders(), getParameterBinders(),
getAffectedTableNames(), getAffectedTableNames(),
null getUniqueConstraintNameThatMayFail(sqlAst)
); );
} }

View File

@ -846,26 +846,26 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
protected JdbcOperationQueryInsert translateInsert(InsertSelectStatement sqlAst) { protected JdbcOperationQueryInsert translateInsert(InsertSelectStatement sqlAst) {
visitInsertStatement( sqlAst ); visitInsertStatement( sqlAst );
return new JdbcOperationQueryInsertImpl(
getSql(),
getParameterBinders(),
getAffectedTableNames(),
getUniqueConstraintNameThatMayFail(sqlAst)
);
}
protected String getUniqueConstraintNameThatMayFail(InsertSelectStatement sqlAst) {
final ConflictClause conflictClause = sqlAst.getConflictClause(); final ConflictClause conflictClause = sqlAst.getConflictClause();
final String uniqueConstraintNameThatMayFail;
if ( conflictClause == null || !conflictClause.getConstraintColumnNames().isEmpty() ) { if ( conflictClause == null || !conflictClause.getConstraintColumnNames().isEmpty() ) {
uniqueConstraintNameThatMayFail = null; return null;
} }
else { else {
if ( sqlAst.getSourceSelectStatement() != null && !isFetchFirstRowOnly( sqlAst.getSourceSelectStatement() ) if ( sqlAst.getSourceSelectStatement() != null && !isFetchFirstRowOnly( sqlAst.getSourceSelectStatement() )
|| sqlAst.getValuesList().size() > 1 ) { || sqlAst.getValuesList().size() > 1 ) {
throw new IllegalQueryOperationException( "Can't emulate conflict clause with constraint name for more than one row to insert" ); throw new IllegalQueryOperationException( "Can't emulate conflict clause with constraint name for more than one row to insert" );
} }
uniqueConstraintNameThatMayFail = conflictClause.getConstraintName() == null return conflictClause.getConstraintName() == null ? "" : conflictClause.getConstraintName();
? ""
: conflictClause.getConstraintName();
} }
return new JdbcOperationQueryInsertImpl(
getSql(),
getParameterBinders(),
getAffectedTableNames(),
uniqueConstraintNameThatMayFail
);
} }
protected JdbcOperationQuerySelect translateSelect(SelectStatement selectStatement) { protected JdbcOperationQuerySelect translateSelect(SelectStatement selectStatement) {
@ -2059,7 +2059,12 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
// propagated, but instead will run the respective conflict action. // propagated, but instead will run the respective conflict action.
final String constraintName = conflictClause.getConstraintName(); final String constraintName = conflictClause.getConstraintName();
if ( constraintName != null ) { if ( constraintName != null ) {
throw new IllegalQueryOperationException( "Dialect does not support constraint name in conflict clause" ); if ( conflictClause.isDoUpdate() ) {
throw new IllegalQueryOperationException( "Insert conflict 'do update' clause with constraint name is not supported" );
}
else {
return;
}
} }
// final List<String> constraintColumnNames = conflictClause.getConstraintColumnNames(); // final List<String> constraintColumnNames = conflictClause.getConstraintColumnNames();
// if ( !constraintColumnNames.isEmpty() ) { // if ( !constraintColumnNames.isEmpty() ) {

View File

@ -5,6 +5,8 @@ import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id; import jakarta.persistence.Id;
import jakarta.persistence.Table; import jakarta.persistence.Table;
import jakarta.persistence.UniqueConstraint; import jakarta.persistence.UniqueConstraint;
import org.hibernate.dialect.H2Dialect;
import org.hibernate.dialect.MySQLDialect;
import org.hibernate.dialect.OracleDialect; import org.hibernate.dialect.OracleDialect;
import org.hibernate.dialect.PostgreSQLDialect; import org.hibernate.dialect.PostgreSQLDialect;
import org.hibernate.dialect.SQLServerDialect; import org.hibernate.dialect.SQLServerDialect;
@ -31,6 +33,7 @@ public class InsertConflictOnConstraintTest {
@RequiresDialect( PostgreSQLDialect.class ) @RequiresDialect( PostgreSQLDialect.class )
@RequiresDialect( OracleDialect.class ) @RequiresDialect( OracleDialect.class )
@RequiresDialect( SQLServerDialect.class ) @RequiresDialect( SQLServerDialect.class )
@RequiresDialect( MySQLDialect.class )
@Test void testDoNothing(SessionFactoryScope scope) { @Test void testDoNothing(SessionFactoryScope scope) {
scope.getSessionFactory().getSchemaManager().truncateMappedObjects(); scope.getSessionFactory().getSchemaManager().truncateMappedObjects();
scope.inTransaction( s -> s.persist(new Constrained())); scope.inTransaction( s -> s.persist(new Constrained()));
@ -38,6 +41,14 @@ public class InsertConflictOnConstraintTest {
scope.inSession( s -> assertEquals(69, s.createSelectionQuery("select count from Constrained", int.class).getSingleResult())); scope.inSession( s -> assertEquals(69, s.createSelectionQuery("select count from Constrained", int.class).getSingleResult()));
} }
@RequiresDialect( H2Dialect.class )
@Test void testDoNothing2(SessionFactoryScope scope) {
scope.getSessionFactory().getSchemaManager().truncateMappedObjects();
scope.inTransaction( s -> s.persist(new Constrained()));
scope.inTransaction( s -> s.createMutationQuery("insert into Constrained(id, name, count) values (4,'Gavin',69) on conflict on constraint count_name_key_index_2 do nothing").executeUpdate());
scope.inSession( s -> assertEquals(69, s.createSelectionQuery("select count from Constrained", int.class).getSingleResult()));
}
@Entity(name = "Constrained") @Entity(name = "Constrained")
@Table(uniqueConstraints = @UniqueConstraint(name = "count_name_key", columnNames = {"count","name"})) @Table(uniqueConstraints = @UniqueConstraint(name = "count_name_key", columnNames = {"count","name"}))
static class Constrained { static class Constrained {