Skip to content

Commit f49cc47

Browse files
rahul2393cloud-java-botolavloite
authored
fix: allow DML THEN RETURN with retryAbortsInternally=false (#4225)
* fix: allow DML THEN RETURN with retryAbortsInternally=false * chore: generate libraries at Fri Nov 14 13:30:19 UTC 2025 * test: add more tests --------- Co-authored-by: cloud-java-bot <[email protected]> Co-authored-by: Knut Olav Løite <[email protected]>
1 parent 8f0da07 commit f49cc47

File tree

6 files changed

+81
-5
lines changed

6 files changed

+81
-5
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ If you are using Maven with [BOM][libraries-bom], add this to your pom.xml file:
1919
<dependency>
2020
<groupId>com.google.cloud</groupId>
2121
<artifactId>libraries-bom</artifactId>
22-
<version>26.70.0</version>
22+
<version>26.71.0</version>
2323
<type>pom</type>
2424
<scope>import</scope>
2525
</dependency>
@@ -41,7 +41,7 @@ If you are using Maven without the BOM, add this to your dependencies:
4141
<dependency>
4242
<groupId>com.google.cloud</groupId>
4343
<artifactId>google-cloud-spanner</artifactId>
44-
<version>6.102.0</version>
44+
<version>6.102.1</version>
4545
</dependency>
4646

4747
```

google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ReadWriteTransaction.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -689,7 +689,19 @@ public ApiFuture<ResultSet> executeQueryAsync(
689689
InterceptorsUsage.IGNORE_INTERCEPTORS,
690690
ImmutableList.of(SpannerGrpc.getExecuteStreamingSqlMethod()));
691691
} else {
692-
res = super.executeQueryAsync(callType, statement, analyzeMode, options);
692+
// Handle both SELECT queries and DML with THEN RETURN without delegating to the base class,
693+
// which rejects non-SELECT statements.
694+
res =
695+
executeStatementAsync(
696+
callType,
697+
statement,
698+
() -> {
699+
checkTimedOut();
700+
checkAborted();
701+
return DirectExecuteResultSet.ofResultSet(
702+
internalExecuteQuery(statement, analyzeMode, options));
703+
},
704+
SpannerGrpc.getExecuteStreamingSqlMethod());
693705
}
694706
ApiFutures.addCallback(res, new StatementResultCallback<>(), MoreExecutors.directExecutor());
695707
return res;

google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/AbstractMockServerTest.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ public abstract class AbstractMockServerTest {
110110
.setMetadata(SINGLE_COL_INT64_RESULTSET_METADATA)
111111
.build();
112112
public static final com.google.spanner.v1.ResultSet UPDATE_RETURNING_RESULTSET =
113-
com.google.spanner.v1.ResultSet.newBuilder()
113+
ResultSet.newBuilder()
114114
.setStats(ResultSetStats.newBuilder().setRowCountExact(1))
115115
.setMetadata(
116116
ResultSetMetadata.newBuilder()
@@ -121,6 +121,10 @@ public abstract class AbstractMockServerTest {
121121
.setName("col")
122122
.setType(Type.newBuilder().setCodeValue(TypeCode.INT64_VALUE))
123123
.build())))
124+
.addRows(
125+
ListValue.newBuilder()
126+
.addValues(Value.newBuilder().setStringValue("1").build())
127+
.build())
124128
.build();
125129

126130
protected static final ResultSet SELECT1_RESULTSET =

google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/AutoDmlBatchMockServerTest.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ public void testDmlWithReturningAfterDml() {
9797
// DML with a THEN RETURN clause cannot be batched. This therefore flushes the batch and
9898
// executes the INSERT ... THEN RETURN statement as a separate ExecuteSqlRequest.
9999
try (ResultSet resultSet = connection.executeQuery(INSERT_RETURNING_STATEMENT)) {
100+
assertTrue(resultSet.next());
101+
assertEquals(1L, resultSet.getLong(0));
100102
assertFalse(resultSet.next());
101103
}
102104

@@ -123,6 +125,8 @@ public void testDmlWithReturningAfterDml_usingExecute() {
123125
StatementResult result = connection.execute(INSERT_RETURNING_STATEMENT);
124126
assertEquals(ResultType.RESULT_SET, result.getResultType());
125127
try (ResultSet resultSet = result.getResultSet()) {
128+
assertTrue(resultSet.next());
129+
assertEquals(1L, resultSet.getLong(0));
126130
assertFalse(resultSet.next());
127131
}
128132

google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ReadWriteTransactionTest.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,21 @@ public void testExecuteUpdate() {
287287
assertThat(get(transaction.executeUpdateAsync(CallType.SYNC, parsedStatement)), is(1L));
288288
}
289289

290+
@Test
291+
public void testExecuteQueryWithDmlReturningWithoutRetry() {
292+
ParsedStatement parsedStatement = mock(ParsedStatement.class);
293+
when(parsedStatement.getType()).thenReturn(StatementType.UPDATE);
294+
when(parsedStatement.isUpdate()).thenReturn(true);
295+
when(parsedStatement.hasReturningClause()).thenReturn(true);
296+
Statement statement = Statement.of("INSERT INTO TEST (ID, NAME) VALUES (1, 'x') THEN RETURN *");
297+
when(parsedStatement.getStatement()).thenReturn(statement);
298+
299+
ReadWriteTransaction transaction = createSubject(/* commitBehavior= */ CommitBehavior.SUCCEED);
300+
ResultSet rs =
301+
get(transaction.executeQueryAsync(CallType.SYNC, parsedStatement, AnalyzeMode.NONE));
302+
assertThat(rs, is(notNullValue()));
303+
}
304+
290305
@Test
291306
public void testGetCommitTimestampBeforeCommit() {
292307
ParsedStatement parsedStatement = mock(ParsedStatement.class);

google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/TransactionMockServerTest.java

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import com.google.cloud.spanner.SpannerException;
3232
import com.google.cloud.spanner.Statement;
3333
import com.google.cloud.spanner.connection.ITAbstractSpannerTest.ITConnection;
34+
import com.google.cloud.spanner.connection.StatementResult.ResultType;
3435
import com.google.spanner.v1.BeginTransactionRequest;
3536
import com.google.spanner.v1.CommitRequest;
3637
import com.google.spanner.v1.ExecuteBatchDmlRequest;
@@ -357,5 +358,45 @@ public long nanoTime() {
357358
}
358359

359360
@Test
360-
public void testTransactionTimeoutInAutoCommit() {}
361+
public void testCanUseAllMethodsWithInternalRetriesDisabled() {
362+
// Verify that all query/update methods work as expected when internal retries have been
363+
// disabled.
364+
try (Connection connection = createConnection()) {
365+
connection.setAutocommit(false);
366+
connection.setRetryAbortsInternally(false);
367+
368+
try (ResultSet result = connection.executeQuery(SELECT1_STATEMENT)) {
369+
assertTrue(result.next());
370+
assertEquals(1L, result.getLong(0));
371+
assertFalse(result.next());
372+
}
373+
assertEquals(1, connection.executeUpdate(INSERT_STATEMENT));
374+
try (ResultSet result = connection.executeQuery(INSERT_RETURNING_STATEMENT)) {
375+
assertTrue(result.next());
376+
assertEquals(1L, result.getLong(0));
377+
assertFalse(result.next());
378+
}
379+
380+
StatementResult statementResult = connection.execute(SELECT1_STATEMENT);
381+
assertEquals(ResultType.RESULT_SET, statementResult.getResultType());
382+
try (ResultSet result = statementResult.getResultSet()) {
383+
assertTrue(result.next());
384+
assertEquals(1L, result.getLong(0));
385+
assertFalse(result.next());
386+
}
387+
388+
statementResult = connection.execute(INSERT_STATEMENT);
389+
assertEquals(ResultType.UPDATE_COUNT, statementResult.getResultType());
390+
assertEquals(1L, statementResult.getUpdateCount().longValue());
391+
392+
statementResult = connection.execute(INSERT_RETURNING_STATEMENT);
393+
assertEquals(ResultType.RESULT_SET, statementResult.getResultType());
394+
try (ResultSet result = statementResult.getResultSet()) {
395+
assertTrue(result.next());
396+
assertEquals(1L, result.getLong(0));
397+
assertFalse(result.next());
398+
}
399+
connection.commit();
400+
}
401+
}
361402
}

0 commit comments

Comments
 (0)