diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ceae046d..c102d5146 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -102,6 +102,9 @@ inserted data or failing with a server-side `SYNTAX_ERROR`. Escape sequences are now recognized only outside of quoted text, and a `{fn ...}` escape is unwrapped at its matching closing brace, so nested braces (e.g. a `{name:Type}` query parameter or a nested escape) stay balanced. (https://github.com/ClickHouse/clickhouse-java/issues/2995) +- **[jdbc-v2]** Fixed prepared statements losing parameter markers after an empty `--` comment line or after + `SELECT * EXCEPT (...)`, which caused parameter binding to fail with `ArrayIndexOutOfBoundsException` for the + affected SQL parser backends. (https://github.com/ClickHouse/clickhouse-java/issues/3052) - **[client-v2]** Fixed LZ4 input streams not closing their underlying HTTP response stream. Closing an LZ4 stream returned by `QueryResponse.getInputStream()` now releases the wrapped transport stream, including after a partial read. (https://github.com/ClickHouse/clickhouse-java/issues/2985) diff --git a/clickhouse-data/src/main/java/com/clickhouse/data/ClickHouseUtils.java b/clickhouse-data/src/main/java/com/clickhouse/data/ClickHouseUtils.java index d3f08d4a8..0ce876e47 100644 --- a/clickhouse-data/src/main/java/com/clickhouse/data/ClickHouseUtils.java +++ b/clickhouse-data/src/main/java/com/clickhouse/data/ClickHouseUtils.java @@ -1150,7 +1150,7 @@ public static int skipQuotedString(String args, int startIndex, int len, char qu */ public static int skipSingleLineComment(String args, int startIndex, int len) { int index = args.indexOf('\n', startIndex); - return index > startIndex ? index + 1 : len; + return index >= startIndex ? index + 1 : len; } /** diff --git a/clickhouse-data/src/test/java/com/clickhouse/data/ClickHouseUtilsTest.java b/clickhouse-data/src/test/java/com/clickhouse/data/ClickHouseUtilsTest.java index d4eb68e80..bd80c0686 100644 --- a/clickhouse-data/src/test/java/com/clickhouse/data/ClickHouseUtilsTest.java +++ b/clickhouse-data/src/test/java/com/clickhouse/data/ClickHouseUtilsTest.java @@ -239,6 +239,9 @@ public void testSkipSingleLineComment() { args.indexOf('\n') + 1); Assert.assertEquals(ClickHouseUtils.skipSingleLineComment(args, args.indexOf("--", 11), args.length()), args.length()); + + args = "--\nselect 1"; + Assert.assertEquals(ClickHouseUtils.skipSingleLineComment(args, 2, args.length()), 3); } @Test(groups = { "unit" }) diff --git a/jdbc-v2/src/main/antlr4/com/clickhouse/jdbc/internal/parser/antlr4/ClickHouseParser.g4 b/jdbc-v2/src/main/antlr4/com/clickhouse/jdbc/internal/parser/antlr4/ClickHouseParser.g4 index 5af82fd12..13e4e7e2e 100644 --- a/jdbc-v2/src/main/antlr4/com/clickhouse/jdbc/internal/parser/antlr4/ClickHouseParser.g4 +++ b/jdbc-v2/src/main/antlr4/com/clickhouse/jdbc/internal/parser/antlr4/ClickHouseParser.g4 @@ -1051,12 +1051,17 @@ columnExprList ; columnsExpr - : (tableIdentifier DOT)? ASTERISK # ColumnsExprAsterisk + : (tableIdentifier DOT)? ASTERISK columnExceptExpr? # ColumnsExprAsterisk | LPAREN selectUnionStmt RPAREN # ColumnsExprSubquery // NOTE: asterisk and subquery goes before |columnExpr| so that we can mark them as multi-column expressions. | columnExpr # ColumnsExprColumn ; +columnExceptExpr + : EXCEPT (STRING_LITERAL | (LPAREN STRING_LITERAL RPAREN)) # ColumnExceptExprRegexp + | EXCEPT (identifier | (LPAREN identifier (COMMA identifier)* RPAREN)) # ColumnExceptExprIdentifiers + ; + columnExpr : CASE columnExpr? (WHEN columnExpr THEN columnExpr)+ (ELSE columnExpr)? END # ColumnExprCase | CAST LPAREN columnExpr AS columnTypeExpr RPAREN # ColumnExprCast diff --git a/jdbc-v2/src/test/java/com/clickhouse/jdbc/internal/BaseSqlParserFacadeTest.java b/jdbc-v2/src/test/java/com/clickhouse/jdbc/internal/BaseSqlParserFacadeTest.java index 61b34a223..4bfde5cfa 100644 --- a/jdbc-v2/src/test/java/com/clickhouse/jdbc/internal/BaseSqlParserFacadeTest.java +++ b/jdbc-v2/src/test/java/com/clickhouse/jdbc/internal/BaseSqlParserFacadeTest.java @@ -408,6 +408,9 @@ public Object[][] testMiscStmtDp() { {"CREATE TABLE check_query_log (N UInt32,S String) Engine = MergeTree", 0}, {"CREATE TABLE check_query_log (N UInt32,S String) Engine = ReplacingMergeTree", 0}, {"select abs(log(e()) - 1) < 1e-8", 0}, + {"--\nselect count(*) from numbers(10) where number = ?", 1}, + {"select count(*) from (\n--\nselect 1 as a) x where x.a = ?", 1}, + {"select count(*) from ( select * EXCEPT (b), b as c from ( select 1 as a, 2 as b ) ) x where x.a = ?", 1}, {"SELECT SearchEngineID, ClientIP, count() AS c, sum(Refresh), avg(ResolutionWidth) " + " FROM test.hits_s3 WHERE SearchPhrase != '' GROUP BY SearchEngineID, ClientIP " + " ORDER BY c DESC LIMIT 10", 0}, @@ -1004,4 +1007,4 @@ public void testUseFunctionOfUnparseableSelect() { Assert.assertFalse(stmt.isInsert(), "Statement is recognized as an insert"); Assert.assertFalse(stmt.isUseFunction(), "Function usage is reported for a statement that is not an insert"); } -} \ No newline at end of file +}