38
38
import org .springframework .data .couchbase .core .mapping .CouchbasePersistentProperty ;
39
39
import org .springframework .data .couchbase .core .mapping .Expiration ;
40
40
import org .springframework .data .couchbase .core .query .N1QLExpression ;
41
+ import org .springframework .data .couchbase .core .query .StringQuery ;
41
42
import org .springframework .data .couchbase .repository .Query ;
42
43
import org .springframework .data .couchbase .repository .query .support .N1qlUtils ;
43
44
import org .springframework .data .mapping .PersistentEntity ;
@@ -122,6 +123,12 @@ public class StringBasedN1qlQueryParser {
122
123
* regexp that detect positional placeholder ($ followed by digits only)
123
124
*/
124
125
public static final Pattern POSITIONAL_PLACEHOLDER_PATTERN = Pattern .compile ("\\ W(\\ $\\ p{Digit}+)\\ b" );
126
+
127
+ /**
128
+ * regexp that detect SPEL Expression (#{..})
129
+ */
130
+ public static final Pattern SPEL_EXPRESSION_PATTERN = Pattern .compile ("(#\\ {[^\\ }]*\\ })" );
131
+
125
132
/**
126
133
* regexp that detects " and ' quote boundaries, ignoring escaped quotes
127
134
*/
@@ -157,7 +164,8 @@ public StringBasedN1qlQueryParser(String statement, CouchbaseQueryMethod queryMe
157
164
this .statement = statement ;
158
165
this .queryMethod = queryMethod ;
159
166
this .couchbaseConverter = couchbaseConverter ;
160
- this .statementContext = createN1qlSpelValues (collection != null ? collection : bucketName , scope , collection ,
167
+ this .statementContext = queryMethod == null ? null
168
+ : createN1qlSpelValues (collection != null ? collection : bucketName , scope , collection ,
161
169
queryMethod .getEntityInformation ().getJavaType (), typeField , typeValue , queryMethod .isCountQuery (), null , null );
162
170
this .parsedExpression = getExpression (statement , queryMethod , accessor , spelExpressionParser ,
163
171
evaluationContextProvider );
@@ -371,6 +379,9 @@ private void checkPlaceholders(String statement) {
371
379
Matcher quoteMatcher = QUOTE_DETECTION_PATTERN .matcher (statement );
372
380
Matcher positionMatcher = POSITIONAL_PLACEHOLDER_PATTERN .matcher (statement );
373
381
Matcher namedMatcher = NAMED_PLACEHOLDER_PATTERN .matcher (statement );
382
+ String queryIdentifier = (this .queryMethod != null ? queryMethod .getClass ().getName ()
383
+ : StringQuery .class .getName ()) + "."
384
+ + (this .queryMethod != null ? queryMethod .getName () : this .statement );
374
385
375
386
List <int []> quotes = new ArrayList <int []>();
376
387
while (quoteMatcher .find ()) {
@@ -383,8 +394,14 @@ private void checkPlaceholders(String statement) {
383
394
while (positionMatcher .find ()) {
384
395
String placeholder = positionMatcher .group (1 );
385
396
// check not in quoted
386
- if (checkNotQuoted (placeholder , positionMatcher .start (), positionMatcher .end (), quotes )) {
387
- LOGGER .trace ("{}: Found positional placeholder {}" , this .queryMethod .getName (), placeholder );
397
+ if (checkNotQuoted (placeholder , positionMatcher .start (), positionMatcher .end (), quotes , queryIdentifier )) {
398
+ if (this .queryMethod == null ) {
399
+ throw new IllegalArgumentException (
400
+ "StringQuery created from StringQuery(String) cannot have parameters. "
401
+ + "They cannot be processed. "
402
+ + "Use an @Query annotated method and the SPEL Expression #{[<n>]} : " + statement );
403
+ }
404
+ LOGGER .trace ("{}: Found positional placeholder {}" , queryIdentifier , placeholder );
388
405
posCount ++;
389
406
parameterNames .add (placeholder .substring (1 )); // save without the leading $
390
407
}
@@ -393,17 +410,21 @@ private void checkPlaceholders(String statement) {
393
410
while (namedMatcher .find ()) {
394
411
String placeholder = namedMatcher .group (1 );
395
412
// check not in quoted
396
- if (checkNotQuoted (placeholder , namedMatcher .start (), namedMatcher .end (), quotes )) {
397
- LOGGER .trace ("{}: Found named placeholder {}" , this .queryMethod .getName (), placeholder );
413
+ if (checkNotQuoted (placeholder , namedMatcher .start (), namedMatcher .end (), quotes , queryIdentifier )) {
414
+ if (this .queryMethod == null ) {
415
+ throw new IllegalArgumentException (
416
+ "StringQuery created from StringQuery(String) cannot have parameters. "
417
+ + "Use an @Query annotated method and the SPEL Expression #{[<n>]} : " + statement );
418
+ }
419
+ LOGGER .trace ("{}: Found named placeholder {}" , queryIdentifier , placeholder );
398
420
namedCount ++;
399
421
parameterNames .add (placeholder .substring (1 ));// save without the leading $
400
422
}
401
423
}
402
424
403
425
if (posCount > 0 && namedCount > 0 ) { // actual values from parameterNames might be more useful
404
426
throw new IllegalArgumentException ("Using both named (" + namedCount + ") and positional (" + posCount
405
- + ") placeholders is not supported, please choose one over the other in " + queryMethod .getClass ().getName ()
406
- + "." + this .queryMethod .getName () + "()" );
427
+ + ") placeholders is not supported, please choose one over the other in " + queryIdentifier + "()" );
407
428
}
408
429
409
430
if (posCount > 0 ) {
@@ -413,12 +434,30 @@ private void checkPlaceholders(String statement) {
413
434
} else {
414
435
placeHolderType = PlaceholderType .NONE ;
415
436
}
437
+
438
+ if (this .queryMethod == null ) {
439
+ Matcher spelMatcher = SPEL_EXPRESSION_PATTERN .matcher (statement );
440
+ while (spelMatcher .find ()) {
441
+ String placeholder = spelMatcher .group (1 );
442
+ // check not in quoted
443
+ if (checkNotQuoted (placeholder , spelMatcher .start (), spelMatcher .end (), quotes , queryIdentifier )) {
444
+ if (this .queryMethod == null ) {
445
+ throw new IllegalArgumentException (
446
+ "StringQuery created from StringQuery(String) cannot SPEL expressions. "
447
+ + "Use an @Query annotated method and the SPEL Expression #{[<n>]} : "
448
+ + statement );
449
+ }
450
+ LOGGER .trace ("{}: Found SPEL Experssion {}" , queryIdentifier , placeholder );
451
+ }
452
+ }
453
+ }
454
+
416
455
}
417
456
418
- private boolean checkNotQuoted (String item , int start , int end , List <int []> quotes ) {
457
+ private boolean checkNotQuoted (String item , int start , int end , List <int []> quotes , String queryIdentifier ) {
419
458
for (int [] quote : quotes ) {
420
459
if (quote [0 ] <= start && quote [1 ] >= end ) {
421
- LOGGER .trace ("{}: potential placeholder {} is inside quotes, ignored" , this . queryMethod . getName () , item );
460
+ LOGGER .trace ("{}: potential placeholder {} is inside quotes, ignored" , queryIdentifier , item );
422
461
return false ;
423
462
}
424
463
}
@@ -647,10 +686,15 @@ public N1qlSpelValues(String selectClause, String entityFields, String bucket, S
647
686
648
687
public N1QLExpression getExpression (String statement , CouchbaseQueryMethod queryMethod , ParameterAccessor accessor ,
649
688
SpelExpressionParser parser , QueryMethodEvaluationContextProvider evaluationContextProvider ) {
650
- Object [] runtimeParameters = getParameters (accessor );
651
- EvaluationContext evaluationContext = evaluationContextProvider .getEvaluationContext (queryMethod .getParameters (),
652
- runtimeParameters );
653
- N1QLExpression parsedStatement = x (doParse (statement , parser , evaluationContext , this .getStatementContext ()));
689
+ N1QLExpression parsedStatement ;
690
+ if (accessor != null && queryMethod != null && parser != null ) {
691
+ Object [] runtimeParameters = getParameters (accessor );
692
+ EvaluationContext evaluationContext = evaluationContextProvider
693
+ .getEvaluationContext (queryMethod .getParameters (), runtimeParameters );
694
+ parsedStatement = x (doParse (statement , parser , evaluationContext , this .getStatementContext ()));
695
+ } else {
696
+ parsedStatement = x (statement );
697
+ }
654
698
checkPlaceholders (parsedStatement .toString ());
655
699
return parsedStatement ;
656
700
}
0 commit comments