This is an automated email from the ASF dual-hosted git repository.
ppa pushed a commit to branch jdbc_over_thin_sql
in repository https://gitbox.apache.org/repos/asf/ignite-3.git
The following commit(s) were added to refs/heads/jdbc_over_thin_sql by this
push:
new 20d3410c1da IGNITE-26790 Cli. SqlExceptionHandler. Correctly translate
SQL exceptions (#6934)
20d3410c1da is described below
commit 20d3410c1daff8c0e5bf534c79d9073596602efc
Author: Max Zhuravkov <[email protected]>
AuthorDate: Wed Nov 12 12:05:00 2025 +0000
IGNITE-26790 Cli. SqlExceptionHandler. Correctly translate SQL exceptions
(#6934)
---
.../cli/commands/sql/ItSqlCommandTest.java | 21 +--
.../cli/commands/sql/ItSqlReplCommandTest.java | 6 +-
.../exception/handler/SqlExceptionHandler.java | 150 +++++++++++----------
3 files changed, 88 insertions(+), 89 deletions(-)
diff --git
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/sql/ItSqlCommandTest.java
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/sql/ItSqlCommandTest.java
index 1c691db8c40..058e7e4f9a0 100644
---
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/sql/ItSqlCommandTest.java
+++
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/sql/ItSqlCommandTest.java
@@ -17,7 +17,6 @@
package org.apache.ignite.internal.cli.commands.sql;
-import static
org.apache.ignite.internal.cli.core.exception.handler.SqlExceptionHandler.CLIENT_CONNECTION_FAILED_MESSAGE;
import static org.junit.jupiter.api.Assertions.assertAll;
import org.junit.jupiter.api.DisplayName;
@@ -100,7 +99,7 @@ class ItSqlCommandTest extends CliSqlCommandTestBase {
assertAll(
() -> assertExitCodeIs(1),
this::assertOutputIsEmpty,
- () -> assertErrOutputContains(CLIENT_CONNECTION_FAILED_MESSAGE)
+ () -> assertErrOutputContains("Connection failed")
);
}
@@ -180,8 +179,7 @@ class ItSqlCommandTest extends CliSqlCommandTestBase {
assertAll(
this::assertOutputIsEmpty,
- // TODO https://issues.apache.org/jira/browse/IGNITE-26790
- // () -> assertErrOutputContains("SQL query execution error"),
+ () -> assertErrOutputContains("SQL query execution error"),
() -> assertErrOutputContains("Division by zero"),
() -> assertErrOutputDoesNotContain("Unknown error")
);
@@ -190,8 +188,7 @@ class ItSqlCommandTest extends CliSqlCommandTestBase {
assertAll(
this::assertOutputIsEmpty,
- // TODO https://issues.apache.org/jira/browse/IGNITE-26790
- // () -> assertErrOutputContains("SQL query execution error"),
+ () -> assertErrOutputContains("SQL query validation error"),
() -> assertErrOutputContains("Object 'NOTEXISTEDTABLE' not
found"),
() -> assertErrOutputDoesNotContain("Unknown error")
);
@@ -207,8 +204,7 @@ class ItSqlCommandTest extends CliSqlCommandTestBase {
assertAll(
this::assertOutputIsEmpty,
- // TODO https://issues.apache.org/jira/browse/IGNITE-26790
- // () -> assertErrOutputContains("SQL query execution
error"),
+ () -> assertErrOutputContains("SQL query execution error"),
() -> assertErrOutputContains(expectedError),
() -> assertErrOutputDoesNotContain("Unknown error")
);
@@ -219,8 +215,7 @@ class ItSqlCommandTest extends CliSqlCommandTestBase {
assertAll(
this::assertOutputIsEmpty,
- // TODO https://issues.apache.org/jira/browse/IGNITE-26790
- // () -> assertErrOutputContains("SQL query execution
error"),
+ () -> assertErrOutputContains("SQL query execution error"),
() -> assertErrOutputContains(expectedError),
() -> assertErrOutputDoesNotContain("Unknown error")
);
@@ -231,8 +226,7 @@ class ItSqlCommandTest extends CliSqlCommandTestBase {
assertAll(
this::assertOutputIsEmpty,
- // TODO https://issues.apache.org/jira/browse/IGNITE-26790
- // () -> assertErrOutputContains("SQL query execution
error"),
+ () -> assertErrOutputContains("SQL query execution error"),
() -> assertErrOutputContains(expectedError),
() -> assertErrOutputDoesNotContain("Unknown error")
);
@@ -243,8 +237,7 @@ class ItSqlCommandTest extends CliSqlCommandTestBase {
assertAll(
this::assertOutputIsEmpty,
- // TODO https://issues.apache.org/jira/browse/IGNITE-26790
- // () -> assertErrOutputContains("SQL query execution
error"),
+ () -> assertErrOutputContains("SQL query execution error"),
() -> assertErrOutputContains(expectedError),
() -> assertErrOutputDoesNotContain("Unknown error")
);
diff --git
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/sql/ItSqlReplCommandTest.java
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/sql/ItSqlReplCommandTest.java
index 7c672122bc9..995d8c921b2 100644
---
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/sql/ItSqlReplCommandTest.java
+++
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/sql/ItSqlReplCommandTest.java
@@ -103,8 +103,7 @@ class ItSqlReplCommandTest extends CliIntegrationTest {
assertAll(
this::assertOutputIsEmpty,
- // TODO https://issues.apache.org/jira/browse/IGNITE-26790
- // () -> assertErrOutputContains("SQL query execution error"),
+ () -> assertErrOutputContains("SQL query execution error"),
() -> assertErrOutputContains("Division by zero"),
() -> assertErrOutputDoesNotContain("Unknown error")
);
@@ -113,8 +112,7 @@ class ItSqlReplCommandTest extends CliIntegrationTest {
assertAll(
this::assertOutputIsEmpty,
- // TODO https://issues.apache.org/jira/browse/IGNITE-26790
- // () -> assertErrOutputContains("SQL query execution error"),
+ () -> assertErrOutputContains("SQL query validation error"),
() -> assertErrOutputContains("Object 'NOTEXISTEDTABLE' not
found"),
() -> assertErrOutputDoesNotContain("Unknown error")
);
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/exception/handler/SqlExceptionHandler.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/exception/handler/SqlExceptionHandler.java
index f3dc69c3584..4bdd8ae18d7 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/exception/handler/SqlExceptionHandler.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/exception/handler/SqlExceptionHandler.java
@@ -22,15 +22,12 @@ import static
org.apache.ignite.lang.ErrorGroups.extractCauseMessage;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
-import java.util.UUID;
import java.util.function.Function;
import javax.net.ssl.SSLHandshakeException;
import org.apache.ignite.client.IgniteClientConnectionException;
import org.apache.ignite.internal.cli.core.exception.ExceptionHandler;
import org.apache.ignite.internal.cli.core.exception.ExceptionWriter;
import org.apache.ignite.internal.cli.core.style.component.ErrorUiComponent;
-import
org.apache.ignite.internal.cli.core.style.component.ErrorUiComponent.ErrorComponentBuilder;
-import org.apache.ignite.internal.jdbc.proto.SqlStateCode;
import org.apache.ignite.internal.logger.IgniteLogger;
import org.apache.ignite.internal.logger.Loggers;
import org.apache.ignite.internal.util.ExceptionUtils;
@@ -49,33 +46,23 @@ public class SqlExceptionHandler implements
ExceptionHandler<SQLException> {
private static final IgniteLogger LOG =
Loggers.forClass(SqlExceptionHandler.class);
- public static final String PARSING_ERROR_MESSAGE = "SQL query parsing
error";
+ private static final String CLIENT_CONNECTION_FAILED_MESSAGE = "Connection
failed";
- public static final String INVALID_PARAMETER_MESSAGE = "Invalid parameter
value";
+ private static final String UNRECOGNIZED_ERROR_MESSAGE = "Unrecognized
error while processing SQL query ";
- public static final String CLIENT_CONNECTION_FAILED_MESSAGE = "Connection
failed";
-
- public static final String CONNECTION_BROKE_MESSAGE = "Connection error";
-
- public static final String UNRECOGNIZED_ERROR_MESSAGE = "Unrecognized
error while processing SQL query ";
-
- private final Map<Integer, Function<IgniteException,
ErrorComponentBuilder>> sqlExceptionMappers = new HashMap<>();
+ private final Map<Short, Function<IgniteException, ErrorUiComponent>>
sqlExceptionMappers = new HashMap<>();
/** Default constructor. */
private SqlExceptionHandler() {
- sqlExceptionMappers.put(Client.CONNECTION_ERR,
SqlExceptionHandler::connectionErrUiComponent);
- sqlExceptionMappers.put(Sql.STMT_PARSE_ERR,
SqlExceptionHandler::sqlParseErrUiComponent);
+ sqlExceptionMappers.put(Client.CLIENT_ERR_GROUP.groupCode(),
SqlExceptionHandler::connectionErrUiComponent);
+ sqlExceptionMappers.put(Sql.SQL_ERR_GROUP.groupCode(),
SqlExceptionHandler::sqlErrUiComponent);
}
- private static ErrorComponentBuilder
sqlParseErrUiComponent(IgniteException e) {
- return fromExWithHeader(PARSING_ERROR_MESSAGE, e.code(), e.traceId(),
e.getMessage());
- }
-
- private static ErrorComponentBuilder
unrecognizedErrComponent(IgniteException e) {
- return fromExWithHeader(UNRECOGNIZED_ERROR_MESSAGE, e.code(),
e.traceId(), e.getMessage());
- }
+ private static ErrorUiComponent connectionErrUiComponent(IgniteException
e) {
+ if (e.code() != Client.CONNECTION_ERR) {
+ return fromIgniteException("Client error", e);
+ }
- private static ErrorComponentBuilder
connectionErrUiComponent(IgniteException e) {
if (e.getCause() instanceof IgniteClientConnectionException) {
IgniteClientConnectionException cause =
(IgniteClientConnectionException) e.getCause();
@@ -84,7 +71,8 @@ public class SqlExceptionHandler implements
ExceptionHandler<SQLException> {
return ErrorUiComponent.builder()
.header("Could not connect to node. Check
authentication configuration")
.details(invalidCredentialsException.getMessage())
- .verbose(extractCauseMessage(cause.getMessage()));
+ .verbose(extractCauseMessage(cause.getMessage()))
+ .build();
}
SSLHandshakeException sslHandshakeException = findCause(cause,
SSLHandshakeException.class);
@@ -92,13 +80,14 @@ public class SqlExceptionHandler implements
ExceptionHandler<SQLException> {
return ErrorUiComponent.builder()
.header("Could not connect to node. Check SSL
configuration")
.details(sslHandshakeException.getMessage())
- .verbose(extractCauseMessage(cause.getMessage()));
+ .verbose(extractCauseMessage(cause.getMessage()))
+ .build();
}
- return fromExWithHeader(CLIENT_CONNECTION_FAILED_MESSAGE,
cause.code(), cause.traceId(), cause.getMessage());
+ return fromIgniteException(CLIENT_CONNECTION_FAILED_MESSAGE,
cause);
}
- return fromExWithHeader(CLIENT_CONNECTION_FAILED_MESSAGE, e.code(),
e.traceId(), e.getMessage());
+ return fromIgniteException(CLIENT_CONNECTION_FAILED_MESSAGE, e);
}
@Nullable
@@ -112,67 +101,86 @@ public class SqlExceptionHandler implements
ExceptionHandler<SQLException> {
return null;
}
- private static ErrorComponentBuilder fromExWithHeader(String header, int
errorCode, UUID traceId, String message) {
- return ErrorUiComponent.builder()
- .header(header)
- .errorCode(String.valueOf(errorCode))
- .traceId(traceId)
- .details(extractCauseMessage(message));
- }
-
@Override
public int handle(ExceptionWriter err, SQLException e) {
Throwable unwrappedCause = ExceptionUtils.unwrapCause(e.getCause());
+ ErrorUiComponent errorComponent;
+
if (unwrappedCause instanceof IgniteException) {
- return handleIgniteException(err, (IgniteException)
unwrappedCause);
- }
+ IgniteException igniteException = (IgniteException) unwrappedCause;
- if (unwrappedCause instanceof IgniteCheckedException) {
- return handleIgniteCheckedException(err, (IgniteCheckedException)
unwrappedCause);
- }
+ errorComponent = sqlExceptionMappers.getOrDefault(
+ igniteException.groupCode(),
+ SqlExceptionHandler::otherIgniteException
+ ).apply(igniteException);
+
+ } else if (unwrappedCause instanceof IgniteCheckedException) {
+ IgniteCheckedException checkedError = (IgniteCheckedException)
unwrappedCause;
+
+ errorComponent =
fromCheckedIgniteException(UNRECOGNIZED_ERROR_MESSAGE, checkedError);
+ } else {
+ LOG.warn("Unrecognized exception", e);
- var errorComponentBuilder = ErrorUiComponent.builder();
-
- switch (e.getSQLState()) {
- case SqlStateCode.CONNECTION_FAILURE:
- case SqlStateCode.CONNECTION_CLOSED:
- case SqlStateCode.CONNECTION_REJECTED:
-
errorComponentBuilder.header(CONNECTION_BROKE_MESSAGE).verbose(extractCauseMessage(e.getMessage()));
- break;
- case SqlStateCode.PARSING_EXCEPTION:
-
errorComponentBuilder.header(PARSING_ERROR_MESSAGE).details(extractCauseMessage(e.getMessage()));
- break;
- case SqlStateCode.INVALID_PARAMETER_VALUE:
-
errorComponentBuilder.header(INVALID_PARAMETER_MESSAGE).verbose(extractCauseMessage(e.getMessage()));
- break;
- case SqlStateCode.CLIENT_CONNECTION_FAILED:
-
errorComponentBuilder.header(CLIENT_CONNECTION_FAILED_MESSAGE).verbose(extractCauseMessage(e.getMessage()));
- break;
- default:
- LOG.error("Unrecognized error", e);
- errorComponentBuilder.header("SQL query execution
error").details(e.getMessage());
+ errorComponent = ErrorUiComponent.builder()
+ .header("Unrecognized exception")
+ .details(String.valueOf(unwrappedCause))
+ .build();
}
- err.write(errorComponentBuilder.build().render());
+ err.write(errorComponent.render());
return 1;
}
- /** Handles IgniteException that has more information like error code and
trace id. */
- private int handleIgniteException(ExceptionWriter err, IgniteException e) {
- var errorComponentBuilder = sqlExceptionMappers.getOrDefault(e.code(),
SqlExceptionHandler::unrecognizedErrComponent);
+ private static ErrorUiComponent sqlErrUiComponent(IgniteException e) {
+ String header;
+
+ if (e.code() == Sql.STMT_PARSE_ERR) {
+ header = "SQL query parsing error";
+ } else if (e.code() == Sql.STMT_VALIDATION_ERR) {
+ header = "SQL query validation error";
+ } else if (e.code() == Sql.CONSTRAINT_VIOLATION_ERR) {
+ header = "Constraint violation";
+ } else if (e.code() == Sql.EXECUTION_CANCELLED_ERR) {
+ header = "Query was cancelled";
+ } else if (e.code() == Sql.RUNTIME_ERR) {
+ header = "SQL query execution error";
+ } else if (e.code() == Sql.MAPPING_ERR) {
+ header = "Unable to map query on current cluster topology";
+ } else if (e.code() == Sql.TX_CONTROL_INSIDE_EXTERNAL_TX_ERR) {
+ header = "Unexpected SQL statement";
+ } else {
+ header = "SQL error";
+ }
- String renderedError = errorComponentBuilder.apply(e).build().render();
- err.write(renderedError);
+ return fromIgniteException(header, e);
+ }
- return 1;
+ private static ErrorUiComponent fromIgniteException(String header,
IgniteException e) {
+ // Header
+ // GROUP_NAME-CODE: ...
+ return ErrorUiComponent.builder()
+ .header(header)
+ .errorCode(String.valueOf(e.code()))
+ .traceId(e.traceId())
+ .details(e.codeAsString() + ": " +
extractCauseMessage(e.getMessage()))
+ .build();
}
- private static int handleIgniteCheckedException(ExceptionWriter err,
IgniteCheckedException e) {
- String renderedError = fromExWithHeader(UNRECOGNIZED_ERROR_MESSAGE,
e.code(), e.traceId(), e.getMessage())
- .build().render();
- err.write(renderedError);
+ private static ErrorUiComponent fromCheckedIgniteException(String header,
IgniteCheckedException e) {
+ // Header
+ // GROUP_NAME-CODE: ...
+ return ErrorUiComponent.builder()
+ .header(header)
+ .errorCode(String.valueOf(e.code()))
+ .traceId(e.traceId())
+ .details(e.codeAsString() + ": " +
extractCauseMessage(e.getMessage()))
+ .build();
+ }
- return 1;
+ private static ErrorUiComponent otherIgniteException(IgniteException e) {
+ // GROUP_NAME error
+ // GROUP_NAME-CODE: ...
+ return fromIgniteException(e.groupName() + " error", e);
}
@Override