Skip to content

Commit 398afbe

Browse files
authored
fix: Statements with no results would return an error (#57)
Statements that return no results, such as DDL statements, would cause an error as PGAdapter tried to get the update count of these. Fixes #56
1 parent 020471b commit 398afbe

File tree

6 files changed

+166
-16
lines changed

6 files changed

+166
-16
lines changed

src/main/java/com/google/cloud/spanner/pgadapter/statements/IntermediateStatement.java

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -203,13 +203,23 @@ public Exception getException() {
203203
* of updateBatchResultCount.
204204
*/
205205
protected void updateResultCount(StatementResult result) {
206-
if (result.getResultType() == StatementResult.ResultType.RESULT_SET) {
207-
this.statementResult = result.getResultSet();
208-
this.hasMoreData = this.statementResult.next();
209-
} else {
210-
this.updateCount = result.getUpdateCount();
211-
this.hasMoreData = false;
212-
this.statementResult = null;
206+
switch (result.getResultType()) {
207+
case RESULT_SET:
208+
this.statementResult = result.getResultSet();
209+
this.hasMoreData = this.statementResult.next();
210+
break;
211+
case UPDATE_COUNT:
212+
this.updateCount = result.getUpdateCount();
213+
this.hasMoreData = false;
214+
this.statementResult = null;
215+
break;
216+
case NO_RESULT:
217+
this.hasMoreData = false;
218+
this.statementResult = null;
219+
break;
220+
default:
221+
throw SpannerExceptionFactory.newSpannerException(
222+
ErrorCode.INTERNAL, "Unknown or unsupported result type: " + result.getResultType());
213223
}
214224
}
215225

src/test/java/com/google/cloud/spanner/pgadapter/JdbcMockServerTest.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import com.google.cloud.spanner.Statement;
2727
import com.google.protobuf.ListValue;
2828
import com.google.protobuf.Value;
29+
import com.google.spanner.admin.database.v1.UpdateDatabaseDdlRequest;
2930
import com.google.spanner.v1.CommitRequest;
3031
import com.google.spanner.v1.ExecuteBatchDmlRequest;
3132
import com.google.spanner.v1.ExecuteSqlRequest;
@@ -56,6 +57,7 @@
5657
import java.util.Base64;
5758
import java.util.List;
5859
import java.util.Map;
60+
import java.util.stream.Collectors;
5961
import org.junit.BeforeClass;
6062
import org.junit.Test;
6163
import org.junit.runner.RunWith;
@@ -477,4 +479,27 @@ public void testTwoQueries() throws SQLException {
477479
}
478480
}
479481
}
482+
483+
@Test
484+
public void testDdl() throws SQLException {
485+
String sql = "CREATE TABLE foo (id bigint primary key)";
486+
487+
try (Connection connection = DriverManager.getConnection(createUrl())) {
488+
try (java.sql.Statement statement = connection.createStatement()) {
489+
// Statement#execute(String) returns false if the result was either an update count or there
490+
// was no result. Statement#getUpdateCount() returns 0 if there was no result.
491+
assertFalse(statement.execute(sql));
492+
assertEquals(0, statement.getUpdateCount());
493+
}
494+
}
495+
496+
List<UpdateDatabaseDdlRequest> updateDatabaseDdlRequests =
497+
mockDatabaseAdmin.getRequests().stream()
498+
.filter(request -> request instanceof UpdateDatabaseDdlRequest)
499+
.map(UpdateDatabaseDdlRequest.class::cast)
500+
.collect(Collectors.toList());
501+
assertEquals(1, updateDatabaseDdlRequests.size());
502+
assertEquals(1, updateDatabaseDdlRequests.get(0).getStatementsCount());
503+
assertEquals(sql, updateDatabaseDdlRequests.get(0).getStatements(0));
504+
}
480505
}

src/test/java/com/google/cloud/spanner/pgadapter/JdbcSimpleModeMockServerTest.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import static org.junit.Assert.assertTrue;
2121

