27
27
import com .google .cloud .spanner .SpannerExceptionFactory ;
28
28
import com .google .cloud .spanner .Statement ;
29
29
import com .google .cloud .spanner .connection .AbstractBaseUnitOfWork .InterceptorsUsage ;
30
+ import com .google .cloud .spanner .connection .SimpleParser .Result ;
30
31
import com .google .cloud .spanner .connection .StatementResult .ClientSideStatementType ;
31
32
import com .google .cloud .spanner .connection .UnitOfWork .CallType ;
32
33
import com .google .common .annotations .VisibleForTesting ;
33
34
import com .google .common .base .Preconditions ;
35
+ import com .google .common .base .Suppliers ;
34
36
import com .google .common .cache .Cache ;
35
37
import com .google .common .cache .CacheBuilder ;
36
38
import com .google .common .cache .CacheStats ;
37
39
import com .google .common .cache .Weigher ;
38
40
import com .google .common .collect .ImmutableMap ;
39
41
import com .google .common .collect .ImmutableSet ;
40
42
import com .google .spanner .v1 .ExecuteSqlRequest .QueryOptions ;
43
+ import java .nio .CharBuffer ;
41
44
import java .util .Collection ;
42
45
import java .util .Collections ;
43
46
import java .util .HashMap ;
44
47
import java .util .Map ;
45
48
import java .util .Objects ;
46
49
import java .util .Set ;
47
50
import java .util .concurrent .Callable ;
51
+ import java .util .function .Supplier ;
48
52
import java .util .logging .Level ;
49
53
import java .util .logging .Logger ;
50
54
import javax .annotation .Nullable ;
@@ -179,24 +183,24 @@ public static class ParsedStatement {
179
183
private final StatementType type ;
180
184
private final ClientSideStatementImpl clientSideStatement ;
181
185
private final Statement statement ;
182
- private final String sqlWithoutComments ;
183
- private final boolean returningClause ;
186
+ private final Supplier < String > sqlWithoutComments ;
187
+ private final Supplier < Boolean > returningClause ;
184
188
private final ReadQueryUpdateTransactionOption [] optionsFromHints ;
185
189
186
190
private static ParsedStatement clientSideStatement (
187
191
ClientSideStatementImpl clientSideStatement ,
188
192
Statement statement ,
189
- String sqlWithoutComments ) {
193
+ Supplier < String > sqlWithoutComments ) {
190
194
return new ParsedStatement (clientSideStatement , statement , sqlWithoutComments );
191
195
}
192
196
193
- private static ParsedStatement ddl (Statement statement , String sqlWithoutComments ) {
197
+ private static ParsedStatement ddl (Statement statement , Supplier < String > sqlWithoutComments ) {
194
198
return new ParsedStatement (StatementType .DDL , statement , sqlWithoutComments );
195
199
}
196
200
197
201
private static ParsedStatement query (
198
202
Statement statement ,
199
- String sqlWithoutComments ,
203
+ Supplier < String > sqlWithoutComments ,
200
204
QueryOptions defaultQueryOptions ,
201
205
ReadQueryUpdateTransactionOption [] optionsFromHints ) {
202
206
return new ParsedStatement (
@@ -205,57 +209,66 @@ private static ParsedStatement query(
205
209
statement ,
206
210
sqlWithoutComments ,
207
211
defaultQueryOptions ,
208
- false ,
212
+ Suppliers . ofInstance ( false ) ,
209
213
optionsFromHints );
210
214
}
211
215
212
216
private static ParsedStatement update (
213
217
Statement statement ,
214
- String sqlWithoutComments ,
215
- boolean returningClause ,
218
+ Supplier < String > sqlWithoutComments ,
219
+ Supplier < Boolean > returningClause ,
216
220
ReadQueryUpdateTransactionOption [] optionsFromHints ) {
217
221
return new ParsedStatement (
218
222
StatementType .UPDATE , statement , sqlWithoutComments , returningClause , optionsFromHints );
219
223
}
220
224
221
- private static ParsedStatement unknown (Statement statement , String sqlWithoutComments ) {
225
+ private static ParsedStatement unknown (
226
+ Statement statement , Supplier <String > sqlWithoutComments ) {
222
227
return new ParsedStatement (StatementType .UNKNOWN , statement , sqlWithoutComments );
223
228
}
224
229
225
230
private ParsedStatement (
226
231
ClientSideStatementImpl clientSideStatement ,
227
232
Statement statement ,
228
- String sqlWithoutComments ) {
233
+ Supplier < String > sqlWithoutComments ) {
229
234
Preconditions .checkNotNull (clientSideStatement );
230
235
Preconditions .checkNotNull (statement );
231
236
this .type = StatementType .CLIENT_SIDE ;
232
237
this .clientSideStatement = clientSideStatement ;
233
238
this .statement = statement ;
234
- this .sqlWithoutComments = Preconditions . checkNotNull ( sqlWithoutComments ) ;
235
- this .returningClause = false ;
239
+ this .sqlWithoutComments = sqlWithoutComments ;
240
+ this .returningClause = Suppliers . ofInstance ( false ) ;
236
241
this .optionsFromHints = EMPTY_OPTIONS ;
237
242
}
238
243
239
244
private ParsedStatement (
240
245
StatementType type ,
241
246
Statement statement ,
242
- String sqlWithoutComments ,
243
- boolean returningClause ,
247
+ Supplier < String > sqlWithoutComments ,
248
+ Supplier < Boolean > returningClause ,
244
249
ReadQueryUpdateTransactionOption [] optionsFromHints ) {
245
250
this (type , null , statement , sqlWithoutComments , null , returningClause , optionsFromHints );
246
251
}
247
252
248
- private ParsedStatement (StatementType type , Statement statement , String sqlWithoutComments ) {
249
- this (type , null , statement , sqlWithoutComments , null , false , EMPTY_OPTIONS );
253
+ private ParsedStatement (
254
+ StatementType type , Statement statement , Supplier <String > sqlWithoutComments ) {
255
+ this (
256
+ type ,
257
+ null ,
258
+ statement ,
259
+ sqlWithoutComments ,
260
+ null ,
261
+ Suppliers .ofInstance (false ),
262
+ EMPTY_OPTIONS );
250
263
}
251
264
252
265
private ParsedStatement (
253
266
StatementType type ,
254
267
ClientSideStatementImpl clientSideStatement ,
255
268
Statement statement ,
256
- String sqlWithoutComments ,
269
+ Supplier < String > sqlWithoutComments ,
257
270
QueryOptions defaultQueryOptions ,
258
- boolean returningClause ,
271
+ Supplier < Boolean > returningClause ,
259
272
ReadQueryUpdateTransactionOption [] optionsFromHints ) {
260
273
Preconditions .checkNotNull (type );
261
274
this .type = type ;
@@ -315,7 +328,7 @@ public StatementType getType() {
315
328
/** @return whether the statement has a returning clause or not. */
316
329
@ InternalApi
317
330
public boolean hasReturningClause () {
318
- return this .returningClause ;
331
+ return this .returningClause . get () ;
319
332
}
320
333
321
334
@ InternalApi
@@ -413,7 +426,7 @@ Statement mergeQueryOptions(Statement statement, QueryOptions defaultQueryOption
413
426
/** @return the SQL statement with all comments removed from the SQL string. */
414
427
@ InternalApi
415
428
public String getSqlWithoutComments () {
416
- return sqlWithoutComments ;
429
+ return sqlWithoutComments . get () ;
417
430
}
418
431
419
432
ClientSideStatement getClientSideStatement () {
@@ -464,7 +477,7 @@ private static boolean isRecordStatementCacheStats() {
464
477
// We do length*2 because Java uses 2 bytes for each char.
465
478
.weigher (
466
479
(Weigher <String , ParsedStatement >)
467
- (key , value ) -> 2 * key .length () + 2 * value .sqlWithoutComments .length ())
480
+ (key , value ) -> 2 * key .length () + 2 * value .statement . getSql () .length ())
468
481
.concurrencyLevel (Runtime .getRuntime ().availableProcessors ());
469
482
if (isRecordStatementCacheStats ()) {
470
483
cacheBuilder .recordStats ();
@@ -511,28 +524,56 @@ ParsedStatement parse(Statement statement, QueryOptions defaultQueryOptions) {
511
524
return parsedStatement .copy (statement , defaultQueryOptions );
512
525
}
513
526
514
- private ParsedStatement internalParse (Statement statement , QueryOptions defaultQueryOptions ) {
515
- StatementHintParser statementHintParser =
516
- new StatementHintParser (getDialect (), statement . getSql () );
527
+ ParsedStatement internalParse (Statement statement , QueryOptions defaultQueryOptions ) {
528
+ String sql = statement . getSql ();
529
+ StatementHintParser statementHintParser = new StatementHintParser (getDialect (), sql );
517
530
ReadQueryUpdateTransactionOption [] optionsFromHints = EMPTY_OPTIONS ;
518
531
if (statementHintParser .hasStatementHints ()
519
532
&& !statementHintParser .getClientSideStatementHints ().isEmpty ()) {
520
533
statement =
521
534
statement .toBuilder ().replace (statementHintParser .getSqlWithoutClientSideHints ()).build ();
522
535
optionsFromHints = convertHintsToOptions (statementHintParser .getClientSideStatementHints ());
523
536
}
524
- String sql = removeCommentsAndTrim (statement .getSql ());
525
- ClientSideStatementImpl client = parseClientSideStatement (sql );
537
+ // Create a supplier that will actually remove all comments and hints from the SQL string to be
538
+ // backwards compatible with anything that really needs the SQL string without comments.
539
+ Supplier <String > sqlWithoutCommentsSupplier =
540
+ Suppliers .memoize (() -> removeCommentsAndTrim (sql ));
541
+
542
+ // Get rid of any spaces/comments at the start of the string.
543
+ SimpleParser simpleParser = new SimpleParser (getDialect (), sql );
544
+ simpleParser .skipWhitespaces ();
545
+ // Create a wrapper around the SQL string from the point after the first whitespace.
546
+ CharBuffer charBuffer = CharBuffer .wrap (sql , simpleParser .getPos (), sql .length ());
547
+ ClientSideStatementImpl client = parseClientSideStatement (charBuffer );
548
+
526
549
if (client != null ) {
527
- return ParsedStatement .clientSideStatement (client , statement , sql );
528
- } else if (isQuery (sql )) {
529
- return ParsedStatement .query (statement , sql , defaultQueryOptions , optionsFromHints );
530
- } else if (isUpdateStatement (sql )) {
531
- return ParsedStatement .update (statement , sql , checkReturningClause (sql ), optionsFromHints );
532
- } else if (isDdlStatement (sql )) {
533
- return ParsedStatement .ddl (statement , sql );
550
+ return ParsedStatement .clientSideStatement (client , statement , sqlWithoutCommentsSupplier );
551
+ } else {
552
+ Result keywordResult = simpleParser .eatNextKeyword ();
553
+ if (keywordResult .isValid ()) {
554
+ String keyword = keywordResult .getValue ().toUpperCase ();
555
+ if (keywordResult .isInParenthesis ()) {
556
+ if (SELECT_STATEMENTS_ALLOWING_PRECEDING_BRACKETS .contains (keyword )) {
557
+ return ParsedStatement .query (
558
+ statement , sqlWithoutCommentsSupplier , defaultQueryOptions , optionsFromHints );
559
+ }
560
+ } else {
561
+ if (selectStatements .contains (keyword )) {
562
+ return ParsedStatement .query (
563
+ statement , sqlWithoutCommentsSupplier , defaultQueryOptions , optionsFromHints );
564
+ } else if (dmlStatements .contains (keyword )) {
565
+ return ParsedStatement .update (
566
+ statement ,
567
+ sqlWithoutCommentsSupplier ,
568
+ Suppliers .memoize (() -> checkReturningClause (sqlWithoutCommentsSupplier .get ())),
569
+ optionsFromHints );
570
+ } else if (ddlStatements .contains (keyword )) {
571
+ return ParsedStatement .ddl (statement , sqlWithoutCommentsSupplier );
572
+ }
573
+ }
574
+ }
534
575
}
535
- return ParsedStatement .unknown (statement , sql );
576
+ return ParsedStatement .unknown (statement , sqlWithoutCommentsSupplier );
536
577
}
537
578
538
579
/**
@@ -546,7 +587,7 @@ private ParsedStatement internalParse(Statement statement, QueryOptions defaultQ
546
587
* statement.
547
588
*/
548
589
@ VisibleForTesting
549
- ClientSideStatementImpl parseClientSideStatement (String sql ) {
590
+ ClientSideStatementImpl parseClientSideStatement (CharSequence sql ) {
550
591
for (ClientSideStatementImpl css : statements ) {
551
592
if (css .matches (sql )) {
552
593
return css ;
0 commit comments