HHH-13661 | Added case to PostgreSQL Dialect to map client operation cancellation to QueryTimeoutException

This commit is contained in:
ENTERPRISE-X64\bertiepinnock 2021-09-11 00:08:55 +01:00 committed by Christian Beikov
parent ad6af3af7d
commit 24b9605c52
2 changed files with 110 additions and 71 deletions

View File

@ -18,6 +18,7 @@ import org.hibernate.JDBCException;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.PessimisticLockException;
import org.hibernate.QueryTimeoutException;
import org.hibernate.cfg.Environment;
import org.hibernate.dialect.function.NoArgSQLFunction;
import org.hibernate.dialect.function.PositionSubstringFunction;
@ -66,7 +67,7 @@ public class PostgreSQL81Dialect extends Dialect {
@Override
public String processSql(String sql, RowSelection selection) {
final boolean hasOffset = LimitHelper.hasFirstRow( selection );
return sql + (hasOffset ? " limit ? offset ?" : " limit ?");
return sql + ( hasOffset ? " limit ? offset ?" : " limit ?" );
}
@Override
@ -118,75 +119,84 @@ public class PostgreSQL81Dialect extends Dialect {
registerColumnType( Types.NUMERIC, "numeric($p, $s)" );
registerColumnType( Types.OTHER, "uuid" );
registerFunction( "abs", new StandardSQLFunction("abs") );
registerFunction( "sign", new StandardSQLFunction("sign", StandardBasicTypes.INTEGER) );
registerFunction( "abs", new StandardSQLFunction( "abs" ) );
registerFunction( "sign", new StandardSQLFunction( "sign", StandardBasicTypes.INTEGER ) );
registerFunction( "acos", new StandardSQLFunction("acos", StandardBasicTypes.DOUBLE) );
registerFunction( "asin", new StandardSQLFunction("asin", StandardBasicTypes.DOUBLE) );
registerFunction( "atan", new StandardSQLFunction("atan", StandardBasicTypes.DOUBLE) );
registerFunction( "cos", new StandardSQLFunction("cos", StandardBasicTypes.DOUBLE) );
registerFunction( "cot", new StandardSQLFunction("cot", StandardBasicTypes.DOUBLE) );
registerFunction( "exp", new StandardSQLFunction("exp", StandardBasicTypes.DOUBLE) );
registerFunction( "ln", new StandardSQLFunction("ln", StandardBasicTypes.DOUBLE) );
registerFunction( "log", new StandardSQLFunction("log", StandardBasicTypes.DOUBLE) );
registerFunction( "sin", new StandardSQLFunction("sin", StandardBasicTypes.DOUBLE) );
registerFunction( "sqrt", new StandardSQLFunction("sqrt", StandardBasicTypes.DOUBLE) );
registerFunction( "cbrt", new StandardSQLFunction("cbrt", StandardBasicTypes.DOUBLE) );
registerFunction( "tan", new StandardSQLFunction("tan", StandardBasicTypes.DOUBLE) );
registerFunction( "radians", new StandardSQLFunction("radians", StandardBasicTypes.DOUBLE) );
registerFunction( "degrees", new StandardSQLFunction("degrees", StandardBasicTypes.DOUBLE) );
registerFunction( "acos", new StandardSQLFunction( "acos", StandardBasicTypes.DOUBLE ) );
registerFunction( "asin", new StandardSQLFunction( "asin", StandardBasicTypes.DOUBLE ) );
registerFunction( "atan", new StandardSQLFunction( "atan", StandardBasicTypes.DOUBLE ) );
registerFunction( "cos", new StandardSQLFunction( "cos", StandardBasicTypes.DOUBLE ) );
registerFunction( "cot", new StandardSQLFunction( "cot", StandardBasicTypes.DOUBLE ) );
registerFunction( "exp", new StandardSQLFunction( "exp", StandardBasicTypes.DOUBLE ) );
registerFunction( "ln", new StandardSQLFunction( "ln", StandardBasicTypes.DOUBLE ) );
registerFunction( "log", new StandardSQLFunction( "log", StandardBasicTypes.DOUBLE ) );
registerFunction( "sin", new StandardSQLFunction( "sin", StandardBasicTypes.DOUBLE ) );
registerFunction( "sqrt", new StandardSQLFunction( "sqrt", StandardBasicTypes.DOUBLE ) );
registerFunction( "cbrt", new StandardSQLFunction( "cbrt", StandardBasicTypes.DOUBLE ) );
registerFunction( "tan", new StandardSQLFunction( "tan", StandardBasicTypes.DOUBLE ) );
registerFunction( "radians", new StandardSQLFunction( "radians", StandardBasicTypes.DOUBLE ) );
registerFunction( "degrees", new StandardSQLFunction( "degrees", StandardBasicTypes.DOUBLE ) );
registerFunction( "stddev", new StandardSQLFunction("stddev", StandardBasicTypes.DOUBLE) );
registerFunction( "variance", new StandardSQLFunction("variance", StandardBasicTypes.DOUBLE) );
registerFunction( "stddev", new StandardSQLFunction( "stddev", StandardBasicTypes.DOUBLE ) );
registerFunction( "variance", new StandardSQLFunction( "variance", StandardBasicTypes.DOUBLE ) );
registerFunction( "random", new NoArgSQLFunction("random", StandardBasicTypes.DOUBLE) );
registerFunction( "rand", new NoArgSQLFunction("random", StandardBasicTypes.DOUBLE) );
registerFunction( "random", new NoArgSQLFunction( "random", StandardBasicTypes.DOUBLE ) );
registerFunction( "rand", new NoArgSQLFunction( "random", StandardBasicTypes.DOUBLE ) );
registerFunction( "round", new StandardSQLFunction("round") );
registerFunction( "trunc", new StandardSQLFunction("trunc") );
registerFunction( "ceil", new StandardSQLFunction("ceil") );
registerFunction( "floor", new StandardSQLFunction("floor") );
registerFunction( "round", new StandardSQLFunction( "round" ) );
registerFunction( "trunc", new StandardSQLFunction( "trunc" ) );
registerFunction( "ceil", new StandardSQLFunction( "ceil" ) );
registerFunction( "floor", new StandardSQLFunction( "floor" ) );
registerFunction( "chr", new StandardSQLFunction("chr", StandardBasicTypes.CHARACTER) );
registerFunction( "lower", new StandardSQLFunction("lower") );
registerFunction( "upper", new StandardSQLFunction("upper") );
registerFunction( "substr", new StandardSQLFunction("substr", StandardBasicTypes.STRING) );
registerFunction( "initcap", new StandardSQLFunction("initcap") );
registerFunction( "to_ascii", new StandardSQLFunction("to_ascii") );
registerFunction( "quote_ident", new StandardSQLFunction("quote_ident", StandardBasicTypes.STRING) );
registerFunction( "quote_literal", new StandardSQLFunction("quote_literal", StandardBasicTypes.STRING) );
registerFunction( "md5", new StandardSQLFunction("md5", StandardBasicTypes.STRING) );
registerFunction( "ascii", new StandardSQLFunction("ascii", StandardBasicTypes.INTEGER) );
registerFunction( "char_length", new StandardSQLFunction("char_length", StandardBasicTypes.LONG) );
registerFunction( "bit_length", new StandardSQLFunction("bit_length", StandardBasicTypes.LONG) );
registerFunction( "octet_length", new StandardSQLFunction("octet_length", StandardBasicTypes.LONG) );
registerFunction( "chr", new StandardSQLFunction( "chr", StandardBasicTypes.CHARACTER ) );
registerFunction( "lower", new StandardSQLFunction( "lower" ) );
registerFunction( "upper", new StandardSQLFunction( "upper" ) );
registerFunction( "substr", new StandardSQLFunction( "substr", StandardBasicTypes.STRING ) );
registerFunction( "initcap", new StandardSQLFunction( "initcap" ) );
registerFunction( "to_ascii", new StandardSQLFunction( "to_ascii" ) );
registerFunction( "quote_ident", new StandardSQLFunction( "quote_ident", StandardBasicTypes.STRING ) );
registerFunction( "quote_literal", new StandardSQLFunction( "quote_literal", StandardBasicTypes.STRING ) );
registerFunction( "md5", new StandardSQLFunction( "md5", StandardBasicTypes.STRING ) );
registerFunction( "ascii", new StandardSQLFunction( "ascii", StandardBasicTypes.INTEGER ) );
registerFunction( "char_length", new StandardSQLFunction( "char_length", StandardBasicTypes.LONG ) );
registerFunction( "bit_length", new StandardSQLFunction( "bit_length", StandardBasicTypes.LONG ) );
registerFunction( "octet_length", new StandardSQLFunction( "octet_length", StandardBasicTypes.LONG ) );
registerFunction( "age", new StandardSQLFunction("age") );
registerFunction( "current_date", new NoArgSQLFunction("current_date", StandardBasicTypes.DATE, false) );
registerFunction( "current_time", new NoArgSQLFunction("current_time", StandardBasicTypes.TIME, false) );
registerFunction( "current_timestamp", new NoArgSQLFunction("current_timestamp", StandardBasicTypes.TIMESTAMP, false) );
registerFunction( "age", new StandardSQLFunction( "age" ) );
registerFunction( "current_date", new NoArgSQLFunction( "current_date", StandardBasicTypes.DATE, false ) );
registerFunction( "current_time", new NoArgSQLFunction( "current_time", StandardBasicTypes.TIME, false ) );
registerFunction(
"current_timestamp",
new NoArgSQLFunction( "current_timestamp", StandardBasicTypes.TIMESTAMP, false )
);
registerFunction( "date_trunc", new StandardSQLFunction( "date_trunc", StandardBasicTypes.TIMESTAMP ) );
registerFunction( "localtime", new NoArgSQLFunction("localtime", StandardBasicTypes.TIME, false) );
registerFunction( "localtimestamp", new NoArgSQLFunction("localtimestamp", StandardBasicTypes.TIMESTAMP, false) );
registerFunction( "now", new NoArgSQLFunction("now", StandardBasicTypes.TIMESTAMP) );
registerFunction( "timeofday", new NoArgSQLFunction("timeofday", StandardBasicTypes.STRING) );
registerFunction( "localtime", new NoArgSQLFunction( "localtime", StandardBasicTypes.TIME, false ) );
registerFunction(
"localtimestamp",
new NoArgSQLFunction( "localtimestamp", StandardBasicTypes.TIMESTAMP, false )
);
registerFunction( "now", new NoArgSQLFunction( "now", StandardBasicTypes.TIMESTAMP ) );
registerFunction( "timeofday", new NoArgSQLFunction( "timeofday", StandardBasicTypes.STRING ) );
registerFunction( "current_user", new NoArgSQLFunction("current_user", StandardBasicTypes.STRING, false) );
registerFunction( "session_user", new NoArgSQLFunction("session_user", StandardBasicTypes.STRING, false) );
registerFunction( "user", new NoArgSQLFunction("user", StandardBasicTypes.STRING, false) );
registerFunction( "current_database", new NoArgSQLFunction("current_database", StandardBasicTypes.STRING, true) );
registerFunction( "current_schema", new NoArgSQLFunction("current_schema", StandardBasicTypes.STRING, true) );
registerFunction( "current_user", new NoArgSQLFunction( "current_user", StandardBasicTypes.STRING, false ) );
registerFunction( "session_user", new NoArgSQLFunction( "session_user", StandardBasicTypes.STRING, false ) );
registerFunction( "user", new NoArgSQLFunction( "user", StandardBasicTypes.STRING, false ) );
registerFunction(
"current_database",
new NoArgSQLFunction( "current_database", StandardBasicTypes.STRING, true )
);
registerFunction( "current_schema", new NoArgSQLFunction( "current_schema", StandardBasicTypes.STRING, true ) );
registerFunction( "to_char", new StandardSQLFunction("to_char", StandardBasicTypes.STRING) );
registerFunction( "to_date", new StandardSQLFunction("to_date", StandardBasicTypes.DATE) );
registerFunction( "to_timestamp", new StandardSQLFunction("to_timestamp", StandardBasicTypes.TIMESTAMP) );
registerFunction( "to_number", new StandardSQLFunction("to_number", StandardBasicTypes.BIG_DECIMAL) );
registerFunction( "to_char", new StandardSQLFunction( "to_char", StandardBasicTypes.STRING ) );
registerFunction( "to_date", new StandardSQLFunction( "to_date", StandardBasicTypes.DATE ) );
registerFunction( "to_timestamp", new StandardSQLFunction( "to_timestamp", StandardBasicTypes.TIMESTAMP ) );
registerFunction( "to_number", new StandardSQLFunction( "to_number", StandardBasicTypes.BIG_DECIMAL ) );
registerFunction( "concat", new VarArgsSQLFunction( StandardBasicTypes.STRING, "(","||",")" ) );
registerFunction( "concat", new VarArgsSQLFunction( StandardBasicTypes.STRING, "(", "||", ")" ) );
registerFunction( "locate", new PositionSubstringFunction() );
registerFunction( "str", new SQLFunctionTemplate(StandardBasicTypes.STRING, "cast(?1 as varchar)") );
registerFunction( "str", new SQLFunctionTemplate( StandardBasicTypes.STRING, "cast(?1 as varchar)" ) );
getDefaultProperties().setProperty( Environment.STATEMENT_BATCH_SIZE, DEFAULT_BATCH_SIZE );
getDefaultProperties().setProperty( Environment.NON_CONTEXTUAL_LOB_CREATION, "true" );
@ -286,7 +296,7 @@ public class PostgreSQL81Dialect extends Dialect {
@Override
public String getLimitString(String sql, boolean hasOffset) {
return sql + (hasOffset ? " limit ? offset ?" : " limit ?");
return sql + ( hasOffset ? " limit ? offset ?" : " limit ?" );
}
@Override
@ -317,12 +327,12 @@ public class PostgreSQL81Dialect extends Dialect {
}
}
LockMode lockMode = lockOptions.getAliasSpecificLockMode( aliases );
if (lockMode == null ) {
if ( lockMode == null ) {
lockMode = lockOptions.getLockMode();
}
switch ( lockMode ) {
case UPGRADE:
return getForUpdateString(aliases);
return getForUpdateString( aliases );
case PESSIMISTIC_READ:
return getReadLockString( aliases, lockOptions.getTimeOut() );
case PESSIMISTIC_WRITE:
@ -330,9 +340,9 @@ public class PostgreSQL81Dialect extends Dialect {
case UPGRADE_NOWAIT:
case FORCE:
case PESSIMISTIC_FORCE_INCREMENT:
return getForUpdateNowaitString(aliases);
return getForUpdateNowaitString( aliases );
case UPGRADE_SKIPLOCKED:
return getForUpdateSkipLockedString(aliases);
return getForUpdateSkipLockedString( aliases );
default:
return "";
}
@ -344,7 +354,7 @@ public class PostgreSQL81Dialect extends Dialect {
}
@Override
public String getCaseInsensitiveLike(){
public String getCaseInsensitiveLike() {
return "ilike";
}
@ -451,19 +461,29 @@ public class PostgreSQL81Dialect extends Dialect {
@Override
protected String doExtractConstraintName(SQLException sqle) throws NumberFormatException {
final int sqlState = Integer.parseInt( JdbcExceptionHelper.extractSqlState( sqle ) );
switch (sqlState) {
switch ( sqlState ) {
// CHECK VIOLATION
case 23514: return extractUsingTemplate( "violates check constraint \"","\"", sqle.getMessage() );
case 23514:
return extractUsingTemplate( "violates check constraint \"", "\"", sqle.getMessage() );
// UNIQUE VIOLATION
case 23505: return extractUsingTemplate( "violates unique constraint \"","\"", sqle.getMessage() );
case 23505:
return extractUsingTemplate( "violates unique constraint \"", "\"", sqle.getMessage() );
// FOREIGN KEY VIOLATION
case 23503: return extractUsingTemplate( "violates foreign key constraint \"","\"", sqle.getMessage() );
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() );
case 23502:
return extractUsingTemplate(
"null value in column \"",
"\" violates not-null constraint",
sqle.getMessage()
);
// TODO: RESTRICT VIOLATION
case 23001: return null;
case 23001:
return null;
// ALL OTHER
default: return null;
default:
return null;
}
}
};
@ -485,6 +505,11 @@ public class PostgreSQL81Dialect extends Dialect {
return new PessimisticLockException( message, sqlException, sql );
}
if ( "57014".equals( sqlState ) ) {
// Operation cancelled by user
return new QueryTimeoutException( message, sqlException, sql );
}
// returning null allows other delegates to operate
return null;
}
@ -637,7 +662,8 @@ public class PostgreSQL81Dialect extends Dialect {
@Override
public ResultSet getResultSet(CallableStatement statement, int position) throws SQLException {
if ( position != 1 ) {
throw new UnsupportedOperationException( "PostgreSQL only supports REF_CURSOR parameters as the first parameter" );
throw new UnsupportedOperationException(
"PostgreSQL only supports REF_CURSOR parameters as the first parameter" );
}
return (ResultSet) statement.getObject( 1 );
}

View File

@ -14,6 +14,7 @@ import org.hibernate.JDBCException;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.PessimisticLockException;
import org.hibernate.QueryTimeoutException;
import org.hibernate.exception.LockAcquisitionException;
import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
@ -57,6 +58,17 @@ public class PostgreSQL81DialectTestCase extends BaseUnitTestCase {
assertTrue(exception instanceof PessimisticLockException);
}
@Test
@TestForIssue( jiraKey = "HHH-13661")
public void testQueryTimeoutException() {
final PostgreSQL81Dialect dialect = new PostgreSQL81Dialect();
final SQLExceptionConversionDelegate delegate = dialect.buildSQLExceptionConversionDelegate();
assertNotNull( delegate );
final JDBCException exception = delegate.convert( new SQLException("Client cancelled operation", "57014"), "", "" );
assertTrue( exception instanceof QueryTimeoutException );
}
/**
* Tests that getForUpdateString(String aliases, LockOptions lockOptions) will return a String
* that will effect the SELECT ... FOR UPDATE OF tableAlias1, ..., tableAliasN
@ -98,4 +110,5 @@ public class PostgreSQL81DialectTestCase extends BaseUnitTestCase {
assertEquals( "PostgreSQL only supports accessing REF_CURSOR parameters by position", e.getMessage() );
}
}
}