2222
import com.google.cloud.spanner.MockSpannerServiceImpl.StatementResult;
23+
import com.google.spanner.admin.database.v1.UpdateDatabaseDdlRequest;
2324
import com.google.spanner.v1.ExecuteSqlRequest;
2425
import com.google.spanner.v1.ExecuteSqlRequest.QueryMode;
2526
import java.math.BigDecimal;
@@ -33,7 +34,9 @@
3334
import java.time.LocalDateTime;
3435
import java.time.OffsetDateTime;
3536
import java.time.ZoneOffset;
37+
import java.util.List;
3638
import java.util.TimeZone;
39+
import java.util.stream.Collectors;
3740
import org.junit.BeforeClass;
3841
import org.junit.Test;
3942
import org.junit.runner.RunWith;
@@ -213,4 +216,27 @@ public void testSelectAtStartOfBatch() throws SQLException {
213216
}
214217
}
215218
}
219+
220+
@Test
221+
public void testDdl() throws SQLException {
222+
String sql = "CREATE TABLE foo (id bigint primary key)";
223+
224+
try (Connection connection = DriverManager.getConnection(createUrl())) {
225+
try (Statement statement = connection.createStatement()) {
226+
// Statement#execute(String) returns false if the result was either an update count or there
227+
// was no result. Statement#getUpdateCount() returns 0 if there was no result.
228+
assertFalse(statement.execute(sql));
229+
assertEquals(0, statement.getUpdateCount());
230+
}
231+
}
232+
233+
List<UpdateDatabaseDdlRequest> updateDatabaseDdlRequests =
234+
mockDatabaseAdmin.getRequests().stream()
235+
.filter(request -> request instanceof UpdateDatabaseDdlRequest)
236+
.map(UpdateDatabaseDdlRequest.class::cast)
237+
.collect(Collectors.toList());
238+
assertEquals(1, updateDatabaseDdlRequests.size());
239+
assertEquals(1, updateDatabaseDdlRequests.get(0).getStatementsCount());
240+
assertEquals(sql, updateDatabaseDdlRequests.get(0).getStatements(0));
241+
}
216242
}

