Skip to content

Commit 714dec3

Browse files
committed
#310 - Fix bind marker reuse when expanding collection arguments using named parameters.
We now correctly reuse bind markers for named parameter substitution when using collection arguments to create dynamic argument lists. Previously, we allocated a new bind marker for each item in the collection which left the parameters intended for reuse unbound.
1 parent 5a5a5fe commit 714dec3

File tree

2 files changed

+60
-6
lines changed

2 files changed

+60
-6
lines changed

Diff for: src/main/java/org/springframework/data/r2dbc/core/NamedParameterUtils.java

+12-6
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
import org.springframework.data.r2dbc.dialect.BindMarkersFactory;
3232
import org.springframework.data.r2dbc.dialect.BindTarget;
3333
import org.springframework.util.Assert;
34-
import org.springframework.util.CollectionUtils;
3534

3635
/**
3736
* Helper methods for named parameter parsing.
@@ -287,6 +286,7 @@ public static PreparedOperation<String> substituteNamedParameters(ParsedSql pars
287286

288287
Iterator<?> entryIter = ((Collection<?>) value).iterator();
289288
int k = 0;
289+
int counter = 0;
290290
while (entryIter.hasNext()) {
291291
if (k > 0) {
292292
actualSql.append(", ");
@@ -300,11 +300,13 @@ public static PreparedOperation<String> substituteNamedParameters(ParsedSql pars
300300
if (m > 0) {
301301
actualSql.append(", ");
302302
}
303-
actualSql.append(marker.addPlaceholder());
303+
actualSql.append(marker.getPlaceholder(counter));
304+
counter++;
304305
}
305306
actualSql.append(')');
306307
} else {
307-
actualSql.append(marker.addPlaceholder());
308+
actualSql.append(marker.getPlaceholder(counter));
309+
counter++;
308310
}
309311

310312
}
@@ -459,12 +461,16 @@ String addPlaceholder() {
459461
}
460462

461463
String getPlaceholder() {
464+
return getPlaceholder(0);
465+
}
466+
467+
String getPlaceholder(int counter) {
462468

463-
if (this.placeholders.isEmpty()) {
464-
return addPlaceholder();
469+
while (counter + 1 > this.placeholders.size()) {
470+
addPlaceholder();
465471
}
466472

467-
return this.placeholders.get(0).getPlaceholder();
473+
return this.placeholders.get(counter).getPlaceholder();
468474
}
469475
}
470476
}

Diff for: src/test/java/org/springframework/data/r2dbc/core/NamedParameterUtilsUnitTests.java

+48
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
import org.springframework.data.r2dbc.dialect.PostgresDialect;
3232
import org.springframework.data.r2dbc.dialect.SqlServerDialect;
3333
import org.springframework.data.r2dbc.mapping.SettableValue;
34+
import org.springframework.util.LinkedMultiValueMap;
35+
import org.springframework.util.MultiValueMap;
3436

3537
/**
3638
* Unit tests for {@link NamedParameterUtils}.
@@ -335,6 +337,52 @@ public void bindNull(int index, Class<?> type) {
335337
});
336338
}
337339

340+
@Test // gh-310
341+
public void multipleEqualCollectionParameterReferencesBindsValueOnce() {
342+
343+
String sql = "SELECT * FROM person where name IN (:ids) or lastname IN (:ids)";
344+
345+
BindMarkersFactory factory = BindMarkersFactory.indexed("$", 0);
346+
347+
MultiValueMap<Integer, Object> bindings = new LinkedMultiValueMap<>();
348+
349+
PreparedOperation<String> operation = NamedParameterUtils.substituteNamedParameters(sql, factory,
350+
new MapBindParameterSource(
351+
Collections.singletonMap("ids", SettableValue.from(Arrays.asList("foo", "bar", "baz")))));
352+
353+
assertThat(operation.toQuery())
354+
.isEqualTo("SELECT * FROM person where name IN ($0, $1, $2) or lastname IN ($0, $1, $2)");
355+
356+
operation.bindTo(new BindTarget() {
357+
@Override
358+
public void bind(String identifier, Object value) {
359+
throw new UnsupportedOperationException();
360+
}
361+
362+
@Override
363+
public void bind(int index, Object value) {
364+
assertThat(index).isIn(0, 1, 2);
365+
assertThat(value).isIn("foo", "bar", "baz");
366+
367+
bindings.add(index, value);
368+
}
369+
370+
@Override
371+
public void bindNull(String identifier, Class<?> type) {
372+
throw new UnsupportedOperationException();
373+
}
374+
375+
@Override
376+
public void bindNull(int index, Class<?> type) {
377+
throw new UnsupportedOperationException();
378+
}
379+
});
380+
381+
assertThat(bindings).containsEntry(0, Collections.singletonList("foo")) //
382+
.containsEntry(1, Collections.singletonList("bar")) //
383+
.containsEntry(2, Collections.singletonList("baz"));
384+
}
385+
338386
@Test // gh-138
339387
public void multipleEqualParameterReferencesForAnonymousMarkersBindsValueMultipleTimes() {
340388

0 commit comments

Comments
 (0)