Skip to content

Commit 3071763

Browse files
committed
DATACOUCH-616 - Fix parsing of String Query containing conditionals in quotes
For a query as below that has conditional portions, the parsing for parameters was being done before the conditional portions were being resolved, which left the parameters between quotes where they wre not being recognized as query parameters. @query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter} " + " #{#projectIds != null ? 'AND iata IN $1' : ''} ") Long count(@param("projectIds") List<String> projectIds)
1 parent 946e835 commit 3071763

File tree

10 files changed

+56
-253
lines changed

10 files changed

+56
-253
lines changed

src/main/java/org/springframework/data/couchbase/core/ReactiveInsertByIdOperationSupport.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
*/
1616
package org.springframework.data.couchbase.core;
1717

18-
import com.couchbase.client.java.kv.UpsertOptions;
1918
import org.springframework.data.couchbase.core.mapping.Document;
2019
import reactor.core.publisher.Flux;
2120
import reactor.core.publisher.Mono;
@@ -98,7 +97,7 @@ private InsertOptions buildInsertOptions() {
9897
} else if (durabilityLevel != DurabilityLevel.NONE) {
9998
options.durability(durabilityLevel);
10099
}
101-
if (expiry != null) {
100+
if (expiry != null && !expiry.equals(Duration.ofSeconds(0))) {
102101
options.expiry(expiry);
103102
} else if (domainType.isAnnotationPresent(Document.class)) {
104103
Document documentAnn = domainType.getAnnotation(Document.class);

src/main/java/org/springframework/data/couchbase/core/ReactiveUpsertByIdOperationSupport.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ private UpsertOptions buildUpsertOptions() {
9898
} else if (durabilityLevel != DurabilityLevel.NONE) {
9999
options.durability(durabilityLevel);
100100
}
101-
if (expiry != null) {
101+
if (expiry != null && ! expiry.equals( Duration.ofSeconds(0))) {
102102
options.expiry(expiry);
103103
} else if (domainType.isAnnotationPresent(Document.class)) {
104104
Document documentAnn = domainType.getAnnotation(Document.class);

src/main/java/org/springframework/data/couchbase/repository/Query.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323

2424
import org.springframework.data.annotation.QueryAnnotation;
2525
import org.springframework.data.couchbase.core.CouchbaseTemplate;
26-
import org.springframework.data.couchbase.repository.query.StringN1qlBasedQuery;
2726

2827
/**
2928
* Annotation to support the use of N1QL queries with Couchbase.

src/main/java/org/springframework/data/couchbase/repository/query/ReactiveStringN1qlBasedQuery.java

Lines changed: 0 additions & 81 deletions
This file was deleted.

src/main/java/org/springframework/data/couchbase/repository/query/StringBasedN1qlQueryParser.java

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,21 @@
3232
import org.springframework.data.couchbase.core.query.N1QLExpression;
3333
import org.springframework.data.couchbase.repository.Query;
3434
import org.springframework.data.couchbase.repository.query.support.N1qlUtils;
35+
import org.springframework.data.domain.Pageable;
36+
import org.springframework.data.domain.Sort;
3537
import org.springframework.data.repository.query.Parameter;
3638
import org.springframework.data.repository.query.ParameterAccessor;
3739
import org.springframework.data.repository.query.QueryMethod;
40+
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
41+
import org.springframework.data.repository.query.ReturnedType;
3842
import org.springframework.expression.EvaluationContext;
3943
import org.springframework.expression.common.TemplateParserContext;
4044
import org.springframework.expression.spel.standard.SpelExpressionParser;
4145

4246
import com.couchbase.client.java.json.JsonArray;
4347
import com.couchbase.client.java.json.JsonObject;
4448
import com.couchbase.client.java.json.JsonValue;
49+
import org.springframework.util.Assert;
4550

4651
/**
4752
* @author Subhashni Balakrishnan
@@ -103,20 +108,23 @@ public class StringBasedN1qlQueryParser {
103108
private static final Logger LOGGER = LoggerFactory.getLogger(StringBasedN1qlQueryParser.class);
104109
private final String statement;
105110
private final QueryMethod queryMethod;
106-
private final PlaceholderType placeHolderType;
111+
private PlaceholderType placeHolderType;
107112
private final N1qlSpelValues statementContext;
108113
private final N1qlSpelValues countContext;
109114
private final CouchbaseConverter couchbaseConverter;
110115
private final Collection<String> parameterNames = new HashSet<String>();
116+
public final N1QLExpression parsedExpression;
111117

112118
public StringBasedN1qlQueryParser(String statement, QueryMethod queryMethod, String bucketName,
113-
CouchbaseConverter couchbaseConverter, String typeField, String typeValue) {
119+
CouchbaseConverter couchbaseConverter, String typeField, String typeValue, ParameterAccessor accessor,
120+
SpelExpressionParser parser, QueryMethodEvaluationContextProvider evaluationContextProvider) {
114121
this.statement = statement;
115122
this.queryMethod = queryMethod;
116-
this.placeHolderType = checkPlaceholders(statement);
117123
this.statementContext = createN1qlSpelValues(bucketName, typeField, typeValue, false);
118124
this.countContext = createN1qlSpelValues(bucketName, typeField, typeValue, true);
119125
this.couchbaseConverter = couchbaseConverter;
126+
this.parsedExpression = getExpression(accessor, getParameters(accessor), null, parser, evaluationContextProvider);
127+
checkPlaceholders( this.parsedExpression.toString() );
120128
}
121129

122130
public static N1qlSpelValues createN1qlSpelValues(String bucketName, String typeField, String typeValue,
@@ -151,7 +159,7 @@ public String doParse(SpelExpressionParser parser, EvaluationContext evaluationC
151159
return parsedExpression.getValue(evaluationContext, String.class);
152160
}
153161

154-
private PlaceholderType checkPlaceholders(String statement) {
162+
private void checkPlaceholders(String statement) {
155163

156164
Matcher quoteMatcher = QUOTE_DETECTION_PATTERN.matcher(statement);
157165
Matcher positionMatcher = POSITIONAL_PLACEHOLDER_PATTERN.matcher(statement);
@@ -192,11 +200,11 @@ private PlaceholderType checkPlaceholders(String statement) {
192200
}
193201

194202
if (posCount > 0) {
195-
return PlaceholderType.POSITIONAL;
203+
placeHolderType = PlaceholderType.POSITIONAL;
196204
} else if (namedCount > 0) {
197-
return PlaceholderType.NAMED;
205+
placeHolderType = PlaceholderType.NAMED;
198206
} else {
199-
return PlaceholderType.NONE;
207+
placeHolderType = PlaceholderType.NONE;
200208
}
201209
}
202210

@@ -402,5 +410,39 @@ public N1qlSpelValues(String selectClause, String entityFields, String bucket, S
402410
this.returning = returning;
403411
}
404412
}
413+
// copied from StringN1qlBasedQuery
414+
private N1QLExpression getExpression(ParameterAccessor accessor, Object[] runtimeParameters,
415+
ReturnedType returnedType, SpelExpressionParser parser, QueryMethodEvaluationContextProvider evaluationContextProvider) {
416+
boolean isCountQuery = queryMethod.getName().toLowerCase().startsWith("count"); // should be queryMethod.isCountQuery()
417+
EvaluationContext evaluationContext = evaluationContextProvider
418+
.getEvaluationContext(queryMethod.getParameters(), runtimeParameters);
419+
N1QLExpression parsedStatement = x(this.doParse(parser, evaluationContext, isCountQuery));
420+
421+
Sort sort = accessor.getSort();
422+
if (sort.isSorted()) {
423+
N1QLExpression[] cbSorts = N1qlUtils.createSort(sort);
424+
parsedStatement = parsedStatement.orderBy(cbSorts);
425+
}
426+
if (queryMethod.isPageQuery()) {
427+
Pageable pageable = accessor.getPageable();
428+
Assert.notNull(pageable, "Pageable must not be null!");
429+
parsedStatement = parsedStatement.limit(pageable.getPageSize()).offset(
430+
Math.toIntExact(pageable.getOffset()));
431+
} else if (queryMethod.isSliceQuery()) {
432+
Pageable pageable = accessor.getPageable();
433+
Assert.notNull(pageable, "Pageable must not be null!");
434+
parsedStatement = parsedStatement.limit(pageable.getPageSize() + 1).offset(
435+
Math.toIntExact(pageable.getOffset()));
436+
}
437+
return parsedStatement;
438+
}
405439

440+
// getExpression() could do this itself, but pass as an arg to be consistent with StringN1qlBasedQuery
441+
private static Object[] getParameters(ParameterAccessor accessor) {
442+
ArrayList<Object> params = new ArrayList<>();
443+
for (Object o : accessor) {
444+
params.add(o);
445+
}
446+
return params.toArray();
447+
}
406448
}

src/main/java/org/springframework/data/couchbase/repository/query/StringN1qlBasedQuery.java

Lines changed: 0 additions & 117 deletions
This file was deleted.

0 commit comments

Comments
 (0)