11
11
import java .sql .SQLWarning ;
12
12
import java .util .ArrayList ;
13
13
import java .util .EnumMap ;
14
+ import java .util .HashMap ;
14
15
import java .util .List ;
16
+ import java .util .Locale ;
15
17
import java .util .Map ;
16
18
import java .util .TimeZone ;
17
19
import java .util .UUID ;
38
40
import ru .yandex .clickhouse .domain .ClickHouseFormat ;
39
41
import ru .yandex .clickhouse .except .ClickHouseException ;
40
42
import ru .yandex .clickhouse .except .ClickHouseExceptionSpecifier ;
43
+ import ru .yandex .clickhouse .jdbc .parser .ClickHouseSqlParser ;
44
+ import ru .yandex .clickhouse .jdbc .parser .ClickHouseSqlStatement ;
45
+ import ru .yandex .clickhouse .jdbc .parser .StatementType ;
41
46
import ru .yandex .clickhouse .response .ClickHouseLZ4Stream ;
42
47
import ru .yandex .clickhouse .response .ClickHouseResponse ;
43
48
import ru .yandex .clickhouse .response .ClickHouseResponseSummary ;
@@ -84,16 +89,56 @@ public class ClickHouseStatementImpl extends ConfigurableApi<ClickHouseStatement
84
89
85
90
private volatile String queryId ;
86
91
92
+ protected ClickHouseSqlStatement parsedStmt ;
93
+
87
94
/**
88
95
* Current database name may be changed by {@link java.sql.Connection#setCatalog(String)}
89
96
* between creation of this object and query execution, but javadoc does not allow
90
97
* {@code setCatalog} influence on already created statements.
91
98
*/
92
99
private final String initialDatabase ;
93
100
101
+ @ Deprecated
94
102
private static final String [] selectKeywords = new String []{"SELECT" , "WITH" , "SHOW" , "DESC" , "EXISTS" , "EXPLAIN" };
103
+ @ Deprecated
95
104
private static final String databaseKeyword = "CREATE DATABASE" ;
96
105
106
+ @ Deprecated
107
+ protected void parseSingleStatement (String sql ) throws SQLException {
108
+ this .parsedStmt = null ;
109
+ ClickHouseSqlStatement [] stmts = ClickHouseSqlParser .parse (sql , properties );
110
+
111
+ if (stmts .length == 1 ) {
112
+ this .parsedStmt = stmts [0 ];
113
+ } else {
114
+ this .parsedStmt = new ClickHouseSqlStatement (sql , StatementType .UNKNOWN );
115
+ // throw new SQLException("Multiple statements are not supported.");
116
+ }
117
+
118
+ if (this .parsedStmt .isIdemponent ()) {
119
+ httpContext .setAttribute ("is_idempotent" , Boolean .TRUE );
120
+ } else {
121
+ httpContext .removeAttribute ("is_idempotent" );
122
+ }
123
+ }
124
+
125
+ @ Deprecated
126
+ private void parseSingleStatement (String sql , ClickHouseFormat preferredFormat ) throws SQLException {
127
+ parseSingleStatement (sql );
128
+
129
+ if (parsedStmt .isQuery () && !parsedStmt .hasFormat ()) {
130
+ String format = preferredFormat .name ();
131
+ Map <String , Integer > positions = new HashMap <>();
132
+ positions .putAll (parsedStmt .getPositions ());
133
+ positions .put (ClickHouseSqlStatement .KEYWORD_FORMAT , sql .length ());
134
+
135
+ sql = new StringBuilder (parsedStmt .getSQL ()).append ("\n FORMAT " ).append (format ).append (';' )
136
+ .toString ();
137
+ parsedStmt = new ClickHouseSqlStatement (sql , parsedStmt .getStatementType (),
138
+ parsedStmt .getCluster (), parsedStmt .getDatabase (), parsedStmt .getTable (),
139
+ format , parsedStmt .getOutfile (), parsedStmt .getParameters (), positions );
140
+ }
141
+ }
97
142
98
143
public ClickHouseStatementImpl (CloseableHttpClient client , ClickHouseConnection connection ,
99
144
ClickHouseProperties properties , int resultSetType ) {
@@ -135,16 +180,29 @@ public ResultSet executeQuery(String sql,
135
180
}
136
181
additionalDBParams .put (ClickHouseQueryParam .EXTREMES , "0" );
137
182
138
- InputStream is = getInputStream (sql , additionalDBParams , externalData , additionalRequestParams );
183
+ parseSingleStatement (sql , ClickHouseFormat .TabSeparatedWithNamesAndTypes );
184
+ if (!parsedStmt .isRecognized () && isSelect (sql )) {
185
+ Map <String , Integer > positions = new HashMap <>();
186
+ String dbName = extractDBName (sql );
187
+ String tableName = extractTableName (sql );
188
+ if (extractWithTotals (sql )) {
189
+ positions .put (ClickHouseSqlStatement .KEYWORD_TOTALS , 1 );
190
+ }
191
+ parsedStmt = new ClickHouseSqlStatement (sql , StatementType .SELECT ,
192
+ null , dbName , tableName , null , null , null , positions );
193
+ // httpContext.setAttribute("is_idempotent", Boolean.TRUE);
194
+ }
139
195
196
+ InputStream is = getInputStream (sql , additionalDBParams , externalData , additionalRequestParams );
197
+
140
198
try {
141
- if (isSelect ( sql )) {
199
+ if (parsedStmt . isQuery ( )) {
142
200
currentUpdateCount = -1 ;
143
201
currentResult = createResultSet (properties .isCompress ()
144
202
? new ClickHouseLZ4Stream (is ) : is , properties .getBufferSize (),
145
- extractDBName ( sql ),
146
- extractTableName ( sql ),
147
- extractWithTotals ( sql ),
203
+ parsedStmt . getDatabaseOrDefault ( properties . getDatabase () ),
204
+ parsedStmt . getTable ( ),
205
+ parsedStmt . hasWithTotals ( ),
148
206
this ,
149
207
getConnection ().getTimeZone (),
150
208
properties
@@ -176,8 +234,15 @@ public ClickHouseResponse executeQueryClickhouseResponse(String sql, Map<ClickHo
176
234
public ClickHouseResponse executeQueryClickhouseResponse (String sql ,
177
235
Map <ClickHouseQueryParam , String > additionalDBParams ,
178
236
Map <String , String > additionalRequestParams ) throws SQLException {
237
+ parseSingleStatement (sql , ClickHouseFormat .JSONCompact );
238
+ if (parsedStmt .isRecognized ()) {
239
+ sql = parsedStmt .getSQL ();
240
+ } else {
241
+ sql = addFormatIfAbsent (sql , ClickHouseFormat .JSONCompact );
242
+ }
243
+
179
244
InputStream is = getInputStream (
180
- addFormatIfAbsent ( sql , ClickHouseFormat . JSONCompact ) ,
245
+ sql ,
181
246
additionalDBParams ,
182
247
null ,
183
248
additionalRequestParams
@@ -206,14 +271,27 @@ public ClickHouseRowBinaryInputStream executeQueryClickhouseRowBinaryStream(Stri
206
271
207
272
@ Override
208
273
public ClickHouseRowBinaryInputStream executeQueryClickhouseRowBinaryStream (String sql , Map <ClickHouseQueryParam , String > additionalDBParams , Map <String , String > additionalRequestParams ) throws SQLException {
274
+ parseSingleStatement (sql , ClickHouseFormat .RowBinary );
275
+ if (parsedStmt .isRecognized ()) {
276
+ sql = parsedStmt .getSQL ();
277
+ } else {
278
+ sql = addFormatIfAbsent (sql , ClickHouseFormat .RowBinary );
279
+ if (isSelect (sql )) {
280
+ parsedStmt = new ClickHouseSqlStatement (sql , StatementType .SELECT );
281
+ // httpContext.setAttribute("is_idempotent", Boolean.TRUE);
282
+ } else {
283
+ parsedStmt = new ClickHouseSqlStatement (sql , StatementType .UNKNOWN );
284
+ }
285
+ }
286
+
209
287
InputStream is = getInputStream (
210
- addFormatIfAbsent ( sql , ClickHouseFormat . RowBinary ) ,
288
+ sql ,
211
289
additionalDBParams ,
212
290
null ,
213
291
additionalRequestParams
214
292
);
215
293
try {
216
- if (isSelect ( sql )) {
294
+ if (parsedStmt . isQuery ( )) {
217
295
currentUpdateCount = -1 ;
218
296
currentRowBinaryResult = new ClickHouseRowBinaryInputStream (properties .isCompress ()
219
297
? new ClickHouseLZ4Stream (is ) : is , getConnection ().getTimeZone (), properties );
@@ -231,6 +309,8 @@ public ClickHouseRowBinaryInputStream executeQueryClickhouseRowBinaryStream(Stri
231
309
232
310
@ Override
233
311
public int executeUpdate (String sql ) throws SQLException {
312
+ parseSingleStatement (sql , ClickHouseFormat .TabSeparatedWithNamesAndTypes );
313
+
234
314
InputStream is = null ;
235
315
try {
236
316
is = getInputStream (sql , null , null , null );
@@ -245,8 +325,7 @@ public int executeUpdate(String sql) throws SQLException {
245
325
@ Override
246
326
public boolean execute (String sql ) throws SQLException {
247
327
// currentResult is stored here. InputString and currentResult will be closed on this.close()
248
- executeQuery (sql );
249
- return isSelect (sql );
328
+ return executeQuery (sql ) != null ;
250
329
}
251
330
252
331
@ Override
@@ -471,6 +550,7 @@ public ClickHouseResponseSummary getResponseSummary() {
471
550
return currentSummary ;
472
551
}
473
552
553
+ @ Deprecated
474
554
static String clickhousifySql (String sql ) {
475
555
return addFormatIfAbsent (sql , ClickHouseFormat .TabSeparatedWithNamesAndTypes );
476
556
}
@@ -479,6 +559,7 @@ static String clickhousifySql(String sql) {
479
559
* Adding FORMAT TabSeparatedWithNamesAndTypes if not added
480
560
* adds format only to select queries
481
561
*/
562
+ @ Deprecated
482
563
private static String addFormatIfAbsent (final String sql , ClickHouseFormat format ) {
483
564
String cleanSQL = sql .trim ();
484
565
if (!isSelect (cleanSQL )) {
@@ -498,6 +579,7 @@ private static String addFormatIfAbsent(final String sql, ClickHouseFormat forma
498
579
return sb .toString ();
499
580
}
500
581
582
+ @ Deprecated
501
583
static boolean isSelect (String sql ) {
502
584
for (int i = 0 ; i < sql .length (); i ++) {
503
585
String nextTwo = sql .substring (i , Math .min (i + 2 , sql .length ()));
@@ -518,6 +600,7 @@ static boolean isSelect(String sql) {
518
600
return false ;
519
601
}
520
602
603
+ @ Deprecated
521
604
private String extractTableName (String sql ) {
522
605
String s = extractDBAndTableName (sql );
523
606
if (s .contains ("." )) {
@@ -527,6 +610,7 @@ private String extractTableName(String sql) {
527
610
}
528
611
}
529
612
613
+ @ Deprecated
530
614
private String extractDBName (String sql ) {
531
615
String s = extractDBAndTableName (sql );
532
616
if (s .contains ("." )) {
@@ -536,6 +620,7 @@ private String extractDBName(String sql) {
536
620
}
537
621
}
538
622
623
+ @ Deprecated
539
624
private String extractDBAndTableName (String sql ) {
540
625
if (Utils .startsWithIgnoreCase (sql , "select" )) {
541
626
String withoutStrings = Utils .retainUnquoted (sql , '\'' );
@@ -558,10 +643,11 @@ private String extractDBAndTableName(String sql) {
558
643
return "system.unknown" ;
559
644
}
560
645
646
+ @ Deprecated
561
647
private boolean extractWithTotals (String sql ) {
562
648
if (Utils .startsWithIgnoreCase (sql , "select" )) {
563
649
String withoutStrings = Utils .retainUnquoted (sql , '\'' );
564
- return withoutStrings .toLowerCase ().contains (" with totals" );
650
+ return withoutStrings .toLowerCase (Locale . ROOT ).contains (" with totals" );
565
651
}
566
652
return false ;
567
653
}
@@ -572,15 +658,23 @@ private InputStream getInputStream(
572
658
List <ClickHouseExternalData > externalData ,
573
659
Map <String , String > additionalRequestParams
574
660
) throws ClickHouseException {
575
- sql = clickhousifySql (sql );
661
+ boolean ignoreDatabase = false ;
662
+ if (parsedStmt .isRecognized ()) {
663
+ sql = parsedStmt .getSQL ();
664
+ // TODO consider more scenarios like drop, show etc.
665
+ ignoreDatabase = parsedStmt .getStatementType () == StatementType .CREATE
666
+ && parsedStmt .containsKeyword (ClickHouseSqlStatement .KEYWORD_DATABASE );
667
+ } else {
668
+ sql = clickhousifySql (sql );
669
+ ignoreDatabase = sql .trim ().regionMatches (true , 0 , databaseKeyword , 0 , databaseKeyword .length ());
670
+ }
576
671
log .debug ("Executing SQL: {}" , sql );
577
672
578
673
additionalClickHouseDBParams = addQueryIdTo (
579
674
additionalClickHouseDBParams == null
580
675
? new EnumMap <ClickHouseQueryParam , String >(ClickHouseQueryParam .class )
581
676
: additionalClickHouseDBParams );
582
677
583
- boolean ignoreDatabase = sql .trim ().regionMatches (true , 0 , databaseKeyword , 0 , databaseKeyword .length ());
584
678
URI uri ;
585
679
if (externalData == null || externalData .isEmpty ()) {
586
680
uri = buildRequestUri (
0 commit comments