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) {
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" );
}
}
}

View File

@ -178,7 +178,7 @@ public class H2LegacySqlAstTranslator<T extends JdbcOperation> extends AbstractS
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" );
}
}
}

View File

@ -84,7 +84,7 @@ public class HSQLLegacySqlAstTranslator<T extends JdbcOperation> extends Abstrac
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" );
}
}
}

View File

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

View File

@ -15,8 +15,6 @@ 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.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;
@ -162,7 +160,7 @@ public class MySQLLegacySqlAstTranslator<T extends JdbcOperation> extends Abstra
getSql(),
getParameterBinders(),
getAffectedTableNames(),
null
getUniqueConstraintNameThatMayFail(sqlAst)
);
}

View File

@ -113,7 +113,7 @@ public class OracleLegacySqlAstTranslator<T extends JdbcOperation> extends Abstr
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" );
}
}
}

View File

@ -135,7 +135,7 @@ public class SQLServerLegacySqlAstTranslator<T extends JdbcOperation> extends Ab
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" );
}
}
}

View File

@ -130,7 +130,7 @@ public class SybaseASELegacySqlAstTranslator<T extends JdbcOperation> extends Ab
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" );
}
}
}

View File

@ -87,7 +87,7 @@ public class SybaseLegacySqlAstTranslator<T extends JdbcOperation> extends Abstr
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" );
}
}
}

View File

@ -382,7 +382,7 @@ public class DB2SqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAst
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" );
}
}
}

View File

@ -174,7 +174,7 @@ public class H2SqlAstTranslator<T extends JdbcOperation> extends SqlAstTranslato
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" );
}
}
}

View File

@ -135,7 +135,7 @@ public class HANASqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAs
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" );
}
}
}

View File

@ -80,7 +80,7 @@ public class HSQLSqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAs
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" );
}
}
}

View File

@ -163,7 +163,7 @@ public class MariaDBSqlAstTranslator<T extends JdbcOperation> extends AbstractSq
getSql(),
getParameterBinders(),
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.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;
@ -1200,6 +1201,16 @@ public class MySQLDialect extends Dialect {
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 );

View File

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

View File

@ -112,7 +112,7 @@ public class OracleSqlAstTranslator<T extends JdbcOperation> extends SqlAstTrans
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" );
}
}
}

View File

@ -708,11 +708,12 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
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;
}

View File

@ -133,7 +133,7 @@ public class SQLServerSqlAstTranslator<T extends JdbcOperation> extends SqlAstTr
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" );
}
}
}

View File

@ -115,7 +115,7 @@ public class SybaseASESqlAstTranslator<T extends JdbcOperation> extends Abstract
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" );
}
}
}

View File

@ -86,7 +86,7 @@ public class SybaseSqlAstTranslator<T extends JdbcOperation> extends AbstractSql
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" );
}
}
}

View File

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

View File

@ -846,26 +846,26 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
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) {
@ -2059,7 +2059,12 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
// 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() ) {

View File

@ -5,6 +5,8 @@ import jakarta.persistence.GeneratedValue;
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;
@ -31,6 +33,7 @@ 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()));
@ -38,6 +41,14 @@ public class InsertConflictOnConstraintTest {
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 {