fix some constraint name extractors, and improve matching of constraint name in 'on conflict on constraint'
add a test
This commit is contained in:
parent
7aba13ee47
commit
a84ba5c8c9
|
@ -709,13 +709,16 @@ public class H2Dialect extends Dialect {
|
|||
// 23001: Unique index or primary key violation: {0}
|
||||
if ( sqle.getSQLState().startsWith( "23" ) ) {
|
||||
final String message = sqle.getMessage();
|
||||
final int idx = message.indexOf( "violation: " );
|
||||
if ( idx > 0 ) {
|
||||
String constraintName = message.substring( idx + "violation: ".length() );
|
||||
final int i = message.indexOf( "violation: " );
|
||||
if ( i > 0 ) {
|
||||
String constraintDescription =
|
||||
message.substring( i + "violation: ".length() )
|
||||
.replace( "\"", "" );
|
||||
if ( sqle.getSQLState().equals( "23506" ) ) {
|
||||
constraintName = constraintName.substring( 1, constraintName.indexOf( ':' ) );
|
||||
constraintDescription = constraintDescription.substring( 1, constraintDescription.indexOf( ':' ) );
|
||||
}
|
||||
return constraintName;
|
||||
final int j = constraintDescription.indexOf(" ON ");
|
||||
return j>0 ? constraintDescription.substring(0, j) : constraintDescription;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
|
|
@ -707,7 +707,12 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
|
|||
switch ( JdbcExceptionHelper.extractErrorCode( sqle ) ) {
|
||||
case 2627:
|
||||
case 2601:
|
||||
return extractUsingTemplate( "'", "'", sqle.getMessage() );
|
||||
String message = sqle.getMessage();
|
||||
int i = message.indexOf("unique index ");
|
||||
if (i>0) {
|
||||
message = message.substring(i);
|
||||
}
|
||||
return extractUsingTemplate( "'", "'", message);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
@ -728,18 +733,12 @@ 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,
|
||||
sqlException,
|
||||
sql,
|
||||
ConstraintViolationException.ConstraintKind.UNIQUE,
|
||||
getViolatedConstraintNameExtractor().extractConstraintName( sqlException )
|
||||
);
|
||||
default:
|
||||
|
|
|
@ -8,7 +8,6 @@ package org.hibernate.sql.exec.internal;
|
|||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.SQLIntegrityConstraintViolationException;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
|
@ -109,10 +108,11 @@ public class StandardJdbcMutationExecutor implements JdbcMutationExecutor {
|
|||
if ( exception instanceof ConstraintViolationException && jdbcMutation instanceof JdbcOperationQueryInsert ) {
|
||||
final ConstraintViolationException constraintViolationException = (ConstraintViolationException) exception;
|
||||
if ( constraintViolationException.getKind() == ConstraintViolationException.ConstraintKind.UNIQUE ) {
|
||||
final String uniqueConstraintNameThatMayFail = ( (JdbcOperationQueryInsert) jdbcMutation ).getUniqueConstraintNameThatMayFail();
|
||||
final JdbcOperationQueryInsert jdbcInsert = (JdbcOperationQueryInsert) jdbcMutation;
|
||||
final String uniqueConstraintNameThatMayFail = jdbcInsert.getUniqueConstraintNameThatMayFail();
|
||||
if ( uniqueConstraintNameThatMayFail != null ) {
|
||||
if ( uniqueConstraintNameThatMayFail.isEmpty()
|
||||
|| uniqueConstraintNameThatMayFail.equalsIgnoreCase( constraintViolationException.getConstraintName() ) ) {
|
||||
final String violatedConstraintName = constraintViolationException.getConstraintName();
|
||||
if ( constraintNameMatches( uniqueConstraintNameThatMayFail, violatedConstraintName ) ) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -124,4 +124,11 @@ public class StandardJdbcMutationExecutor implements JdbcMutationExecutor {
|
|||
executionContext.afterStatement( logicalConnection );
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean constraintNameMatches(String uniqueConstraintNameThatMayFail, String violatedConstraintName) {
|
||||
return uniqueConstraintNameThatMayFail.isEmpty()
|
||||
|| uniqueConstraintNameThatMayFail.equalsIgnoreCase(violatedConstraintName)
|
||||
|| violatedConstraintName != null && violatedConstraintName.indexOf('.') > 0
|
||||
&& uniqueConstraintNameThatMayFail.equalsIgnoreCase(violatedConstraintName.substring(violatedConstraintName.lastIndexOf('.') + 1));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,9 @@ import jakarta.persistence.GeneratedValue;
|
|||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
import jakarta.persistence.UniqueConstraint;
|
||||
import org.hibernate.dialect.OracleDialect;
|
||||
import org.hibernate.dialect.PostgreSQLDialect;
|
||||
import org.hibernate.dialect.SQLServerDialect;
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.RequiresDialect;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
|
@ -16,17 +18,28 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
|||
|
||||
@SessionFactory
|
||||
@DomainModel(annotatedClasses = InsertConflictOnConstraintTest.Constrained.class)
|
||||
@RequiresDialect(PostgreSQLDialect.class)
|
||||
public class InsertConflictOnConstraintTest {
|
||||
@Test void test(SessionFactoryScope scope) {
|
||||
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 constrained_count_name_key do update set count = 96").executeUpdate());
|
||||
scope.inSession( s -> assertEquals(96, s.createSelectionQuery("select count from Constrained", int.class).getSingleResult()));
|
||||
|
||||
@RequiresDialect(PostgreSQLDialect.class)
|
||||
@Test void testDoUpdate(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 update set count = 96").executeUpdate());
|
||||
scope.inSession( s -> assertEquals(96, s.createSelectionQuery("select count from Constrained", int.class).getSingleResult()));
|
||||
}
|
||||
|
||||
@RequiresDialect( PostgreSQLDialect.class )
|
||||
@RequiresDialect( OracleDialect.class )
|
||||
@RequiresDialect( SQLServerDialect.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()));
|
||||
}
|
||||
|
||||
@Entity(name = "Constrained")
|
||||
@Table(uniqueConstraints = @UniqueConstraint(columnNames = {"count","name"}))
|
||||
@Table(uniqueConstraints = @UniqueConstraint(name = "count_name_key", columnNames = {"count","name"}))
|
||||
static class Constrained {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
|
|
Loading…
Reference in New Issue