Skip to content

jdbc-v2 0.9.7: SQL parser silently reports argCount=0 for two constructs (bare -- comment; SELECT * EXCEPT (...)), causing ArrayIndexOutOfBoundsException on bind #3052

Description

@lyfuci

Description

With clickhouse-jdbc 0.9.7 (jdbc-v2), two SQL constructs make the client-side parser return argCount == 0 even though the statement contains a ? placeholder. Binding then fails with:

java.lang.ArrayIndexOutOfBoundsException: Index 0 out of bounds for length 0
	at com.clickhouse.jdbc.PreparedStatementImpl.setInt(PreparedStatementImpl.java:174)

Both statements are accepted by the server without complaint — the failure is purely in the client-side parser.

This looks like the same family as #2461 / #2478 / #2568 (all area:sql-parser, fixed in 0.9.1 / 0.9.3), but these two triggers are still present in 0.9.7.

Interestingly the two triggers affect different parser implementations, so which one bites depends on which parser is selected.

Trigger A — a bare -- comment line breaks ANTLR4Parser

A line comment consisting of exactly two dashes and nothing after them (not even a space):

--
select count(*) from numbers(10) where number = ?

-- (with a trailing space), --x, and /**/ all parse fine. Position does not matter — the same happens when the empty -- line is inside a subquery:

select count(*) from (
--
select 1 as a) x where x.a = ?

Trigger B — SELECT * EXCEPT (...) breaks ANTLR4AndParamsParser

select count(*) from ( select * EXCEPT (b), b as c from ( select 1 as a, 2 as b ) ) x where x.a = ?

Results

Measured by calling the parser directly (see below), argCount for each case:

SQL ANTLR4Parser ANTLR4AndParamsParser
select count(*) from numbers(10) where number = ? (baseline) 1 1
bare -- comment line 0 1
-- (dash dash space) 1 1
--x 1 1
/**/ 1 1
bare -- inside a subquery 0 1
SELECT * EXCEPT (...) 1 0

How this was measured

No server needed — the parser is client-side, so it can be driven directly:

Class<?> c = Class.forName("com.clickhouse.jdbc.internal.SqlParserFacade$ANTLR4Parser");
Constructor<?> ct = c.getDeclaredConstructor(boolean.class);
ct.setAccessible(true);
Object p = ct.newInstance(true);
Method parse = c.getMethod("parsePreparedStatement", String.class);
parse.setAccessible(true);
Method argCount = Class.forName("com.clickhouse.jdbc.internal.ParsedPreparedStatement")
        .getMethod("getArgCount");

System.out.println(argCount.invoke(parse.invoke(p, "--\nselect count(*) from numbers(10) where number = ?")));
// prints 0

Why this is painful to diagnose

The parse failure is silent. argCount is simply 0, and nothing is logged at the point of failure. The symptom only appears later, at bind time, as an ArrayIndexOutOfBoundsException from PreparedStatementImpl, which gives no hint that the SQL failed to parse.

In our case the SQL came from a Hibernate @Subselect view definition: queries without a WHERE parameter worked fine, and the moment any filter was added the request failed with the AIOOBE above. That combination points investigators at the condition-building code rather than at the driver.

Two suggestions, independent of the parse bugs themselves:

  1. When the parser fails or finds no parameters while the raw SQL clearly contains ?, log a warning (or throw a SQLException describing the parse failure) instead of silently producing argCount = 0.
  2. PreparedStatementImpl.setXxx could raise a SQLException with the parameter index and the detected count, rather than letting the raw ArrayIndexOutOfBoundsException escape.

Environment

  • com.clickhouse:clickhouse-jdbc:0.9.7 (all)
  • JDK 25 (GraalVM CE 25.0.4)
  • Reproduced offline against the parser only, so server version is not involved

Metadata

Metadata

Assignees

No one assigned

    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