src/test/java/com/google/cloud/spanner/pgadapter/ProtocolTest.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import com.google.cloud.spanner.Statement;
2929
import com.google.cloud.spanner.connection.Connection;
3030
import com.google.cloud.spanner.connection.StatementResult;
31+
import com.google.cloud.spanner.connection.StatementResult.ResultType;
3132
import com.google.cloud.spanner.pgadapter.ConnectionHandler.ConnectionStatus;
3233
import com.google.cloud.spanner.pgadapter.ConnectionHandler.QueryMode;
3334
import com.google.cloud.spanner.pgadapter.metadata.ConnectionMetadata;
@@ -155,6 +156,7 @@ public void testQueryMessage() throws Exception {
155156
String expectedSQL = "SELECT * FROM users";
156157

157158
Mockito.when(connection.execute(Statement.of(expectedSQL))).thenReturn(statementResult);
159+
when(statementResult.getResultType()).thenReturn(ResultType.RESULT_SET);
158160
when(statementResult.getResultSet()).thenReturn(resultSet);
159161
Mockito.when(connectionHandler.getServer()).thenReturn(server);
160162
Mockito.when(server.getOptions()).thenReturn(options);
@@ -189,6 +191,7 @@ public void testQueryUsesPSQLStatementWhenPSQLModeSelectedMessage() throws Excep
189191
String expectedSQL = "SELECT * FROM users";
190192

191193
when(connection.execute(Statement.of(expectedSQL))).thenReturn(statementResult);
194+
when(statementResult.getResultType()).thenReturn(ResultType.RESULT_SET);
192195
Mockito.when(statementResult.getResultSet()).thenReturn(resultSet);
193196
Mockito.when(connectionHandler.getServer()).thenReturn(server);
194197
Mockito.when(server.getOptions()).thenReturn(options);
@@ -1120,8 +1123,8 @@ public void testQueryMessageInTransaction() throws Exception {
11201123

11211124
when(connection.isInTransaction()).thenReturn(true);
11221125
when(connectionHandler.getSpannerConnection()).thenReturn(connection);
1123-
ResultSet resultSet = mock(ResultSet.class);
1124-
when(statementResult.getResultSet()).thenReturn(resultSet);
1126+
when(statementResult.getResultType()).thenReturn(ResultType.UPDATE_COUNT);
1127+
when(statementResult.getUpdateCount()).thenReturn(1L);
11251128
when(connection.execute(Statement.of(expectedSQL))).thenReturn(statementResult);
11261129
when(connectionHandler.getConnectionMetadata()).thenReturn(connectionMetadata);
11271130
when(connectionHandler.getServer()).thenReturn(server);
@@ -1143,11 +1146,11 @@ public void testQueryMessageInTransaction() throws Exception {
11431146
assertEquals('\0', outputResult.readByte());
11441147
assertEquals('\0', outputResult.readByte());
11451148
assertEquals('\0', outputResult.readByte());
1146-
// 15 = 4 + "INSERT".length() + " 0 0".length() + 1 (header + command length + null terminator)
1149+
// 15 = 4 + "INSERT".length() + " 0 1".length() + 1 (header + command length + null terminator)
11471150
assertEquals(15, outputResult.readByte());
11481151
byte[] command = new byte[10];
11491152
assertEquals(10, outputResult.read(command, 0, 10));
1150-
assertEquals("INSERT 0 0", new String(command));
1153+
assertEquals("INSERT 0 1", new String(command));
11511154
assertEquals('\0', outputResult.readByte());
11521155
// ReadyResponse in transaction ('T')
11531156
assertEquals('Z', outputResult.readByte());

src/test/java/com/google/cloud/spanner/pgadapter/StatementTest.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import static org.junit.Assert.assertEquals;
1818
import static org.junit.Assert.assertFalse;
19+
import static org.junit.Assert.assertNull;
1920
import static org.junit.Assert.assertTrue;
2021
import static org.mockito.ArgumentMatchers.any;
2122
import static org.mockito.Mockito.when;
@@ -136,7 +137,7 @@ public void testBasicUpdateStatement() throws Exception {
136137
assertEquals(intermediateStatement.getUpdateCount().longValue(), 1L);
137138
assertTrue(intermediateStatement.isExecuted());
138139
assertEquals(intermediateStatement.getResultType(), ResultType.UPDATE_COUNT);
139-
Assert.assertNull(intermediateStatement.getStatementResult());
140+
assertNull(intermediateStatement.getStatementResult());
140141
assertFalse(intermediateStatement.isHasMoreData());
141142
assertFalse(intermediateStatement.hasException());
142143
assertEquals(intermediateStatement.getResultFormatCode(0), 0);
@@ -167,7 +168,7 @@ public void testBasicZeroUpdateCountResultStatement() throws Exception {
167168
assertEquals(intermediateStatement.getUpdateCount().longValue(), 0L);
168169
assertTrue(intermediateStatement.isExecuted());
169170
assertEquals(intermediateStatement.getResultType(), ResultType.UPDATE_COUNT);
170-
Assert.assertNull(intermediateStatement.getStatementResult());
171+
assertNull(intermediateStatement.getStatementResult());
171172
assertFalse(intermediateStatement.isHasMoreData());
172173
assertFalse(intermediateStatement.hasException());
173174
assertEquals(intermediateStatement.getResultFormatCode(0), 0);
@@ -180,7 +181,7 @@ public void testBasicZeroUpdateCountResultStatement() throws Exception {
180181
@Test
181182
public void testBasicNoResultStatement() throws Exception {
182183
when(statementResult.getResultType()).thenReturn(StatementResult.ResultType.NO_RESULT);
183-
when(statementResult.getUpdateCount()).thenReturn(0L);
184+
when(statementResult.getUpdateCount()).thenThrow(new IllegalStateException());
184185
when(connection.execute(Statement.of("CREATE TABLE users (name varchar(100) primary key)")))
185186
.thenReturn(statementResult);
186187

@@ -195,10 +196,10 @@ public void testBasicNoResultStatement() throws Exception {
195196
Mockito.verify(connection, Mockito.times(1))
196197
.execute(Statement.of("CREATE TABLE users (name varchar(100) primary key)"));
197198
assertFalse(intermediateStatement.containsResultSet());
198-
assertEquals(intermediateStatement.getUpdateCount().longValue(), 0L);
199+
assertNull(intermediateStatement.getUpdateCount());
199200
assertTrue(intermediateStatement.isExecuted());
200201
assertEquals(intermediateStatement.getResultType(), ResultType.NO_RESULT);
201-
Assert.assertNull(intermediateStatement.getStatementResult());
202+
assertNull(intermediateStatement.getStatementResult());
202203
assertFalse(intermediateStatement.isHasMoreData());
203204
assertFalse(intermediateStatement.hasException());
204205
assertEquals(intermediateStatement.getResultFormatCode(0), 0);
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// Copyright 2022 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.cloud.spanner.pgadapter.statements;
16+
17+
import static org.junit.Assert.assertEquals;
18+
import static org.junit.Assert.assertFalse;
19+
import static org.junit.Assert.assertNull;
20+
import static org.junit.Assert.assertSame;
21+
import static org.junit.Assert.assertTrue;
22+
import static org.mockito.Mockito.mock;
23+
import static org.mockito.Mockito.when;
24+
25+
import com.google.cloud.spanner.ResultSet;
26+
import com.google.cloud.spanner.connection.Connection;
27+
import com.google.cloud.spanner.connection.StatementResult;
28+
import com.google.cloud.spanner.connection.StatementResult.ResultType;
29+
import org.junit.Test;
30+
import org.junit.runner.RunWith;
31+
import org.junit.runners.JUnit4;
32+
import org.mockito.Mock;
33+
34+
@RunWith(JUnit4.class)
35+
public class IntermediateStatementTest {
36+
@Mock private Connection connection;
37+
38+
@Test
39+
public void testUpdateResultCount_ResultSet() {
40+
IntermediateStatement statement = new IntermediateStatement("select foo from bar", connection);
41+
ResultSet resultSet = mock(ResultSet.class);
42+
when(resultSet.next()).thenReturn(true, false);
43+
StatementResult result = mock(StatementResult.class);
44+
when(result.getResultType()).thenReturn(ResultType.RESULT_SET);
45+
when(result.getResultSet()).thenReturn(resultSet);
46+
when(result.getUpdateCount()).thenThrow(new IllegalStateException());
47+
48+
statement.updateResultCount(result);
49+
50+
assertTrue(statement.hasMoreData);
51+
assertNull(statement.updateCount);
52+
assertSame(resultSet, statement.statementResult);
53+
}
54+
55+
@Test
56+
public void testUpdateResultCount_UpdateCount() {
57+
IntermediateStatement statement = new IntermediateStatement("update bar set foo=1", connection);
58+
StatementResult result = mock(StatementResult.class);
59+
when(result.getResultType()).thenReturn(ResultType.UPDATE_COUNT);
60+
when(result.getResultSet()).thenThrow(new IllegalStateException());
61+
when(result.getUpdateCount()).thenReturn(100L);
62+
63+
statement.updateResultCount(result);
64+
65+
assertFalse(statement.hasMoreData);
66+
assertEquals(100L, statement.updateCount.longValue());
67+
assertNull(statement.statementResult);
68+
}
69+
70+
@Test
71+
public void testUpdateResultCount_NoResult() {
72+
IntermediateStatement statement =
73+
new IntermediateStatement("create table bar (foo bigint primary key)", connection);
74+
StatementResult result = mock(StatementResult.class);
75+
when(result.getResultType()).thenReturn(ResultType.NO_RESULT);
76+
when(result.getResultSet()).thenThrow(new IllegalStateException());
77+
when(result.getUpdateCount()).thenThrow(new IllegalStateException());
78+
79+
statement.updateResultCount(result);
80+
81+
assertFalse(statement.hasMoreData);
82+
assertNull(statement.updateCount);
83+
assertNull(statement.statementResult);
84+
}
85+
}

0 commit comments

Comments
 (0)