From cc3810bebcb18bf0aa9667521445dc3be7c2f98f Mon Sep 17 00:00:00 2001 From: Zhang Zhenhao <741788769@qq.com> Date: Wed, 21 Oct 2026 16:57:25 +0800 Subject: [PATCH] IGNITE-12381 SQL: Propagate H2 error codes to IgniteSQLException --- .../processors/query/h2/H2Connection.java | 5 +- .../internal/processors/query/h2/H2Utils.java | 35 ++++++++++++++ .../processors/query/h2/QueryParser.java | 3 +- .../IgniteCacheSqlQueryErrorSelfTest.java | 48 +++++++++++++++++++ 4 files changed, 87 insertions(+), 4 deletions(-) diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2Connection.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2Connection.java index 0f088258769d0..8faf8c8d91e83 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2Connection.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2Connection.java @@ -24,7 +24,6 @@ import java.sql.SQLException; import java.util.Objects; import org.apache.ignite.IgniteLogger; -import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode; import org.apache.ignite.internal.processors.query.IgniteSQLException; import org.apache.ignite.internal.processors.query.h2.sql.GridSqlQueryParser; import org.apache.ignite.internal.util.typedef.internal.S; @@ -164,7 +163,7 @@ PreparedStatement prepareStatement(String sql, byte qryFlags) { return stmt; } catch (SQLException e) { - throw new IgniteSQLException("Failed to parse query. " + e.getMessage(), IgniteQueryErrorCode.PARSING, e); + throw new IgniteSQLException("Failed to parse query. " + e.getMessage(), H2Utils.h2ErrorCode(e.getErrorCode()), e); } } @@ -209,7 +208,7 @@ PreparedStatement prepareStatementNoCache(String sql) { return conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); } catch (SQLException e) { - throw new IgniteSQLException("Failed to parse query. " + e.getMessage(), IgniteQueryErrorCode.PARSING, e); + throw new IgniteSQLException("Failed to parse query. " + e.getMessage(), H2Utils.h2ErrorCode(e.getErrorCode()), e); } } diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2Utils.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2Utils.java index 322cfb1f6e203..8541243ab982b 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2Utils.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2Utils.java @@ -74,6 +74,7 @@ import org.apache.ignite.internal.util.typedef.F; import org.apache.ignite.internal.util.typedef.internal.SB; import org.apache.ignite.internal.util.typedef.internal.U; +import org.h2.api.ErrorCode; import org.h2.engine.Session; import org.h2.index.Index; import org.h2.jdbc.JdbcConnection; @@ -1050,6 +1051,40 @@ public static Setter setter(Class cls, String fldName) return new Setter<>(field); } + /** + * Maps H2 error code to the matching Ignite query error code when possible. + * + * @param h2ErrCode H2 error code. + * @return Ignite query error code, or the default parsing code when there is no more specific counterpart. + */ + public static int h2ErrorCode(int h2ErrCode) { + switch (h2ErrCode) { + case ErrorCode.TABLE_OR_VIEW_ALREADY_EXISTS_1: + return IgniteQueryErrorCode.TABLE_ALREADY_EXISTS; + + case ErrorCode.TABLE_OR_VIEW_NOT_FOUND_1: + return IgniteQueryErrorCode.TABLE_NOT_FOUND; + + case ErrorCode.INDEX_ALREADY_EXISTS_1: + return IgniteQueryErrorCode.INDEX_ALREADY_EXISTS; + + case ErrorCode.INDEX_NOT_FOUND_1: + return IgniteQueryErrorCode.INDEX_NOT_FOUND; + + case ErrorCode.DUPLICATE_COLUMN_NAME_1: + return IgniteQueryErrorCode.COLUMN_ALREADY_EXISTS; + + case ErrorCode.COLUMN_NOT_FOUND_1: + return IgniteQueryErrorCode.COLUMN_NOT_FOUND; + + case ErrorCode.SCHEMA_NOT_FOUND_1: + return IgniteQueryErrorCode.SCHEMA_NOT_FOUND; + + default: + return IgniteQueryErrorCode.PARSING; + } + } + /** * Field getter. */ diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/QueryParser.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/QueryParser.java index c805564fc524e..f5b85eb480e57 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/QueryParser.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/QueryParser.java @@ -516,7 +516,8 @@ else if (!prepared.isQuery()) { IgniteQueryErrorCode.DB_UNRECOVERABLE_ERROR, e); } else - throw new IgniteSQLException("Failed to parse query. " + e.getMessage(), IgniteQueryErrorCode.PARSING, e); + throw new IgniteSQLException("Failed to parse query. " + e.getMessage(), + H2Utils.h2ErrorCode(e.getErrorCode()), e); } catch (IgniteCheckedException e) { throw new IgniteSQLException("Failed to parse query. " + e.getMessage(), IgniteQueryErrorCode.PARSING, e); diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheSqlQueryErrorSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheSqlQueryErrorSelfTest.java index 932fa42a0fe28..5d4e540ca7f83 100644 --- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheSqlQueryErrorSelfTest.java +++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheSqlQueryErrorSelfTest.java @@ -20,6 +20,9 @@ import java.util.concurrent.Callable; import javax.cache.CacheException; import org.apache.ignite.cache.query.SqlFieldsQuery; +import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode; +import org.apache.ignite.internal.processors.query.IgniteSQLException; +import org.apache.ignite.internal.util.typedef.X; import org.apache.ignite.testframework.GridTestUtils; import org.junit.Test; @@ -194,6 +197,51 @@ public void testDdlWrongSyntax() throws Exception { "Failed to parse query. Syntax error in SQL statement \"ALTER TABLE TEST DROP COLUMN [*]"); } + /** + * Checks error codes of the queries failed to parse. + * + * @throws Exception If failed. + */ + @Test + public void testParseErrorCodes() throws Exception { + checkSqlErrorCode("select from wrong", IgniteQueryErrorCode.TABLE_NOT_FOUND); + checkSqlErrorCode("select wrong from test", IgniteQueryErrorCode.COLUMN_NOT_FOUND); + checkSqlErrorCode("select * from wrong_schema.test", IgniteQueryErrorCode.SCHEMA_NOT_FOUND); + checkSqlErrorCode("insert into wrong (id, val) values (3, 'val3')", IgniteQueryErrorCode.TABLE_NOT_FOUND); + checkSqlErrorCode("alter table wrong drop column val", IgniteQueryErrorCode.TABLE_NOT_FOUND); + + // Generic syntax errors must be reported with the default parsing code. + checkSqlErrorCode("select from test where", IgniteQueryErrorCode.PARSING); + } + + /** + * Checks SQL error code. + * + * @param sql SQL command. + * @param expCode Expected error code. + */ + private void checkSqlErrorCode(final String sql, int expCode) { + execute("DROP TABLE IF EXISTS wrong"); + execute("DROP TABLE IF EXISTS test"); + + execute("CREATE TABLE test (id INT PRIMARY KEY, val VARCHAR)"); + + execute("INSERT INTO test (id, val) VALUES (1, 'val1')"); + + try { + execute(sql); + + fail("Exception is expected: " + sql); + } + catch (CacheException e) { + IgniteSQLException sqlEx = X.cause(e, IgniteSQLException.class); + + assertNotNull("Failed to find SQL error for query: " + sql, sqlEx); + + assertEquals("Unexpected error code for query: " + sql, expCode, sqlEx.statusCode()); + } + } + /** * Checks SQL error message. *