[jdbc-v2] prepareStatement() throws unchecked ServerException/ClientException, not SQLException, when the RowBinary writer schema lookup fails
メンテナーはふだん 1 日以内に返信
まだ誰も着手していません。
評価
- 難易度
- 2/5
- 見積もり時間
- 1〜3時間
- 初心者へのやさしさ
- 82/100
- issue の種類
- バグ
- 明瞭さ
- 明確に書かれている
- 活発さ
- 活発
- 技術スタック
- java
調査の方向性
jdbc-v2/src/main/java/com/clickhouse/jdbc/ConnectionImpl.javaから始め、オーバーロードprepareStatement(String, int, int, int)とそのclient.getTableSchema呼び出しを確認します。近くのJDBCコードでExceptionUtils.toSqlStateがどのように使われているかを読み、未知のテーブル、アクセス拒否、タイムアウトなど、スキーマ検索の失敗を対象とするテストを実行するか追加します。これらの失敗が期待されるSQL stateとエラーコードを持つSQLExceptionとして公開され、検索が成功した場合も引き続き正常に動作すれば完了です。
索引モデルが issue の本文から書いたものです。
説明
Description
With the beta RowBinary writer on (beta.row_binary_for_simple_insert=true), Connection.prepareStatement("INSERT INTO t VALUES (?)") sends DESCRIBE TABLE t to the server. When that lookup fails, prepareStatement() throws the unchecked client-v2 exception (com.clickhouse.client.api.ServerException or ClientException, both RuntimeException) and not a java.sql.SQLException.
The JDBC contract of prepareStatement is throws SQLException. Callers, connection pools and frameworks catch SQLException, so this exception goes past their error handling. With the writer off, the same failures come from executeUpdate() as SQLException with the correct SQL state.
All failure types of the lookup are affected: unknown table, access denied, socket timeout, server not reachable.
Found by automated analysis of the client while working on #3268. Verified against a live server (not by inspection only).
Steps to reproduce
- Open a jdbc-v2 connection with
beta.row_binary_for_simple_insert=true. - Call
prepareStatement("INSERT INTO no_such_table VALUES (?)"), or prepare an INSERT into a table that the user cannotSHOW COLUMNSon, or let theDESCRIBE TABLErequest time out (socket_timeout). - Look at the exception type.
Error Log or Exception StackTrace
Output of the code example below (main f9586aba0, ClickHouse 26.9.1.1629):
==== writer=false
A unknown table prepareStatement -> OK (PreparedStatementImpl)
A unknown table executeUpdate -> THREW java.sql.SQLException (sqlState/code=42S02/60, root=ServerException: Code: 60. DB::Exception: Table default.no_such_table does not exist. (UNKNOWN_TABLE))
B no access prepareStatement -> OK (PreparedStatementImpl)
B no access executeUpdate -> THREW java.sql.SQLException (sqlState/code=22000/497, root=ServerException: Code: 497 ...)
C socket timeout prepareStatement -> OK (PreparedStatementImpl)
C socket timeout executeUpdate -> THREW java.sql.SQLException (sqlState/code=HY000/0, root=java.net.SocketTimeoutException: Read timed out)
==== writer=true
A unknown table prepareStatement -> THREW com.clickhouse.client.api.ServerException (instanceof SQLException=false, Code: 60 ... (UNKNOWN_TABLE))
A' existing table prepareStatement -> OK (WriterStatementImpl)
B no access prepareStatement -> THREW com.clickhouse.client.api.ServerException (instanceof SQLException=false, Code: 497 ...)
C socket timeout prepareStatement -> THREW com.clickhouse.client.api.ClientException (instanceof SQLException=false, root=java.net.SocketTimeoutException: Read timed out)
Expected Behaviour
prepareStatement() throws java.sql.SQLException for all failures of the schema lookup, with the same mapping as the rest of jdbc-v2 (ExceptionUtils.toSqlState): for example SQL state 42S02 and error code 60 for an unknown table, as the writer-off path does today.
Root cause. jdbc-v2/src/main/java/com/clickhouse/jdbc/ConnectionImpl.java:489 (prepareStatement(String, int, int, int)) calls client.getTableSchema(table, schema) without a try/catch. Client.getTableSchemaImpl (client-v2/.../Client.java:2390-2411) re-throws ServerException and wraps all other failures in ClientException. Both extend ClickHouseException extends RuntimeException, so they go out of prepareStatement() unchanged. The other overloads (prepareStatement(String), and the autoGeneratedKeys / columnIndexes / columnNames overloads with ignore_unsupported_values) delegate to this method, so they are affected too.
Suggested fix. Catch the failure of the getTableSchema call and re-throw it with ExceptionUtils.toSqlState(e), which already maps ServerException (incl. TABLE_NOT_FOUND → 42S02) and ClientException. Keep the current behavior when the lookup succeeds, and keep the writer-off path (no server call in prepareStatement) as it is.
Code Example
// user "lead_noacc" has no grants: CREATE USER lead_noacc IDENTIFIED WITH plaintext_password BY 'pw'
// table: CREATE TABLE default.lead_t (id Int32) ENGINE = Memory
Properties p = new Properties();
p.setProperty("user", "default");
p.setProperty("password", "");
p.setProperty("beta.row_binary_for_simple_insert", "true");
try (Connection c = new com.clickhouse.jdbc.Driver().connect("jdbc:clickhouse://localhost:8123/default", p)) {
try {
c.prepareStatement("INSERT INTO no_such_table VALUES (?)");
} catch (SQLException e) {
System.out.println("SQLException " + e.getSQLState()); // expected
} catch (RuntimeException e) {
System.out.println("unchecked " + e.getClass().getName()); // actual: com.clickhouse.client.api.ServerException
}
}
// Same result for user "lead_noacc" with "INSERT INTO default.lead_t VALUES (?)" (ServerException, code 497),
// and ClientException (root SocketTimeoutException) when socket_timeout expires during DESCRIBE TABLE.
Configuration
Client Configuration
beta.row_binary_for_simple_insert=true
// timeout case only: socket_timeout=1000, with the DESCRIBE TABLE response delayed
Environment
- Cloud
- Client version: jdbc-v2 from
main(f9586aba0, 0.12.0-rc1-SNAPSHOT) - Language version: JDK 17
- OS: Linux (Docker)
ClickHouse Server
- ClickHouse Server version: 26.9.1.1629
- ClickHouse Server non-default settings, if any: none
CREATE TABLEstatements for tables involved:CREATE TABLE default.lead_t (id Int32) ENGINE = Memory- Sample data for all these tables: not needed
- 主要言語
- Java
- スター
- 1.6k
- フォーク
- 649
- 平均マージ
- 1日 20時間
- マージ済み PR(30日)
- 39
環境構築
- Dockerfile・Docker Compose ファイルなし
- プルリクエストのテンプレートあり
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
ClickHouse/clickhouse-java のほかの issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
ClickHouse/clickhouse-java#3349 ·
メンテナーはふだん 1 日以内に返信
-
bug jdbc-v2
難易度 2/5 1〜3時間 初心者へのやさしさ 85/100
ClickHouse/clickhouse-java#3347 ·
メンテナーはふだん 1 日以内に返信
-
[client-v2] Enum8/Enum16 insert accepts a non-member integer and uses Enum.ordinal(), silently storing values that are not enum members対応中かも @jaya232 が 6 日前に担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
ClickHouse/clickhouse-java#3304 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
bug jdbc-v2
難易度 2/5 1〜3時間 初心者へのやさしさ 70/100
ClickHouse/clickhouse-java#3214 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
ClickHouse/clickhouse-java#3212 ·
メンテナーはふだん 1 日以内に返信
ClickHouse/clickhouse-java の issue をすべて見る
似ている issue
-
bug
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
メンテナーはふだん 1 日以内に返信
-
bug:unconfirmed
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
UniversalMediaServer/UniversalMediaServer#6473 ·
メンテナーはふだん 1 日以内に返信
-
難易度 1/5 1時間未満 初心者へのやさしさ 82/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
StarRocks/starrocks#83246 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
メンテナーはふだん 2 日以内に返信