Skip to content

[backfill: ClickHouse/clickhouse-java] jdbc-v2 INSERT with backtick-quoted column names throws NoSuchColumnException under BETA_ROW_BINARY_WRITER #2978

Description

@alex-clickhouse

Target client repo

ClickHouse/clickhouse-java

Severity

sev:1 — opt-in surface (only triggers when the BETA_ROW_BINARY_WRITER beta feature is enabled), visible exception (not silent corruption), and the workaround is a one-line edit (drop the backticks from the column list).

Description

When a PreparedStatement is created for an INSERT INTO t (\c1`, `c2`) VALUES (?, ?)-style SQL while the beta clickhouse.jdbc.beta.row_binary_writerdriver property is enabled,jdbc-v2's parser extracts the column names verbatim with the surrounding backticks. Those quoted names are then used as keys against TableSchema.colIndex, which stores bare (unquoted) names, and the lookup throws NoSuchColumnException`.

This is the JDBC-v2 mirror of clickhouse-go's conn_http_batch.go bug: column names parsed from the SQL are not unescaped before being matched against the server-reported schema.

Code path:

  1. jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/SqlParserFacade.java line 343–348 (enterInsertStmt):

    List<ClickHouseParser.NestedIdentifierContext> names = columns.nestedIdentifier();
    String[] insertColumns = new String[names.size()];
    for (int i = 0; i < names.size(); i++) {
        insertColumns[i] = names.get(i).getText();   // raw token text, keeps backticks
    }
    parsedStatement.setInsertColumns(insertColumns);

    Compare with the table identifier handling a few lines below, which does call ClickHouseSqlUtils.unescape(...).

  2. jdbc-v2/src/main/java/com/clickhouse/jdbc/WriterStatementImpl.java line 57–63:

    if (parsedStatement.getInsertColumns() != null) {
        List<ClickHouseColumn> insertColumns = new ArrayList<>();
        for (String column : parsedStatement.getInsertColumns()) {
            insertColumns.add(tableSchema.getColumnByName(column));   // NoSuchColumnException
        }
        ...
    }
  3. client-v2/src/main/java/com/clickhouse/client/api/metadata/TableSchema.java nameToIndex throws:

    NoSuchColumnException: Result has no column with name '`field1`'
    

The PreparedStatementImpl (non-beta) path is unaffected because it forwards the SQL string to the server, which knows how to parse backtick-quoted identifiers itself.

ClickHouse server version

26.4.2.10 (SELECT version() against the local test server). Code analysis only — not run against the server.

Reproduction (jdbc-v2)

// Setup schema
try (Statement st = conn.createStatement()) {
    st.execute("DROP TABLE IF EXISTS bt_cols");
    st.execute("CREATE TABLE bt_cols (field1 String, field2 String) ENGINE = Memory");
}

// Enable beta RowBinary writer path
Properties props = new Properties();
props.setProperty("clickhouse.jdbc.beta.row_binary_writer", "true");
try (Connection conn = DriverManager.getConnection("jdbc:ch:http://localhost:8123/default", props)) {
    String sql = "INSERT INTO bt_cols (`field1`, `field2`) VALUES (?, ?)";
    try (PreparedStatement ps = conn.prepareStatement(sql)) {
        ps.setString(1, "a");
        ps.setString(2, "b");
        ps.executeUpdate();   // expected: 1 row inserted
    }
}

Expected: one row ('a', 'b') inserted.
Actual: prepareStatement(...) (or executeUpdate() depending on where the column lookup runs first) throws NoSuchColumnException: Result has no column with name '\field1`'`.

Removing the backticks (INSERT INTO bt_cols (field1, field2) VALUES (?, ?)) succeeds. Disabling the beta row_binary_writer property also avoids the bug because the legacy PreparedStatementImpl path forwards the SQL to the server unchanged.

Suggested fix

In jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/SqlParserFacade.java, unescape each column identifier the same way table/database identifiers are unescaped a few lines below:

insertColumns[i] = ClickHouseSqlUtils.unescape(names.get(i).getText());

This matches the existing handling for tableId.identifier() and databaseIdentifier and should also handle double-quoted identifiers (\"col\") for free since unescape covers both quote styles.

Source bug

ClickHouse/clickhouse-go#848

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions