From 0de1d5563cfbe27c11ba19b50aeee273b75eccfc Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Thu, 28 Mar 2024 15:25:50 +0100 Subject: [PATCH] [jOOQ/jOOQ#15732] Reverse engineer error message to detect SQLStateClass --- .../jooq/exception/DataAccessException.java | 18 ++++++++- .../org/jooq/exception/DataException.java | 6 +++ ...IntegrityConstraintViolationException.java | 6 +++ .../org/jooq/impl/AbstractBindContext.java | 2 +- .../java/org/jooq/impl/AbstractQuery.java | 4 +- .../java/org/jooq/impl/AbstractQueryPart.java | 2 +- .../java/org/jooq/impl/AbstractRoutine.java | 2 +- jOOQ/src/main/java/org/jooq/impl/DSL.java | 7 ++-- .../org/jooq/impl/DefaultExecuteContext.java | 4 +- .../org/jooq/impl/MetaDataFieldProvider.java | 2 +- jOOQ/src/main/java/org/jooq/impl/R2DBC.java | 6 +-- jOOQ/src/main/java/org/jooq/impl/Tools.java | 38 ++++++++++++------- 12 files changed, 68 insertions(+), 29 deletions(-) diff --git a/jOOQ/src/main/java/org/jooq/exception/DataAccessException.java b/jOOQ/src/main/java/org/jooq/exception/DataAccessException.java index b637b41965..38cce0291b 100644 --- a/jOOQ/src/main/java/org/jooq/exception/DataAccessException.java +++ b/jOOQ/src/main/java/org/jooq/exception/DataAccessException.java @@ -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; diff --git a/jOOQ/src/main/java/org/jooq/exception/DataException.java b/jOOQ/src/main/java/org/jooq/exception/DataException.java index 407f027433..8fe9598bb6 100644 --- a/jOOQ/src/main/java/org/jooq/exception/DataException.java +++ b/jOOQ/src/main/java/org/jooq/exception/DataException.java @@ -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); } } diff --git a/jOOQ/src/main/java/org/jooq/exception/IntegrityConstraintViolationException.java b/jOOQ/src/main/java/org/jooq/exception/IntegrityConstraintViolationException.java index 37d9ea1c9a..d3a0a89a70 100644 --- a/jOOQ/src/main/java/org/jooq/exception/IntegrityConstraintViolationException.java +++ b/jOOQ/src/main/java/org/jooq/exception/IntegrityConstraintViolationException.java @@ -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); } } diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractBindContext.java b/jOOQ/src/main/java/org/jooq/impl/AbstractBindContext.java index 27133a7775..27142d565e 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractBindContext.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractBindContext.java @@ -74,7 +74,7 @@ abstract class AbstractBindContext extends AbstractContext implemen return bindValue0(value, field); } catch (SQLException e) { - throw Tools.translate(null, e); + throw Tools.translate(this, null, e); } } diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractQuery.java b/jOOQ/src/main/java/org/jooq/impl/AbstractQuery.java index 43098e6ad4..9dd269de43 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractQuery.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractQuery.java @@ -246,7 +246,7 @@ abstract class AbstractQuery 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 extends AbstractAttachableQueryPa statement.cancel(); } catch (SQLException e) { - throw Tools.translate(rendered.sql, e); + throw Tools.translate(create(), rendered.sql, e); } } } diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractQueryPart.java b/jOOQ/src/main/java/org/jooq/impl/AbstractQueryPart.java index def68af7d8..d6f01c4ab8 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractQueryPart.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractQueryPart.java @@ -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); diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractRoutine.java b/jOOQ/src/main/java/org/jooq/impl/AbstractRoutine.java index a029bcd9be..1864ee1f7f 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractRoutine.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractRoutine.java @@ -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; } diff --git a/jOOQ/src/main/java/org/jooq/impl/DSL.java b/jOOQ/src/main/java/org/jooq/impl/DSL.java index f30edf7440..60f2676c0a 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DSL.java +++ b/jOOQ/src/main/java/org/jooq/impl/DSL.java @@ -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); } } } diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultExecuteContext.java b/jOOQ/src/main/java/org/jooq/impl/DefaultExecuteContext.java index 9171f25986..af731bae85 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultExecuteContext.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultExecuteContext.java @@ -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", diff --git a/jOOQ/src/main/java/org/jooq/impl/MetaDataFieldProvider.java b/jOOQ/src/main/java/org/jooq/impl/MetaDataFieldProvider.java index 6d8386a3ca..e9572d16a8 100644 --- a/jOOQ/src/main/java/org/jooq/impl/MetaDataFieldProvider.java +++ b/jOOQ/src/main/java/org/jooq/impl/MetaDataFieldProvider.java @@ -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); diff --git a/jOOQ/src/main/java/org/jooq/impl/R2DBC.java b/jOOQ/src/main/java/org/jooq/impl/R2DBC.java index 1fe5da7fcf..c16e7036e9 100644 --- a/jOOQ/src/main/java/org/jooq/impl/R2DBC.java +++ b/jOOQ/src/main/java/org/jooq/impl/R2DBC.java @@ -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 diff --git a/jOOQ/src/main/java/org/jooq/impl/Tools.java b/jOOQ/src/main/java/org/jooq/impl/Tools.java index c6fe6e247e..32b6c2e277 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Tools.java +++ b/jOOQ/src/main/java/org/jooq/impl/Tools.java @@ -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);