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