HHH-18772 introduce AuthException and simplify SQLStateConversionDelegate
Signed-off-by: Gavin King <gavin@hibernate.org>
This commit is contained in:
parent
55255e9d4a
commit
b44833b7c9
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
* Copyright Red Hat Inc. and Hibernate Authors
|
||||||
|
*/
|
||||||
|
package org.hibernate.exception;
|
||||||
|
|
||||||
|
import org.hibernate.JDBCException;
|
||||||
|
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link JDBCException} indicating an authentication or authorization failure.
|
||||||
|
*
|
||||||
|
* @since 7.0
|
||||||
|
*
|
||||||
|
* @author Gavin King
|
||||||
|
*/
|
||||||
|
public class AuthException extends JDBCException {
|
||||||
|
/**
|
||||||
|
* Constructor for AuthException.
|
||||||
|
*
|
||||||
|
* @param root The underlying exception.
|
||||||
|
*/
|
||||||
|
public AuthException(String message, SQLException root) {
|
||||||
|
super( message, root );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for AuthException.
|
||||||
|
*
|
||||||
|
* @param message Optional message.
|
||||||
|
* @param root The underlying exception.
|
||||||
|
*/
|
||||||
|
public AuthException(String message, SQLException root, String sql) {
|
||||||
|
super( message, root, sql );
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,7 +9,7 @@ import java.sql.SQLException;
|
||||||
import org.hibernate.JDBCException;
|
import org.hibernate.JDBCException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extends {@link JDBCException} indicating that evaluation of the
|
* A {@link JDBCException} indicating that evaluation of the
|
||||||
* valid SQL statement against the given data resulted in some
|
* valid SQL statement against the given data resulted in some
|
||||||
* illegal operation, mismatched types or incorrect cardinality.
|
* illegal operation, mismatched types or incorrect cardinality.
|
||||||
*
|
*
|
||||||
|
@ -17,7 +17,7 @@ import org.hibernate.JDBCException;
|
||||||
*/
|
*/
|
||||||
public class DataException extends JDBCException {
|
public class DataException extends JDBCException {
|
||||||
/**
|
/**
|
||||||
* Constructor for JDBCException.
|
* Constructor for DataException.
|
||||||
*
|
*
|
||||||
* @param root The underlying exception.
|
* @param root The underlying exception.
|
||||||
*/
|
*/
|
||||||
|
@ -26,7 +26,7 @@ public class DataException extends JDBCException {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for JDBCException.
|
* Constructor for DataException.
|
||||||
*
|
*
|
||||||
* @param message Optional message.
|
* @param message Optional message.
|
||||||
* @param root The underlying exception.
|
* @param root The underlying exception.
|
||||||
|
|
|
@ -5,11 +5,11 @@
|
||||||
package org.hibernate.exception.internal;
|
package org.hibernate.exception.internal;
|
||||||
|
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.hibernate.JDBCException;
|
import org.hibernate.JDBCException;
|
||||||
import org.hibernate.PessimisticLockException;
|
import org.hibernate.PessimisticLockException;
|
||||||
import org.hibernate.QueryTimeoutException;
|
import org.hibernate.QueryTimeoutException;
|
||||||
|
import org.hibernate.exception.AuthException;
|
||||||
import org.hibernate.exception.ConstraintViolationException;
|
import org.hibernate.exception.ConstraintViolationException;
|
||||||
import org.hibernate.exception.DataException;
|
import org.hibernate.exception.DataException;
|
||||||
import org.hibernate.exception.JDBCConnectionException;
|
import org.hibernate.exception.JDBCConnectionException;
|
||||||
|
@ -17,15 +17,19 @@ import org.hibernate.exception.LockAcquisitionException;
|
||||||
import org.hibernate.exception.SQLGrammarException;
|
import org.hibernate.exception.SQLGrammarException;
|
||||||
import org.hibernate.exception.spi.AbstractSQLExceptionConversionDelegate;
|
import org.hibernate.exception.spi.AbstractSQLExceptionConversionDelegate;
|
||||||
import org.hibernate.exception.spi.ConversionContext;
|
import org.hibernate.exception.spi.ConversionContext;
|
||||||
import org.hibernate.internal.util.JdbcExceptionHelper;
|
|
||||||
|
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
|
import static org.hibernate.internal.util.JdbcExceptionHelper.determineSqlStateClassCode;
|
||||||
|
import static org.hibernate.internal.util.JdbcExceptionHelper.extractErrorCode;
|
||||||
|
import static org.hibernate.internal.util.JdbcExceptionHelper.extractSqlState;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link org.hibernate.exception.spi.SQLExceptionConverter} implementation which performs conversion based
|
* A {@link org.hibernate.exception.spi.SQLExceptionConverter} implementation which performs conversion based
|
||||||
* on the underlying SQLState. Interpretation of a SQL error based on SQLState is not nearly as accurate as
|
* on the underlying SQLState. Interpretation of a SQL error based on SQLState is not nearly as accurate as
|
||||||
* using the ErrorCode (which is, however, vendor-specific).
|
* using the ErrorCode (which is, however, vendor-specific).
|
||||||
* <p>
|
*
|
||||||
|
* @implNote
|
||||||
* SQLState codes are defined by both ANSI SQL specs and X/Open. Some "classes" are shared, others are
|
* SQLState codes are defined by both ANSI SQL specs and X/Open. Some "classes" are shared, others are
|
||||||
* specific to one or another, yet others are custom vendor classes. Unfortunately I have not been able to
|
* specific to one or another, yet others are custom vendor classes. Unfortunately I have not been able to
|
||||||
* find a "blessed" list of X/Open codes. These codes are cobbled together between ANSI SQL spec and error
|
* find a "blessed" list of X/Open codes. These codes are cobbled together between ANSI SQL spec and error
|
||||||
|
@ -35,90 +39,61 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
*/
|
*/
|
||||||
public class SQLStateConversionDelegate extends AbstractSQLExceptionConversionDelegate {
|
public class SQLStateConversionDelegate extends AbstractSQLExceptionConversionDelegate {
|
||||||
|
|
||||||
private static final Set<String> SQL_GRAMMAR_CATEGORIES = buildGrammarCategories();
|
|
||||||
private static Set<String> buildGrammarCategories() {
|
|
||||||
return Set.of(
|
|
||||||
"07", // "dynamic SQL error"
|
|
||||||
"20",
|
|
||||||
"2A", // "direct SQL syntax error or access rule violation"
|
|
||||||
"37", // "dynamic SQL syntax error or access rule violation"
|
|
||||||
"42", // "syntax error or access rule violation"
|
|
||||||
"65", // Oracle specific as far as I can tell
|
|
||||||
"S0" // MySQL specific as far as I can tell
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final Set<String> DATA_CATEGORIES = buildDataCategories();
|
|
||||||
private static Set<String> buildDataCategories() {
|
|
||||||
return Set.of(
|
|
||||||
"21", // "cardinality violation"
|
|
||||||
"22" // "data exception"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final Set<String> INTEGRITY_VIOLATION_CATEGORIES = buildContraintCategories();
|
|
||||||
private static Set<String> buildContraintCategories() {
|
|
||||||
return Set.of(
|
|
||||||
"23", // "integrity constraint violation"
|
|
||||||
"27", // "triggered data change violation"
|
|
||||||
"44" // "with check option violation"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final Set<String> CONNECTION_CATEGORIES = buildConnectionCategories();
|
|
||||||
private static Set<String> buildConnectionCategories() {
|
|
||||||
return Set.of(
|
|
||||||
"08" // "connection exception"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SQLStateConversionDelegate(ConversionContext conversionContext) {
|
public SQLStateConversionDelegate(ConversionContext conversionContext) {
|
||||||
super( conversionContext );
|
super( conversionContext );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable JDBCException convert(SQLException sqlException, String message, String sql) {
|
public @Nullable JDBCException convert(SQLException sqlException, String message, String sql) {
|
||||||
final String sqlState = JdbcExceptionHelper.extractSqlState( sqlException );
|
final String sqlState = extractSqlState( sqlException );
|
||||||
final int errorCode = JdbcExceptionHelper.extractErrorCode( sqlException );
|
|
||||||
|
|
||||||
if ( sqlState != null ) {
|
if ( sqlState != null ) {
|
||||||
String sqlStateClassCode = JdbcExceptionHelper.determineSqlStateClassCode( sqlState );
|
switch ( sqlState ) {
|
||||||
|
case "42501":
|
||||||
if ( sqlStateClassCode != null ) {
|
return new AuthException( message, sqlException, sql );
|
||||||
if ( SQL_GRAMMAR_CATEGORIES.contains( sqlStateClassCode ) ) {
|
case "40001":
|
||||||
return new SQLGrammarException( message, sqlException, sql );
|
return new LockAcquisitionException( message, sqlException, sql );
|
||||||
|
case "40XL1", "40XL2":
|
||||||
|
// Derby "A lock could not be obtained within the time requested."
|
||||||
|
return new PessimisticLockException( message, sqlException, sql );
|
||||||
|
case "70100":
|
||||||
|
// MySQL Query execution was interrupted
|
||||||
|
return new QueryTimeoutException( message, sqlException, sql );
|
||||||
|
case "72000":
|
||||||
|
if ( extractErrorCode( sqlException ) == 1013 ) {
|
||||||
|
// Oracle user requested cancel of current operation
|
||||||
|
return new QueryTimeoutException( message, sqlException, sql );
|
||||||
}
|
}
|
||||||
else if ( INTEGRITY_VIOLATION_CATEGORIES.contains( sqlStateClassCode ) ) {
|
}
|
||||||
|
switch ( determineSqlStateClassCode( sqlState ) ) {
|
||||||
|
case
|
||||||
|
"07", // "dynamic SQL error"
|
||||||
|
"20",
|
||||||
|
"2A", // "direct SQL syntax error or access rule violation"
|
||||||
|
"37", // "dynamic SQL syntax error or access rule violation"
|
||||||
|
"42", // "syntax error or access rule violation"
|
||||||
|
"65", // Oracle specific as far as I can tell
|
||||||
|
"S0": // MySQL specific as far as I can tell
|
||||||
|
return new SQLGrammarException( message, sqlException, sql );
|
||||||
|
case
|
||||||
|
"23", // "integrity constraint violation"
|
||||||
|
"27", // "triggered data change violation"
|
||||||
|
"44": // "with check option violation"
|
||||||
final String constraintName = getConversionContext()
|
final String constraintName = getConversionContext()
|
||||||
.getViolatedConstraintNameExtractor()
|
.getViolatedConstraintNameExtractor()
|
||||||
.extractConstraintName( sqlException );
|
.extractConstraintName( sqlException );
|
||||||
return new ConstraintViolationException( message, sqlException, sql, constraintName );
|
return new ConstraintViolationException( message, sqlException, sql, constraintName );
|
||||||
}
|
case
|
||||||
else if ( CONNECTION_CATEGORIES.contains( sqlStateClassCode ) ) {
|
"08": // "connection exception"
|
||||||
return new JDBCConnectionException( message, sqlException, sql );
|
return new JDBCConnectionException( message, sqlException, sql );
|
||||||
}
|
case
|
||||||
else if ( DATA_CATEGORIES.contains( sqlStateClassCode ) ) {
|
"21", // "cardinality violation"
|
||||||
|
"22": // "data exception"
|
||||||
return new DataException( message, sqlException, sql );
|
return new DataException( message, sqlException, sql );
|
||||||
|
case
|
||||||
|
"28": // "authentication failure"
|
||||||
|
return new AuthException( message, sqlException, sql );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( "40001".equals( sqlState ) ) {
|
|
||||||
return new LockAcquisitionException( message, sqlException, sql );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( "40XL1".equals( sqlState ) || "40XL2".equals( sqlState )) {
|
|
||||||
// Derby "A lock could not be obtained within the time requested."
|
|
||||||
return new PessimisticLockException( message, sqlException, sql );
|
|
||||||
}
|
|
||||||
|
|
||||||
// MySQL Query execution was interrupted
|
|
||||||
if ( "70100".equals( sqlState ) ||
|
|
||||||
// Oracle user requested cancel of current operation
|
|
||||||
( "72000".equals( sqlState ) && errorCode == 1013 ) ) {
|
|
||||||
return new QueryTimeoutException( message, sqlException, sql );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,9 +56,6 @@ public final class JdbcExceptionHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String determineSqlStateClassCode(String sqlState) {
|
public static String determineSqlStateClassCode(String sqlState) {
|
||||||
if ( sqlState == null || sqlState.length() < 2 ) {
|
return sqlState == null || sqlState.length() < 2 ? sqlState : sqlState.substring( 0, 2 );
|
||||||
return sqlState;
|
|
||||||
}
|
|
||||||
return sqlState.substring( 0, 2 );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue