[jOOQ/jOOQ#15732] Reverse engineer error message to detect SQLStateClass

This commit is contained in:
Lukas Eder 2024-03-28 15:25:50 +01:00
parent 2de610aecc
commit 0de1d5563c
12 changed files with 68 additions and 29 deletions

View File

@ -76,6 +76,8 @@ import io.r2dbc.spi.R2dbcException;
*/
public class DataAccessException extends RuntimeException {
SQLStateClass sqlStateClass;
/**
* Constructor for DataAccessException.
*
@ -123,6 +125,9 @@ public class DataAccessException extends RuntimeException {
*/
@NotNull
public SQLStateClass sqlStateClass() {
if (sqlStateClass != null)
return sqlStateClass;
SQLException s = getCause(SQLException.class);
if (s != null)
return sqlStateClass(s);
@ -134,6 +139,14 @@ public class DataAccessException extends RuntimeException {
return SQLStateClass.NONE;
}
/**
* Set the {@link SQLStateClass}.
*/
public DataAccessException sqlStateClass(SQLStateClass c) {
this.sqlStateClass = c;
return this;
}
/**
* Decode the {@link SQLException#getSQLState()} into {@link SQLStateClass}.
*/
@ -155,11 +168,12 @@ public class DataAccessException extends RuntimeException {
if (e.getSQLState() != null)
return SQLStateClass.fromCode(e.getSQLState());
else if (e.getSQLState() == null && causePrefix(e, "org.sqlite"))
else if (causePrefix(e, "org.sqlite"))
return SQLStateClass.fromSQLiteVendorCode(e.getErrorCode());
else if (e.getSQLState() == null && causePrefix(e, "io.trino"))
else if (causePrefix(e, "io.trino"))
return SQLStateClass.fromTrinoVendorCode(e.getErrorCode());
else
return SQLStateClass.NONE;

View File

@ -37,6 +37,8 @@
*/
package org.jooq.exception;
import static org.jooq.exception.SQLStateClass.C22_DATA_EXCEPTION;
import java.sql.SQLDataException;
import java.sql.SQLException;
@ -61,6 +63,8 @@ public class DataException extends DataAccessException {
*/
public DataException(String message) {
super(message);
sqlStateClass(C22_DATA_EXCEPTION);
}
/**
@ -72,5 +76,7 @@ public class DataException extends DataAccessException {
*/
public DataException(String message, Throwable cause) {
super(message, cause);
sqlStateClass(C22_DATA_EXCEPTION);
}
}

View File

@ -37,6 +37,8 @@
*/
package org.jooq.exception;
import static org.jooq.exception.SQLStateClass.C23_INTEGRITY_CONSTRAINT_VIOLATION;
import java.sql.SQLIntegrityConstraintViolationException;
/**
@ -59,6 +61,8 @@ public class IntegrityConstraintViolationException extends DataAccessException {
*/
public IntegrityConstraintViolationException(String message) {
super(message);
sqlStateClass(C23_INTEGRITY_CONSTRAINT_VIOLATION);
}
/**
@ -70,5 +74,7 @@ public class IntegrityConstraintViolationException extends DataAccessException {
*/
public IntegrityConstraintViolationException(String message, Throwable cause) {
super(message, cause);
sqlStateClass(C23_INTEGRITY_CONSTRAINT_VIOLATION);
}
}

View File

@ -74,7 +74,7 @@ abstract class AbstractBindContext extends AbstractContext<BindContext> implemen
return bindValue0(value, field);
}
catch (SQLException e) {
throw Tools.translate(null, e);
throw Tools.translate(this, null, e);
}
}

View File

@ -246,7 +246,7 @@ abstract class AbstractQuery<R extends Record> extends AbstractAttachableQueryPa
statement = null;
}
catch (SQLException e) {
throw Tools.translate(rendered.sql, e);
throw Tools.translate(create(), rendered.sql, e);
}
}
}
@ -258,7 +258,7 @@ abstract class AbstractQuery<R extends Record> extends AbstractAttachableQueryPa
statement.cancel();
}
catch (SQLException e) {
throw Tools.translate(rendered.sql, e);
throw Tools.translate(create(), rendered.sql, e);
}
}
}

View File

