[jdbc-v2] Connection.setSchema is ignored by the RowBinary writer INSERT path (rows go to the connection's configured database)
I maintainer di solito rispondono entro 1 giorno
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 2/5
- Tempo stimato
- 1-3 ore
- Idoneità per principianti
- 72/100
Direzione di ricerca
Inizia da jdbc-v2/src/main/java/com/clickhouse/jdbc/WriterStatementImpl.java in executeLargeUpdate, dove InsertSettings viene costruito senza un database (intorno alle righe 117-124), e confronta con come ConnectionImpl.setSchema imposta le impostazioni di query predefinite. Esegui il test di riproduzione nell'issue con il flag beta del writer RowBinary attivo e disattivato. È completato quando la riga finisce nel database selezionato con setSchema e il caso in cui la tabella esiste solo lì non genera più UNKNOWN_TABLE.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Description
In jdbc-v2, Connection.setSchema(db) updates the connection's defaultQuerySettings (ConnectionImpl.setSchema, jdbc-v2/src/main/java/com/clickhouse/jdbc/ConnectionImpl.java:671-675), and StatementImpl/PreparedStatementImpl pick that up because they merge connection.getDefaultQuerySettings() into their local QuerySettings.
The beta RowBinary writer path does not. When beta.row_binary_for_simple_insert=true and the INSERT qualifies for the writer, ConnectionImpl.prepareStatement creates a WriterStatementImpl, and WriterStatementImpl.executeLargeUpdate builds a fresh InsertSettings with no database:
InsertSettings settings = new InsertSettings();
settings.setQueryId(setLastQueryID(settings.getQueryId()));
... connection.getClient().insert(tableSchema.getTableName(), in, writer.getFormat(), settings)
(jdbc-v2/src/main/java/com/clickhouse/jdbc/WriterStatementImpl.java:117-124)
So the INSERT is sent with the database from the client configuration (the one from the JDBC URL / database property), not the one selected with setSchema. Note that the schema probe does use the right database — client.getTableSchema(table, schema) at ConnectionImpl.java:486 — so when a table with the same name exists in both databases there is no error at all: the rows silently land in the wrong database.
Consequences:
- Table exists in both databases → rows are written to the connection's configured database, silently. Data loss / data in the wrong place.
- Table exists only in the database selected with
setSchema→Code: 60 ... Table default.<t> does not exist. (UNKNOWN_TABLE), even though the schema lookup for the same table succeeded a moment earlier.
The non-writer path (PreparedStatementImpl, i.e. with the beta flag off) behaves correctly.
ClickHouse server version
26.9.18.11 (official build), local HTTP server, verified against a running server.
Reproduction
TestNG test (jdbc-v2), run against a local server on localhost:8123:
package com.clickhouse.jdbc;
import org.testng.Assert;
import org.testng.annotations.Test;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Properties;
public class SetSchemaWriterTest {
private static final String URL = "jdbc:ch:http://localhost:8123/default";
private Connection conn(Properties extra) throws Exception {
Properties info = new Properties();
info.setProperty("user", "default");
info.setProperty("password", "");
if (extra != null) {
info.putAll(extra);
}
return new ConnectionImpl(URL, info);
}
private void exec(String sql) throws Exception {
try (Connection c = conn(null); Statement s = c.createStatement()) {
s.execute(sql);
}
}
private int count(String fqTable) throws Exception {
try (Connection c = conn(null);
Statement s = c.createStatement();
ResultSet rs = s.executeQuery("SELECT count() FROM " + fqTable)) {
rs.next();
return rs.getInt(1);
}
}
@Test
public void testSetSchemaRespectedByWriterStatement() throws Exception {
String otherDb = "scratch_other_db";
String table = "scratch_t";
exec("DROP DATABASE IF EXISTS " + otherDb);
exec("CREATE DATABASE " + otherDb);
exec("CREATE TABLE " + otherDb + "." + table + " (x UInt32) ENGINE = Memory");
exec("DROP TABLE IF EXISTS default." + table);
exec("CREATE TABLE default." + table + " (x UInt32) ENGINE = Memory");
Properties props = new Properties();
props.setProperty(DriverProperties.BETA_ROW_BINARY_WRITER.getKey(), "true");
try (Connection c = conn(props)) {
c.setSchema(otherDb);
try (Statement s = c.createStatement();
ResultSet rs = s.executeQuery("SELECT currentDatabase()")) {
rs.next();
System.out.println("currentDatabase() via Statement = " + rs.getString(1)); // scratch_other_db
}
try (PreparedStatement ps = c.prepareStatement("INSERT INTO " + table + " VALUES (?)")) {
System.out.println("stmt class = " + ps.getClass().getName()); // WriterStatementImpl
ps.setInt(1, 1);
ps.executeUpdate();
}
}
Assert.assertEquals(count(otherDb + "." + table), 1, "row should land in the schema set via setSchema");
Assert.assertEquals(count("default." + table), 0);
}
}
Observed output and result:
currentDatabase() via Statement = scratch_other_db
stmt class = com.clickhouse.jdbc.WriterStatementImpl
rows in scratch_other_db.scratch_t = 0, rows in default.scratch_t = 1
FAILED: row should land in the schema set via setSchema expected [1] but found [0]
Expected: 1 row in scratch_other_db.scratch_t, 0 in default.scratch_t.
Actual: the row is written to default.scratch_t.
Same test with the beta flag off (statement is PreparedStatementImpl) passes: the row lands in scratch_other_db.
Variant where the table exists only in the database set by setSchema (no default.scratch_only_t), same writer path:
java.sql.SQLException: Code: 60. DB::Exception: Table default.scratch_only_t does not exist.
Maybe you meant scratch_other_db3.scratch_only_t?. (UNKNOWN_TABLE) (version 26.9.18.11 (official build))
Suggested fix
In WriterStatementImpl.executeLargeUpdate (jdbc-v2/src/main/java/com/clickhouse/jdbc/WriterStatementImpl.java:119), set the database on the InsertSettings from the connection at write time, e.g. settings.setDatabase(connection.getSchema()) (or the database the TableSchema was resolved against, tableSchema.getDatabaseName()), so the INSERT targets the same database that the schema probe used. Reading it at execution time also makes a setSchema call after prepareStatement behave sensibly.
InsertSettings.setDatabase already exists (client-v2/src/main/java/com/clickhouse/client/api/insert/InsertSettings.java:197) and drives the request database.
Link
Analogous issue reported for the .NET client: https://github.com/ClickHouse/clickhouse-cs/issues/683 (ClickHouseConnection.PostStreamAsync and ClickHouseBulkCopy ignore ChangeDatabase). Found by automated cross-client analysis (Polyglot AI) and verified against a live server.
- Lingua principale
- Java
- Stelle
- 1.6k
- Fork
- 640
- Merge medio
- 1g 12h
- PR unite (30g)
- 42
Preparare l'ambiente
- Nessun Dockerfile né file Docker Compose
- Ha un modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di ClickHouse/clickhouse-java
-
bug jdbc-v2
Difficoltà 2/5 1-3 ore Idoneità per principianti 70/100
ClickHouse/clickhouse-java#3214 ·
I maintainer di solito rispondono entro 1 giorno
-
bug client-api-v2 test
Difficoltà 2/5 1-3 ore Idoneità per principianti 92/100
ClickHouse/clickhouse-java#3076 ·
I maintainer di solito rispondono entro 1 giorno
-
area:sql-parser bug client-v1
Difficoltà 1/5 1-3 ore Idoneità per principianti 92/100
ClickHouse/clickhouse-java#3066 ·
I maintainer di solito rispondono entro 1 giorno
-
bug client-api-v2 jdbc-v2
Difficoltà 1/5 1-3 ore Idoneità per principianti 78/100
ClickHouse/clickhouse-java#2957 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
bug client-v1 wontfix
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
ClickHouse/clickhouse-java#2895 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di ClickHouse/clickhouse-java
Issue simili
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 61/100
Vakore/ZappierGames#162 ·
-
enhancement
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
I maintainer di solito rispondono entro 1 giorno
-
[Bug] Lock owned by another client (e.g. Text app) cannot be unlocked from the appForse già presa @falkloewert l’ha presa oggi. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
I maintainer di solito rispondono entro 1 giorno
-
bug grammar sev-0
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 86/100
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100