Skip to content

Commit 718f733

Browse files
Merge branch 'main' of https://github.com/oracle/oracle-r2dbc into 11_non_empty_update_count
2 parents be41161 + 2ed864e commit 718f733

File tree

2 files changed

+108
-25
lines changed

2 files changed

+108
-25
lines changed

src/main/java/oracle/r2dbc/impl/OracleStatementImpl.java

Lines changed: 42 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ final class OracleStatementImpl implements Statement {
226226
*/
227227
@Override
228228
public Statement bind(int index, Object value) {
229-
requireNonNull(value, "value must not be null");
229+
requireNonNull(value, "value is null");
230230
requireValidIndex(index);
231231
bindValues[index] = convertToJdbcBindValue(value);
232232
return this;
@@ -249,11 +249,13 @@ public Statement bind(int index, Object value) {
249249
* syntax.
250250
* </p><p>
251251
* If the specified {@code identifier} matches more than one parameter name,
252-
* this method binds the {@code value} to the first matching parameter that
253-
* appears when the SQL command is read from left to right. (Note: It is
254-
* not recommended to use duplicate parameter names. Use
255-
* {@link #bind(int, Object)} to set a value for a duplicate parameter name
256-
* at a given index).
252+
* then this method binds the {@code value} to all parameters having a
253+
* matching name. For instance, when {@code 9} is bound to the parameter
254+
* named "x", the following SQL would return all names having a birthday on
255+
* the 9th day of the 9th month:
256+
* <pre>
257+
* SELECT name FROM birthday WHERE month=:x AND day=:x
258+
* </pre>
257259
* </p>
258260
* @throws IllegalArgumentException {@inheritDoc}
259261
* @throws IllegalArgumentException If the {@code identifier} does match a
@@ -264,9 +266,9 @@ public Statement bind(int index, Object value) {
264266
*/
265267
@Override
266268
public Statement bind(String identifier, Object value) {
267-
requireNonNull(identifier, "identifier must not be null");
268-
requireNonNull(value, "value must not be null");
269-
bindValues[indexOfIdentifier(identifier)] = convertToJdbcBindValue(value);
269+
requireNonNull(identifier, "identifier is null");
270+
requireNonNull(value, "value is null");
271+
bindNamedParameter(identifier, value);
270272
return this;
271273
}
272274

@@ -281,7 +283,7 @@ public Statement bind(String identifier, Object value) {
281283
*/
282284
@Override
283285
public Statement bindNull(int index, Class<?> type) {
284-
requireNonNull(type, "class type must not be null");
286+
requireNonNull(type, "type is null");
285287
requireValidIndex(index);
286288
bindValues[index] = null;
287289
return this;
@@ -310,16 +312,26 @@ public Statement bindNull(int index, Class<?> type) {
310312
* duplicate parameter names. Use {@link #bindNull(int, Class)} to set the
311313
* SQL {@code NULL} value for a duplicate parameter name at a given index).
312314
* </p>
315+
* </p><p>
316+
* If the specified {@code identifier} matches more than one parameter name,
317+
* then this method binds the SQL {@code NULL} value to all parameters
318+
* having a matching name. For instance, when {@code NULL} is bound to the
319+
* parameter named "x", the following SQL would create a birthday with
320+
* {@code NULL} values for month and day:
321+
* <pre>
322+
* INSERT INTO birthday (name, month, day) VALUES ('Plato', :x, :x)
323+
* </pre>
324+
* </p>
313325
* @throws IllegalArgumentException {@inheritDoc}
314326
* @throws IllegalArgumentException If the {@code identifier} does match a
315327
* case sensitive parameter name that appears in this {@code Statement's}
316328
* SQL command.
317329
*/
318330
@Override
319331
public Statement bindNull(String identifier, Class<?> type) {
320-
requireNonNull(identifier, "identifier must not be null");
321-
requireNonNull(type, "class type must not be null");
322-
bindValues[indexOfIdentifier(identifier)] = null;
332+
requireNonNull(identifier, "identifier is null");
333+
requireNonNull(type, "type is null");
334+
bindNamedParameter(identifier, null);
323335
return this;
324336
}
325337

@@ -694,21 +706,26 @@ else if (index >= parameterNames.size()) {
694706
}
695707

696708
/**
697-
* Returns the 0-based index of a named parameter matching the specified
698-
* {@code identifier}. The match is case-sensitive.
699-
* @param identifier A parameter identifier
700-
* @return The 0-based parameter index of the {@code identifier}
709+
* Binds a {@code value} to all named parameters matching the specified
710+
* {@code name}. The match is case-sensitive.
711+
* @param name A parameter name. Not null.
712+
* @param value A value to bind. May be null.
701713
* @throws IllegalArgumentException if no named parameter matches the
702714
* {@code identifier}
703715
*/
704-
private int indexOfIdentifier(String identifier) {
705-
int index = parameterNames.indexOf(identifier);
706-
if (index == -1) {
707-
throw new IllegalArgumentException(
708-
"Unrecognized parameter identifier: " + identifier);
716+
private void bindNamedParameter(String name, Object value) {
717+
boolean isMatched = false;
718+
719+
for (int i = 0; i < parameterNames.size(); i++) {
720+
if (name.equals(parameterNames.get(i))) {
721+
isMatched = true;
722+
bindValues[i] = convertToJdbcBindValue(value);
723+
}
709724
}
710-
else {
711-
return index;
725+
726+
if (! isMatched) {
727+
throw new IllegalArgumentException(
728+
"Unrecognized parameter identifier: " + name);
712729
}
713730
}
714731

@@ -730,7 +747,7 @@ private int indexOfIdentifier(String identifier) {
730747
*/
731748
private Object convertToJdbcBindValue(Object bindValue) {
732749
if (bindValue == null) {
733-
return bindValue;
750+
return null;
734751
}
735752
else if (bindValue instanceof io.r2dbc.spi.Blob) {
736753
return convertBlobBind((io.r2dbc.spi.Blob) bindValue);

src/test/java/oracle/r2dbc/impl/OracleStatementImplTest.java

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ class UnsupportedType {
168168
asList(row.get(0, Integer.class), row.get(1,Integer.class)),
169169
connection.createStatement(
170170
"SELECT x, y FROM testBindByIndex WHERE x = 3 ORDER BY y"));
171+
171172
}
172173
finally {
173174
awaitExecution(connection.createStatement(
@@ -311,6 +312,43 @@ class UnsupportedType {
311312
asList(row.get(0, Integer.class), row.get(1,Integer.class)),
312313
connection.createStatement(
313314
"SELECT x, y FROM testBindByName WHERE x = 3 ORDER BY y"));
315+
316+
// When the same name is used for multiple parameters, expect a value
317+
// bound to that name to be set as the value for all of those parameters.
318+
// Expect a value bound to the index of one of those parameters to be
319+
// set only for the parameter at that index.
320+
awaitUpdate(asList(1, 1, 1),
321+
connection
322+
.createStatement("INSERT INTO testBindByName VALUES (:same, :same)")
323+
.bind("same", 4).add()
324+
.bind("same", 4).bind(1, 5).add()
325+
.bind(0, 4).bind(1, 6));
326+
awaitQuery(asList(asList(4,4)),
327+
row ->
328+
asList(row.get(0, Integer.class), row.get(1,Integer.class)),
329+
connection.createStatement(
330+
"SELECT x, y FROM testBindByName WHERE x = :x_and_y AND y = :x_and_y")
331+
.bind("x_and_y", 4));
332+
awaitQuery(
333+
asList(asList(4, 5), asList(4, 6)),
334+
row ->
335+
asList(row.get(0, Integer.class), row.get(1,Integer.class)),
336+
connection.createStatement(
337+
"SELECT x, y FROM testBindByName" +
338+
" WHERE x = :both AND y <> :both" +
339+
" ORDER BY y")
340+
.bind("both", 4));
341+
awaitQuery(asList(asList(4,4)),
342+
row ->
343+
asList(row.get(0, Integer.class), row.get(1,Integer.class)),
344+
connection.createStatement(
345+
"SELECT x, y FROM testBindByName" +
346+
" WHERE x = :x_and_y" +
347+
" AND (x * y) = :x_times_y" +
348+
" AND y = :x_and_y")
349+
.bind("x_times_y", 16)
350+
.bind("x_and_y", 4));
351+
314352
}
315353
finally {
316354
awaitExecution(connection.createStatement("DROP TABLE testBindByName"));
@@ -617,6 +655,34 @@ public void testBindNullByName() {
617655
asList(row.get(0, Integer.class), row.get(1,Integer.class)),
618656
connection.createStatement(
619657
"SELECT x, y FROM testNullBindByName WHERE x = 3 ORDER BY y"));
658+
659+
// When the same name is used for multiple parameters, expect a value
660+
// bound to that name to be set as the value for all of those parameters.
661+
// Expect a value bound to the index of one of those parameters to be
662+
// set only for the parameter at that index.
663+
awaitOne(connection.createStatement(
664+
"DELETE FROM testNullBindByName WHERE x IS NULL AND y IS NULL")
665+
.execute());
666+
awaitUpdate(asList(1, 1, 1),
667+
connection
668+
.createStatement(
669+
"INSERT INTO testNullBindByName VALUES (:same, :same)")
670+
.bindNull("same", Integer.class).add()
671+
.bindNull("same", Integer.class).bind(0, 4).add()
672+
.bind(0, 5).bindNull(1, Integer.class));
673+
awaitQuery(asList(asList(null, null)),
674+
row ->
675+
asList(row.get(0, Integer.class), row.get(1,Integer.class)),
676+
connection.createStatement(
677+
"SELECT x, y FROM testNullBindByName" +
678+
" WHERE x IS NULL AND y IS NULL"));
679+
awaitQuery(asList(asList(4, null), asList(5, null)),
680+
row ->
681+
asList(row.get(0, Integer.class), row.get(1,Integer.class)),
682+
connection.createStatement(
683+
"SELECT x, y FROM testNullBindByName" +
684+
" WHERE x >= 4 AND x IS NOT NULL AND y IS NULL" +
685+
" ORDER BY x, y"));
620686
}
621687
finally {
622688
awaitExecution(connection.createStatement(

0 commit comments

Comments
 (0)