Skip to content

Commit

Permalink
fix constraint name extraction and 'on constraint' on MySQL
Browse files Browse the repository at this point in the history
  • Loading branch information
gavinking committed Mar 4, 2024
1 parent a84ba5c commit 115ddff
Show file tree
Hide file tree
Showing 24 changed files with 64 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ protected void visitInsertStatementOnly(InsertSelectStatement statement) {
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" );
throw new IllegalQueryOperationException( "Insert conflict 'do update' clause with constraint name is not supported" );
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ protected void renderDmlTargetTableExpression(NamedTableReference tableReference
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" );
throw new IllegalQueryOperationException( "Insert conflict 'do update' clause with constraint name is not supported" );
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ protected void renderDmlTargetTableExpression(NamedTableReference tableReference
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" );
throw new IllegalQueryOperationException( "Insert conflict 'do update' clause with constraint name is not supported" );
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ protected JdbcOperationQueryInsert translateInsert(InsertSelectStatement sqlAst)
getSql(),
getParameterBinders(),
getAffectedTableNames(),
null
getUniqueConstraintNameThatMayFail(sqlAst)
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.collections.Stack;
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.spi.AbstractSqlAstTranslator;
import org.hibernate.sql.ast.tree.MutationStatement;
Expand Down Expand Up @@ -162,7 +160,7 @@ protected JdbcOperationQueryInsert translateInsert(InsertSelectStatement sqlAst)
getSql(),
getParameterBinders(),
getAffectedTableNames(),
null
getUniqueConstraintNameThatMayFail(sqlAst)
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ protected void renderDmlTargetTableExpression(NamedTableReference tableReference
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" );
throw new IllegalQueryOperationException( "Insert conflict 'do update' clause with constraint name is not supported" );
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ protected void renderFromClauseAfterUpdateSet(UpdateStatement statement) {
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" );
throw new IllegalQueryOperationException( "Insert conflict 'do update' clause with constraint name is not supported" );
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ protected void renderFromClauseAfterUpdateSet(UpdateStatement statement) {
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" );
throw new IllegalQueryOperationException( "Insert conflict 'do update' clause with constraint name is not supported" );
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ protected void renderFromClauseAfterUpdateSet(UpdateStatement statement) {
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" );
throw new IllegalQueryOperationException( "Insert conflict 'do update' clause with constraint name is not supported" );
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,7 @@ protected void visitInsertStatementOnly(InsertSelectStatement statement) {
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" );
throw new IllegalQueryOperationException( "Insert conflict 'do update' clause with constraint name is not supported" );
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ protected void renderDmlTargetTableExpression(NamedTableReference tableReference
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" );
throw new IllegalQueryOperationException( "Insert conflict 'do update' clause with constraint name is not supported" );
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ protected void renderDmlTargetTableExpression(NamedTableReference tableReference
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" );
throw new IllegalQueryOperationException( "Insert conflict 'do update' clause with constraint name is not supported" );
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ protected void renderDmlTargetTableExpression(NamedTableReference tableReference
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" );
throw new IllegalQueryOperationException( "Insert conflict 'do update' clause with constraint name is not supported" );
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ protected JdbcOperationQueryInsert translateInsert(InsertSelectStatement sqlAst)
getSql(),
getParameterBinders(),
getAffectedTableNames(),
null
getUniqueConstraintNameThatMayFail(sqlAst)
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
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.LockAcquisitionException;
import org.hibernate.exception.LockTimeoutException;
import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
Expand Down Expand Up @@ -1200,6 +1201,16 @@ public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() {
case 1207:
case 1206:
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 );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ protected JdbcOperationQueryInsert translateInsert(InsertSelectStatement sqlAst)
getSql(),
getParameterBinders(),
getAffectedTableNames(),
null
getUniqueConstraintNameThatMayFail(sqlAst)
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ protected void renderDmlTargetTableExpression(NamedTableReference tableReference
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" );
throw new IllegalQueryOperationException( "Insert conflict 'do update' clause with constraint name is not supported" );
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -708,11 +708,12 @@ public ViolatedConstraintNameExtractor getViolatedConstraintNameExtractor() {
case 2627:
case 2601:
String message = sqle.getMessage();
int i = message.indexOf("unique index ");
if (i>0) {
message = message.substring(i);
if ( message.contains("unique index ") ) {
return extractUsingTemplate( "unique index '", "'", message);
}
else {
return extractUsingTemplate( "'", "'", message);
}
return extractUsingTemplate( "'", "'", message);
default:
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ protected void renderFromClauseAfterUpdateSet(UpdateStatement statement) {
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" );
throw new IllegalQueryOperationException( "Insert conflict 'do update' clause with constraint name is not supported" );
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ protected void renderFromClauseAfterUpdateSet(UpdateStatement statement) {
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" );
throw new IllegalQueryOperationException( "Insert conflict 'do update' clause with constraint name is not supported" );
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ protected void renderFromClauseAfterUpdateSet(UpdateStatement statement) {
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" );
throw new IllegalQueryOperationException( "Insert conflict 'do update' clause with constraint name is not supported" );
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ protected JdbcOperationQueryInsert translateInsert(InsertSelectStatement sqlAst)
getSql(),
getParameterBinders(),
getAffectedTableNames(),
null
getUniqueConstraintNameThatMayFail(sqlAst)
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -846,26 +846,26 @@ protected JdbcOperationQueryUpdate translateUpdate(UpdateStatement sqlAst) {
protected JdbcOperationQueryInsert translateInsert(InsertSelectStatement sqlAst) {
visitInsertStatement( sqlAst );

return new JdbcOperationQueryInsertImpl(
getSql(),
getParameterBinders(),
getAffectedTableNames(),
getUniqueConstraintNameThatMayFail(sqlAst)
);
}

protected String getUniqueConstraintNameThatMayFail(InsertSelectStatement sqlAst) {
final ConflictClause conflictClause = sqlAst.getConflictClause();
final String uniqueConstraintNameThatMayFail;
if ( conflictClause == null || !conflictClause.getConstraintColumnNames().isEmpty() ) {
uniqueConstraintNameThatMayFail = null;
return null;
}
else {
if ( sqlAst.getSourceSelectStatement() != null && !isFetchFirstRowOnly( sqlAst.getSourceSelectStatement() )
|| sqlAst.getValuesList().size() > 1 ) {
throw new IllegalQueryOperationException( "Can't emulate conflict clause with constraint name for more than one row to insert" );
}
uniqueConstraintNameThatMayFail = conflictClause.getConstraintName() == null
? ""
: conflictClause.getConstraintName();
return conflictClause.getConstraintName() == null ? "" : conflictClause.getConstraintName();
}
return new JdbcOperationQueryInsertImpl(
getSql(),
getParameterBinders(),
getAffectedTableNames(),
uniqueConstraintNameThatMayFail
);
}

protected JdbcOperationQuerySelect translateSelect(SelectStatement selectStatement) {
Expand Down Expand Up @@ -2059,7 +2059,12 @@ protected void visitOnDuplicateKeyConflictClause(ConflictClause conflictClause)
// propagated, but instead will run the respective conflict action.
final String constraintName = conflictClause.getConstraintName();
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();
// if ( !constraintColumnNames.isEmpty() ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.persistence.UniqueConstraint;
import org.hibernate.dialect.H2Dialect;
import org.hibernate.dialect.MySQLDialect;
import org.hibernate.dialect.OracleDialect;
import org.hibernate.dialect.PostgreSQLDialect;
import org.hibernate.dialect.SQLServerDialect;
Expand All @@ -31,13 +33,22 @@ public class InsertConflictOnConstraintTest {
@RequiresDialect( PostgreSQLDialect.class )
@RequiresDialect( OracleDialect.class )
@RequiresDialect( SQLServerDialect.class )
@RequiresDialect( MySQLDialect.class )
@Test void testDoNothing(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 do nothing").executeUpdate());
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")
@Table(uniqueConstraints = @UniqueConstraint(name = "count_name_key", columnNames = {"count","name"}))
static class Constrained {
Expand Down

1 comment on commit 115ddff

@gavinking
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.