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

Reply via email to