…olders
SqlParserFacade.parseParameters knew '--'/'#' line comments, nested block
comments and quoted strings, but not the '//' line comments and the
$tag$...$tag$ heredocs that the server lexer also accepts. A '?' inside
either was counted as a bind parameter, so PreparedStatement expected a
value the application could not supply and executeQuery() failed with
"Parameter at position 'N' is not set" for a query the server executes
fine.
The scan now treats '//' like the other line comment markers and skips a
heredoc as an opaque token. A '$' is only a heredoc opener when it does
not continue an identifier (a$b, a$x$), its tag contains word characters
only, and a matching closing tag exists - otherwise it stays an ordinary
character, matching the server lexer.
Fixes: #3009
Description
Fixes #3009.
SqlParserFacade.parseParametersis the linear scan that finds JDBC?placeholders (used by theJAVACCandANTLR4parser backends). It skipped quoted tokens,--/#line comments and nested/* */block comments, but it did not know two token kinds the server lexer also accepts://line comments and heredocs / dollar-quoted strings ($$...$$,$tag$...$tag$). A?inside either was therefore counted as a bind parameter, so thePreparedStatementexpected a value the application had no way to supply andexecuteQuery()failed withSQLException: Parameter at position 'N' is not setfor a query the server executes fine (verified on26.5:SELECT number FROM numbers(3) WHERE number = 1 // ?returns1,SELECT $$?$$returns?).The scan now treats
//like the other line-comment markers and skips a heredoc as an opaque token. Heredoc detection follows the server lexer: a$opens a heredoc only when it does not continue an identifier ($is a valid identifier character, soa$banda$x$are identifiers, not heredoc openers), the tag between the two dollar signs contains word characters only, and a matching closing tag exists; otherwise the$stays an ordinary character. The closing tag is searched non-greedily from the end of the opening tag, so$$a$b$$is one heredoc holdinga$b, matching the server.Nothing else changes:
--,#,#!, nested/* */, quoted tokens, the?::casthandling and division (4 / 2) behave exactly as before.Changes
jdbc-v2/.../internal/SqlParserFacade.java—parseParametersalso skips//line comments; newprivate static skipHeredoc(plus an ASCIIisWordCharhelper) skips$tag$...$tag$/$$...$$and returnsstartIndex + 1when there is no heredoc at that position.jdbc-v2/.../internal/BaseSqlParserFacadeTest.java— new parametrizedtestCommentsAndHeredocs(TestNG@DataProvider) pinning the placeholder count for//comments and heredocs, plus contrast cases that must keep their current behavior (//and$$inside string literals and comments, division,$in identifiers such asa$b/a$x$, an unterminated heredoc, and the already-supported--/#/#!/ nested block comments). It returns early for theANTLR4_PARAMS_PARSERbackend, which collects placeholders from the ANTLR grammar rather than from this scan (its lexer has no token for either kind — pre-existing and out of scope here, as is its existing gap on nested block comments).jdbc-v2/.../PreparedStatementTest.java— new integration testtestPlaceholdersWithCommentsAndHeredocsexercising the liveprepareStatement→setString→executeQuerypath, including the identifier contrast case.CHANGELOG.md— Bug Fixes entry.Test
Both tests were run against unpatched
mainfirst:SELECT 1 // ?→expected [0] but found [1],SELECT $tag$ ? $tag$ AS v→expected [0] but found [1],SELECT $$?$$, ?, $$?$$→expected [1] but found [3];java.sql.SQLException: Parameter at position '2' is not set.With the fix:
mvn -B -pl jdbc-v2 test→ 1376 tests, 0 failures;mvn -B -pl jdbc-v2 -DskipUTs=true -Dit.test=PreparedStatementTest verify→ 76 tests, 0 failures (ClickHouse26.5.1). Expected values were derived from the server, not from the client's current output.Pre-PR validation gate
main)PreparedStatement.executeQuery), not only on a helperAGENTS.md/docs/changes_checklist.md— change is confined tojdbc-v2; the new methods areprivate static(smallest visibility, no public API growth, so nodocs/features.mdchange); no configuration property, default, or exception type changed; per the conditional logic or guard changed and string, SQL, or serialized output changed sections the new branches are covered by focused parser regression tests including boundary inputs (empty tag$$$$, unterminated heredoc, end-of-string//, adjacent heredocs) and the previously-working inputs are pinned as contrast cases;CHANGELOG.mdupdated