Skip to content

Commit 68b8dd4

Browse files
committed
fixed parser grammar.
1 parent 562db61 commit 68b8dd4

File tree

7 files changed

+152
-22
lines changed

7 files changed

+152
-22
lines changed

jdbc-v2/src/main/antlr4/com/clickhouse/jdbc/internal/ClickHouseLexer.g4

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,5 +296,5 @@ UNDERSCORE : '_';
296296
// Comments and whitespace
297297

298298
MULTI_LINE_COMMENT : '/*' .*? '*/' -> skip;
299-
SINGLE_LINE_COMMENT : '--' ~('\n' | '\r')* ('\n' | '\r' | EOF) -> skip;
299+
SINGLE_LINE_COMMENT : ('--' | '#!' | '#') ~('\n' | '\r')* ('\n' | '\r' | EOF) -> skip;
300300
WHITESPACE : [ \u000B\u000C\t\r\n] -> skip; // '\n' can be part of multiline single query

jdbc-v2/src/main/antlr4/com/clickhouse/jdbc/internal/ClickHouseParser.g4

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ assignmentValues
327327
assignmentValue
328328
: literal # InsertRawValue
329329
| QUERY # InsertParameter
330-
| identifier LPAREN (literal COMMA)* QUERY (COMMA literal)* RPAREN # InsertParameterFuncExpr
330+
| identifier (LPAREN columnExprList? RPAREN)? # InsertParameterFuncExpr
331331

332332
;
333333

