From dbc76c6ee31000d54f37a809f7f33aa6b4c29526 Mon Sep 17 00:00:00 2001 From: hutiefang76 <137664623+hutiefang76@users.noreply.github.com> Date: Wed, 9 Sep 2026 07:52:00 +0800 Subject: [PATCH] Fix SQLState for missing tables in JDBC v2 --- CHANGELOG.md | 3 ++ .../jdbc/internal/ExceptionUtils.java | 7 ++++- .../jdbc/internal/ExceptionUtilsTest.java | 31 +++++++++++++++++++ 3 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 jdbc-v2/src/test/java/com/clickhouse/jdbc/internal/ExceptionUtilsTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 9eff8d530..3f79ebedf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -137,6 +137,9 @@ ### Bug Fixes +- **[jdbc-v2]** Fixed `SQLException#getSQLState()` returning the generic data-exception state `22000` + when ClickHouse reports an unknown table. The driver now returns `42S02` (base table or view not found) while + preserving the ClickHouse error code and original exception. (https://github.com/ClickHouse/clickhouse-java/issues/3104) - **[jdbc-v2]** Fixed `DatabaseMetaData#getTables` reporting `TABLE_TYPE = TABLE` for a table with the `BigQuery` engine (present in `system.table_engines` since ClickHouse `26.8`). The engine was missing from the engine-to-table-type mapping, so it fell back to the default `TABLE`, and `getTables(..., types = {"REMOTE TABLE"})` diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/ExceptionUtils.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/ExceptionUtils.java index d0947d735..81ebb4243 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/ExceptionUtils.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/ExceptionUtils.java @@ -21,6 +21,7 @@ public final class ExceptionUtils { public static final String SQL_STATE_INVALID_SCHEMA = "3F000"; public static final String SQL_STATE_INVALID_TX_STATE = "25000"; public static final String SQL_STATE_DATA_EXCEPTION = "22000"; + private static final String SQL_STATE_TABLE_NOT_FOUND = "42S02"; // Used only when feature is not supported public static final String SQL_STATE_FEATURE_NOT_SUPPORTED = "0A000"; // Used only when method is called on wrong object type (for example, PreparedStatement.addBatch(String)) @@ -60,7 +61,11 @@ public static SQLException toSqlState(String message, String debugMessage, Excep } else if (cause instanceof ConnectionInitiationException) { return new SQLException(exceptionMessage, SQL_STATE_CONNECTION_EXCEPTION, cause); } else if (cause instanceof ServerException) { - return new SQLException(exceptionMessage, SQL_STATE_DATA_EXCEPTION, ((ServerException) cause).getCode(), cause); + int errorCode = ((ServerException) cause).getCode(); + String sqlState = errorCode == ServerException.TABLE_NOT_FOUND + ? SQL_STATE_TABLE_NOT_FOUND + : SQL_STATE_DATA_EXCEPTION; + return new SQLException(exceptionMessage, sqlState, errorCode, cause); } else if (cause instanceof ClientException) { return new SQLException(exceptionMessage, SQL_STATE_CLIENT_ERROR, cause); } else if (cause instanceof MalformedURLException) { diff --git a/jdbc-v2/src/test/java/com/clickhouse/jdbc/internal/ExceptionUtilsTest.java b/jdbc-v2/src/test/java/com/clickhouse/jdbc/internal/ExceptionUtilsTest.java new file mode 100644 index 000000000..0fc3802cb --- /dev/null +++ b/jdbc-v2/src/test/java/com/clickhouse/jdbc/internal/ExceptionUtilsTest.java @@ -0,0 +1,31 @@ +package com.clickhouse.jdbc.internal; + +import com.clickhouse.client.api.ServerException; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.sql.SQLException; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertSame; + +public class ExceptionUtilsTest { + @DataProvider + public Object[][] serverExceptions() { + return new Object[][] { + {ServerException.TABLE_NOT_FOUND, "42S02"}, + {ServerException.ErrorCodes.UNKNOWN_SETTING.getCode(), ExceptionUtils.SQL_STATE_DATA_EXCEPTION} + }; + } + + @Test(dataProvider = "serverExceptions") + public void shouldMapServerExceptionToSqlState(int errorCode, String expectedSqlState) { + ServerException cause = new ServerException(errorCode, "Server error", 400, "query-id"); + + SQLException exception = ExceptionUtils.toSqlState(cause); + + assertEquals(exception.getSQLState(), expectedSqlState); + assertEquals(exception.getErrorCode(), errorCode); + assertSame(exception.getCause(), cause); + } +}