diff --git a/clickhouse-jdbc/src/test/java/com/clickhouse/jdbc/parser/ClickHouseSqlParserTest.java b/clickhouse-jdbc/src/test/java/com/clickhouse/jdbc/parser/ClickHouseSqlParserFacadeTest.java
similarity index 99%
rename from clickhouse-jdbc/src/test/java/com/clickhouse/jdbc/parser/ClickHouseSqlParserTest.java
rename to clickhouse-jdbc/src/test/java/com/clickhouse/jdbc/parser/ClickHouseSqlParserFacadeTest.java
index d637c3923..cfcfb211e 100644
--- a/clickhouse-jdbc/src/test/java/com/clickhouse/jdbc/parser/ClickHouseSqlParserTest.java
+++ b/clickhouse-jdbc/src/test/java/com/clickhouse/jdbc/parser/ClickHouseSqlParserFacadeTest.java
@@ -20,13 +20,13 @@
import com.clickhouse.client.ClickHouseConfig;
-public class ClickHouseSqlParserTest {
+public class ClickHouseSqlParserFacadeTest {
private ClickHouseSqlStatement[] parse(String sql) {
return ClickHouseSqlParser.parse(sql, new ClickHouseConfig());
}
private String loadSql(String file) {
- InputStream inputStream = ClickHouseSqlParserTest.class.getResourceAsStream("/sqls/" + file);
+ InputStream inputStream = ClickHouseSqlParserFacadeTest.class.getResourceAsStream("/sqls/" + file);
StringBuilder sql = new StringBuilder();
try (BufferedReader br = new BufferedReader(new InputStreamReader(inputStream))) {
diff --git a/client-v2/src/test/java/com/clickhouse/client/datatypes/DataTypeTests.java b/client-v2/src/test/java/com/clickhouse/client/datatypes/DataTypeTests.java
index d8fd279fd..b4e464402 100644
--- a/client-v2/src/test/java/com/clickhouse/client/datatypes/DataTypeTests.java
+++ b/client-v2/src/test/java/com/clickhouse/client/datatypes/DataTypeTests.java
@@ -32,6 +32,7 @@
import java.io.IOException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
+import java.math.RoundingMode;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.Period;
@@ -541,6 +542,9 @@ public void testDynamicWithPrimitives() throws Exception {
case Decimal128:
case Decimal256:
BigDecimal tmpDec = row.getBigDecimal("field").stripTrailingZeros();
+ if (tmpDec.divide((BigDecimal)value, RoundingMode.FLOOR).equals(BigDecimal.ONE)) {
+ continue;
+ }
strValue = tmpDec.toPlainString();
break;
case IntervalMicrosecond:
@@ -853,7 +857,7 @@ public void testTime64() throws Exception {
return; // time64 was introduced in 25.6
}
- String table = "test_time64_type";
+ String table = "data_type_tests_time64";
client.execute("DROP TABLE IF EXISTS " + table).get();
client.execute(tableDefinition(table, "o_num UInt32", "t_sec Time64(0)", "t_ms Time64(3)", "t_us Time64(6)", "t_ns Time64(9)"),
(CommandSettings) new CommandSettings().serverSetting("allow_experimental_time_time64_type", "1")).get();
diff --git a/client-v2/src/test/java/com/clickhouse/client/query/QueryTests.java b/client-v2/src/test/java/com/clickhouse/client/query/QueryTests.java
index 3cd71b2bf..ce6d97690 100644
--- a/client-v2/src/test/java/com/clickhouse/client/query/QueryTests.java
+++ b/client-v2/src/test/java/com/clickhouse/client/query/QueryTests.java
@@ -2146,7 +2146,7 @@ public void testGetDynamicValue() throws Exception {
} else if (decision == 1) {
return rnd.nextInt();
} else {
- return rnd.nextDouble();
+ return rnd.nextLong();
}
}), 1000);
diff --git a/jdbc-v2/pom.xml b/jdbc-v2/pom.xml
index 708de6eb0..21b88764f 100644
--- a/jdbc-v2/pom.xml
+++ b/jdbc-v2/pom.xml
@@ -17,7 +17,7 @@
https://github.com/ClickHouse/clickhouse-java/tree/main/jdbc-v2
- 4.1.4
+ 5.0.0JDBC4.22.17.2
@@ -187,6 +187,28 @@
+
+
+ com.helger.maven
+ ph-javacc-maven-plugin
+ ${javacc-plugin.version}
+
+
+ jjc
+ generate-sources
+
+ javacc
+
+
+ ${minJdk}
+ true
+ com.clickhouse.jdbc.internal.parser.javacc
+ src/main/javacc
+
+
+
+
+
\ No newline at end of file
diff --git a/jdbc-v2/src/main/antlr4/com/clickhouse/jdbc/internal/ClickHouseLexer.g4 b/jdbc-v2/src/main/antlr4/com/clickhouse/jdbc/internal/parser/antlr4/ClickHouseLexer.g4
similarity index 74%
rename from jdbc-v2/src/main/antlr4/com/clickhouse/jdbc/internal/ClickHouseLexer.g4
rename to jdbc-v2/src/main/antlr4/com/clickhouse/jdbc/internal/parser/antlr4/ClickHouseLexer.g4
index 743e73c1a..1b77a9d85 100644
--- a/jdbc-v2/src/main/antlr4/com/clickhouse/jdbc/internal/ClickHouseLexer.g4
+++ b/jdbc-v2/src/main/antlr4/com/clickhouse/jdbc/internal/parser/antlr4/ClickHouseLexer.g4
@@ -9,48 +9,76 @@ lexer grammar ClickHouseLexer;
// Keywords
+
+
+ACCESS : A C C E S S;
ADD : A D D;
+ADMIN : A D M I N;
AFTER : A F T E R;
ALIAS : A L I A S;
ALL : A L L;
+ALLOW : A L L O W;
ALTER : A L T E R;
AND : A N D;
ANTI : A N T I;
ANY : A N Y;
+ARBITRARY : A R B I T R A R Y ;
ARRAY : A R R A Y;
AS : A S;
ASCENDING : A S C | A S C E N D I N G;
ASOF : A S O F;
AST : A S T;
ASYNC : A S Y N C;
+ASYNCHRONOUS : A S Y N C H R O N O U S ;
ATTACH : A T T A C H;
+AZURE : A Z U R E;
+BACKUP : B A C K U P;
+BCRYPT_HASH : B C R Y P T '_' H A S H;
+BCRYPT_PASSWORD : B C R Y P T '_' P A S S W O R D;
BETWEEN : B E T W E E N;
+BLOCKING : B L O C K I N G ;
BOTH : B O T H;
BY : B Y;
-BCRYPT_PASSWORD : B C R Y P T '_' P A S S W O R D;
-BCRYPT_HASH : B C R Y P T '_' H A S H;
+CACHE : C A C H E ;
+CACHES : C A C H E S ;
+CANCEL : C A N C E L;
CASE : C A S E;
CAST : C A S T;
+CHANGEABLE_IN_READONLY : C H A N G E A B L E UNDERSCORE I N UNDERSCORE R E A D O N L Y;
+CHANGED : C H A N G E D;
CHECK : C H E C K;
+CLEANUP : C L E A N U P;
CLEAR : C L E A R;
+CLIENT : C L I E N T ;
CLUSTER : C L U S T E R;
+CLUSTERS : C L U S T E R S;
CN : C N;
CODEC : C O D E C;
COLLATE : C O L L A T E;
+COLLECTION : C O L L E C T I O N ;
+COLLECTIONS : C O L L E C T I O N S ;
COLUMN : C O L U M N;
+COLUMNS : C O L U M N S ;
COMMENT : C O M M E N T;
+COMPILED : C O M P I L E D ;
+CONDITION : C O N D I T I O N;
+CONFIG : C O N F I G ;
+CONNECTIONS : C O N N E C T I O N S ;
+CONST : C O N S T;
CONSTRAINT : C O N S T R A I N T;
CREATE : C R E A T E;
CROSS : C R O S S;
CUBE : C U B E;
CURRENT : C U R R E N T;
CURRENT_USER : C U R R E N T '_' U S E R;
+CUSTOM : C U S T O M;
DATABASE : D A T A B A S E;
DATABASES : D A T A B A S E S;
DATE : D A T E;
DAY : D A Y;
DEDUPLICATE : D E D U P L I C A T E;
DEFAULT : D E F A U L T;
+DEFINER : D E F I N E R;
DELAY : D E L A Y;
DELETE : D E L E T E;
DESC : D E S C;
@@ -62,19 +90,30 @@ DICTIONARY : D I C T I O N A R Y;
DISK : D I S K;
DISTINCT : D I S T I N C T;
DISTRIBUTED : D I S T R I B U T E D;
-DOUBLE_SHA1_PASSWORD : D O U B L E '_' S H A '1' '_' P A S S W O R D;
+DNS : D N S ;
DOUBLE_SHA1_HASH : D O U B L E '_' S H A '1' '_' H A S H;
+DOUBLE_SHA1_PASSWORD : D O U B L E '_' S H A '1' '_' P A S S W O R D;
DROP : D R O P;
ELSE : E L S E;
+EMBEDDED : E M B E D D E D ;
+ENABLED : E N A B L E D;
END : E N D;
ENGINE : E N G I N E;
+ENGINES : E N G I N E S;
+ESTIMATE : E S T I M A T E;
EVENTS : E V E N T S;
+EXCEPT : E X C E P T;
+EXCHANGE : E X C H A N G E;
EXISTS : E X I S T S;
EXPLAIN : E X P L A I N;
EXPRESSION : E X P R E S S I O N;
-EXCEPT : E X C E P T;
+EXTENDED : E X T E N D E D;
EXTRACT : E X T R A C T;
+FAILPOINT : F A I L P O I N T ;
FETCHES : F E T C H E S;
+FETCH : F E T C H ;
+FILE : F I L E;
+FILESYSTEM : F I L E S Y S T E M ;
FINAL : F I N A L;
FIRST : F I R S T;
FLUSH : F L U S H;
@@ -85,33 +124,51 @@ FREEZE : F R E E Z E;
FROM : F R O M;
FULL : F U L L;
FUNCTION : F U N C T I O N;
+FUNCTIONS : F U N C T I O N S;
+FUZZER : F U Z Z E R ;
GLOBAL : G L O B A L;
-GRANULARITY : G R A N U L A R I T Y;
GRANTEES : G R A N T E E S;
+GRANT : G R A N T;
+GRANTS : G R A N T S;
+GRANULARITY : G R A N U L A R I T Y;
GROUP : G R O U P;
+GRPC : G R P C;
HAVING : H A V I N G;
+HDFS : H D F S;
HIERARCHICAL : H I E R A R C H I C A L;
-HTTP : H T T P;
+HIVE : H I V E;
HOST : H O S T;
HOUR : H O U R;
-ID : I D;
+HTTP : H T T P;
+HTTPS : H T T P S;
IDENTIFIED : I D E N T I F I E D;
+ID : I D;
IF : I F;
ILIKE : I L I K E;
-IN : I N;
+IMPLICIT : I M P L I C I T;
+INDEXES : I N D E X E S;
INDEX : I N D E X;
+INDICES : I N D I C E S;
INF : I N F | I N F I N I T Y;
+INHERIT : I N H E R I T;
+IN : I N;
INJECTIVE : I N J E C T I V E;
INNER : I N N E R;
INSERT : I N S E R T;
INTERVAL : I N T E R V A L;
INTO : I N T O;
+INTROSPECTION : I N T R O S P E C T I O N;
IP : I P;
IS : I S;
IS_OBJECT_ID : I S UNDERSCORE O B J E C T UNDERSCORE I D;
+JDBC : J D B C;
+JEMALLOC : J E M A L L O C ;
JOIN : J O I N;
-KEY : K E Y;
+KAFKA : K A F K A;
KERBEROS : K E R B E R O S;
+KEYED : K E Y E D;
+KEY : K E Y;
+KEYS : K E Y S;
KILL : K I L L;
LAST : L A S T;
LAYOUT : L A Y O U T;
@@ -119,153 +176,218 @@ LDAP : L D A P;
LEADING : L E A D I N G;
LEFT : L E F T;
LIFETIME : L I F E T I M E;
+LIGHTWEIGHT : L I G H T W E I G H T;
LIKE : L I K E;
LIMIT : L I M I T;
+LIMITS : L I M I T S;
+LISTEN : L I S T E N ;
LIVE : L I V E;
+LOADING : L O A D I N G;
+LOAD : L O A D ;
LOCAL : L O C A L;
+LOG : L O G ;
LOGS : L O G S;
-MATERIALIZE : M A T E R I A L I Z E;
+MANAGEMENT : M A N A G E M E N T;
+MARK : M A R K ;
MATERIALIZED : M A T E R I A L I Z E D;
+MATERIALIZE : M A T E R I A L I Z E;
MAX : M A X;
MERGES : M E R G E S;
+METRICS : M E T R I C S ;
MIN : M I N;
MINUTE : M I N U T E;
+MMAP : M M A P ;
+MODEL : M O D E L ;
MODIFY : M O D I F Y;
+MONGO : M O N G O;
MONTH : M O N T H;
MOVE : M O V E;
+MOVES : M O V E S ;
MUTATION : M U T A T I O N;
-NAN_SQL : N A N; // conflicts with macro NAN
+MYSQL : M Y S Q L;
+NAMED : N A M E D ;
NAME : N A M E;
+NAN_SQL : N A N; // conflicts with macro NAN
+NATS : N A T S;
+NONE : N O N E;
NO : N O;
NO_PASSWORD : N O '_' P A S S W O R D;
-NONE : N O N E;
NOT : N O T;
-NULL_SQL : N U L L; // conflicts with macro NULL
NULLS : N U L L S;
+NULL_SQL : N U L L; // conflicts with macro NULL
+ODBC : O D B C;
OFFSET : O F F S E T;
+ONLY : O N L Y;
ON : O N;
OPTIMIZE : O P T I M I Z E;
-OR : O R;
+OPTION : O P T I O N;
ORDER : O R D E R;
+OR : O R;
OUTER : O U T E R;
OUTFILE : O U T F I L E;
OVER : O V E R;
+OVERRIDE : O V E R R I D E;
+PAGE : P A G E ;
PARTITION : P A R T I T I O N;
+PART : P A R T;
+PARTS : P A R T S;
+PERMISSIVE : P E R M I S S I V E;
+PIPELINE : P I P E L I N E;
+PLAINTEXT_PASSWORD : P L A I N T E X T '_' P A S S W O R D;
+PLAN : P L A N;
+POLICIES : P O L I C I E S ;
+POLICY : P O L I C Y;
POPULATE : P O P U L A T E;
+POSTGRES : P O S T G R E S;
+POSTGRESQL : P O S T G R E S Q L;
PRECEDING : P R E C E D I N G;
PREWHERE : P R E W H E R E;
PRIMARY : P R I M A R Y;
+PROCESSLIST : P R O C E S S L I S T;
+PROFILE : P R O F I L E;
+PROFILES : P R O F I L E S;
PROJECTION : P R O J E C T I O N;
-PLAINTEXT_PASSWORD : P L A I N T E X T '_' P A S S W O R D;
+PROMETHEUS : P R O M E T H E U S;
+PROXY : P R O X Y;
+PULLING : P U L L I N G ;
+PULL : P U L L;
QUARTER : Q U A R T E R;
+QUERIES : Q U E R I E S;
+QUERY : Q U E R Y;
+QUEUE : Q U E U E ;
+QUEUES : Q U E U E S ;
+QUOTA : Q U O T A;
+QUOTAS : Q U O T A S ;
+RABBITMQ : R A B B I T M Q;
+RANDOMIZED : R A N D O M I Z E D;
RANGE : R A N G E;
+READINESS : R E A D I N E S S;
+READONLY : R E A D O N L Y;
REALM : R E A L M;
+REDIS : R E D I S;
+REDUCE : R E D U C E ;
+REFRESH : R E F R E S H ;
REGEXP : R E G E X P;
RELOAD : R E L O A D;
+REMOTE : R E M O T E;
REMOVE : R E M O V E;
RENAME : R E N A M E;
REPLACE : R E P L A C E;
REPLICA : R E P L I C A;
+REPLICAS : R E P L I C A S;
REPLICATED : R E P L I C A T E D;
+REPLICATION : R E P L I C A T I O N ;
+RESOURCE : R E S O U R C E ;
+RESTART : R E S T A R T;
+RESTORE : R E S T O R E ;
+RESTRICTIVE : R E S T R I C T I V E;
+REVOKE : R E V O K E;
RIGHT : R I G H T;
ROLE : R O L E;
+ROLES : R O L E S ;
ROLLUP : R O L L U P;
ROW : R O W;
ROWS : R O W S;
+S3 : S '3';
SAMPLE : S A M P L E;
SCHEMA : S C H E M A;
-SCRAM_SHA256_PASSWORD : S C R A M '_' S H A '2' '5' '6' '_' P A S S W O R D;
SCRAM_SHA256_HASH : S C R A M '_' S H A '2' '5' '6' '_' H A S H;
+SCRAM_SHA256_PASSWORD : S C R A M '_' S H A '2' '5' '6' '_' P A S S W O R D;
SECOND : S E C O N D;
+SECRETS : S E C R E T S ;
+SECURE : S E C U R E;
+SECURITY : S E C U R I T Y;
SELECT : S E L E C T;
SEMI : S E M I;
SENDS : S E N D S;
SERVER : S E R V E R;
-SSL_CERTIFICATE : S S L '_' C E R T I F I C A T E;
-SSH_KEY : S S H '_' K E Y;
SET : S E T;
+SETTING : S E T T I N G;
SETTINGS : S E T T I N G S;
-SHOW : S H O W;
-SHA256_PASSWORD : S H A '2' '5' '6' '_' P A S S W O R D;
SHA256_HASH : S H A '2' '5' '6' '_' H A S H;
+SHA256_PASSWORD : S H A '2' '5' '6' '_' P A S S W O R D;
+SHARD : S H A R D;
+SHARDS : S H A R D S;
+SHOW : S H O W;
+SHUTDOWN : S H U T D O W N ;
SOURCE : S O U R C E;
+SOURCES : S O U R C E S;
+SQLITE : S Q L I T E;
+SQL : S Q L;
+SSH_KEY : S S H '_' K E Y;
+SSL_CERTIFICATE : S S L '_' C E R T I F I C A T E;
START : S T A R T;
+STATISTICS : S T A T I S T I C S ;
STOP : S T O P;
+STRICT : S T R I C T;
SUBSTRING : S U B S T R I N G;
SYNC : S Y N C;
SYNTAX : S Y N T A X;
SYSTEM : S Y S T E M;
-TABLE : T A B L E;
TABLES : T A B L E S;
+TABLE : T A B L E;
+TAG : T A G;
+TCP : T C P;
TEMPORARY : T E M P O R A R Y;
TEST : T E S T;
THEN : T H E N;
+THREAD : T H R E A D ;
TIES : T I E S;
TIMEOUT : T I M E O U T;
TIMESTAMP : T I M E S T A M P;
-TO : T O;
TOP : T O P;
TOTALS : T O T A L S;
+TO : T O;
+TRACKING : T R A C K I N G;
TRAILING : T R A I L I N G;
+TRANSACTION : T R A N S A C T I O N;
+TREE : T R E E;
TRIM : T R I M;
TRUNCATE : T R U N C A T E;
TTL : T T L;
TYPE : T Y P E;
UNBOUNDED : U N B O U N D E D;
+UNCOMPRESSED : U N C O M P R E S S E D ;
+UNDROP : U N D R O P;
+UNFREEZE : U N F R E E Z E ;
UNION : U N I O N;
+UNLOAD : U N L O A D ;
+UNTIL : U N T I L;
UPDATE : U P D A T E;
-USE : U S E;
+URL : U R L;
+USERS : U S E R S ;
USER : U S E R;
+USE : U S E;
USING : U S I N G;
UUID : U U I D;
+VALID : V A L I D;
VALUES : V A L U E S;
+VIEWS : V I E W S;
VIEW : V I E W;
+VIRTUAL : V I R T U A L;
VOLUME : V O L U M E;
+WAIT : W A I T;
WATCH : W A T C H;
WEEK : W E E K;
WHEN : W H E N;
WHERE : W H E R E;
WINDOW : W I N D O W;
WITH : W I T H;
+WORKLOAD : W O R K L O A D;
+WRITABLE : W R I T A B L E;
YEAR : Y E A R | Y Y Y Y;
-QUOTA : Q U O T A;
-ACCESS : A C C E S S;
-GRANT : G R A N T;
-WAIT : W A I T;
-CLEANUP : C L E A N U P;
-DEFINER : D E F I N E R;
-RESTART : R E S T A R T;
-SOURCES : S O U R C E S;
-AZURE : A Z U R E;
-FILE : F I L E;
-HDFS : H D F S;
-HIVE : H I V E;
-JDBC : J D B C;
-KAFKA : K A F K A;
-MONGO : M O N G O;
-MYSQL : M Y S Q L;
-NATS : N A T S;
-ODBC : O D B C;
-POSTGRES : P O S T G R E S;
-RABBITMQ : R A B B I T M Q;
-REDIS : R E D I S;
-REMOTE : R E M O T E;
-S3 : S '3';
-SQLITE : S Q L I T E;
-URL : U R L;
-LOADING : L O A D I N G;
-VIRTUAL : V I R T U A L;
-VIEWS : V I E W S;
-POLICY : P O L I C Y;
-PERMISSIVE : P E R M I S S I V E;
-RESTRICTIVE : R E S T R I C T I V E;
+ZKPATH : Z K P A T H;
+SUM : S U M;
+AVG : A V G;
JSON_FALSE : 'false';
JSON_TRUE : 'true';
// Tokens
-IDENTIFIER: (LETTER | UNDERSCORE) (LETTER | UNDERSCORE | DEC_DIGIT)*
+IDENTIFIER:
+ (LETTER | UNDERSCORE) (LETTER | UNDERSCORE | DEC_DIGIT)*
+ | DEC_DIGIT+ (LETTER | UNDERSCORE) (LETTER | UNDERSCORE | DEC_DIGIT)*
| BACKQUOTE ( ~([\\`]) | (BACKSLASH .) | (BACKQUOTE BACKQUOTE))* BACKQUOTE
| QUOTE_DOUBLE (~([\\"]) | (BACKSLASH .) | (QUOTE_DOUBLE QUOTE_DOUBLE))* QUOTE_DOUBLE
;
@@ -342,7 +464,7 @@ LT : '<';
NOT_EQ : '!=' | '<>';
PERCENT : '%';
PLUS : '+';
-QUERY : '?';
+JDBC_PARAM_PLACEHOLDER : '?';
QUOTE_DOUBLE : '"';
QUOTE_SINGLE : '\'';
RBRACE : '}';
diff --git a/jdbc-v2/src/main/antlr4/com/clickhouse/jdbc/internal/ClickHouseParser.g4 b/jdbc-v2/src/main/antlr4/com/clickhouse/jdbc/internal/parser/antlr4/ClickHouseParser.g4
similarity index 69%
rename from jdbc-v2/src/main/antlr4/com/clickhouse/jdbc/internal/ClickHouseParser.g4
rename to jdbc-v2/src/main/antlr4/com/clickhouse/jdbc/internal/parser/antlr4/ClickHouseParser.g4
index d6b06a28f..bd94b69c9 100644
--- a/jdbc-v2/src/main/antlr4/com/clickhouse/jdbc/internal/ClickHouseParser.g4
+++ b/jdbc-v2/src/main/antlr4/com/clickhouse/jdbc/internal/parser/antlr4/ClickHouseParser.g4
@@ -22,41 +22,38 @@ query
| createStmt // DDL
| describeStmt
| dropStmt // DDL
+ | undropStmt // DDL
| existsStmt
| explainStmt
| killStmt // DDL
| optimizeStmt // DDL
| renameStmt // DDL
- | selectUnionStmt
| setStmt
| setRoleStmt
| showStmt
| systemStmt
| truncateStmt // DDL
+ | deleteStmt
+ | updateStmt
| useStmt
| watchStmt
- | ctes? selectStmt
+ | selectStmt
+ | selectUnionStmt
| grantStmt
+ | revokeStmt
+ | exchangeStmt
+ | moveStmt
;
-// CTE statement
-ctes
- : LPAREN? WITH cteUnboundCol? (COMMA cteUnboundCol)* COMMA? namedQuery (COMMA namedQuery)* RPAREN?
- ;
+// DELETE statement
-namedQuery
- : name = identifier (columnAliases)? AS LPAREN query RPAREN
+deleteStmt
+ : DELETE FROM tableIdentifier clusterClause? (IN partitionClause)? whereClause?
;
-columnAliases
- : LPAREN identifier (',' identifier)* RPAREN
- ;
-
-cteUnboundCol
- : (literal AS identifier) # CteUnboundColLiteral
- | (QUERY AS identifier) # CteUnboundColParam
- | LPAREN columnExpr RPAREN AS identifier # CteUnboundColExpr
- | LPAREN ctes? selectStmt RPAREN AS identifier # CteUnboundNestedSelect
+// UPDATE statement
+updateStmt
+ : UPDATE tableIdentifier clusterClause? SET assignmentExprList whereClause?
;
// ALTER statement
@@ -66,9 +63,9 @@ alterStmt
;
alterTableClause
- : ADD COLUMN (IF NOT EXISTS)? tableColumnDfnt (AFTER nestedIdentifier)? # AlterTableClauseAddColumn
- | ADD INDEX (IF NOT EXISTS)? tableIndexDfnt (AFTER nestedIdentifier)? # AlterTableClauseAddIndex
- | ADD PROJECTION (IF NOT EXISTS)? tableProjectionDfnt (AFTER nestedIdentifier)? # AlterTableClauseAddProjection
+ : ADD COLUMN (IF NOT EXISTS)? tableColumnDfnt alterTableColumnPosition? # AlterTableClauseAddColumn
+ | ADD INDEX (IF NOT EXISTS)? tableIndexDfnt alterTableColumnPosition? # AlterTableClauseAddIndex
+ | ADD PROJECTION (IF NOT EXISTS)? tableProjectionDfnt alterTableColumnPosition? # AlterTableClauseAddProjection
| ATTACH partitionClause (FROM tableIdentifier)? # AlterTableClauseAttach
| CLEAR COLUMN (IF EXISTS)? nestedIdentifier (IN partitionClause)? # AlterTableClauseClearColumn
| CLEAR INDEX (IF EXISTS)? nestedIdentifier (IN partitionClause)? # AlterTableClauseClearIndex
@@ -87,8 +84,10 @@ alterTableClause
| MODIFY COLUMN (IF EXISTS)? nestedIdentifier COMMENT STRING_LITERAL # AlterTableClauseModifyComment
| MODIFY COLUMN (IF EXISTS)? nestedIdentifier REMOVE tableColumnPropertyType # AlterTableClauseModifyRemove
| MODIFY COLUMN (IF EXISTS)? tableColumnDfnt # AlterTableClauseModify
+ | ALTER COLUMN (IF EXISTS)? identifier TYPE? columnTypeExpr codecExpr? ttlClause? settingExprList? alterTableColumnPosition? # AlterTableClauseAlterType
| MODIFY ORDER BY columnExpr # AlterTableClauseModifyOrderBy
| MODIFY ttlClause # AlterTableClauseModifyTTL
+ | MODIFY COMMENT literal # AlterTableClauseModifyComment
| MOVE partitionClause (
TO DISK STRING_LITERAL
| TO VOLUME STRING_LITERAL
@@ -100,12 +99,18 @@ alterTableClause
| UPDATE assignmentExprList whereClause # AlterTableClauseUpdate
;
+alterTableColumnPosition
+ : (AFTER nestedIdentifier)
+ | FIRST
+ ;
+
assignmentExprList
: assignmentExpr (COMMA assignmentExpr)*
;
assignmentExpr
: nestedIdentifier EQ_SINGLE columnExpr
+ | nestedIdentifier EQ_SINGLE JDBC_PARAM_PLACEHOLDER
;
tableColumnPropertyType
@@ -124,21 +129,25 @@ partitionClause
// ATTACH statement
attachStmt
- : ATTACH DICTIONARY tableIdentifier clusterClause? # AttachDictionaryStmt
+ : ATTACH TABLE (IF NOT EXISTS)? tableIdentifier clusterClause?
+ | ATTACH DICTIONARY (IF NOT EXISTS)? tableIdentifier clusterClause?
+ | ATTACH DATABASE (IF NOT EXISTS)? databaseIdentifier engineExpr? clusterClause?
;
// CHECK statement
checkStmt
- : CHECK TABLE tableIdentifier partitionClause?
+ : CHECK TABLE tableIdentifier (PARTITION identifier | PART identifier)? (FORMAT identifier)? settingsClause? # checkTableStmt
+ | CHECK ALL TABLES (FORMAT identifier)? settingsClause? # checkAllTablesStmt
+ | CHECK GRANT privilege columnsClause? ON grantTableIdentifier # checkGrantStmt
;
// CREATE statement
createStmt
- : (ATTACH | CREATE) DATABASE (IF NOT EXISTS)? databaseIdentifier clusterClause? engineExpr? # CreateDatabaseStmt
- | (ATTACH | CREATE (OR REPLACE)? | REPLACE) DICTIONARY (IF NOT EXISTS)? tableIdentifier uuidClause? clusterClause? dictionarySchemaClause
- dictionaryEngineClause # CreateDictionaryStmt
+ : CREATE DATABASE (IF NOT EXISTS)? databaseIdentifier clusterClause? engineExpr? # CreateDatabaseStmt
+ | (CREATE (OR REPLACE)? | REPLACE) DICTIONARY (IF NOT EXISTS)? tableIdentifier uuidClause? clusterClause? dictionarySchemaClause
+ dictionaryEngineClause sourceClause layoutClause lifetimeClause dictionarySettingsClause? (COMMENT literal)? # CreateDictionaryStmt
| (ATTACH | CREATE) LIVE VIEW (IF NOT EXISTS)? tableIdentifier uuidClause? clusterClause? (
WITH TIMEOUT DECIMAL_LITERAL?
)? destinationClause? tableSchemaClause? subqueryClause # CreateLiveViewStmt
@@ -146,9 +155,9 @@ createStmt
destinationClause
| engineClause POPULATE?
) subqueryClause # CreateMaterializedViewStmt
- | (ATTACH | CREATE (OR REPLACE)? | REPLACE) TEMPORARY? TABLE (IF NOT EXISTS)? tableIdentifier uuidClause? clusterClause? tableSchemaClause?
- engineClause? subqueryClause? # CreateTableStmt
- | (ATTACH | CREATE) (OR REPLACE)? VIEW (IF NOT EXISTS)? tableIdentifier uuidClause? clusterClause? tableSchemaClause? subqueryClause #
+ | (ATTACH | CREATE (OR REPLACE)? | REPLACE) TEMPORARY? TABLE (IF NOT EXISTS)? tableIdentifier uuidClause? clusterClause? tableSchemaClause
+ engineClause? subqueryClause? # CreateTableStmt
+ | (ATTACH | CREATE) (OR REPLACE)? VIEW (IF NOT EXISTS)? tableIdentifier alias? uuidClause? clusterClause? tableSchemaClause? subqueryClause #
CreateViewStmt
| CREATE USER ((IF NOT EXISTS) | (OR REPLACE))? userIdentifier (COMMA userIdentifier)* clusterClause?
userIdentifiedClause?
@@ -162,6 +171,28 @@ createStmt
| CREATE (ROW)? POLICY (IF NOT EXISTS | OR REPLACE)? identifier clusterClause? ON tableIdentifier
(IN identifier)? (AS (PERMISSIVE | RESTRICTIVE))? (FOR SELECT)? USING columnExpr
(TO identifier | ALL | ALL EXCEPT identifier)? # CreatePolicyStmt
+ | CREATE SETTINGS? PROFILE ((IF NOT EXISTS) | (OR REPLACE))? identifier (COMMA identifier)* clusterClause?
+ (IN identifier)? ((SETTINGS identifier (EQ_SINGLE literal)? (MIN EQ_SINGLE? literal)? (MAX EQ_SINGLE? literal)?
+ (CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY)?)
+ | ( INHERIT identifier))? (TO identifier | ALL | ALL EXCEPT identifier)? # createProfileStmt
+ | CREATE FUNCTION identifier clusterClause? AS LPAREN (identifier)? (COMMA identifier)? RPAREN ARROW .+? #createFunctionStmt
+ | CREATE NAMED COLLECTION (IF NOT EXISTS)? identifier clusterClause? AS nameCollectionKey (COMMA nameCollectionKey)* #createNamedCollectionStmt
+ | CREATE QUOTA (IF NOT EXISTS | OR REPLACE)? identifier clusterClause? (IN identifier)?
+ (KEYED BY identifier | NOT KEYED)?
+ quotaForClause (COMMA quotaForClause)*
+ (TO (identifier (COMMA identifier)* | ALL | CURRENT_USER | ALL EXCEPT identifier (COMMA identifier)* ))? # createQuotaStmt
+ ;
+
+quotaMaxExpr
+ : identifier EQ_SINGLE numberLiteral
+ ;
+
+quotaForClause
+ : FOR RANDOMIZED? INTERVAL numberLiteral interval (MAX quotaMaxExpr (COMMA quotaMaxExpr)*)+?
+ ;
+
+nameCollectionKey
+ : (identifier EQ_SINGLE literal (NOT? OVERRIDE)?)
;
userIdentifier
@@ -205,7 +236,7 @@ dictionarySchemaClause
;
dictionaryAttrDfnt
- : identifier columnTypeExpr
+ : identifier columnTypeExpr ((DEFAULT | EXPRESSION) columnExpr)? (IS_OBJECT_ID|HIERARCHICAL|INJECTIVE)?
;
dictionaryEngineClause
@@ -213,7 +244,7 @@ dictionaryEngineClause
;
dictionaryPrimaryKeyClause
- : PRIMARY KEY columnExprList
+ : PRIMARY KEY (identifier) (COMMA identifier)*
;
dictionaryArgExpr
@@ -221,7 +252,7 @@ dictionaryArgExpr
;
sourceClause
- : SOURCE LPAREN identifier LPAREN dictionaryArgExpr* RPAREN RPAREN
+ : SOURCE LPAREN identifier LPAREN settingExprList RPAREN RPAREN
;
lifetimeClause
@@ -298,7 +329,7 @@ tableElementExpr
;
tableColumnDfnt
- : nestedIdentifier columnTypeExpr tableColumnPropertyExpr? (COMMENT STRING_LITERAL)? codecExpr? (
+ : nestedIdentifier columnTypeExpr (NULL_SQL | NOT NULL_SQL)? tableColumnPropertyExpr? (COMMENT STRING_LITERAL)? codecExpr? (
TTL columnExpr
)?
| nestedIdentifier columnTypeExpr? tableColumnPropertyExpr (COMMENT STRING_LITERAL)? codecExpr? (
@@ -307,7 +338,7 @@ tableColumnDfnt
;
tableColumnPropertyExpr
- : (DEFAULT | MATERIALIZED | ALIAS) columnExpr
+ : (DEFAULT | MATERIALIZED | ALIAS ) columnExpr
;
tableIndexDfnt
@@ -330,6 +361,11 @@ ttlExpr
: columnExpr (DELETE | TO DISK STRING_LITERAL | TO VOLUME STRING_LITERAL)?
;
+// MOVE statement
+moveStmt
+ : MOVE (USER | ROLE | QUOTA | SETTINGS PROFILE | ROW POLICY) identifier TO identifier
+ ;
+
// DESCRIBE statement
describeStmt
@@ -339,24 +375,30 @@ describeStmt
// DROP statement
dropStmt
- : (DETACH | DROP) DATABASE (IF EXISTS)? databaseIdentifier clusterClause? # DropDatabaseStmt
- | (DETACH | DROP) (DICTIONARY | TEMPORARY? TABLE | VIEW | ROLE | USER) (IF EXISTS)? tableIdentifier clusterClause? (
- NO DELAY
- )? # DropTableStmt
+ : (DETACH | DROP) DATABASE (IF EXISTS)? databaseIdentifier clusterClause? SYNC?
+ | (DETACH | DROP) (DICTIONARY | TEMPORARY? TABLE | VIEW) (IF EXISTS)? tableIdentifier clusterClause?
+ (NO DELAY)? SYNC?
+ | (DETACH | DROP) (USER | ROLE | QUOTA | SETTINGS? PROFILE) (IF EXISTS)? identifier clusterClause? (FROM identifier)?
+ | (DETACH | DROP) ROW? POLICY (IF EXISTS)? identifier ON grantTableIdentifier (COMMA grantTableIdentifier)* clusterClause? (FROM identifier)?
+ | (DETACH | DROP) (FUNCTION | NAMED COLLECTION) (IF EXISTS)? identifier clusterClause?
+ ;
+
+undropStmt
+ : UNDROP TABLE tableIdentifier uuidClause? clusterClause?
;
// EXISTS statement
existsStmt
- : EXISTS DATABASE databaseIdentifier # ExistsDatabaseStmt
- | EXISTS (DICTIONARY | TEMPORARY? TABLE | VIEW)? tableIdentifier # ExistsTableStmt
+ : EXISTS DATABASE databaseIdentifier (INTO OUTFILE filename)? (FORMAT identifier)? # ExistsDatabaseStmt
+ | EXISTS (DICTIONARY | TEMPORARY? TABLE | VIEW)? tableIdentifier (INTO OUTFILE filename)? (FORMAT identifier)? # ExistsTableStmt
;
// EXPLAIN statement
explainStmt
- : EXPLAIN AST query # ExplainASTStmt
- | EXPLAIN SYNTAX query # ExplainSyntaxStmt
+ : EXPLAIN (AST | SYNTAX | QUERY TREE | PLAN | PIPELINE | ESTIMATE | TABLE OVERRIDE)? settingExprList? .+?
+ | EXPLAIN .+?
;
// INSERT statement
@@ -382,27 +424,35 @@ assignmentValues
assignmentValue
: literal # InsertRawValue
- | QUERY # InsertParameter
+ | JDBC_PARAM_PLACEHOLDER # InsertParameter
| identifier (LPAREN columnExprList? RPAREN)? # InsertParameterFuncExpr
- | LPAREN columnExpr RPAREN # InserParameterExpr
+ | LPAREN? columnExpr RPAREN? # InserParameterExpr
;
// KILL statement
killStmt
- : KILL MUTATION clusterClause? whereClause (SYNC | ASYNC | TEST)? # KillMutationStmt
+ : KILL MUTATION clusterClause? whereClause (SYNC | ASYNC | TEST)? (FORMAT identifier)? # KillMutationStmt
+ | KILL QUERY clusterClause? whereClause (SYNC | ASYNC | TEST)? (FORMAT identifier)? # KillQueryStmt
;
// OPTIMIZE statement
optimizeStmt
- : OPTIMIZE TABLE tableIdentifier clusterClause? partitionClause? FINAL? DEDUPLICATE?
+ : OPTIMIZE TABLE tableIdentifier clusterClause? partitionClause? FINAL? DEDUPLICATE? optimizeByExpr?
+ ;
+
+optimizeByExpr
+ : BY ASTERISK (EXCEPT LPAREN? (identifier (COMMA identifier)*) RPAREN? )?
+ | BY identifier (COMMA identifier)*
+ | BY COLUMNS LPAREN literal RPAREN (EXCEPT LPAREN? (identifier (COMMA identifier)*) RPAREN? )?
;
// RENAME statement
renameStmt
: RENAME TABLE tableIdentifier TO tableIdentifier (COMMA tableIdentifier TO tableIdentifier)* clusterClause?
+ | RENAME
;
// PROJECTION SELECT statement
@@ -423,7 +473,7 @@ selectStmtWithParens
;
selectStmt
- : withClause? SELECT DISTINCT? topClause? columnExprList fromClause? arrayJoinClause? windowClause? prewhereClause? whereClause? groupByClause? (
+ : cteClause? SELECT DISTINCT? topClause? columnExprList fromClause? arrayJoinClause? windowClause? prewhereClause? whereClause? groupByClause? (
WITH (CUBE | ROLLUP)
)? (WITH TOTALS)? havingClause? orderByClause? limitByClause? limitClause? settingsClause?
;
@@ -432,19 +482,43 @@ withClause
: WITH columnExprList
;
+// CTE statement
+cteClause
+ : WITH (cteUnboundCol | namedQuery) (COMMA (cteUnboundCol | namedQuery))*
+ ;
+
+
+namedQuery
+ : identifier (columnAliases)? AS LPAREN? ( selectStmt | selectStmtWithParens | selectUnionStmt) RPAREN?
+ ;
+
+columnAliases
+ : LPAREN identifier (',' identifier)* RPAREN
+ ;
+
+cteUnboundCol
+ : literal AS identifier # CteUnboundColLiteral
+ | JDBC_PARAM_PLACEHOLDER AS identifier # CteUnboundColParam
+ | LPAREN? columnExpr RPAREN? AS? identifier? # CteUnboundColExpr
+ | LPAREN selectStmt RPAREN AS identifier # CteUnboundSubQuery
+// | LPAREN cteStmt? selectStmt RPAREN AS identifier # CteUnboundNestedSelect
+ ;
+
topClause
: TOP DECIMAL_LITERAL (WITH TIES)?
;
fromClause
: FROM joinExpr
- | FROM identifier LPAREN QUERY RPAREN
- | FROM ctes
+ | FROM tableIdentifier
+ | FROM identifier LPAREN JDBC_PARAM_PLACEHOLDER RPAREN
+ | FROM selectStmt
| FROM identifier LPAREN viewParam (COMMA viewParam)? RPAREN
+ | FROM tableFunctionExpr
;
viewParam
- : identifier EQ_SINGLE (literal | QUERY)
+ : identifier EQ_SINGLE (literal | JDBC_PARAM_PLACEHOLDER)
;
arrayJoinClause
@@ -579,10 +653,16 @@ winFrameBound
//rangeClause: RANGE LPAREN (MIN identifier MAX identifier | MAX identifier MIN identifier) RPAREN;
+// EXCHANGE statement
+exchangeStmt
+ : EXCHANGE (TABLES | DICTIONARIES) tableIdentifier AND tableIdentifier clusterClause?
+ ;
+
+
// SET statement
setStmt
- : SET settingExprList
+ : SET (identifier | settingExpr)
;
// SET ROLE statement
@@ -595,10 +675,23 @@ setRolesList
: identifier (COMMA identifier)*
;
+// GRANT statements
+
grantStmt
- : GRANT clusterClause? ((privilege ON grantTableIdentifier) | (identifier (COMMA identifier)*))
+ : GRANT clusterClause? ((identifier (COMMA identifier)*) | (privelegeList ON grantTableIdentifier))
TO (CURRENT_USER | identifier) (COMMA identifier)*
- (WITH GRANT OPTION)? (WITH REPLACE OPTION)?
+ (WITH ADMIN OPTION)? (WITH GRANT OPTION)? (WITH REPLACE OPTION)?
+ | GRANT CURRENT GRANTS (LPAREN ((privelegeList ON grantTableIdentifier) | (identifier (COMMA identifier)*)) RPAREN)?
+ TO (CURRENT_USER | identifier (COMMA identifier)*)
+ (WITH GRANT OPTION)? (WITH REPLACE OPTION)?
+ ;
+
+// REVOKE statements
+revokeStmt
+ : REVOKE clusterClause? privelegeList ON grantTableIdentifier
+ FROM ((CURRENT_USER | identifier) (COMMA identifier)* | ALL | ALL EXCEPT (CURRENT_USER | identifier) (COMMA identifier)* )
+ | REVOKE clusterClause? (ADMIN OPTION FOR)? identifier (COMMA identifier)*
+ FROM ((CURRENT_USER | identifier) (COMMA identifier)* | ALL | ALL EXCEPT (CURRENT_USER | identifier) (COMMA identifier)* )
;
grantTableIdentifier
@@ -608,6 +701,15 @@ grantTableIdentifier
| (ASTERISK DOT)? ASTERISK
;
+privelegeList
+ : columnPrivilege (COMMA columnPrivilege)*
+ ;
+
+
+columnPrivilege
+ : privilege (LPAREN identifier (COMMA identifier)* RPAREN)?
+ ;
+
privilege
:
| ACCESS MANAGEMENT
@@ -830,24 +932,90 @@ systemPrivilege
// SHOW statements
showStmt
- : SHOW CREATE DATABASE databaseIdentifier # showCreateDatabaseStmt
- | SHOW CREATE DICTIONARY tableIdentifier # showCreateDictionaryStmt
- | SHOW CREATE TEMPORARY? TABLE? tableIdentifier # showCreateTableStmt
- | SHOW DATABASES # showDatabasesStmt
- | SHOW DICTIONARIES (FROM databaseIdentifier)? # showDictionariesStmt
- | SHOW TEMPORARY? TABLES ((FROM | IN) databaseIdentifier)? (LIKE STRING_LITERAL | whereClause)? limitClause? # showTablesStmt
+ : SHOW CREATE? (TEMPORARY? TABLE | DICTIONARY | VIEW | DATABASE) tableIdentifier (INTO OUTFILE literal)? (FORMAT identifier)? # showCreateStmt
+ | SHOW DATABASES (NOT? (LIKE | ILIKE) literal) (LIMIT numberLiteral)? (INTO OUTFILE filename)? (FORMAT identifier)? # showDatabasesStmt
+ | SHOW FULL? TEMPORARY? TABLES showFromDbClause? (NOT? (LIKE | ILIKE) literal)? (LIMIT numberLiteral)? (INTO OUTFILE filename)? (FORMAT identifier)? # showTablesStmt
+ | SHOW EXTENDED? FULL? COLUMNS showFromTableFromDbClause? (NOT? (LIKE | ILIKE) literal)? (LIMIT numberLiteral)? (INTO OUTFILE filename)? (FORMAT identifier)? # showColumnsStmt
+ | SHOW DICTIONARIES showFromDbClause? (NOT? (LIKE | ILIKE) literal)? (LIMIT numberLiteral)? (INTO OUTFILE filename)? (FORMAT identifier)? # showDictionariesStmt
+ | SHOW EXTENDED? (INDEX | INDEXES | INDICES | KEYS ) (FROM | IN) identifier showFromTableFromDbClause? (WHERE columnExpr)? (INTO OUTFILE filename)? (FORMAT identifier)? # showIndexStmt
+ | SHOW PROCESSLIST (INTO OUTFILE filename)? (FORMAT identifier)? # showProcessListStmt
+ | SHOW GRANTS (FOR identifier (COMMA identifier)*)? (WITH IMPLICIT)? FINAL? # showGrantsStmt
+ | SHOW CREATE USER ((identifier (COMMA identifier)*) | CURRENT_USER) # showCreateUserStmt
+ | SHOW CREATE ROLE (identifier (COMMA identifier)*) # showCreateRoleStmt
+ | SHOW CREATE ROW? POLICY identifier ON tableIdentifier # showCreatePolicyStmt
+ | SHOW CREATE QUOTA ((identifier (COMMA identifier)*) | CURRENT) # showCreateQuotaStmt
+ | SHOW CREATE (SETTINGS)? PROFILE identifier (COMMA identifier)* # showCreateProfile
+ | SHOW USERS # showUsersStmt
+ | SHOW (CURRENT|ENABLED)? ROLES # showRolesStmt
+ | SHOW SETTINGS? PROFILES # showProfilesStmt
+ | SHOW ROW? POLICIES (ON identifier)? # showPoliciesStmt
+ | SHOW QUOTAS # showQuotasStmt
+ | SHOW CURRENT? QUOTA # showQuotaStmt
+ | SHOW ACCESS # showAccessStmt
+ | SHOW CLUSTER identifier # showClusterStmt
+ | SHOW CLUSTERS (NOT? (LIKE | ILIKE) literal)? (LIMIT numberLiteral)? (INTO OUTFILE filename)? (FORMAT identifier)? # showClustersStmt
+ | SHOW CHANGED? SETTINGS (LIKE | ILIKE) literal # showSettingsStmt
+ | SHOW SETTING identifier # showSettingStmt
+ | SHOW FILESYSTEM CACHES # showFSCachesStmt
+ | SHOW ENGINES (INTO OUTFILE filename)? (FORMAT identifier)? # showEnginesStmt
+ | SHOW FUNCTIONS (NOT? (LIKE | ILIKE) literal)? # showFunctionsStmt
+ | SHOW MERGES (NOT? (LIKE | ILIKE) literal)? (LIMIT numberLiteral)? (INTO OUTFILE filename)? (FORMAT identifier)? # showMergesStmt
+ ;
+
+showFromDbClause
+ : ((FROM | IN) identifier)
+ ;
+
+showFromTableFromDbClause
+ : ((FROM | IN) identifier) showFromDbClause?
;
// SYSTEM statements
systemStmt
: SYSTEM FLUSH DISTRIBUTED tableIdentifier
- | SYSTEM FLUSH LOGS
- | SYSTEM RELOAD DICTIONARIES
+ | SYSTEM RELOAD DICTIONARIES clusterClause? identifier?
| SYSTEM RELOAD DICTIONARY tableIdentifier
- | SYSTEM (START | STOP) (DISTRIBUTED SENDS | FETCHES | TTL? MERGES) tableIdentifier
- | SYSTEM (START | STOP) REPLICATED SENDS
- | SYSTEM SYNC REPLICA tableIdentifier
+ | SYSTEM RELOAD MODEL clusterClause? identifier?
+ | SYSTEM RELOAD FUNCTIONS clusterClause?
+ | SYSTEM RELOAD FUNCTION clusterClause? identifier
+ | SYSTEM RELOAD ASYNCHRONOUS METRICS clusterClause?
+ | SYSTEM DROP DNS CACHE
+ | SYSTEM DROP MARK CACHE
+ | SYSTEM DROP REPLICA literal (FROM SHARD literal)? (FROM (TABLE tableIdentifier) | (FROM DATABASE identifier) | (ZKPATH literal))?
+ | SYSTEM DROP UNCOMPRESSED CACHE
+ | SYSTEM DROP COMPILED EXPRESSION CACHE
+ | SYSTEM DROP QUERY CONDITION CACHE
+ | SYSTEM DROP QUERY CACHE (TAG literal)?
+ | SYSTEM DROP FORMAT SCHEMA CACHE (FOR literal)?
+ | SYSTEM FLUSH LOGS
+ | SYSTEM RELOAD CONFIG clusterClause?
+ | SYSTEM RELOAD USERS clusterClause?
+ | SYSTEM SHUTDOWN
+ | SYSTEM KILL
+ | SYSTEM (START | FLUSH | STOP) (DISTRIBUTED SENDS? | FETCHES | TTL? MERGES) tableIdentifier clusterClause? settingsClause?
+ | SYSTEM (START | STOP) LISTEN clusterClause? (QUERIES ALL | QUERIES DEFAULT | QUERIES CUSTOM | TCP | TCP WITH PROXY | TCP SECURE | HTTP | HTTPS | MYSQL | GRPC | POSTGRESQL | PROMETHEUS | CUSTOM literal)
+ | SYSTEM (START | STOP) MERGES clusterClause? ((ON VOLUME identifier) | tableIdentifier)?
+ | SYSTEM (START | STOP) TTL MERGES clusterClause? tableIdentifier?
+ | SYSTEM (START | STOP) MOVES clusterClause? tableIdentifier?
+ | SYSTEM UNFREEZE WITH NAME literal
+ | SYSTEM WAIT LOADING PARTS clusterClause? tableIdentifier?
+ | SYSTEM (START | STOP) FETCHES clusterClause? tableIdentifier?
+ | SYSTEM (START | STOP) REPLICATED SENDS clusterClause? tableIdentifier?
+ | SYSTEM (START | STOP) REPLICATION QUEUES clusterClause? tableIdentifier?
+ | SYSTEM (START | STOP) PULLING REPLICATION LOG clusterClause? tableIdentifier?
+ | SYSTEM SYNC REPLICA clusterClause? tableIdentifier? (IF EXISTS)? (STRICT | LIGHTWEIGHT | FROM literal | PULL)?
+ | SYSTEM SYNC DATABASE REPLICA identifier
+ | SYSTEM RESTART REPLICA clusterClause? tableIdentifier?
+ | SYSTEM RESTORE DATABASE? REPLICA identifier clusterClause?
+ | SYSTEM RESTART REPLICAS
+ | SYSTEM DROP FILESYSTEM CACHE clusterClause?
+ | SYSTEM SYNC FILE CACHE clusterClause?
+ | SYSTEM (LOAD | UNLOAD) PRIMARY KEY tableIdentifier?
+ | SYSTEM REFRESH VIEW tableIdentifier
+ | SYSTEM REPLICATED? (START | STOP) ((VIEW tableIdentifier) | VIEWS)
+ | SYSTEM CANCEL VIEW tableIdentifier
+ | SYSTEM WAIT VIEW tableIdentifier
;
// TRUNCATE statements
@@ -935,7 +1103,7 @@ columnExpr
| columnExpr OR columnExpr # ColumnExprOr
// TODO(ilezhankin): `BETWEEN a AND b AND c` is parsed in a wrong way: `BETWEEN (a AND b) AND c`
| columnExpr NOT? BETWEEN columnExpr AND columnExpr # ColumnExprBetween
- | columnExpr QUERY columnExpr COLON columnExpr # ColumnExprTernaryOp
+ | columnExpr JDBC_PARAM_PLACEHOLDER columnExpr COLON columnExpr # ColumnExprTernaryOp
| columnExpr (alias | AS identifier) # ColumnExprAlias
| (tableIdentifier DOT)? ASTERISK # ColumnExprAsterisk // single-column only
| LPAREN selectUnionStmt RPAREN # ColumnExprSubquery // single-column only
@@ -943,13 +1111,13 @@ columnExpr
| LPAREN columnExprList RPAREN # ColumnExprTuple
| LBRACKET columnExprList? RBRACKET # ColumnExprArray
| columnIdentifier # ColumnExprIdentifier
- | QUERY (CAST_OP identifier)? # ColumnExprParam
+ | JDBC_PARAM_PLACEHOLDER (CAST_OP identifier)? # ColumnExprParam
| columnExpr REGEXP literal # ColumnExprRegexp
;
columnArgList
: columnArgExpr (COMMA columnArgExpr)*
- | QUERY (COMMA QUERY)*
+ | JDBC_PARAM_PLACEHOLDER (COMMA JDBC_PARAM_PLACEHOLDER)*
;
columnArgExpr
@@ -986,6 +1154,10 @@ tableIdentifier
: (databaseIdentifier DOT)? identifier
;
+viewIdentifier
+ : tableIdentifier
+ ;
+
tableArgList
: tableArgExpr (COMMA tableArgExpr)*
;
@@ -1027,6 +1199,10 @@ literal
| NULL_SQL
;
+filename
+ : STRING_LITERAL
+ ;
+
interval
: SECOND
| MINUTE
@@ -1124,6 +1300,7 @@ keyword
| INSERT
| INTERVAL
| INTO
+ | IP
| IS
| IS_OBJECT_ID
| JOIN
@@ -1141,6 +1318,7 @@ keyword
| LIVE
| LOCAL
| LOGS
+ | LOG
| MATERIALIZE
| MATERIALIZED
| MAX
@@ -1167,9 +1345,11 @@ keyword
| PRECEDING
| PREWHERE
| PRIMARY
+ | PROFILE
| RANGE
| RELOAD
| REMOVE
+ | REMOTE
| RENAME
| REPLACE
| REPLICA
@@ -1178,6 +1358,7 @@ keyword
| ROLLUP
| ROW
| ROWS
+ | REVOKE
| SAMPLE
| SELECT
| SEMI
@@ -1204,6 +1385,7 @@ keyword
| TRAILING
| TRIM
| TRUNCATE
+ | TRACKING
| TO
| TOP
| TTL
@@ -1214,7 +1396,9 @@ keyword
| USE
| USING
| USER
+ | USERS
| UUID
+ | URL
| VALUES
| VIEW
| VOLUME
@@ -1223,6 +1407,11 @@ keyword
| WHERE
| WINDOW
| WITH
+ | QUERIES
+ | SUM
+ | AVG
+ | REFRESH
+ | EXPLAIN
;
keywordForAlias
@@ -1237,6 +1426,7 @@ keywordForAlias
| CURRENT
| INDEX
| TABLES
+ | TABLE
| TEST
| VIEW
| PRIMARY
@@ -1247,6 +1437,7 @@ keywordForAlias
| HOUR
| MINUTE
| SECOND
+ | REVOKE
;
alias
diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/ConnectionImpl.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/ConnectionImpl.java
index 40611d191..b3238e872 100644
--- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/ConnectionImpl.java
+++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/ConnectionImpl.java
@@ -12,7 +12,7 @@
import com.clickhouse.jdbc.internal.FeatureManager;
import com.clickhouse.jdbc.internal.JdbcConfiguration;
import com.clickhouse.jdbc.internal.ParsedPreparedStatement;
-import com.clickhouse.jdbc.internal.SqlParser;
+import com.clickhouse.jdbc.internal.SqlParserFacade;
import com.clickhouse.jdbc.metadata.DatabaseMetaDataImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -64,7 +64,7 @@ public class ConnectionImpl implements Connection, JdbcV2Wrapper {
private final DatabaseMetaDataImpl metadata;
protected final Calendar defaultCalendar;
- private final SqlParser sqlParser;
+ private final SqlParserFacade sqlParser;
private Executor networkTimeoutExecutor;
@@ -117,7 +117,9 @@ public ConnectionImpl(String url, Properties info) throws SQLException {
this.metadata = new DatabaseMetaDataImpl(this, false, url);
this.defaultCalendar = Calendar.getInstance();
- this.sqlParser = new SqlParser();
+
+ this.sqlParser = SqlParserFacade.getParser(config.getDriverProperty(DriverProperties.SQL_PARSER.getKey(),
+ DriverProperties.SQL_PARSER.getDefaultValue()));
this.featureManager = new FeatureManager(this.config);
} catch (SQLException e) {
throw e;
@@ -126,7 +128,7 @@ public ConnectionImpl(String url, Properties info) throws SQLException {
}
}
- public SqlParser getSqlParser() {
+ public SqlParserFacade getSqlParser() {
return sqlParser;
}
diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/DriverProperties.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/DriverProperties.java
index 6bae0071b..f623edda4 100644
--- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/DriverProperties.java
+++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/DriverProperties.java
@@ -57,6 +57,17 @@ public enum DriverProperties {
*
*/
USE_MAX_RESULT_ROWS("jdbc_use_max_result_rows", String.valueOf(Boolean.FALSE)),
+
+ /**
+ * Configures what SQL parser will be used. Choices:
+ *
+ *
ANTLR4 - parser extracts required information but PreparedStatement parameters parsed separately.
+ *
ANTLR4_PARAMS_PARSER - parser extracts required information AND parameter positions.
+ *
JAVACC - parser extracts required information but PreparedStatement parameters parsed separately.
+ *
+ */
+// SQL_PARSER("jdbc_sql_parser", "JAVACC", List.of("ANTLR4", "ANTLR4_PARAMS_PARSER", "JAVACC")),
+ SQL_PARSER("jdbc_sql_parser", "ANTLR4", List.of("ANTLR4", "ANTLR4_PARAMS_PARSER", "JAVACC")),
;
diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/ParsedPreparedStatement.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/ParsedPreparedStatement.java
index 6fcac9d3f..b17db6703 100644
--- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/ParsedPreparedStatement.java
+++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/ParsedPreparedStatement.java
@@ -1,20 +1,12 @@
package com.clickhouse.jdbc.internal;
-import com.clickhouse.client.api.sql.SQLUtils;
-import org.antlr.v4.runtime.tree.ErrorNode;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.List;
/**
- * Parser listener that collects information for prepared statement.
+ * Model of parsed statement with parameters
*/
-public class ParsedPreparedStatement extends ClickHouseParserBaseListener {
- private static final Logger LOG = LoggerFactory.getLogger(ParsedPreparedStatement.class);
+public final class ParsedPreparedStatement {
private String table;
@@ -42,7 +34,7 @@ public class ParsedPreparedStatement extends ClickHouseParserBaseListener {
private int assignValuesListStopPosition = -1;
- private int assignValuesGroups = -1;
+ private int assignValuesGroups = 0;
public void setHasResultSet(boolean hasResultSet) {
this.hasResultSet = hasResultSet;
@@ -76,10 +68,18 @@ public String[] getInsertColumns() {
return insertColumns;
}
+ public void setInsertColumns(String[] insertColumns) {
+ this.insertColumns = insertColumns;
+ }
+
public String getTable() {
return table;
}
+ public void setTable(String table) {
+ this.table = table;
+ }
+
public int[] getParamPositions() {
return paramPositions;
}
@@ -96,10 +96,18 @@ public int getAssignValuesListStartPosition() {
return assignValuesListStartPosition;
}
+ public void setAssignValuesListStartPosition(int assignValuesListStartPosition) {
+ this.assignValuesListStartPosition = assignValuesListStartPosition;
+ }
+
public int getAssignValuesListStopPosition() {
return assignValuesListStopPosition;
}
+ public void setAssignValuesListStopPosition(int assignValuesListStopPosition) {
+ this.assignValuesListStopPosition = assignValuesListStopPosition;
+ }
+
public void setUseDatabase(String useDatabase) {
this.useDatabase = useDatabase;
}
@@ -132,135 +140,11 @@ public void setHasErrors(boolean hasErrors) {
this.hasErrors = hasErrors;
}
- @Override
- public void enterQueryStmt(ClickHouseParser.QueryStmtContext ctx) {
- ClickHouseParser.QueryContext qCtx = ctx.query();
- if (qCtx != null) {
- if (qCtx.selectStmt() != null || qCtx.selectUnionStmt() != null || qCtx.showStmt() != null || qCtx.describeStmt() != null) {
- setHasResultSet(true);
- }
- }
- }
-
- @Override
- public void enterUseStmt(ClickHouseParser.UseStmtContext ctx) {
- if (ctx.databaseIdentifier() != null) {
- setUseDatabase(SQLUtils.unquoteIdentifier(ctx.databaseIdentifier().getText()));
- }
- }
-
- @Override
- public void enterSetRoleStmt(ClickHouseParser.SetRoleStmtContext ctx) {
- if (ctx.NONE() != null) {
- setRoles(Collections.emptyList());
- } else {
- List roles = new ArrayList<>();
- for (ClickHouseParser.IdentifierContext id : ctx.setRolesList().identifier()) {
- roles.add(SQLUtils.unquoteIdentifier(id.getText()));
- }
- setRoles(roles);
- }
- }
-
- @Override
- public void enterColumnExprParam(ClickHouseParser.ColumnExprParamContext ctx) {
- appendParameter(ctx.start.getStartIndex());
- }
-
- @Override
- public void enterColumnExprPrecedence3(ClickHouseParser.ColumnExprPrecedence3Context ctx) {
- super.enterColumnExprPrecedence3(ctx);
- }
-
- @Override
- public void enterCteUnboundColParam(ClickHouseParser.CteUnboundColParamContext ctx) {
- appendParameter(ctx.start.getStartIndex());
- }
-
- @Override
- public void visitErrorNode(ErrorNode node) {
- setHasErrors(true);
- }
-
- @Override
- public void enterInsertParameterFuncExpr(ClickHouseParser.InsertParameterFuncExprContext ctx) {
- setUseFunction(true);
- }
-
- @Override
- public void enterAssignmentValuesList(ClickHouseParser.AssignmentValuesListContext ctx) {
- assignValuesListStartPosition = ctx.getStart().getStartIndex();
- assignValuesListStopPosition = ctx.getStop().getStopIndex();
- }
-
- @Override
- public void enterInsertParameter(ClickHouseParser.InsertParameterContext ctx) {
- appendParameter(ctx.start.getStartIndex());
- }
-
- @Override
- public void enterFromClause(ClickHouseParser.FromClauseContext ctx) {
- if (ctx.QUERY() != null) {
- appendParameter(ctx.QUERY().getSymbol().getStartIndex());
- }
- }
-
- @Override
- public void enterViewParam(ClickHouseParser.ViewParamContext ctx) {
- if (ctx.QUERY() != null) {
- appendParameter(ctx.QUERY().getSymbol().getStartIndex());
- }
- }
-
- private void appendParameter(int startIndex) {
+ void appendParameter(int startIndex) {
argCount++;
if (argCount > paramPositions.length) {
paramPositions = Arrays.copyOf(paramPositions, paramPositions.length + 10);
}
paramPositions[argCount - 1] = startIndex;
- if (LOG.isTraceEnabled()) {
- LOG.trace("parameter position {}", startIndex);
- }
- }
-
- @Override
- public void enterTableExprIdentifier(ClickHouseParser.TableExprIdentifierContext ctx) {
- if (ctx.tableIdentifier() != null) {
- this.table = SQLUtils.unquoteIdentifier(ctx.tableIdentifier().getText());
- }
- }
-
- @Override
- public void enterInsertStmt(ClickHouseParser.InsertStmtContext ctx) {
- ClickHouseParser.TableIdentifierContext tableId = ctx.tableIdentifier();
- if (tableId != null) {
- this.table = SQLUtils.unquoteIdentifier(tableId.getText());
- }
-
- ClickHouseParser.ColumnsClauseContext columns = ctx.columnsClause();
- if (columns != null) {
- List names = columns.nestedIdentifier();
- this.insertColumns = new String[names.size()];
- for (int i = 0; i < names.size(); i++) {
- this.insertColumns[i] = names.get(i).getText();
- }
- }
-
- setInsert(true);
- }
-
- @Override
- public void enterDataClauseSelect(ClickHouseParser.DataClauseSelectContext ctx) {
- setInsertWithSelect(true);
- }
-
- @Override
- public void enterDataClauseValues(ClickHouseParser.DataClauseValuesContext ctx) {
- setAssignValuesGroups(ctx.assignmentValues().size());
- }
-
- @Override
- public void exitInsertParameterFuncExpr(ClickHouseParser.InsertParameterFuncExprContext ctx) {
- setUseFunction(true);
}
}
diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/ParsedStatement.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/ParsedStatement.java
index ee94eaf67..f9eb5cde8 100644
--- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/ParsedStatement.java
+++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/ParsedStatement.java
@@ -1,13 +1,11 @@
package com.clickhouse.jdbc.internal;
-import com.clickhouse.client.api.sql.SQLUtils;
-import org.antlr.v4.runtime.tree.ErrorNode;
-
-import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
-public class ParsedStatement extends ClickHouseParserBaseListener {
+/**
+ * Model of parsed statement when no parameters are used.
+ */
+public final class ParsedStatement {
private String useDatabase;
@@ -15,8 +13,6 @@ public class ParsedStatement extends ClickHouseParserBaseListener {
private boolean insert;
- private String insertTableId;
-
private List roles;
private boolean hasErrors;
@@ -41,14 +37,6 @@ public boolean isInsert() {
return insert;
}
- public void setInsertTableId(String insertTableId) {
- this.insertTableId = insertTableId;
- }
-
- public String getInsertTableId() {
- return insertTableId;
- }
-
public String getUseDatabase() {
return useDatabase;
}
@@ -68,42 +56,4 @@ public boolean isHasErrors() {
public void setHasErrors(boolean hasErrors) {
this.hasErrors = hasErrors;
}
-
- @Override
- public void visitErrorNode(ErrorNode node) {
- setHasErrors(true);
- }
-
- @Override
- public void enterQueryStmt(ClickHouseParser.QueryStmtContext ctx) {
- ClickHouseParser.QueryContext qCtx = ctx.query();
- if (qCtx != null) {
- if (qCtx.selectStmt() != null || qCtx.selectUnionStmt() != null || qCtx.showStmt() != null
- || qCtx.describeStmt() != null) {
- setHasResultSet(true);
- }
- }
- }
-
- @Override
- public void enterUseStmt(ClickHouseParser.UseStmtContext ctx) {
- if (ctx.databaseIdentifier() != null) {
- setUseDatabase(SQLUtils.unquoteIdentifier(ctx.databaseIdentifier().getText()));
- }
- }
-
- @Override
- public void enterSetRoleStmt(ClickHouseParser.SetRoleStmtContext ctx) {
- if (ctx.NONE() != null) {
- setRoles(Collections.emptyList());
- } else {
- List roles = new ArrayList<>();
- for (ClickHouseParser.IdentifierContext id : ctx.setRolesList().identifier()) {
- roles.add(SQLUtils.unquoteIdentifier(id.getText()));
- }
- setRoles(roles);
- }
- }
-
-
}
diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/SqlParser.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/SqlParser.java
deleted file mode 100644
index 8a57a550d..000000000
--- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/SqlParser.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package com.clickhouse.jdbc.internal;
-
-import org.antlr.v4.runtime.BaseErrorListener;
-import org.antlr.v4.runtime.CharStream;
-import org.antlr.v4.runtime.CharStreams;
-import org.antlr.v4.runtime.CommonTokenStream;
-import org.antlr.v4.runtime.RecognitionException;
-import org.antlr.v4.runtime.Recognizer;
-import org.antlr.v4.runtime.tree.IterativeParseTreeWalker;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class SqlParser {
-
- private static final Logger LOG = LoggerFactory.getLogger(SqlParser.class);
-
- public ParsedStatement parsedStatement(String sql) {
- ParsedStatement parserListener = new ParsedStatement();
- walkSql(sql, parserListener);
- return parserListener;
- }
-
- public ParsedPreparedStatement parsePreparedStatement(String sql) {
- ParsedPreparedStatement parserListener = new ParsedPreparedStatement();
- walkSql(sql, parserListener);
- return parserListener;
- }
-
- private ClickHouseParser walkSql(String sql, ClickHouseParserBaseListener listener ) {
- CharStream charStream = CharStreams.fromString(sql);
- ClickHouseLexer lexer = new ClickHouseLexer(charStream);
- ClickHouseParser parser = new ClickHouseParser(new CommonTokenStream(lexer));
- parser.removeErrorListeners();
- parser.addErrorListener(new ParserErrorListener());
-
- ClickHouseParser.QueryStmtContext parseTree = parser.queryStmt();
- IterativeParseTreeWalker.DEFAULT.walk(listener, parseTree);
-
- return parser;
- }
-
- private static class ParserErrorListener extends BaseErrorListener {
- @Override
- public void syntaxError(Recognizer, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) {
- LOG.warn("SQL syntax error at line: " + line + ", pos: " + charPositionInLine + ", " + msg);
- }
- }
-}
diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/SqlParserFacade.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/SqlParserFacade.java
new file mode 100644
index 000000000..27e5d83d8
--- /dev/null
+++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/SqlParserFacade.java
@@ -0,0 +1,422 @@
+package com.clickhouse.jdbc.internal;
+
+import com.clickhouse.client.api.sql.SQLUtils;
+import com.clickhouse.data.ClickHouseUtils;
+import com.clickhouse.jdbc.internal.parser.antlr4.ClickHouseLexer;
+import com.clickhouse.jdbc.internal.parser.antlr4.ClickHouseParser;
+import com.clickhouse.jdbc.internal.parser.antlr4.ClickHouseParserBaseListener;
+import com.clickhouse.jdbc.internal.parser.javacc.ClickHouseSqlParser;
+import com.clickhouse.jdbc.internal.parser.javacc.ClickHouseSqlStatement;
+import com.clickhouse.jdbc.internal.parser.javacc.JdbcParseHandler;
+import com.clickhouse.jdbc.internal.parser.javacc.StatementType;
+import org.antlr.v4.runtime.BaseErrorListener;
+import org.antlr.v4.runtime.CharStream;
+import org.antlr.v4.runtime.CharStreams;
+import org.antlr.v4.runtime.CommonTokenStream;
+import org.antlr.v4.runtime.RecognitionException;
+import org.antlr.v4.runtime.Recognizer;
+import org.antlr.v4.runtime.tree.ErrorNode;
+import org.antlr.v4.runtime.tree.IterativeParseTreeWalker;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public abstract class SqlParserFacade {
+
+ private static final Logger LOG = LoggerFactory.getLogger(SqlParserFacade.class);
+
+ public abstract ParsedStatement parsedStatement(String sql);
+
+ public abstract ParsedPreparedStatement parsePreparedStatement(String sql);
+
+ private static class JavaCCParser extends SqlParserFacade {
+
+ @Override
+ public ParsedStatement parsedStatement(String sql) {
+ ParsedStatement stmt = new ParsedStatement();
+ ClickHouseSqlStatement parsedStmt = parse(sql);
+ if (parsedStmt.getStatementType() == StatementType.USE) {
+ stmt.setUseDatabase(parsedStmt.getDatabase());
+ }
+
+ String rolesCount = parsedStmt.getSettings().get("_ROLES_COUNT");
+ if (rolesCount != null) {
+ int rolesCountInt = Integer.parseInt(rolesCount);
+ ArrayList roles = new ArrayList<>(rolesCountInt);
+ boolean resetRoles = false;
+ for (int i = 0; i < rolesCountInt; i++) {
+ String role = parsedStmt.getSettings().get("_ROLE_" + i);
+ if (role.equalsIgnoreCase("NONE")) {
+ resetRoles = true;
+ }
+ roles.add(parsedStmt.getSettings().get("_ROLE_" + i));
+ }
+ if (resetRoles) {
+ roles.clear();
+ }
+ stmt.setRoles(roles);
+ }
+
+ stmt.setInsert(parsedStmt.getStatementType() == StatementType.INSERT);
+ stmt.setHasErrors(parsedStmt.getStatementType() == StatementType.UNKNOWN);
+ stmt.setHasResultSet(isStmtWithResultSet(parsedStmt));
+ return stmt;
+ }
+
+ private boolean isStmtWithResultSet(ClickHouseSqlStatement parsedStmt) {
+ return parsedStmt.getStatementType() == StatementType.SELECT || parsedStmt.getStatementType() == StatementType.SHOW
+ || parsedStmt.getStatementType() == StatementType.EXPLAIN || parsedStmt.getStatementType() == StatementType.DESCRIBE
+ || parsedStmt.getStatementType() == StatementType.EXISTS || parsedStmt.getStatementType() == StatementType.CHECK;
+
+ }
+
+ @Override
+ public ParsedPreparedStatement parsePreparedStatement(String sql) {
+ ParsedPreparedStatement stmt = new ParsedPreparedStatement();
+ ClickHouseSqlStatement parsedStmt = parse(sql);
+ if (parsedStmt.getStatementType() == StatementType.USE) {
+ stmt.setUseDatabase(parsedStmt.getDatabase());
+ }
+ stmt.setInsert(parsedStmt.getStatementType() == StatementType.INSERT);
+ stmt.setHasErrors(parsedStmt.getStatementType() == StatementType.UNKNOWN);
+ stmt.setHasResultSet(isStmtWithResultSet(parsedStmt));
+ String tableName = parsedStmt.getTable();
+ if (parsedStmt.getDatabase() != null && parsedStmt.getTable() != null) {
+ tableName = String.format("%s.%s", parsedStmt.getDatabase(), parsedStmt.getTable());
+ }
+ stmt.setTable(tableName);
+ stmt.setInsertWithSelect(parsedStmt.containsKeyword("SELECT") && (parsedStmt.getStatementType() == StatementType.INSERT));
+ stmt.setAssignValuesGroups(parsedStmt.getValueGroups());
+
+ Integer startIndex = parsedStmt.getPositions().get(ClickHouseSqlStatement.KEYWORD_VALUES_START);
+ if (startIndex != null) {
+ int endIndex = parsedStmt.getPositions().get(ClickHouseSqlStatement.KEYWORD_VALUES_END);
+ stmt.setAssignValuesListStartPosition(startIndex);
+ stmt.setAssignValuesListStopPosition(endIndex);
+ String query = parsedStmt.getSQL();
+ for (int i = startIndex + 1; i < endIndex; i++) {
+ char ch = query.charAt(i);
+ if (ch != '?' && ch != ',' && !Character.isWhitespace(ch)) {
+ stmt.setUseFunction(true);
+ break;
+ }
+ }
+ }
+
+ stmt.setUseFunction(false);
+ parseParameters(sql, stmt);
+ return stmt;
+ }
+
+
+ public ClickHouseSqlStatement parse(String sql) {
+ JdbcParseHandler handler = JdbcParseHandler.getInstance();
+ ClickHouseSqlStatement[] stmts = ClickHouseSqlParser.parse(sql, handler);
+ if (stmts.length > 1) {
+ throw new RuntimeException("More than one SQL statement found: " + sql);
+ }
+ return stmts[0];
+ }
+ }
+
+ private static class ANTLR4Parser extends SqlParserFacade {
+
+ @Override
+ public ParsedStatement parsedStatement(String sql) {
+ ParsedStatement stmt = new ParsedStatement();
+ parseSQL(sql, new ParsedStatementListener(stmt));
+ return stmt;
+ }
+
+ @Override
+ public ParsedPreparedStatement parsePreparedStatement(String sql) {
+ ParsedPreparedStatement stmt = new ParsedPreparedStatement();
+ parseSQL(sql, new ParsedPreparedStatementListener(stmt));
+ parseParameters(sql, stmt);
+ return stmt;
+ }
+
+ protected ClickHouseParser parseSQL(String sql, ClickHouseParserBaseListener listener) {
+ CharStream charStream = CharStreams.fromString(sql);
+ ClickHouseLexer lexer = new ClickHouseLexer(charStream);
+ ClickHouseParser parser = new ClickHouseParser(new CommonTokenStream(lexer));
+ parser.removeErrorListeners();
+ parser.addErrorListener(new ParserErrorListener());
+
+ ClickHouseParser.QueryStmtContext parseTree = parser.queryStmt();
+ IterativeParseTreeWalker.DEFAULT.walk(listener, parseTree);
+
+ return parser;
+ }
+
+ private static class ParserErrorListener extends BaseErrorListener {
+ @Override
+ public void syntaxError(Recognizer, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) {
+ LOG.debug("SQL syntax error at line: " + line + ", pos: " + charPositionInLine + ", " + msg);
+ }
+ }
+
+ static boolean isStmtWithResultSet(ClickHouseParser.QueryStmtContext stmtContext) {
+ ClickHouseParser.QueryContext qCtx = stmtContext.query();
+ return qCtx != null && (qCtx.selectStmt() != null || qCtx.selectUnionStmt() != null ||
+ qCtx.showStmt() != null || qCtx.explainStmt() != null || qCtx.describeStmt() != null ||
+ qCtx.existsStmt() != null || qCtx.checkStmt() != null);
+ }
+
+ private static class ParsedStatementListener extends ClickHouseParserBaseListener {
+
+ private final ParsedStatement parsedStatement;
+
+ public ParsedStatementListener(ParsedStatement parsedStatement) {
+ this.parsedStatement = parsedStatement;
+ }
+
+ @Override
+ public void visitErrorNode(ErrorNode node) {
+ parsedStatement.setHasErrors(true);
+ }
+
+ @Override
+ public void enterQueryStmt(ClickHouseParser.QueryStmtContext ctx) {
+ if (isStmtWithResultSet(ctx)) {
+ parsedStatement.setHasResultSet(true);
+ }
+ }
+
+ @Override
+ public void enterUseStmt(ClickHouseParser.UseStmtContext ctx) {
+ if (ctx.databaseIdentifier() != null) {
+ parsedStatement.setUseDatabase(SQLUtils.unquoteIdentifier(ctx.databaseIdentifier().getText()));
+ }
+ }
+
+ @Override
+ public void enterSetRoleStmt(ClickHouseParser.SetRoleStmtContext ctx) {
+ if (ctx.NONE() != null) {
+ parsedStatement.setRoles(Collections.emptyList());
+ } else {
+ List roles = new ArrayList<>();
+ for (ClickHouseParser.IdentifierContext id : ctx.setRolesList().identifier()) {
+ roles.add(SQLUtils.unquoteIdentifier(id.getText()));
+ }
+ parsedStatement.setRoles(roles);
+ }
+ }
+ }
+
+ protected static class ParsedPreparedStatementListener extends ClickHouseParserBaseListener {
+
+ protected final ParsedPreparedStatement parsedStatement;
+
+ public ParsedPreparedStatementListener(ParsedPreparedStatement parsedStatement) {
+ this.parsedStatement = parsedStatement;
+ }
+
+ @Override
+ public void enterQueryStmt(ClickHouseParser.QueryStmtContext ctx) {
+ if (isStmtWithResultSet(ctx)) {
+ parsedStatement.setHasResultSet(true);
+ }
+ }
+
+ @Override
+ public void enterUseStmt(ClickHouseParser.UseStmtContext ctx) {
+ if (ctx.databaseIdentifier() != null) {
+ parsedStatement.setUseDatabase(SQLUtils.unquoteIdentifier(ctx.databaseIdentifier().getText()));
+ }
+ }
+
+ @Override
+ public void enterSetRoleStmt(ClickHouseParser.SetRoleStmtContext ctx) {
+ if (ctx.NONE() != null) {
+ parsedStatement.setRoles(Collections.emptyList());
+ } else {
+ List roles = new ArrayList<>();
+ for (ClickHouseParser.IdentifierContext id : ctx.setRolesList().identifier()) {
+ roles.add(SQLUtils.unquoteIdentifier(id.getText()));
+ }
+ parsedStatement.setRoles(roles);
+ }
+ }
+
+ @Override
+ public void enterColumnExprPrecedence3(ClickHouseParser.ColumnExprPrecedence3Context ctx) {
+ super.enterColumnExprPrecedence3(ctx);
+ }
+
+ @Override
+ public void visitErrorNode(ErrorNode node) {
+ parsedStatement.setHasErrors(true);
+ }
+
+ @Override
+ public void enterInsertParameterFuncExpr(ClickHouseParser.InsertParameterFuncExprContext ctx) {
+ parsedStatement.setUseFunction(true);
+ }
+
+ @Override
+ public void enterAssignmentValuesList(ClickHouseParser.AssignmentValuesListContext ctx) {
+ parsedStatement.setAssignValuesListStartPosition(ctx.getStart().getStartIndex());
+ parsedStatement.setAssignValuesListStopPosition(ctx.getStop().getStopIndex());
+ }
+
+
+ @Override
+ public void enterTableExprIdentifier(ClickHouseParser.TableExprIdentifierContext ctx) {
+ if (ctx.tableIdentifier() != null) {
+ parsedStatement.setTable(SQLUtils.unquoteIdentifier(ctx.tableIdentifier().getText()));
+ }
+ }
+
+ @Override
+ public void enterInsertStmt(ClickHouseParser.InsertStmtContext ctx) {
+ ClickHouseParser.TableIdentifierContext tableId = ctx.tableIdentifier();
+ if (tableId != null) {
+ parsedStatement.setTable(SQLUtils.unquoteIdentifier(tableId.getText()));
+ }
+
+ ClickHouseParser.ColumnsClauseContext columns = ctx.columnsClause();
+ if (columns != null) {
+ List names = columns.nestedIdentifier();
+ String[] insertColumns = new String[names.size()];
+ for (int i = 0; i < names.size(); i++) {
+ insertColumns[i] = names.get(i).getText();
+ }
+ parsedStatement.setInsertColumns(insertColumns);
+ }
+
+ parsedStatement.setInsert(true);
+ }
+
+ @Override
+ public void enterDataClauseSelect(ClickHouseParser.DataClauseSelectContext ctx) {
+ parsedStatement.setInsertWithSelect(true);
+ }
+
+ @Override
+ public void enterDataClauseValues(ClickHouseParser.DataClauseValuesContext ctx) {
+ parsedStatement.setAssignValuesGroups(ctx.assignmentValues().size());
+ }
+
+ @Override
+ public void exitInsertParameterFuncExpr(ClickHouseParser.InsertParameterFuncExprContext ctx) {
+ parsedStatement.setUseFunction(true);
+ }
+ }
+ }
+
+ private static class ANTLR4AndParamsParser extends ANTLR4Parser {
+
+ @Override
+ public ParsedPreparedStatement parsePreparedStatement(String sql) {
+ ParsedPreparedStatement stmt = new ParsedPreparedStatement();
+ parseSQL(sql, new ParseStatementAndParamsListener(stmt));
+ return stmt;
+ }
+
+ private static class ParseStatementAndParamsListener extends ParsedPreparedStatementListener {
+
+ public ParseStatementAndParamsListener(ParsedPreparedStatement parsedStatement) {
+ super(parsedStatement);
+ }
+
+ @Override
+ public void enterColumnExprParam(ClickHouseParser.ColumnExprParamContext ctx) {
+ parsedStatement.appendParameter(ctx.start.getStartIndex());
+ }
+
+
+ @Override
+ public void enterCteUnboundColParam(ClickHouseParser.CteUnboundColParamContext ctx) {
+ parsedStatement.appendParameter(ctx.start.getStartIndex());
+ }
+
+ @Override
+ public void enterInsertParameter(ClickHouseParser.InsertParameterContext ctx) {
+ parsedStatement.appendParameter(ctx.start.getStartIndex());
+ }
+
+ @Override
+ public void enterFromClause(ClickHouseParser.FromClauseContext ctx) {
+ if (ctx.JDBC_PARAM_PLACEHOLDER() != null) {
+ parsedStatement.appendParameter(ctx.JDBC_PARAM_PLACEHOLDER().getSymbol().getStartIndex());
+ }
+ }
+
+ @Override
+ public void enterViewParam(ClickHouseParser.ViewParamContext ctx) {
+ if (ctx.JDBC_PARAM_PLACEHOLDER() != null) {
+ parsedStatement.appendParameter(ctx.JDBC_PARAM_PLACEHOLDER().getSymbol().getStartIndex());
+ }
+ }
+ }
+ }
+
+ private static void parseParameters(String originalQuery, ParsedPreparedStatement stmt) {
+ int len = originalQuery.length();
+ for (int i = 0; i < len; i++) {
+ char ch = originalQuery.charAt(i);
+ if (ClickHouseUtils.isQuote(ch)) {
+ i = ClickHouseUtils.skipQuotedString(originalQuery, i, len, ch) - 1;
+ } else if (ch == '?') {
+ int idx = ClickHouseUtils.skipContentsUntil(originalQuery, i + 2, len, '?', ':');
+ if (idx < len && originalQuery.charAt(idx - 1) == ':' && originalQuery.charAt(idx) != ':'
+ && originalQuery.charAt(idx - 2) != ':') {
+ i = idx - 1;
+ } else {
+ stmt.appendParameter(i);
+ }
+ } else if (ch == ';') {
+ continue;
+ } else if (i + 1 < len) {
+ char nextCh = originalQuery.charAt(i + 1);
+ if ((ch == '-' && nextCh == ch) || (ch == '#')) {
+ i = ClickHouseUtils.skipSingleLineComment(originalQuery, i + 2, len) - 1;
+ } else if (ch == '/' && nextCh == '*') {
+ i = ClickHouseUtils.skipMultiLineComment(originalQuery, i + 2, len) - 1;
+ }
+ }
+ }
+ }
+
+
+ public enum SQLParser {
+ /**
+ * JavaCC used to determine sql type (SELECT, INSERT, etc.) and extract some information
+ * Separate procedure parses sql for `?` parameter placeholders.
+ */
+ JAVACC,
+
+ /**
+ * ANTLR4 used to determine sql type (SELECT, INSERT, etc.) and extract some information and parameters
+ */
+ ANTLR4_PARAMS_PARSER,
+
+ /**
+ * ANTLR4 used to determine sql type (SELECT, INSERT, etc.), extract some information.
+ * Separate procedure parses sql for `?` parameter placeholders.
+ */
+ ANTLR4
+ }
+
+ public static SqlParserFacade getParser(String name) throws SQLException {
+ try {
+ SQLParser parserSelection = SQLParser.valueOf(name);
+ switch (parserSelection) {
+ case JAVACC:
+ return new JavaCCParser();
+ case ANTLR4_PARAMS_PARSER:
+ return new ANTLR4AndParamsParser();
+ case ANTLR4:
+ return new ANTLR4Parser();
+ }
+ throw new SQLException("Unsupported parser: " + parserSelection);
+ } catch (IllegalArgumentException e) {
+ throw new SQLException("Unknown parser: " + name);
+ }
+ }
+}
diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/parser/javacc/ClickHouseSqlStatement.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/parser/javacc/ClickHouseSqlStatement.java
new file mode 100644
index 000000000..7dfd22876
--- /dev/null
+++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/parser/javacc/ClickHouseSqlStatement.java
@@ -0,0 +1,373 @@
+package com.clickhouse.jdbc.internal.parser.javacc;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.Set;
+
+public class ClickHouseSqlStatement {
+ public static final String DEFAULT_DATABASE = "system";
+ public static final String DEFAULT_TABLE = "unknown";
+
+ public static final String KEYWORD_DATABASE = "DATABASE";
+ public static final String KEYWORD_EXISTS = "EXISTS";
+ public static final String KEYWORD_FORMAT = "FORMAT";
+ public static final String KEYWORD_REPLACE = "REPLACE";
+ public static final String KEYWORD_TOTALS = "TOTALS";
+ public static final String KEYWORD_VALUES = "VALUES";
+
+ public static final String KEYWORD_TABLE_COLUMNS_START = "ColumnsStart";
+ public static final String KEYWORD_TABLE_COLUMNS_END = "ColumnsEnd";
+ public static final String KEYWORD_VALUES_START = "ValuesStart";
+ public static final String KEYWORD_VALUES_END = "ValuesEnd";
+
+ public static final String ROLES_COUNT_SETTINGS_KEY = "_ROLES_COUNT";
+ public static final String ROLES_PREFIX_SETTINGS_KEY = "_ROLE_";
+
+ private final String sql;
+ private final StatementType stmtType;
+ private final String cluster;
+ private final String database;
+ private final String table;
+ private final String input;
+ private final String compressAlgorithm;
+ private final String compressLevel;
+ private final String format;
+ private final String file;
+ private final List parameters;
+ private final Map positions;
+ private final Map settings;
+ private final Set tempTables;
+ private final int valueGroups;
+
+ public ClickHouseSqlStatement(String sql) {
+ this(sql, StatementType.UNKNOWN, null, null, null, null, null, null, null, null, null, null, null, null, 0);
+ }
+
+ public ClickHouseSqlStatement(String sql, StatementType stmtType) {
+ this(sql, stmtType, null, null, null, null, null, null, null, null, null, null, null, null, 0);
+ }
+
+ public ClickHouseSqlStatement(String sql, StatementType stmtType, String cluster, String database, String table,
+ String input, String compressAlgorithm, String compressLevel, String format, String file,
+ List parameters, Map positions, Map settings,
+ Set tempTables, int valueGroups) {
+ this.sql = sql;
+ this.stmtType = stmtType;
+
+ this.cluster = cluster;
+ this.database = database;
+ this.table = table == null || table.isEmpty() ? DEFAULT_TABLE : table;
+ this.input = input;
+ this.compressAlgorithm = compressAlgorithm;
+ this.compressLevel = compressLevel;
+ this.format = format;
+ this.file = file;
+ this.valueGroups = valueGroups;
+
+ if (parameters != null && !parameters.isEmpty()) {
+ this.parameters = Collections.unmodifiableList(parameters);
+ } else {
+ this.parameters = Collections.emptyList();
+ }
+
+ if (positions != null && !positions.isEmpty()) {
+ Map p = new HashMap<>();
+ for (Entry e : positions.entrySet()) {
+ String keyword = e.getKey();
+ Integer position = e.getValue();
+
+ if (keyword != null && position != null) {
+ p.put(keyword, position);
+ }
+ }
+ this.positions = Collections.unmodifiableMap(p);
+ } else {
+ this.positions = Collections.emptyMap();
+ }
+
+ if (settings != null && !settings.isEmpty()) {
+ Map s = new LinkedHashMap<>();
+ for (Entry e : settings.entrySet()) {
+ String key = e.getKey();
+ String value = e.getValue();
+
+ if (key != null && value != null) {
+ s.put(key, String.valueOf(e.getValue()));
+ }
+ }
+ this.settings = Collections.unmodifiableMap(s);
+ } else {
+ this.settings = Collections.emptyMap();
+ }
+
+ if (tempTables != null && !tempTables.isEmpty()) {
+ Set s = new LinkedHashSet<>();
+ s.addAll(tempTables);
+ this.tempTables = Collections.unmodifiableSet(s);
+ } else {
+ this.tempTables = Collections.emptySet();
+ }
+ }
+
+ public String getSQL() {
+ return this.sql;
+ }
+
+ public boolean isRecognized() {
+ return stmtType != StatementType.UNKNOWN;
+ }
+
+ public boolean isDDL() {
+ return this.stmtType.getLanguageType() == LanguageType.DDL;
+ }
+
+ public boolean isDML() {
+ return this.stmtType.getLanguageType() == LanguageType.DML;
+ }
+
+ public boolean isQuery() {
+ return this.stmtType.getOperationType() == OperationType.READ && !this.hasFile();
+ }
+
+ public boolean isMutation() {
+ return this.stmtType.getOperationType() == OperationType.WRITE || this.hasFile();
+ }
+
+ public boolean isTCL() {
+ return this.stmtType.getLanguageType() == LanguageType.TCL;
+ }
+
+ public boolean isIdemponent() {
+ boolean result = this.stmtType.isIdempotent() && !this.hasFile();
+
+ if (!result) { // try harder
+ switch (this.stmtType) {
+ case ATTACH:
+ case CREATE:
+ case DETACH:
+ case DROP:
+ result = positions.containsKey(KEYWORD_EXISTS) || positions.containsKey(KEYWORD_REPLACE);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return result;
+ }
+
+ public LanguageType getLanguageType() {
+ return this.stmtType.getLanguageType();
+ }
+
+ public OperationType getOperationType() {
+ return this.stmtType.getOperationType();
+ }
+
+ public StatementType getStatementType() {
+ return this.stmtType;
+ }
+
+ public String getCluster() {
+ return this.cluster;
+ }
+
+ public String getDatabase() {
+ return this.database;
+ }
+
+ public String getDatabaseOrDefault(String database) {
+ return this.database == null ? (database == null ? DEFAULT_DATABASE : database) : this.database;
+ }
+
+ public String getTable() {
+ return this.table;
+ }
+
+ public String getInput() {
+ return this.input;
+ }
+
+ public String getCompressAlgorithm() {
+ return this.compressAlgorithm;
+ }
+
+ public String getCompressLevel() {
+ return this.compressLevel;
+ }
+
+ public String getFormat() {
+ return this.format;
+ }
+
+ public String getFile() {
+ return this.file;
+ }
+
+ public String getContentBetweenKeywords(String startKeyword, String endKeyword) {
+ return getContentBetweenKeywords(startKeyword, endKeyword, 0);
+ }
+
+ public String getContentBetweenKeywords(String startKeyword, String endKeyword, int startOffset) {
+ if (startOffset < 0) {
+ startOffset = 0;
+ }
+ Integer startPos = positions.get(startKeyword);
+ Integer endPos = positions.get(endKeyword);
+
+ String content = "";
+ if (startPos != null && endPos != null && startPos + startOffset < endPos) {
+ content = sql.substring(startPos + startOffset, endPos);
+ }
+
+ return content;
+ }
+
+ public boolean containsKeyword(String keyword) {
+ if (keyword == null || keyword.isEmpty()) {
+ return false;
+ }
+
+ return positions.containsKey(keyword.toUpperCase(Locale.ROOT));
+ }
+
+ public boolean hasCompressAlgorithm() {
+ return this.compressAlgorithm != null && !this.compressAlgorithm.isEmpty();
+ }
+
+ public boolean hasCompressLevel() {
+ return this.compressLevel != null && !this.compressLevel.isEmpty();
+ }
+
+ public boolean hasFormat() {
+ return this.format != null && !this.format.isEmpty();
+ }
+
+ public boolean hasInput() {
+ return this.input != null && !this.input.isEmpty();
+ }
+
+ public boolean hasFile() {
+ return this.file != null && !this.file.isEmpty();
+ }
+
+ public boolean hasSettings() {
+ return !this.settings.isEmpty();
+ }
+
+ public boolean hasWithTotals() {
+ return this.positions.containsKey(KEYWORD_TOTALS);
+ }
+
+ public boolean hasValues() {
+ return this.positions.containsKey(KEYWORD_VALUES);
+ }
+
+ public boolean hasTempTable() {
+ return !this.tempTables.isEmpty();
+ }
+
+ public List getParameters() {
+ return this.parameters;
+ }
+
+ public int getStartPosition(String keyword) {
+ int position = -1;
+
+ if (!this.positions.isEmpty() && keyword != null) {
+ Integer p = this.positions.get(keyword.toUpperCase(Locale.ROOT));
+ if (p != null) {
+ position = p.intValue();
+ }
+ }
+
+ return position;
+ }
+
+ public int getEndPosition(String keyword) {
+ int position = getStartPosition(keyword);
+
+ return position != -1 && keyword != null ? position + keyword.length() : position;
+ }
+
+ public Map getPositions() {
+ return this.positions;
+ }
+
+ public int getValueGroups() {
+ return valueGroups;
+ }
+
+ public Map getSettings() {
+ return this.settings;
+ }
+
+ public Set getTempTables() {
+ return this.tempTables;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append('[').append(stmtType.name()).append(']').append(" cluster=").append(cluster).append(", database=")
+ .append(database).append(", table=").append(table).append(", input=").append(input)
+ .append(", compressAlgorithm=").append(compressAlgorithm).append(", compressLevel=")
+ .append(compressLevel).append(", format=").append(format).append(", outfile=").append(file)
+ .append(", parameters=").append(parameters).append(", positions=").append(positions)
+ .append(", settings=").append(settings).append(", tempTables=").append(settings).append("\nSQL:\n")
+ .append(sql);
+
+ return sb.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((sql == null) ? 0 : sql.hashCode());
+ result = prime * result + ((cluster == null) ? 0 : cluster.hashCode());
+ result = prime * result + ((database == null) ? 0 : database.hashCode());
+ result = prime * result + table.hashCode();
+ result = prime * result + ((input == null) ? 0 : input.hashCode());
+ result = prime * result + ((compressAlgorithm == null) ? 0 : compressAlgorithm.hashCode());
+ result = prime * result + ((compressLevel == null) ? 0 : compressLevel.hashCode());
+ result = prime * result + ((format == null) ? 0 : format.hashCode());
+ result = prime * result + ((file == null) ? 0 : file.hashCode());
+ result = prime * result + ((stmtType == null) ? 0 : stmtType.hashCode());
+
+ result = prime * result + parameters.hashCode();
+ result = prime * result + positions.hashCode();
+ result = prime * result + settings.hashCode();
+ result = prime * result + tempTables.hashCode();
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+
+ ClickHouseSqlStatement other = (ClickHouseSqlStatement) obj;
+ return stmtType == other.stmtType && Objects.equals(sql, other.sql) && Objects.equals(cluster, other.cluster)
+ && Objects.equals(database, other.database) && Objects.equals(table, other.table)
+ && Objects.equals(input, other.input) && Objects.equals(compressAlgorithm, other.compressAlgorithm)
+ && Objects.equals(compressLevel, other.compressLevel) && Objects.equals(format, other.format)
+ && Objects.equals(file, other.file) && parameters.equals(other.parameters)
+ && positions.equals(other.positions) && settings.equals(other.settings)
+ && tempTables.equals(other.tempTables);
+ }
+}
diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/parser/javacc/ClickHouseSqlUtils.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/parser/javacc/ClickHouseSqlUtils.java
new file mode 100644
index 000000000..6faf418c4
--- /dev/null
+++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/parser/javacc/ClickHouseSqlUtils.java
@@ -0,0 +1,73 @@
+package com.clickhouse.jdbc.internal.parser.javacc;
+
+public final class ClickHouseSqlUtils {
+ public static boolean isQuote(char ch) {
+ return ch == '"' || ch == '\'' || ch == '`';
+ }
+
+ /**
+ * Escape quotes in given string.
+ *
+ * @param str string
+ * @param quote quote to escape
+ * @return escaped string
+ */
+ public static String escape(String str, char quote) {
+ if (str == null) {
+ return str;
+ }
+
+ int len = str.length();
+ StringBuilder sb = new StringBuilder(len + 10).append(quote);
+
+ for (int i = 0; i < len; i++) {
+ char ch = str.charAt(i);
+ if (ch == quote || ch == '\\') {
+ sb.append('\\');
+ }
+ sb.append(ch);
+ }
+
+ return sb.append(quote).toString();
+ }
+
+ /**
+ * Unescape quoted string.
+ *
+ * @param str quoted string
+ * @return unescaped string
+ */
+ public static String unescape(String str) {
+ if (str == null || str.isEmpty()) {
+ return str;
+ }
+
+ int len = str.length();
+ char quote = str.charAt(0);
+ if (!isQuote(quote) || quote != str.charAt(len - 1)) { // not a quoted string
+ return str;
+ }
+
+ StringBuilder sb = new StringBuilder(len = len - 1);
+ for (int i = 1; i < len; i++) {
+ char ch = str.charAt(i);
+
+ if (++i >= len) {
+ sb.append(ch);
+ } else {
+ char nextChar = str.charAt(i);
+ if (ch == '\\' || (ch == quote && nextChar == quote)) {
+ sb.append(nextChar);
+ } else {
+ sb.append(ch);
+ i--;
+ }
+ }
+ }
+
+ return sb.toString();
+ }
+
+ private ClickHouseSqlUtils() {
+ }
+}
diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/parser/javacc/JdbcParseHandler.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/parser/javacc/JdbcParseHandler.java
new file mode 100644
index 000000000..4f36c5729
--- /dev/null
+++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/parser/javacc/JdbcParseHandler.java
@@ -0,0 +1,165 @@
+package com.clickhouse.jdbc.internal.parser.javacc;
+
+import com.clickhouse.client.config.ClickHouseDefaults;
+import com.clickhouse.data.ClickHouseChecker;
+import com.clickhouse.data.ClickHouseFormat;
+import com.clickhouse.data.ClickHouseUtils;
+
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class JdbcParseHandler extends ParseHandler {
+ private static final String SETTING_MUTATIONS_SYNC = "mutations_sync";
+
+ private static final JdbcParseHandler INSTANCE;
+
+ static {
+ INSTANCE = new JdbcParseHandler(true, true, true);
+ };
+
+ public static JdbcParseHandler getInstance() {
+ return INSTANCE;
+ }
+
+ private final boolean allowLocalFile;
+ private final boolean allowLightWeightDelete;
+ private final boolean allowLightWeightUpdate;
+
+ private void addMutationSetting(String sql, StringBuilder builder, Map positions,
+ Map settings, int index) {
+ boolean hasSetting = settings != null && !settings.isEmpty();
+ String setting = hasSetting ? settings.get(SETTING_MUTATIONS_SYNC) : null;
+ if (setting == null) {
+ String keyword = "SETTINGS";
+ Integer settingsIndex = positions.get(keyword);
+
+ if (settingsIndex == null) {
+ builder.append(sql.substring(index)).append(" SETTINGS mutations_sync=1");
+ if (hasSetting) {
+ builder.append(',');
+ }
+ } else {
+ builder.append(sql.substring(index, settingsIndex)).append("SETTINGS mutations_sync=1,")
+ .append(sql.substring(settingsIndex + keyword.length()));
+ }
+ } else {
+ builder.append(sql.substring(index));
+ }
+ }
+
+ private ClickHouseSqlStatement handleDelete(String sql, StatementType stmtType, String cluster, String database,
+ String table, String input, String compressAlgorithm, String compressLevel, String format, String file,
+ List parameters, Map positions, Map settings,
+ Set tempTables) {
+ StringBuilder builder = new StringBuilder();
+ int index = positions.get("DELETE");
+ if (index > 0) {
+ builder.append(sql.substring(0, index));
+ }
+ index = positions.get("FROM");
+ Integer whereIdx = positions.get("WHERE");
+ if (whereIdx != null) {
+ builder.append("ALTER TABLE ");
+ if (!ClickHouseChecker.isNullOrEmpty(database)) {
+ builder.append('`').append(database).append('`').append('.');
+ }
+ builder.append('`').append(table).append('`').append(" DELETE ");
+ addMutationSetting(sql, builder, positions, settings, whereIdx);
+ } else {
+ builder.append("TRUNCATE TABLE").append(sql.substring(index + 4));
+ }
+ return new ClickHouseSqlStatement(builder.toString(), stmtType, cluster, database, table, input,
+ compressAlgorithm, compressLevel, format, file, parameters, null, settings, null, 0);
+ }
+
+ private ClickHouseSqlStatement handleUpdate(String sql, StatementType stmtType, String cluster, String database,
+ String table, String input, String compressAlgorithm, String compressLevel, String format, String file,
+ List parameters, Map positions, Map settings,
+ Set tempTables) {
+ StringBuilder builder = new StringBuilder();
+ int index = positions.get("UPDATE");
+ if (index > 0) {
+ builder.append(sql.substring(0, index));
+ }
+ builder.append("ALTER TABLE ");
+ index = positions.get("SET");
+ if (!ClickHouseChecker.isNullOrEmpty(database)) {
+ builder.append('`').append(database).append('`').append('.');
+ }
+ builder.append('`').append(table).append('`').append(" UPDATE"); // .append(sql.substring(index + 3));
+ addMutationSetting(sql, builder, positions, settings, index + 3);
+ return new ClickHouseSqlStatement(builder.toString(), stmtType, cluster, database, table, input,
+ compressAlgorithm, compressLevel, format, file, parameters, null, settings, null, 0);
+ }
+
+ private ClickHouseSqlStatement handleInFileForInsertQuery(String sql, StatementType stmtType, String cluster,
+ String database, String table, String input, String compressAlgorithm, String compressLevel, String format,
+ String file, List parameters, Map positions, Map settings,
+ Set tempTables, int valueGroups) {
+ StringBuilder builder = new StringBuilder(sql.length());
+ builder.append(sql.substring(0, positions.get("FROM")));
+ Integer index = positions.get("SETTINGS");
+ if (index == null || index < 0) {
+ index = positions.get("FORMAT");
+ }
+ if (index != null && index > 0) {
+ builder.append(sql.substring(index));
+ } else {
+ ClickHouseFormat f = ClickHouseFormat.fromFileName(ClickHouseUtils.unescape(file));
+ if (f == null) {
+ f = (ClickHouseFormat) ClickHouseDefaults.FORMAT.getDefaultValue();
+ }
+ format = f.name();
+ builder.append("FORMAT ").append(format);
+ }
+ return new ClickHouseSqlStatement(builder.toString(), stmtType, cluster, database, table, input,
+ compressAlgorithm, compressLevel, format, file, parameters, null, settings, null, valueGroups);
+ }
+
+ private ClickHouseSqlStatement handleOutFileForSelectQuery(String sql, StatementType stmtType, String cluster,
+ String database, String table, String input, String compressAlgorithm, String compressLevel, String format,
+ String file, List parameters, Map positions, Map settings,
+ Set tempTables) {
+ StringBuilder builder = new StringBuilder(sql.length());
+ builder.append(sql.substring(0, positions.get("INTO")));
+ Integer index = positions.get("FORMAT");
+ if (index != null && index > 0) {
+ builder.append(sql.substring(index));
+ }
+ return new ClickHouseSqlStatement(builder.toString(), stmtType, cluster, database, table, input,
+ compressAlgorithm, compressLevel, format, file, parameters, null, settings, null, 0);
+ }
+
+ @Override
+ public ClickHouseSqlStatement handleStatement(String sql, StatementType stmtType, String cluster, String database,
+ String table, String input, String compressAlgorithm, String compressLevel, String format, String file,
+ List parameters, Map positions, Map settings,
+ Set tempTables, int valueGroups) {
+ boolean hasFile = allowLocalFile && !ClickHouseChecker.isNullOrEmpty(file) && file.charAt(0) == '\'';
+ ClickHouseSqlStatement s = null;
+ if (stmtType == StatementType.DELETE) {
+ s = allowLightWeightDelete ? s
+ : handleDelete(sql, stmtType, cluster, database, table, input, compressAlgorithm, compressLevel,
+ format, file, parameters, positions, settings, tempTables);
+ } else if (stmtType == StatementType.UPDATE) {
+ s = allowLightWeightUpdate ? s
+ : handleUpdate(sql, stmtType, cluster, database, table, input, compressAlgorithm, compressLevel,
+ format, file, parameters, positions, settings, tempTables);
+ } else if (stmtType == StatementType.INSERT && hasFile) {
+ s = handleInFileForInsertQuery(sql, stmtType, cluster, database, table, input, compressAlgorithm,
+ compressLevel, format, file, parameters, positions, settings, tempTables, valueGroups);
+ } else if (stmtType == StatementType.SELECT && hasFile) {
+ s = handleOutFileForSelectQuery(sql, stmtType, cluster, database, table, input, compressAlgorithm,
+ compressLevel, format, file, parameters, positions, settings, tempTables);
+ }
+ return s;
+ }
+
+ private JdbcParseHandler(boolean allowLightWeightDelete, boolean allowLightWeightUpdate, boolean allowLocalFile) {
+ this.allowLightWeightDelete = allowLightWeightDelete;
+ this.allowLightWeightUpdate = allowLightWeightUpdate;
+ this.allowLocalFile = allowLocalFile;
+ }
+}
diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/parser/javacc/LanguageType.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/parser/javacc/LanguageType.java
new file mode 100644
index 000000000..1198930e8
--- /dev/null
+++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/parser/javacc/LanguageType.java
@@ -0,0 +1,9 @@
+package com.clickhouse.jdbc.internal.parser.javacc;
+
+public enum LanguageType {
+ UNKNOWN, // unknown language
+ DCL, // data control language
+ DDL, // data definition language
+ DML, // data manipulation language
+ TCL // transaction control language
+}
diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/parser/javacc/OperationType.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/parser/javacc/OperationType.java
new file mode 100644
index 000000000..06650cade
--- /dev/null
+++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/parser/javacc/OperationType.java
@@ -0,0 +1,5 @@
+package com.clickhouse.jdbc.internal.parser.javacc;
+
+public enum OperationType {
+ UNKNOWN, READ, WRITE
+}
diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/parser/javacc/ParseHandler.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/parser/javacc/ParseHandler.java
new file mode 100644
index 000000000..6f1af61a6
--- /dev/null
+++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/parser/javacc/ParseHandler.java
@@ -0,0 +1,58 @@
+package com.clickhouse.jdbc.internal.parser.javacc;
+
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public abstract class ParseHandler {
+ /**
+ * Handle macro like "#include('/tmp/template.sql')".
+ *
+ * @param name name of the macro
+ * @param parameters parameters
+ * @return output of the macro, could be null or empty string
+ */
+ public String handleMacro(String name, List parameters) {
+ return null;
+ }
+
+ /**
+ * Handle parameter.
+ *
+ * @param cluster cluster
+ * @param database database
+ * @param table table
+ * @param columnIndex columnIndex(starts from 1 not 0)
+ * @return parameter value
+ */
+ public String handleParameter(String cluster, String database, String table, int columnIndex) {
+ return null;
+ }
+
+ /**
+ * Hanlde statemenet.
+ *
+ * @param sql sql statement
+ * @param stmtType statement type
+ * @param cluster cluster
+ * @param database database
+ * @param table table
+ * @param compressAlgorithm compression algorithm
+ * @param compressLevel compression level
+ * @param format format
+ * @param input input
+ * @param file infile or outfile
+ * @param parameters positions of parameters
+ * @param positions keyword positions
+ * @param settings settings
+ * @param tempTables temporary tables
+ * @return sql statement, or null means no change
+ */
+ public ClickHouseSqlStatement handleStatement(String sql, StatementType stmtType, String cluster, String database,
+ String table, String input, String compressAlgorithm, String compressLevel, String format, String file,
+ List parameters, Map positions, Map settings,
+ Set tempTables, int valueGroup) {
+ return null;
+ }
+}
diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/parser/javacc/StatementType.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/parser/javacc/StatementType.java
new file mode 100644
index 000000000..ecf7af12d
--- /dev/null
+++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/parser/javacc/StatementType.java
@@ -0,0 +1,58 @@
+package com.clickhouse.jdbc.internal.parser.javacc;
+
+public enum StatementType {
+ UNKNOWN(LanguageType.UNKNOWN, OperationType.UNKNOWN, false), // unknown statement
+ ALTER(LanguageType.DDL, OperationType.UNKNOWN, false), // alter statement
+ ALTER_DELETE(LanguageType.DML, OperationType.WRITE, false), // delete statement
+ ALTER_UPDATE(LanguageType.DML, OperationType.WRITE, false), // update statement
+ ATTACH(LanguageType.DDL, OperationType.UNKNOWN, false), // attach statement
+ CHECK(LanguageType.DDL, OperationType.UNKNOWN, true), // check statement
+ CREATE(LanguageType.DDL, OperationType.UNKNOWN, false), // create statement
+ DELETE(LanguageType.DML, OperationType.WRITE, false), // the upcoming light-weight delete statement
+ DESCRIBE(LanguageType.DDL, OperationType.READ, true), // describe/desc statement
+ DETACH(LanguageType.DDL, OperationType.UNKNOWN, false), // detach statement
+ DROP(LanguageType.DDL, OperationType.UNKNOWN, false), // drop statement
+ EXISTS(LanguageType.DML, OperationType.READ, true), // exists statement
+ EXPLAIN(LanguageType.DDL, OperationType.READ, true), // explain statement
+ GRANT(LanguageType.DCL, OperationType.UNKNOWN, true), // grant statement
+ INSERT(LanguageType.DML, OperationType.WRITE, false), // insert statement
+ KILL(LanguageType.DCL, OperationType.UNKNOWN, false), // kill statement
+ OPTIMIZE(LanguageType.DDL, OperationType.UNKNOWN, false), // optimize statement
+ RENAME(LanguageType.DDL, OperationType.UNKNOWN, false), // rename statement
+ REVOKE(LanguageType.DCL, OperationType.UNKNOWN, true), // revoke statement
+ SELECT(LanguageType.DML, OperationType.READ, true), // select statement
+ SET(LanguageType.DCL, OperationType.UNKNOWN, true), // set statement
+ SHOW(LanguageType.DDL, OperationType.READ, true), // show statement
+ SYSTEM(LanguageType.DDL, OperationType.UNKNOWN, false), // system statement
+ TRUNCATE(LanguageType.DDL, OperationType.UNKNOWN, true), // truncate statement
+ UPDATE(LanguageType.DML, OperationType.WRITE, false), // the upcoming light-weight update statement
+ USE(LanguageType.DDL, OperationType.UNKNOWN, true), // use statement
+ WATCH(LanguageType.DDL, OperationType.UNKNOWN, true), // watch statement
+ TRANSACTION(LanguageType.TCL, OperationType.WRITE, true), // TCL statement
+ UNDROP(LanguageType.DDL, OperationType.UNKNOWN, false),
+ MOVE(LanguageType.DCL, OperationType.UNKNOWN, false),
+ EXCHANGE(LanguageType.DML, OperationType.UNKNOWN, false),
+
+ ;
+ private LanguageType langType;
+ private OperationType opType;
+ private boolean idempotent;
+
+ StatementType(LanguageType langType, OperationType operationType, boolean idempotent) {
+ this.langType = langType;
+ this.opType = operationType;
+ this.idempotent = idempotent;
+ }
+
+ LanguageType getLanguageType() {
+ return this.langType;
+ }
+
+ OperationType getOperationType() {
+ return this.opType;
+ }
+
+ boolean isIdempotent() {
+ return this.idempotent;
+ }
+}
diff --git a/jdbc-v2/src/main/javacc/ClickHouseSqlParser.jj b/jdbc-v2/src/main/javacc/ClickHouseSqlParser.jj
index e3eadced5..fae8e3cdd 100644
--- a/jdbc-v2/src/main/javacc/ClickHouseSqlParser.jj
+++ b/jdbc-v2/src/main/javacc/ClickHouseSqlParser.jj
@@ -25,7 +25,7 @@ options {
PARSER_BEGIN(ClickHouseSqlParser)
-package com.clickhouse.jdbc.parser;
+package com.clickhouse.jdbc.internal.parser.javacc;
import java.io.StringReader;
@@ -51,7 +51,6 @@ public class ClickHouseSqlParser {
private final List statements = new ArrayList<>();
- private ClickHouseConfig config;
private ParseHandler handler;
private int anyArgsListStart = -1;
@@ -76,14 +75,7 @@ public class ClickHouseSqlParser {
return !(getToken(1).kind == AND && token_source.parentToken == BETWEEN);
}
- public static ClickHouseSqlStatement[] parse(String sql, ClickHouseConfig config) {
- return parse(sql, config, null);
- }
-
- public static ClickHouseSqlStatement[] parse(String sql, ClickHouseConfig config, ParseHandler handler) {
- if (config == null) {
- config = new ClickHouseConfig();
- }
+ public static ClickHouseSqlStatement[] parse(String sql, ParseHandler handler) {
ClickHouseSqlStatement[] stmts = new ClickHouseSqlStatement[] {
new ClickHouseSqlStatement(sql, StatementType.UNKNOWN) };
@@ -92,7 +84,7 @@ public class ClickHouseSqlParser {
return stmts;
}
- ClickHouseSqlParser p = new ClickHouseSqlParser(sql, config, handler);
+ ClickHouseSqlParser p = new ClickHouseSqlParser(sql, handler);
try {
stmts = p.sql();
} catch (Exception e) {
@@ -106,10 +98,9 @@ public class ClickHouseSqlParser {
return stmts;
}
- public ClickHouseSqlParser(String sql, ClickHouseConfig config, ParseHandler handler) {
+ public ClickHouseSqlParser(String sql, ParseHandler handler) {
this(new StringReader(sql));
- this.config = config;
this.handler = handler;
}
@@ -183,6 +174,7 @@ TOKEN_MGR_DECLS: {
String compressLevel = null;
String format = null;
String file = null;
+ int valueGroups = 0;
final List parameters = new ArrayList<>();
final Map positions = new HashMap<>();
@@ -285,12 +277,12 @@ TOKEN_MGR_DECLS: {
if (handler != null) {
s = handler.handleStatement(
- sqlStmt, stmtType, cluster, database, table, input, compressAlgorithm, compressLevel, format, file, parameters, positions, settings, tempTables);
+ sqlStmt, stmtType, cluster, database, table, input, compressAlgorithm, compressLevel, format, file, parameters, positions, settings, tempTables, valueGroups);
}
if (s == null) {
s = new ClickHouseSqlStatement(
- sqlStmt, stmtType, cluster, database, table, input, compressAlgorithm, compressLevel, format, file, parameters, positions, settings, tempTables);
+ sqlStmt, stmtType, cluster, database, table, input, compressAlgorithm, compressLevel, format, file, parameters, positions, settings, tempTables, valueGroups);
}
// reset variables
@@ -332,6 +324,9 @@ TOKEN_MGR_DECLS: {
this.settings.put(key.toLowerCase(Locale.ROOT), value);
}
+ void incValueGroup() {
+ this.valueGroups++;
+ }
}
SKIP: {
@@ -375,7 +370,7 @@ SKIP: {
}
}
}
- | { append(image); }
+ | { append(image); }
| "/*" { commentNestingDepth = 1; append(image); }: MULTI_LINE_COMMENT
}
@@ -420,6 +415,7 @@ void stmt(): {} {
| detachStmt() { token_source.stmtType = StatementType.DETACH; }
| dropStmt() { token_source.stmtType = StatementType.DROP; }
| existsStmt() { token_source.stmtType = StatementType.EXISTS; }
+ | exchangeStmt() { token_source.stmtType = StatementType.EXCHANGE; }
| explainStmt() { token_source.stmtType = StatementType.EXPLAIN; }
| insertStmt() { token_source.stmtType = StatementType.INSERT; }
| grantStmt() { token_source.stmtType = StatementType.GRANT; }
@@ -427,15 +423,18 @@ void stmt(): {} {
| optimizeStmt() { token_source.stmtType = StatementType.OPTIMIZE; }
| renameStmt() { token_source.stmtType = StatementType.RENAME; }
| revokeStmt() { token_source.stmtType = StatementType.REVOKE; }
+ | selectStmt() { token_source.stmtType = StatementType.SELECT; }
| selectStmt() { token_source.stmtType = StatementType.SELECT; }
| setStmt() { token_source.stmtType = StatementType.SET; }
| showStmt() { token_source.stmtType = StatementType.SHOW; }
| systemStmt() { token_source.stmtType = StatementType.SYSTEM; }
| truncateStmt() { token_source.stmtType = StatementType.TRUNCATE; }
+ | undropStmt() { token_source.stmtType = StatementType.UNDROP; }
| updateStmt() { token_source.stmtType = StatementType.UPDATE; }
| useStmt() { token_source.stmtType = StatementType.USE; }
| watchStmt() { token_source.stmtType = StatementType.WATCH; }
| txStmt() { token_source.stmtType = StatementType.TRANSACTION; }
+ | moveStmt() { token_source.stmtType = StatementType.MOVE; }
}
// https://clickhouse.tech/docs/en/sql-reference/statements/alter/
@@ -480,9 +479,21 @@ void checkStmt(): {} { // not interested
anyExprList()
}
+void undropStmt(): {} { // not interested
+ anyExprList()
+}
+
+void moveStmt(): {} { // not interested
+ (LOOKAHEAD(2) | )? anyExprList()
+}
+
+void exchangeStmt(): {} { // not interested
+ ( | ) anyExprList()
+}
+
// https://clickhouse.tech/docs/en/sql-reference/statements/create/
void createStmt(): {} {
- (
+ (LOOKAHEAD(2) )? (
LOOKAHEAD(2)
(
{ token_source.addPosition(token); }
@@ -578,12 +589,12 @@ void dataClause(): {} {
try {
LOOKAHEAD(2) { token_source.addPosition(token); }
{ token_source.addCustomKeywordPosition(ClickHouseSqlStatement.KEYWORD_VALUES_START, token); }
- columnExprList()
+ columnExprList() { token_source.incValueGroup(); }
{ token_source.addCustomKeywordPosition(ClickHouseSqlStatement.KEYWORD_VALUES_END, token); }
(
LOOKAHEAD(2)
()?
- { token_source.removePosition(ClickHouseSqlStatement.KEYWORD_VALUES_START); }
+ { token_source.removePosition(ClickHouseSqlStatement.KEYWORD_VALUES_START); token_source.incValueGroup(); }
columnExprList()
{ token_source.removePosition(ClickHouseSqlStatement.KEYWORD_VALUES_END); }
)*
@@ -621,7 +632,6 @@ void revokeStmt(): {} { // not interested
// https://clickhouse.tech/docs/en/sql-reference/statements/select/
void selectStmt(): {} {
- // FIXME with (select 1), (select 2), 3 select *
(withClause())?