diff --git a/jdbc/src/main/java/tech/ydb/jdbc/context/YdbExecutor.java b/jdbc/src/main/java/tech/ydb/jdbc/context/YdbExecutor.java index b589465..d0784ba 100644 --- a/jdbc/src/main/java/tech/ydb/jdbc/context/YdbExecutor.java +++ b/jdbc/src/main/java/tech/ydb/jdbc/context/YdbExecutor.java @@ -16,10 +16,8 @@ import tech.ydb.core.Issue; import tech.ydb.core.Result; import tech.ydb.core.Status; -import tech.ydb.jdbc.exception.YdbConditionallyRetryableException; import tech.ydb.jdbc.exception.YdbExecutionException; -import tech.ydb.jdbc.exception.YdbNonRetryableException; -import tech.ydb.jdbc.exception.YdbRetryableException; +import tech.ydb.jdbc.exception.YdbStatusException; import tech.ydb.table.Session; /** @@ -119,67 +117,8 @@ private void simpleExecute(Supplier> supplier) throws private void validate(String message, Status status) throws SQLException { issues.addAll(Arrays.asList(status.getIssues())); - - switch (status.getCode()) { - case SUCCESS: - return; - - case BAD_REQUEST: - case INTERNAL_ERROR: - case CLIENT_UNAUTHENTICATED: - // gRPC reports, request is not authenticated - // Maybe internal error, maybe some issue with token - case UNAUTHORIZED: - // Unauthorized by database - case SCHEME_ERROR: - case GENERIC_ERROR: - case CLIENT_CALL_UNIMPLEMENTED: - case UNSUPPORTED: - case UNUSED_STATUS: - case ALREADY_EXISTS: - throw new YdbNonRetryableException(message, status.getCode()); - - case ABORTED: - case UNAVAILABLE: - // Some of database parts are not available - case OVERLOADED: - // Database is overloaded, need to retry with exponential backoff - case TRANSPORT_UNAVAILABLE: - // Some issues with networking - case CLIENT_RESOURCE_EXHAUSTED: - // No resources to handle client request - case NOT_FOUND: - // Could be 'prepared query' issue, could be 'transaction not found' - // Should be retries with new session - case BAD_SESSION: - // Retry with new session - case SESSION_EXPIRED: - // Retry with new session - throw new YdbRetryableException(message, status.getCode()); - - case CANCELLED: - // Query was canceled due to query timeout (CancelAfter) - // Query was definitely canceled by database - case CLIENT_CANCELLED: - case CLIENT_INTERNAL_ERROR: - // Some unknown client side error, probably on transport layer - throw new YdbConditionallyRetryableException(message, status.getCode()); - - case UNDETERMINED: - case TIMEOUT: - // Database cannot respond in time, need to retry with exponential backoff - case PRECONDITION_FAILED: - case CLIENT_DEADLINE_EXCEEDED: - // Query was canceled on transport layer - case SESSION_BUSY: - // Another query is executing already, retry with new session - case CLIENT_DISCOVERY_FAILED: - // Some issue with database endpoints discovery - case CLIENT_LIMITS_REACHED: - // Client side session limit was reached - throw new YdbConditionallyRetryableException(message, status.getCode()); - default: - throw new YdbNonRetryableException(message, status.getCode()); + if (!status.isSuccess()) { + throw YdbStatusException.newException(message, status); } } } diff --git a/jdbc/src/main/java/tech/ydb/jdbc/exception/YdbConditionallyRetryableException.java b/jdbc/src/main/java/tech/ydb/jdbc/exception/YdbConditionallyRetryableException.java index 4916fe5..599e60d 100644 --- a/jdbc/src/main/java/tech/ydb/jdbc/exception/YdbConditionallyRetryableException.java +++ b/jdbc/src/main/java/tech/ydb/jdbc/exception/YdbConditionallyRetryableException.java @@ -1,12 +1,12 @@ package tech.ydb.jdbc.exception; -import tech.ydb.core.StatusCode; +import tech.ydb.core.Status; // Treat this as non retryable exception by nature, i.e. need to handle in consciously public class YdbConditionallyRetryableException extends YdbNonRetryableException { - private static final long serialVersionUID = 1135970796364528563L; + private static final long serialVersionUID = -2371144941971339449L; - public YdbConditionallyRetryableException(String message, StatusCode statusCode) { - super(message, statusCode); + YdbConditionallyRetryableException(String message, String sqlState, Status status) { + super(message, sqlState, status); } } diff --git a/jdbc/src/main/java/tech/ydb/jdbc/exception/YdbExecutionStatusException.java b/jdbc/src/main/java/tech/ydb/jdbc/exception/YdbExecutionStatusException.java deleted file mode 100644 index 720cc11..0000000 --- a/jdbc/src/main/java/tech/ydb/jdbc/exception/YdbExecutionStatusException.java +++ /dev/null @@ -1,18 +0,0 @@ -package tech.ydb.jdbc.exception; - -import tech.ydb.core.StatusCode; - -public class YdbExecutionStatusException extends YdbExecutionException { - private static final long serialVersionUID = 4476269562160877309L; - - private final StatusCode statusCode; - - public YdbExecutionStatusException(String message, StatusCode statusCode) { - super(message, null, statusCode.getCode()); - this.statusCode = statusCode; - } - - public StatusCode getStatusCode() { - return statusCode; - } -} diff --git a/jdbc/src/main/java/tech/ydb/jdbc/exception/YdbNonRetryableException.java b/jdbc/src/main/java/tech/ydb/jdbc/exception/YdbNonRetryableException.java index daa6c3d..8044984 100644 --- a/jdbc/src/main/java/tech/ydb/jdbc/exception/YdbNonRetryableException.java +++ b/jdbc/src/main/java/tech/ydb/jdbc/exception/YdbNonRetryableException.java @@ -1,11 +1,11 @@ package tech.ydb.jdbc.exception; -import tech.ydb.core.StatusCode; +import tech.ydb.core.Status; -public class YdbNonRetryableException extends YdbExecutionStatusException { - private static final long serialVersionUID = 1170815831963616837L; +public class YdbNonRetryableException extends YdbStatusException { + private static final long serialVersionUID = 687247673341671225L; - public YdbNonRetryableException(String message, StatusCode statusCode) { - super(message, statusCode); + YdbNonRetryableException(String message, String sqlState, Status status) { + super(message, sqlState, status); } } diff --git a/jdbc/src/main/java/tech/ydb/jdbc/exception/YdbRetryableException.java b/jdbc/src/main/java/tech/ydb/jdbc/exception/YdbRetryableException.java index 20bbd72..ed1b922 100644 --- a/jdbc/src/main/java/tech/ydb/jdbc/exception/YdbRetryableException.java +++ b/jdbc/src/main/java/tech/ydb/jdbc/exception/YdbRetryableException.java @@ -1,11 +1,11 @@ package tech.ydb.jdbc.exception; -import tech.ydb.core.StatusCode; +import tech.ydb.core.Status; -public class YdbRetryableException extends YdbExecutionStatusException { - private static final long serialVersionUID = 688604408491567864L; +public class YdbRetryableException extends YdbStatusException { + private static final long serialVersionUID = 2082287790625648960L; - public YdbRetryableException(String message, StatusCode statusCode) { - super(message, statusCode); + YdbRetryableException(String message, String sqlState, Status status) { + super(message, sqlState, status); } } diff --git a/jdbc/src/main/java/tech/ydb/jdbc/exception/YdbStatusException.java b/jdbc/src/main/java/tech/ydb/jdbc/exception/YdbStatusException.java new file mode 100644 index 0000000..4edb87b --- /dev/null +++ b/jdbc/src/main/java/tech/ydb/jdbc/exception/YdbStatusException.java @@ -0,0 +1,40 @@ +package tech.ydb.jdbc.exception; + +import tech.ydb.core.Status; +import tech.ydb.core.StatusCode; + +public class YdbStatusException extends YdbExecutionException { + private static final long serialVersionUID = -8082086858749679589L; + + private final Status status; + + protected YdbStatusException(String message, String state, Status status) { + super(message, state, status.getCode().getCode()); + this.status = status; + } + + public Status getStatus() { + return status; + } + + public static YdbStatusException newException(String message, Status status) { + if (status.getCode().isRetryable(false)) { + String sqlState = "Retryable[" + status.toString() + "]"; + return new YdbRetryableException(message, sqlState, status); + } + + if (status.getCode().isRetryable(true)) { + String sqlState = "ConditionallyRetryable[" + status.toString() + "]"; + return new YdbConditionallyRetryableException(message, sqlState, status); + } + + String sqlState = "NonRetryable[" + status.toString() + "]"; + return new YdbNonRetryableException(message, sqlState, status); + } + + public static YdbStatusException newBadRequest(String message) { + Status status = Status.of(StatusCode.BAD_REQUEST); + String sqlState = "NonRetryable[" + status.toString() + "]"; + return new YdbNonRetryableException(message, sqlState, status); + } +} diff --git a/jdbc/src/main/java/tech/ydb/jdbc/impl/YdbDatabaseMetaDataImpl.java b/jdbc/src/main/java/tech/ydb/jdbc/impl/YdbDatabaseMetaDataImpl.java index 2cc5494..df715f1 100644 --- a/jdbc/src/main/java/tech/ydb/jdbc/impl/YdbDatabaseMetaDataImpl.java +++ b/jdbc/src/main/java/tech/ydb/jdbc/impl/YdbDatabaseMetaDataImpl.java @@ -30,7 +30,7 @@ import tech.ydb.jdbc.common.FixedResultSetFactory; import tech.ydb.jdbc.common.YdbFunctions; import tech.ydb.jdbc.context.YdbExecutor; -import tech.ydb.jdbc.exception.YdbExecutionStatusException; +import tech.ydb.jdbc.exception.YdbStatusException; import tech.ydb.proto.scheme.SchemeOperationProtos; import tech.ydb.scheme.SchemeClient; import tech.ydb.scheme.description.ListDirectoryResult; @@ -1352,8 +1352,8 @@ private TableDescription describeTable(String table) throws SQLException { return executor.call("Describe table " + table, () -> session.describeTable(databaseWithSuffix + table, settings) ); - } catch (YdbExecutionStatusException ex) { - if (ex.getStatusCode() != StatusCode.SCHEME_ERROR) { // ignore scheme errors like path not found + } catch (YdbStatusException ex) { + if (ex.getStatus().getCode() != StatusCode.SCHEME_ERROR) { // ignore scheme errors like path not found throw ex; } LOGGER.log(Level.WARNING, "Cannot describe table {0} -> {1}", diff --git a/jdbc/src/main/java/tech/ydb/jdbc/impl/params/BatchedParams.java b/jdbc/src/main/java/tech/ydb/jdbc/impl/params/BatchedParams.java index 3546726..1ec911c 100644 --- a/jdbc/src/main/java/tech/ydb/jdbc/impl/params/BatchedParams.java +++ b/jdbc/src/main/java/tech/ydb/jdbc/impl/params/BatchedParams.java @@ -8,10 +8,9 @@ import java.util.List; import java.util.Map; -import tech.ydb.core.StatusCode; import tech.ydb.jdbc.YdbConst; import tech.ydb.jdbc.common.TypeDescription; -import tech.ydb.jdbc.exception.YdbNonRetryableException; +import tech.ydb.jdbc.exception.YdbStatusException; import tech.ydb.jdbc.impl.YdbJdbcParams; import tech.ydb.table.query.Params; import tech.ydb.table.values.ListType; @@ -86,10 +85,7 @@ private StructValue validatedCurrentStruct() throws SQLException { continue; } - throw new YdbNonRetryableException( - YdbConst.MISSING_VALUE_FOR_PARAMETER + prm.displayName(), - StatusCode.BAD_REQUEST - ); + throw YdbStatusException.newBadRequest(YdbConst.MISSING_VALUE_FOR_PARAMETER + prm.displayName()); } return StructValue.of(currentValues); } diff --git a/jdbc/src/main/java/tech/ydb/jdbc/query/YdbQuery.java b/jdbc/src/main/java/tech/ydb/jdbc/query/YdbQuery.java index d9274b9..361ec70 100644 --- a/jdbc/src/main/java/tech/ydb/jdbc/query/YdbQuery.java +++ b/jdbc/src/main/java/tech/ydb/jdbc/query/YdbQuery.java @@ -6,9 +6,8 @@ import java.util.List; import java.util.Map; -import tech.ydb.core.StatusCode; import tech.ydb.jdbc.YdbConst; -import tech.ydb.jdbc.exception.YdbNonRetryableException; +import tech.ydb.jdbc.exception.YdbStatusException; import tech.ydb.table.query.Params; import tech.ydb.table.values.Value; @@ -52,10 +51,7 @@ public String getYqlQuery(Params params) throws SQLException { for (int idx = 0; idx < indexesArgsNames.size(); idx += 1) { String prm = indexesArgsNames.get(idx); if (!values.containsKey(prm)) { - throw new YdbNonRetryableException( - YdbConst.MISSING_VALUE_FOR_PARAMETER + prm, - StatusCode.BAD_REQUEST - ); + throw YdbStatusException.newBadRequest(YdbConst.MISSING_VALUE_FOR_PARAMETER + prm); } if (opts.isDeclareJdbcParameters()) { diff --git a/jdbc/src/main/java/tech/ydb/jdbc/query/YdbQueryBuilder.java b/jdbc/src/main/java/tech/ydb/jdbc/query/YdbQueryBuilder.java index 6371fe6..7100bdd 100644 --- a/jdbc/src/main/java/tech/ydb/jdbc/query/YdbQueryBuilder.java +++ b/jdbc/src/main/java/tech/ydb/jdbc/query/YdbQueryBuilder.java @@ -3,9 +3,8 @@ import java.util.ArrayList; import java.util.List; -import tech.ydb.core.StatusCode; import tech.ydb.jdbc.YdbConst; -import tech.ydb.jdbc.exception.YdbNonRetryableException; +import tech.ydb.jdbc.exception.YdbStatusException; /** * @@ -37,14 +36,13 @@ public String createNextArgName() { } } - public void setQueryType(QueryType type) throws YdbNonRetryableException { + public void setQueryType(QueryType type) throws YdbStatusException { if (forcedType != null) { return; } if (currentType != null && currentType != type) { - String msg = YdbConst.MULTI_TYPES_IN_ONE_QUERY + currentType + ", " + type; - throw new YdbNonRetryableException(msg, StatusCode.BAD_REQUEST); + throw YdbStatusException.newBadRequest(YdbConst.MULTI_TYPES_IN_ONE_QUERY + currentType + ", " + type); } this.currentType = type; } diff --git a/jdbc/src/test/java/tech/ydb/jdbc/impl/YdbPreparedStatementImplTest.java b/jdbc/src/test/java/tech/ydb/jdbc/impl/YdbPreparedStatementImplTest.java index be43910..0346d12 100644 --- a/jdbc/src/test/java/tech/ydb/jdbc/impl/YdbPreparedStatementImplTest.java +++ b/jdbc/src/test/java/tech/ydb/jdbc/impl/YdbPreparedStatementImplTest.java @@ -427,7 +427,7 @@ public void executeScanQueryAsUpdate() throws SQLException { statement.setInt("key", 1); statement.setString("c_Text", "value-1"); - ExceptionAssert.ydbConditionallyRetryable("Scan query should have a single result set", + ExceptionAssert.ydbNonRetryable("Scan query should have a single result set", statement::executeScanQuery); } } diff --git a/jdbc/src/test/java/tech/ydb/jdbc/impl/YdbPreparedStatementWithDataQueryImplTest.java b/jdbc/src/test/java/tech/ydb/jdbc/impl/YdbPreparedStatementWithDataQueryImplTest.java index ff4f3d6..17d6651 100644 --- a/jdbc/src/test/java/tech/ydb/jdbc/impl/YdbPreparedStatementWithDataQueryImplTest.java +++ b/jdbc/src/test/java/tech/ydb/jdbc/impl/YdbPreparedStatementWithDataQueryImplTest.java @@ -290,7 +290,7 @@ public void executeScanQueryAsUpdate() throws SQLException { statement.setInt("key", 1); statement.setString("c_Text", "value-1"); - ExceptionAssert.ydbConditionallyRetryable("Scan query should have a single result set", statement::execute); + ExceptionAssert.ydbNonRetryable("Scan query should have a single result set", statement::execute); } } } diff --git a/jdbc/src/test/java/tech/ydb/jdbc/impl/YdbStatementImplTest.java b/jdbc/src/test/java/tech/ydb/jdbc/impl/YdbStatementImplTest.java index e215bf2..fa5f231 100644 --- a/jdbc/src/test/java/tech/ydb/jdbc/impl/YdbStatementImplTest.java +++ b/jdbc/src/test/java/tech/ydb/jdbc/impl/YdbStatementImplTest.java @@ -312,7 +312,7 @@ public void executeScanQueryOnSystemTable() throws SQLException { @Test public void executeScanQueryMultiResult() { - ExceptionAssert.ydbConditionallyRetryable("Scan query should have a single result set", + ExceptionAssert.ydbNonRetryable("Scan query should have a single result set", () -> statement.executeUpdate("scan select 2 + 2;scan select 2 + 3") ); } @@ -320,7 +320,7 @@ public void executeScanQueryMultiResult() { @Test public void executeScanQueryAsUpdate() { // Looks weird - ExceptionAssert.ydbConditionallyRetryable("Scan query should have a single result set", + ExceptionAssert.ydbNonRetryable("Scan query should have a single result set", () -> statement.executeUpdate("SCAN\n" + TEST_UPSERT1_SQL) ); }