jdbc-v2/src/main/java/com/clickhouse/jdbc/ConnectionImpl.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -376,8 +376,18 @@ public PreparedStatement prepareStatement(String sql, int resultSetType, int res
376376

377377
ParsedPreparedStatement parsedStatement = sqlParser.parsePreparedStatement(sql);
378378

379-
if (config.isBetaFeatureEnabled(DriverProperties.BETA_ROW_BINARY_WRITER)) {
380-
if (parsedStatement.isInsert() && parsedStatement.isCanStream()) {
379+
if (parsedStatement.isInsert() && config.isBetaFeatureEnabled(DriverProperties.BETA_ROW_BINARY_WRITER)) {
380+
/*
381+
* RowBinary can be used when
382+
* - INSERT INTO t (c1, c2) VALUES (?, ?)
383+
* - INSERT INTO t VALUES (?, ?, ?)
384+
* - number of arguments matches schema or column list
385+
* RowBinary cannot be used when
386+
* - INSERT INTO t VALUES (now(), ?, ?) !# there is a function in the values
387+
* - INSERT INTO t VALUES (now(), ?, 1), (now(), ?, 2) !# multiple values list
388+
* - INSERT INTO t SELECT ?, ?, ? !# insert from select
389+
*/
390+
if (!parsedStatement.isInsertWithSelect()) {
381391
TableSchema tableSchema = client.getTableSchema(parsedStatement.getTable(), schema);
382392
return new WriterStatementImpl(this, sql, tableSchema, parsedStatement);
383393
}

jdbc-v2/src/main/java/com/clickhouse/jdbc/PreparedStatementImpl.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ public void addBatch() throws SQLException {
283283
}
284284
batchValues.add(valuesClause);
285285
} else {
286-
addBatch(buildSQL());
286+
super.addBatch(buildSQL());
287287
}
288288
}
289289

@@ -295,7 +295,11 @@ public int[] executeBatch() throws SQLException {
295295
// run executeBatch
296296
return executeInsertBatch().stream().mapToInt(Integer::intValue).toArray();
297297
} else {
298-
return super.executeBatch();
298+
List<Integer> results = new ArrayList<>();
299+
for (String sql : batch) {
300+
results.add(executeUpdateImpl(sql, localSettings));
301+
}
302+
return results.stream().mapToInt(Integer::intValue).toArray();
299303
}
300304
}
301305

@@ -306,7 +310,11 @@ public long[] executeLargeBatch() throws SQLException {
306310
if (insertStmtWithValues) {
307311
return executeInsertBatch().stream().mapToLong(Integer::longValue).toArray();
308312
} else {
309-
return super.executeLargeBatch();
313+
List<Integer> results = new ArrayList<>();
314+
for (String sql : batch) {
315+
results.add(executeUpdateImpl(sql, localSettings));
316+
}
317+
return results.stream().mapToLong(Integer::longValue).toArray();
310318
}
311319
}
312320

jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/ParsedPreparedStatement.java

Lines changed: 67 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
import org.slf4j.Logger;
55
import org.slf4j.LoggerFactory;
66

7+
import java.util.ArrayList;
78
import java.util.Arrays;
9+
import java.util.Collections;
810
import java.util.List;
911

1012
/**
@@ -16,6 +18,8 @@ public class ParsedPreparedStatement extends ClickHouseParserBaseListener {
1618

1719
private String table;
1820

21+
private String useDatabase;
22+
1923
private String[] insertColumns;
2024

2125
private boolean hasFuncWrappedParameter;
@@ -26,9 +30,11 @@ public class ParsedPreparedStatement extends ClickHouseParserBaseListener {
2630

2731
private boolean insert;
2832

29-
private int argCount;
33+
private boolean insertWithSelect;
3034

31-
private boolean canStream;
35+
private List<String> roles;
36+
37+
private int argCount;
3238

3339
private int[] paramPositions = new int[16];
3440

@@ -52,6 +58,14 @@ public boolean isInsert() {
5258
return insert;
5359
}
5460

61+
public void setInsertWithSelect(boolean insertWithSelect) {
62+
this.insertWithSelect = insertWithSelect;
63+
}
64+
65+
public boolean isInsertWithSelect() {
66+
return insertWithSelect;
67+
}
68+
5569
public void setArgCount(int argCount) {
5670
this.argCount = argCount;
5771
}
@@ -60,10 +74,6 @@ public int getArgCount() {
6074
return argCount;
6175
}
6276

63-
public void setCanStream(boolean canStream) {
64-
this.canStream = canStream;
65-
}
66-
6777
public String[] getInsertColumns() {
6878
return insertColumns;
6979
}
@@ -76,12 +86,12 @@ public int[] getParamPositions() {
7686
return paramPositions;
7787
}
7888

79-
public boolean isCanStream() {
80-
// there are next forms of INSERT that can be streamed
81-
// INSERT INTO `table` [(col1, col2)] VALUES (?, ?, ?..)
82-
// INSERT INTO `table` [(col1, col2)] FORMAT TabSeparated - this need additional support
83-
// INSERT with select or functions around parameters cannot be streamed
84-
return canStream;
89+
public void setRoles(List<String> roles) {
90+
this.roles = roles;
91+
}
92+
93+
public List<String> getRoles() {
94+
return roles;
8595
}
8696

8797
public int getAssignValuesListStartPosition() {
@@ -92,6 +102,45 @@ public int getAssignValuesListStopPosition() {
92102
return assignValuesListStopPosition;
93103
}
94104

105+
public void setUseDatabase(String useDatabase) {
106+
this.useDatabase = useDatabase;
107+
}
108+
109+
public String getUseDatabase() {
110+
return useDatabase;
111+
}
112+
113+
@Override
114+
public void enterQueryStmt(ClickHouseParser.QueryStmtContext ctx) {
115+
ClickHouseParser.QueryContext qCtx = ctx.query();
116+
if (qCtx != null) {
117+
if (qCtx.selectStmt() != null || qCtx.selectUnionStmt() != null || qCtx.showStmt() != null
118+
|| qCtx.describeStmt() != null) {
119+
setHasResultSet(true);
120+
}
121+
}
122+
}
123+
124+
@Override
125+
public void enterUseStmt(ClickHouseParser.UseStmtContext ctx) {
126+
if (ctx.databaseIdentifier() != null) {
127+
setUseDatabase(JdbcUtils.unquoteIdentifier(ctx.databaseIdentifier().getText()));
128+
}
129+
}
130+
131+
@Override
132+
public void enterSetRoleStmt(ClickHouseParser.SetRoleStmtContext ctx) {
133+
if (ctx.NONE() != null) {
134+
setRoles(Collections.emptyList());
135+
} else {
136+
List<String> roles = new ArrayList<>();
137+
for (ClickHouseParser.IdentifierContext id : ctx.setRolesList().identifier()) {
138+
roles.add(JdbcUtils.unquoteIdentifier(id.getText()));
139+
}
140+
setRoles(roles);
141+
}
142+
}
143+
95144
@Override
96145
public void enterColumnExprParam(ClickHouseParser.ColumnExprParamContext ctx) {
97146
appendParameter(ctx.start.getStartIndex());
@@ -145,6 +194,11 @@ public void enterInsertStmt(ClickHouseParser.InsertStmtContext ctx) {
145194
}
146195
}
147196

148-
super.enterInsertStmt(ctx);
197+
setInsert(true);
198+
}
199+
200+
@Override
201+
public void enterDataClauseSelect(ClickHouseParser.DataClauseSelectContext ctx) {
202+
setInsertWithSelect(true);
149203
}
150204
}

jdbc-v2/src/test/java/com/clickhouse/jdbc/PreparedStatementTest.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -609,9 +609,10 @@ void testClearParameters() throws Exception {
609609
@DataProvider
610610
Object[][] testBatchInsertWithRowBinary_dp() {
611611
return new Object[][]{
612-
{"INSERT INTO \n `%s` \nVALUES (?, ?, abs(?), ?)", PreparedStatementImpl.class}, // only string possible
612+
{"INSERT INTO \n `%s` \nVALUES (?, ?, abs(?), ?)", PreparedStatementImpl.class}, // only string possible (because of abs(?))
613613
{"INSERT INTO\n `%s` \nVALUES (?, ?, ?, ?)", WriterStatementImpl.class}, // row binary writer
614-
{" INSERT INTO %s (ts, v1, v2, v3) VALUES (?, ?, ?, ?)", PreparedStatementImpl.class} // only string supported now
614+
{" INSERT INTO %s (ts, v1, v2, v3) VALUES (?, ?, ?, ?)", PreparedStatementImpl.class}, // only string supported now
615+
{"INSERT INTO %s SELECT ?, ?, ?, ?", PreparedStatementImpl.class}, // only string possible (because of SELECT)
615616
};
616617
}
617618

jdbc-v2/src/test/java/com/clickhouse/jdbc/internal/SqlParserTest.java

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import org.testng.annotations.Test;
55

66
import static org.testng.Assert.assertEquals;
7+
import static org.testng.Assert.assertFalse;
8+
import static org.testng.Assert.assertTrue;
79

810
public class SqlParserTest {
911

@@ -113,4 +115,59 @@ public void testParseSelectPrepared() throws Exception {
113115
System.out.println(sql);
114116
System.out.println(compiledSql);
115117
}
118+
119+
@Test
120+
public void testPreparedStatementCreateSQL() {
121+
SqlParser parser = new SqlParser();
122+
123+
String sql = "CREATE TABLE IF NOT EXISTS `with_complex_id` (`v?``1` Int32, " +
124+
"\"v?\"\"2\" Int32,`v?\\`3` Int32, \"v?\\\"4\" Int32) ENGINE MergeTree ORDER BY ();";
125+
ParsedPreparedStatement parsed = parser.parsePreparedStatement(sql);
126+
// TODO: extend test expecting no errors
127+
assertFalse(parsed.isInsert());
128+
129+
sql = "CREATE TABLE IF NOT EXISTS `test_stmt_split2` (v1 Int32, v2 String) ENGINE MergeTree ORDER BY (); ";
130+
parsed = parser.parsePreparedStatement(sql);
131+
assertFalse(parsed.isInsert());
132+
}
133+
134+
135+
@Test
136+
public void testPreparedStatementInsertSQL() {
137+
SqlParser parser = new SqlParser();
138+
139+
String sql = "INSERT INTO `test_stmt_split2` VALUES (1, 'abc'), (2, '?'), (3, '?')";
140+
ParsedPreparedStatement parsed = parser.parsePreparedStatement(sql);
141+
// TODO: extend test expecting no errors
142+
assertTrue(parsed.isInsert());
143+
assertFalse(parsed.isHasResultSet());
144+
assertFalse(parsed.isInsertWithSelect());
145+
146+
sql = "-- line comment1 ?\n"
147+
+ "# line comment2 ?\n"
148+
+ "#! line comment3 ?\n"
149+
+ "/* block comment ? \n */"
150+
+ "INSERT INTO `with_complex_id`(`v?``1`, \"v?\"\"2\",`v?\\`3`, \"v?\\\"4\") VALUES (?, ?, ?, ?);";
151+
parsed = parser.parsePreparedStatement(sql);
152+
// TODO: extend test expecting no errors
153+
assertTrue(parsed.isInsert());
154+
assertFalse(parsed.isHasResultSet());
155+
assertFalse(parsed.isInsertWithSelect());
156+
157+
sql = "INSERT INTO tt SELECT now(), 10, 20.0, 30";
158+
parsed = parser.parsePreparedStatement(sql);
159+
// TODO: extend test expecting no errors
160+
assertTrue(parsed.isInsert());
161+
assertFalse(parsed.isHasResultSet());
162+
assertTrue(parsed.isInsertWithSelect());
163+
164+
165+
sql = "INSERT INTO `users` (`name`, `last_login`, `password`, `id`) VALUES\n" +
166+
" (?, `parseDateTimeBestEffort`(?, ?), ?, 1)\n";
167+
parsed = parser.parsePreparedStatement(sql);
168+
// TODO: extend test expecting no errors
169+
assertTrue(parsed.isInsert());
170+
assertFalse(parsed.isHasResultSet());
171+
assertFalse(parsed.isInsertWithSelect());
172+
}
116173
}

0 commit comments

Comments
 (0)