@ -243,7 +243,7 @@ abstract class AbstractQueryPart implements QueryPartInternal {
* Internal convenience method
*/
protected final DataAccessException translate(String sql, SQLException e) {
return Tools.translate(sql, e);
return Tools.translate(create(), sql, e);
}
private static final JooqLogger log = JooqLogger.getLogger(AbstractQueryPart.class, "serialization", 100);

View File

@ -626,7 +626,7 @@ implements
listener.executeEnd(ctx);
if (e != null)
results.resultsOrRows().add(new ResultOrRowsImpl(Tools.translate(ctx.sql(), e)));
results.resultsOrRows().add(new ResultOrRowsImpl(Tools.translate(ctx, ctx.sql(), e)));
return e;
}

View File

@ -126,6 +126,7 @@ import static org.jooq.impl.SQLDataType.JSON;
import static org.jooq.impl.SQLDataType.JSONB;
import static org.jooq.impl.SQLDataType.TIME;
import static org.jooq.impl.SQLDataType.TIMESTAMP;
import static org.jooq.impl.Tools.CTX;
import static org.jooq.impl.Tools.EMPTY_FIELD;
import static org.jooq.impl.Tools.combine;
import static org.jooq.impl.Tools.configuration;
@ -578,7 +579,7 @@ public class DSL {
return new DefaultCloseableDSLContext(new DefaultCloseableConnectionProvider(connection), JDBCUtils.dialect(connection));
}
catch (SQLException e) {
throw Tools.translate("Error when initialising Connection", e);
throw Tools.translate(CTX.get(), "Error when initialising Connection", e);
}
}
}
@ -620,7 +621,7 @@ public class DSL {
return new DefaultCloseableDSLContext(new DefaultCloseableConnectionProvider(connection), JDBCUtils.dialect(connection));
}
catch (SQLException e) {
throw Tools.translate("Error when initialising Connection", e);
throw Tools.translate(CTX.get(), "Error when initialising Connection", e);
}
}
}
@ -661,7 +662,7 @@ public class DSL {
return new DefaultCloseableDSLContext(new DefaultCloseableConnectionProvider(connection), JDBCUtils.dialect(connection));
}
catch (SQLException e) {
throw Tools.translate("Error when initialising Connection", e);
throw Tools.translate(CTX.get(), "Error when initialising Connection", e);
}
}
}

View File

