Skip to content

Commit 77e7421

Browse files
authored
fix: return EmptyQueryResponse for empty statements (#126)
Empty SQL statements should cause the server to return an EmptyQueryResponse instead of a CommandCompleteResponse. See https://www.postgresql.org/docs/14/protocol-message-formats.html
1 parent b5a32c3 commit 77e7421

File tree

3 files changed

+64
-0
lines changed

3 files changed

+64
-0
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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.wireoutput;
16+
17+
import java.io.DataOutputStream;
18+
import java.text.MessageFormat;
19+
20+
public class EmptyQueryResponse extends WireOutput {
21+
public EmptyQueryResponse(DataOutputStream output) {
22+
super(output, 4);
23+
}
24+
25+
@Override
26+
protected void sendPayload() throws Exception {}
27+
28+
@Override
29+
public byte getIdentifier() {
30+
return 'I';
31+
}
32+
33+
@Override
34+
protected String getMessageName() {
35+
return "EmptyQueryResponse";
36+
}
37+
38+
@Override
39+
protected String getPayloadString() {
40+
return new MessageFormat("Length: {0}").format(new Object[] {this.length});
41+
}
42+
}

src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/QueryMessage.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import com.google.cloud.spanner.pgadapter.statements.MatcherStatement;
2828
import com.google.cloud.spanner.pgadapter.utils.StatementParser;
2929
import com.google.cloud.spanner.pgadapter.wireoutput.CopyInResponse;
30+
import com.google.cloud.spanner.pgadapter.wireoutput.EmptyQueryResponse;
3031
import com.google.cloud.spanner.pgadapter.wireoutput.ErrorResponse;
3132
import com.google.cloud.spanner.pgadapter.wireoutput.ErrorResponse.State;
3233
import com.google.cloud.spanner.pgadapter.wireoutput.ReadyResponse;
@@ -121,6 +122,8 @@ public void handleQuery() throws Exception {
121122

122123
// Return early as we do not respond with CommandComplete after a COPY command.
123124
return;
125+
} else if ("".equals(this.statement.getCommandTag(index))) {
126+
new EmptyQueryResponse(outputStream).send();
124127
} else {
125128
if (this.statement.containsResultSet(index)) {
126129
new RowDescriptionResponse(

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,25 @@ public void testEmptyStatement() throws SQLException {
104104
assertEquals(0, mockSpanner.countRequestsOfType(ExecuteSqlRequest.class));
105105
}
106106

107+
@Test
108+
public void testEmptyStatementFollowedByNonEmptyStatement() throws SQLException {
109+
try (Connection connection = DriverManager.getConnection(createUrl())) {
110+
try (Statement statement = connection.createStatement()) {
111+
// Execute should return false for the first statement.
112+
assertFalse(statement.execute(""));
113+
114+
assertTrue(statement.execute("SELECT 1"));
115+
try (ResultSet resultSet = statement.getResultSet()) {
116+
assertTrue(resultSet.next());
117+
assertEquals(1L, resultSet.getLong(1));
118+
assertFalse(resultSet.next());
119+
}
120+
assertFalse(statement.getMoreResults());
121+
}
122+
}
123+
assertEquals(1, mockSpanner.countRequestsOfType(ExecuteSqlRequest.class));
124+
}
125+
107126
@Test
108127
public void testWrongDialect() {
109128
// Let the mock server respond with the Google SQL dialect instead of PostgreSQL. The

0 commit comments

Comments
 (0)