@ -797,7 +797,7 @@ class DefaultExecuteContext implements ExecuteContext {
@Override
public final void exception(RuntimeException e) {
this.exception = Tools.translate(sql(), e);
this.exception = Tools.translate(this, sql(), e);
if (Boolean.TRUE.equals(settings().isDebugInfoOnStackTrace())) {
@ -824,7 +824,7 @@ class DefaultExecuteContext implements ExecuteContext {
@Override
public final void sqlException(SQLException e) {
this.sqlException = e;
exception(Tools.translate(sql(), e));
exception(Tools.translate(this, sql(), e));
if (family() == SQLDialect.DEFAULT && logDefaultDialect.isWarnEnabled())
logDefaultDialect.warn("Unsupported dialect",

View File

@ -160,7 +160,7 @@ final class MetaDataFieldProvider implements Serializable {
}
}
catch (SQLException e) {
throw Tools.translate(null, e);
throw Tools.translate(configuration.dsl(), null, e);
}
return new FieldsImpl<>(fields);

View File

@ -246,7 +246,7 @@ final class R2DBC {
@Override
public final void onError(Throwable t) {
complete(true, () -> resultSubscriber.downstream.subscriber.onError(translate(resultSubscriber.downstream.sql(), t)));
complete(true, () -> resultSubscriber.downstream.subscriber.onError(translate(resultSubscriber.downstream.configuration.dsl(), resultSubscriber.downstream.sql(), t)));
}
@Override
@ -282,7 +282,7 @@ final class R2DBC {
@Override
public final void onError(Throwable t) {
complete(true, () -> downstream.subscriber.onError(translate(downstream.sql(), t)));
complete(true, () -> downstream.subscriber.onError(translate(downstream.configuration.dsl(), downstream.sql(), t)));
}
@Override
@ -397,7 +397,7 @@ final class R2DBC {
@Override
public final void onError(Throwable t) {
downstream.subscriber.onError(translate(downstream.sql(), t));
downstream.subscriber.onError(translate(downstream.configuration.dsl(), downstream.sql(), t));
}
@Override

View File

@ -96,6 +96,7 @@ import static org.jooq.conf.SettingsTools.updatablePrimaryKeys;
import static org.jooq.conf.ThrowExceptions.THROW_FIRST;
import static org.jooq.conf.ThrowExceptions.THROW_NONE;
import static org.jooq.exception.DataAccessException.sqlStateClass;
import static org.jooq.exception.SQLStateClass.C23_INTEGRITY_CONSTRAINT_VIOLATION;
import static org.jooq.impl.CacheType.REFLECTION_CACHE_GET_ANNOTATED_GETTER;
import static org.jooq.impl.CacheType.REFLECTION_CACHE_GET_ANNOTATED_MEMBERS;
import static org.jooq.impl.CacheType.REFLECTION_CACHE_GET_ANNOTATED_SETTERS;
@ -3552,13 +3553,13 @@ final class Tools {
/**
* Translate a {@link R2dbcException} to a {@link DataAccessException}
*/
static final RuntimeException translate(String sql, Throwable t) {
static final RuntimeException translate(Scope scope, String sql, Throwable t) {
if (t instanceof R2dbcException e)
return translate(sql, e);
return translate(scope, sql, e);
else if (t instanceof SQLException e)
return translate(sql, e);
return translate(scope, sql, e);
else if (t instanceof RuntimeException e)
return translate(sql, e);
return translate(scope, sql, e);
else if (t != null)
return new DataAccessException("SQL [" + sql + "]; Unspecified Throwable", t);
else
@ -3568,9 +3569,9 @@ final class Tools {
/**
* Translate a {@link R2dbcException} to a {@link DataAccessException}
*/
static final DataAccessException translate(String sql, R2dbcException e) {
static final DataAccessException translate(Scope scope, String sql, R2dbcException e) {
if (e != null)
return translate(sql, e, sqlStateClass(e));
return translate(scope, sql, e, sqlStateClass(e));
else
return new DataAccessException("SQL [" + sql + "]; Unspecified R2dbcException");
}
@ -3578,28 +3579,39 @@ final class Tools {
/**
* Translate a {@link SQLException} to a {@link DataAccessException}
*/
static final DataAccessException translate(String sql, SQLException e) {
static final DataAccessException translate(Scope scope, String sql, SQLException e) {
if (e != null)
return translate(sql, e, sqlStateClass(e));
return translate(scope, sql, e, sqlStateClass(e));
else
return new DataAccessException("SQL [" + sql + "]; Unspecified SQLException");
}
private static final DataAccessException translate(String sql, Exception e, SQLStateClass sqlState) {
private static final DataAccessException translate(Scope scope, String sql, Exception e, SQLStateClass sqlState) {
switch (sqlState) {
case C22_DATA_EXCEPTION:
return new DataException("SQL [" + sql + "]; " + e.getMessage(), e);
case C23_INTEGRITY_CONSTRAINT_VIOLATION:
return new IntegrityConstraintViolationException("SQL [" + sql + "]; " + e.getMessage(), e);
default:
return new DataAccessException("SQL [" + sql + "]; " + e.getMessage(), e);
case NONE:
switch (scope.family()) {
case DUCKDB: {
String m = e.getMessage().toLowerCase();
if (m.contains("constraint violated: duplicate key"))
return new IntegrityConstraintViolationException("SQL [" + sql + "]; " + e.getMessage(), e);
else if (m.contains("constraint Error: not nullL constraint failed"));
return new IntegrityConstraintViolationException("SQL [" + sql + "]; " + e.getMessage(), e);
}
}
}
return new DataAccessException("SQL [" + sql + "]; " + e.getMessage(), e);
}
/**
* Translate a {@link RuntimeException} to a {@link DataAccessException}
*/
static final RuntimeException translate(String sql, RuntimeException e) {
static final RuntimeException translate(Scope scope, String sql, RuntimeException e) {
if (e != null)
return e;
else
@ -5045,7 +5057,7 @@ final class Tools {
if (ctx.settings().getThrowExceptions() == THROW_NONE) {
ctx.sqlException(e);
results.resultsOrRows().add(new ResultOrRowsImpl(Tools.translate(ctx.sql(), e)));
results.resultsOrRows().add(new ResultOrRowsImpl(Tools.translate(ctx, ctx.sql(), e)));
}
else {
consumeExceptions(ctx.configuration(), ctx.statement(), e);