From d787ea3811d878e53ad981c9a0eec5762b945de3 Mon Sep 17 00:00:00 2001 From: jsking Date: Thu, 17 Feb 2022 15:59:29 -0800 Subject: [PATCH 01/17] Adding Copy command parsing in QueryMessage and basic e2e test. --- .ci/e2e-expected/copy-from-stdin.txt | 12 ++++++++++ .ci/evaluate-with-psql.sh | 14 +++++++++++ .../spanner/pgadapter/ConnectionHandler.java | 6 ++++- .../pgadapter/wireprotocol/QueryMessage.java | 23 ++++++++++++++++--- 4 files changed, 51 insertions(+), 4 deletions(-) create mode 100644 .ci/e2e-expected/copy-from-stdin.txt diff --git a/.ci/e2e-expected/copy-from-stdin.txt b/.ci/e2e-expected/copy-from-stdin.txt new file mode 100644 index 000000000..25d9d2e6d --- /dev/null +++ b/.ci/e2e-expected/copy-from-stdin.txt @@ -0,0 +1,12 @@ + id | age | name +----+-----+------ + 1 | 1 | John + 2 | 20 | Joe + 3 | 23 | Jack + 5 | 5 | 5 + 6 | 6 | 6 + 7 | 7 | 7 + 8 | 8 | 8 + 9 | 9 | 9 + 10 | 10 | 10 +(9 rows) diff --git a/.ci/evaluate-with-psql.sh b/.ci/evaluate-with-psql.sh index a5cf266ae..62ab9816f 100644 --- a/.ci/evaluate-with-psql.sh +++ b/.ci/evaluate-with-psql.sh @@ -110,3 +110,17 @@ echo "------Test \"-c option invalid begin/commit batching\"------" /usr/lib/postgresql/"${PSQL_VERSION}"/bin/psql -h localhost -p 4242 -d "${GOOGLE_CLOUD_DATABASE_WITH_VERSION}" -c "$(cat .ci/e2e-batching/invalid-commit-batch.txt)" &> .ci/e2e-result/invalid-commit-batching.txt diff -i -w -s .ci/e2e-result/invalid-commit-batching.txt .ci/e2e-expected/invalid-commit-batching.txt RETURN_CODE=$((${RETURN_CODE}||$?)) + +echo "------Test \"COPY FROM STDIN\"------" +/usr/lib/postgresql/"${PSQL_VERSION}"/bin/psql -h localhost -p 4242 -d "${GOOGLE_CLOUD_DATABASE_WITH_VERSION}" -c "COPY users FROM STDIN;" < .ci/e2e-result/copy-from-stdin.txt +diff -i -w -s .ci/e2e-result/copy-from-stdin.txt .ci/e2e-expected/copy-from-stdin.txt +RETURN_CODE=$((${RETURN_CODE}||$?)) \ No newline at end of file diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/ConnectionHandler.java b/src/main/java/com/google/cloud/spanner/pgadapter/ConnectionHandler.java index e47ab9249..7c7d92d80 100644 --- a/src/main/java/com/google/cloud/spanner/pgadapter/ConnectionHandler.java +++ b/src/main/java/com/google/cloud/spanner/pgadapter/ConnectionHandler.java @@ -319,12 +319,16 @@ public synchronized IntermediateStatement getActiveStatement() { return activeStatementsMap.get(this.connectionId); } + public synchronized ConnectionStatus getStatus() { + return status; + } + public synchronized void setStatus(ConnectionStatus status) { this.status = status; } /** Status of a {@link ConnectionHandler} */ - private enum ConnectionStatus { + public enum ConnectionStatus { UNAUTHENTICATED, IDLE, COPY_IN, diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/QueryMessage.java b/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/QueryMessage.java index a60a85d18..cf890958a 100644 --- a/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/QueryMessage.java +++ b/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/QueryMessage.java @@ -16,10 +16,13 @@ import com.google.cloud.spanner.jdbc.CloudSpannerJdbcConnection; import com.google.cloud.spanner.pgadapter.ConnectionHandler; +import com.google.cloud.spanner.pgadapter.ConnectionHandler.ConnectionStatus; import com.google.cloud.spanner.pgadapter.ConnectionHandler.QueryMode; +import com.google.cloud.spanner.pgadapter.statements.CopyStatement; import com.google.cloud.spanner.pgadapter.statements.IntermediateStatement; import com.google.cloud.spanner.pgadapter.statements.MatcherStatement; import com.google.cloud.spanner.pgadapter.utils.StatementParser; +import com.google.cloud.spanner.pgadapter.wireoutput.CopyInResponse; import com.google.cloud.spanner.pgadapter.wireoutput.ReadyResponse; import com.google.cloud.spanner.pgadapter.wireoutput.ReadyResponse.Status; import com.google.cloud.spanner.pgadapter.wireoutput.RowDescriptionResponse; @@ -29,13 +32,17 @@ public class QueryMessage extends ControlMessage { protected static final char IDENTIFIER = 'Q'; + protected static final String COPY = "COPY"; private IntermediateStatement statement; public QueryMessage(ConnectionHandler connection) throws Exception { super(connection); String query = StatementParser.removeCommentsAndTrim(this.readAll()); - if (!connection.getServer().getOptions().requiresMatcher()) { + String command = StatementParser.parseCommand(query); + if (command.equalsIgnoreCase(COPY)) { + this.statement = new CopyStatement(query, this.connection.getJdbcConnection()); + } else if (!connection.getServer().getOptions().requiresMatcher()) { this.statement = new IntermediateStatement(query, this.connection.getJdbcConnection()); } else { this.statement = new MatcherStatement(query, this.connection); @@ -47,7 +54,9 @@ public QueryMessage(ConnectionHandler connection) throws Exception { protected void sendPayload() throws Exception { this.statement.execute(); this.handleQuery(); - this.connection.removeActiveStatement(this.statement); + if (!this.statement.getCommand().equalsIgnoreCase(COPY)) { + this.connection.removeActiveStatement(this.statement); + } } @Override @@ -81,7 +90,15 @@ public void handleQuery() throws Exception { if (this.statement.hasException()) { this.handleError(this.statement.getException()); } else { - if (this.statement.containsResultSet()) { + if (this.statement.getCommand().equalsIgnoreCase(COPY)) { + CopyStatement copyStatement = (CopyStatement) this.statement; + new CopyInResponse( + this.outputStream, + copyStatement.getTableColumns().size(), + copyStatement.getFormatCode()) + .send(); + this.connection.setStatus(ConnectionStatus.COPY_IN); + } else if (this.statement.containsResultSet()) { new RowDescriptionResponse( this.outputStream, this.statement, From 0692526eb7c0dfc5a16135d40f4b37783cd2d44e Mon Sep 17 00:00:00 2001 From: jsking Date: Thu, 17 Feb 2022 16:14:30 -0800 Subject: [PATCH 02/17] Update --- .../cloud/spanner/pgadapter/statements/CopyStatement.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/statements/CopyStatement.java b/src/main/java/com/google/cloud/spanner/pgadapter/statements/CopyStatement.java index c0ea84195..d40e685c5 100644 --- a/src/main/java/com/google/cloud/spanner/pgadapter/statements/CopyStatement.java +++ b/src/main/java/com/google/cloud/spanner/pgadapter/statements/CopyStatement.java @@ -127,6 +127,11 @@ public MutationWriter getMutationWriter() { return this.mutationWriter; } + /** @return 0 for text/csv formatting and 1 for binary */ + public int getFormatCode() { + return (options.getFormat() == CopyTreeParser.CopyOptions.Format.BINARY) ? 1 : 0; + } + private void verifyCopyColumns() throws SQLException { if (options.getColumnNames().size() == 0) { // Use all columns if none were specified. From 79f75a02f90d070e32a5b4a4690aa32a56f0a4ab Mon Sep 17 00:00:00 2001 From: jsking Date: Thu, 17 Feb 2022 16:39:20 -0800 Subject: [PATCH 03/17] Update --- .../cloud/spanner/pgadapter/wireprotocol/CopyDoneMessage.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/CopyDoneMessage.java b/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/CopyDoneMessage.java index f26bcdcf7..7a67a5cec 100644 --- a/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/CopyDoneMessage.java +++ b/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/CopyDoneMessage.java @@ -51,6 +51,7 @@ protected void sendPayload() throws Exception { // Spanner returned an error when trying to commit the batch of mutations. mw.writeMutationsToErrorFile(); mw.closeErrorFile(); + this.connection.setStatus(ConnectionStatus.IDLE); this.connection.removeActiveStatement(this.statement); throw e; } @@ -58,6 +59,7 @@ protected void sendPayload() throws Exception { mw.closeErrorFile(); } new ReadyResponse(this.outputStream, ReadyResponse.Status.IDLE).send(); + this.connection.setStatus(ConnectionStatus.IDLE); this.connection.removeActiveStatement(this.statement); } From 8887b4ce9babc44d4a12c510ab56423a654b0268 Mon Sep 17 00:00:00 2001 From: jsking Date: Thu, 17 Feb 2022 16:48:23 -0800 Subject: [PATCH 04/17] Update --- .../wireprotocol/ControlMessage.java | 70 +++++++++++-------- 1 file changed, 41 insertions(+), 29 deletions(-) diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/ControlMessage.java b/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/ControlMessage.java index 41528d20f..b1eac1990 100644 --- a/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/ControlMessage.java +++ b/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/ControlMessage.java @@ -15,6 +15,7 @@ package com.google.cloud.spanner.pgadapter.wireprotocol; import com.google.cloud.spanner.pgadapter.ConnectionHandler; +import com.google.cloud.spanner.pgadapter.ConnectionHandler.ConnectionStatus; import com.google.cloud.spanner.pgadapter.ConnectionHandler.QueryMode; import com.google.cloud.spanner.pgadapter.metadata.SendResultSetState; import com.google.cloud.spanner.pgadapter.statements.IntermediateStatement; @@ -49,35 +50,46 @@ public ControlMessage(ConnectionHandler connection) throws IOException { */ public static ControlMessage create(ConnectionHandler connection) throws Exception { char nextMsg = (char) connection.getConnectionMetadata().getInputStream().readUnsignedByte(); - switch (nextMsg) { - case QueryMessage.IDENTIFIER: - return new QueryMessage(connection); - case ParseMessage.IDENTIFIER: - return new ParseMessage(connection); - case BindMessage.IDENTIFIER: - return new BindMessage(connection); - case DescribeMessage.IDENTIFIER: - return new DescribeMessage(connection); - case ExecuteMessage.IDENTIFIER: - return new ExecuteMessage(connection); - case CloseMessage.IDENTIFIER: - return new CloseMessage(connection); - case SyncMessage.IDENTIFIER: - return new SyncMessage(connection); - case TerminateMessage.IDENTIFIER: - return new TerminateMessage(connection); - case CopyDoneMessage.IDENTIFIER: - return new CopyDoneMessage(connection); - case CopyDataMessage.IDENTIFIER: - return new CopyDataMessage(connection); - case CopyFailMessage.IDENTIFIER: - return new CopyFailMessage(connection); - case FunctionCallMessage.IDENTIFIER: - return new FunctionCallMessage(connection); - case FlushMessage.IDENTIFIER: - return new FlushMessage(connection); - default: - throw new IllegalStateException("Unknown message"); + if (connection.getStatus() == ConnectionStatus.COPY_IN) { + switch (nextMsg) { + case CopyDoneMessage.IDENTIFIER: + return new CopyDoneMessage(connection); + case CopyDataMessage.IDENTIFIER: + return new CopyDataMessage(connection); + default: + throw new IllegalStateException("Expected 0 or more Copy Data messages."); + } + } else { + switch (nextMsg) { + case QueryMessage.IDENTIFIER: + return new QueryMessage(connection); + case ParseMessage.IDENTIFIER: + return new ParseMessage(connection); + case BindMessage.IDENTIFIER: + return new BindMessage(connection); + case DescribeMessage.IDENTIFIER: + return new DescribeMessage(connection); + case ExecuteMessage.IDENTIFIER: + return new ExecuteMessage(connection); + case CloseMessage.IDENTIFIER: + return new CloseMessage(connection); + case SyncMessage.IDENTIFIER: + return new SyncMessage(connection); + case TerminateMessage.IDENTIFIER: + return new TerminateMessage(connection); + case CopyDoneMessage.IDENTIFIER: + return new CopyDoneMessage(connection); + case CopyDataMessage.IDENTIFIER: + return new CopyDataMessage(connection); + case CopyFailMessage.IDENTIFIER: + return new CopyFailMessage(connection); + case FunctionCallMessage.IDENTIFIER: + return new FunctionCallMessage(connection); + case FlushMessage.IDENTIFIER: + return new FlushMessage(connection); + default: + throw new IllegalStateException("Unknown message"); + } } } From 5d23d9a6e58d7ce7472df2dde9adca126cf704f4 Mon Sep 17 00:00:00 2001 From: jsking Date: Mon, 21 Feb 2022 18:17:12 -0800 Subject: [PATCH 05/17] Update: refactor some of the code and fix tests. --- .../spanner/pgadapter/wireprotocol/ControlMessage.java | 8 ++------ .../spanner/pgadapter/wireprotocol/CopyDoneMessage.java | 1 + .../com/google/cloud/spanner/pgadapter/ProtocolTest.java | 4 ++++ 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/ControlMessage.java b/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/ControlMessage.java index b1eac1990..1da089f16 100644 --- a/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/ControlMessage.java +++ b/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/ControlMessage.java @@ -56,6 +56,8 @@ public static ControlMessage create(ConnectionHandler connection) throws Excepti return new CopyDoneMessage(connection); case CopyDataMessage.IDENTIFIER: return new CopyDataMessage(connection); + case CopyFailMessage.IDENTIFIER: + return new CopyFailMessage(connection); default: throw new IllegalStateException("Expected 0 or more Copy Data messages."); } @@ -77,12 +79,6 @@ public static ControlMessage create(ConnectionHandler connection) throws Excepti return new SyncMessage(connection); case TerminateMessage.IDENTIFIER: return new TerminateMessage(connection); - case CopyDoneMessage.IDENTIFIER: - return new CopyDoneMessage(connection); - case CopyDataMessage.IDENTIFIER: - return new CopyDataMessage(connection); - case CopyFailMessage.IDENTIFIER: - return new CopyFailMessage(connection); case FunctionCallMessage.IDENTIFIER: return new FunctionCallMessage(connection); case FlushMessage.IDENTIFIER: diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/CopyDoneMessage.java b/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/CopyDoneMessage.java index 7a67a5cec..c11128aae 100644 --- a/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/CopyDoneMessage.java +++ b/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/CopyDoneMessage.java @@ -15,6 +15,7 @@ package com.google.cloud.spanner.pgadapter.wireprotocol; import com.google.cloud.spanner.pgadapter.ConnectionHandler; +import com.google.cloud.spanner.pgadapter.ConnectionHandler.ConnectionStatus; import com.google.cloud.spanner.pgadapter.ConnectionHandler.QueryMode; import com.google.cloud.spanner.pgadapter.statements.CopyStatement; import com.google.cloud.spanner.pgadapter.utils.MutationWriter; diff --git a/src/test/java/com/google/cloud/spanner/pgadapter/ProtocolTest.java b/src/test/java/com/google/cloud/spanner/pgadapter/ProtocolTest.java index 41a286bce..607424ca1 100644 --- a/src/test/java/com/google/cloud/spanner/pgadapter/ProtocolTest.java +++ b/src/test/java/com/google/cloud/spanner/pgadapter/ProtocolTest.java @@ -23,6 +23,7 @@ import static org.mockito.Mockito.when; import com.google.cloud.spanner.jdbc.CloudSpannerJdbcConnection; +import com.google.cloud.spanner.pgadapter.ConnectionHandler.ConnectionStatus; import com.google.cloud.spanner.pgadapter.ConnectionHandler.QueryMode; import com.google.cloud.spanner.pgadapter.metadata.ConnectionMetadata; import com.google.cloud.spanner.pgadapter.metadata.DescribePortalMetadata; @@ -1196,6 +1197,7 @@ public void testCopyDataMessage() throws Exception { CopyStatement copyStatement = Mockito.mock(CopyStatement.class); Mockito.when(connectionHandler.getActiveStatement()).thenReturn(copyStatement); Mockito.when(connectionHandler.getConnectionMetadata()).thenReturn(connectionMetadata); + Mockito.when(connectionHandler.getStatus()).thenReturn(ConnectionStatus.COPY_IN); Mockito.when(connectionMetadata.getInputStream()).thenReturn(inputStream); Mockito.when(connectionMetadata.getOutputStream()).thenReturn(outputStream); @@ -1225,6 +1227,7 @@ public void testCopyDoneMessage() throws Exception { CopyStatement copyStatement = Mockito.mock(CopyStatement.class); Mockito.when(connectionHandler.getActiveStatement()).thenReturn(copyStatement); Mockito.when(connectionHandler.getConnectionMetadata()).thenReturn(connectionMetadata); + Mockito.when(connectionHandler.getStatus()).thenReturn(ConnectionStatus.COPY_IN); Mockito.when(connectionMetadata.getInputStream()).thenReturn(inputStream); Mockito.when(connectionMetadata.getOutputStream()).thenReturn(outputStream); @@ -1260,6 +1263,7 @@ public void testCopyFailMessage() throws Exception { CopyStatement copyStatement = Mockito.mock(CopyStatement.class); Mockito.when(connectionHandler.getActiveStatement()).thenReturn(copyStatement); Mockito.when(connectionHandler.getConnectionMetadata()).thenReturn(connectionMetadata); + Mockito.when(connectionHandler.getStatus()).thenReturn(ConnectionStatus.COPY_IN); Mockito.when(connectionMetadata.getInputStream()).thenReturn(inputStream); Mockito.when(connectionMetadata.getOutputStream()).thenReturn(outputStream); From 169f2bb9cfc7956b39ee5fe9cc1cff9505fa952d Mon Sep 17 00:00:00 2001 From: jsking Date: Tue, 22 Feb 2022 22:51:09 -0800 Subject: [PATCH 06/17] Update --- .../cloud/spanner/pgadapter/statements/CopyStatement.java | 2 +- .../java/com/google/cloud/spanner/pgadapter/ProtocolTest.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/statements/CopyStatement.java b/src/main/java/com/google/cloud/spanner/pgadapter/statements/CopyStatement.java index d40e685c5..939a74566 100644 --- a/src/main/java/com/google/cloud/spanner/pgadapter/statements/CopyStatement.java +++ b/src/main/java/com/google/cloud/spanner/pgadapter/statements/CopyStatement.java @@ -173,7 +173,7 @@ private void queryInformationSchema() throws SQLException { + COLUMN_NAME + ", " + SPANNER_TYPE - + " FROM information_schema.columns WHERE table_name = \"?\""); + + " FROM information_schema.columns WHERE table_name = ?"); statement.setString(1, getTableName()); ResultSet result = statement.executeQuery(); diff --git a/src/test/java/com/google/cloud/spanner/pgadapter/ProtocolTest.java b/src/test/java/com/google/cloud/spanner/pgadapter/ProtocolTest.java index 87943e2c8..a09ceb63a 100644 --- a/src/test/java/com/google/cloud/spanner/pgadapter/ProtocolTest.java +++ b/src/test/java/com/google/cloud/spanner/pgadapter/ProtocolTest.java @@ -1221,6 +1221,7 @@ public void testMultipleCopyDataMessages() throws Exception { Mockito.when(connection.prepareStatement(ArgumentMatchers.anyString())) .thenReturn(preparedStatement); Mockito.when(connectionHandler.getJdbcConnection()).thenReturn(connection); + Mockito.when(connectionHandler.getStatus()).thenReturn(ConnectionStatus.COPY_IN); Mockito.when(statement.getUpdateCount()).thenReturn(1); byte[] messageMetadata = {'d'}; From f3d3c6722fab2e734e8c69646822164e3c544b30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?= Date: Wed, 23 Feb 2022 15:18:30 +0100 Subject: [PATCH 07/17] fix: do not return Ready response for Copy messages Copy messages should not respond with a Ready response, only with its own specific Copy responses. Failure to do so can cause the calling client to think that the operation has already finished, which again can cause the entire copy operation to fail if the connection is closed directly after receiving the Ready response. Also the current Copy operation should not execute a rollback if something fails, as it is operating in auto-commit mode. Trying to rollback when in auto-commit mode will cause an error. --- .../pgadapter/utils/MutationWriter.java | 25 +++-- .../pgadapter/wireprotocol/QueryMessage.java | 3 + .../spanner/pgadapter/JdbcMockServerTest.java | 106 ++++++++++++++++++ 3 files changed, 124 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/utils/MutationWriter.java b/src/main/java/com/google/cloud/spanner/pgadapter/utils/MutationWriter.java index e817fa789..9531cb185 100644 --- a/src/main/java/com/google/cloud/spanner/pgadapter/utils/MutationWriter.java +++ b/src/main/java/com/google/cloud/spanner/pgadapter/utils/MutationWriter.java @@ -75,7 +75,7 @@ public int getRowCount() { public void addCopyData(ConnectionHandler connectionHandler, byte[] payload) throws Exception { this.payload.write(payload, 0, payload.length); if (!commitSizeIsWithinLimit()) { - rollback(connectionHandler); + handleError(connectionHandler); throw new SQLException( "Commit size: " + this.payload.size() + " has exceeded the limit: " + COMMIT_LIMIT); } @@ -87,7 +87,7 @@ public void buildMutationList(ConnectionHandler connectionHandler) throws Except for (CSVRecord record : records) { // Check that the number of columns in a record matches the number of columns in the table if (record.size() != this.tableColumns.keySet().size()) { - rollback(connectionHandler); + handleError(connectionHandler); throw new SQLException( "Invalid COPY data: Row length mismatched. Expected " + this.tableColumns.keySet().size() @@ -122,7 +122,7 @@ public void buildMutationList(ConnectionHandler connectionHandler) throws Except break; } } catch (NumberFormatException | DateTimeParseException e) { - rollback(connectionHandler); + handleError(connectionHandler); throw new SQLException( "Invalid input syntax for type " + columnType.toString() @@ -131,10 +131,10 @@ public void buildMutationList(ConnectionHandler connectionHandler) throws Except + recordValue + "\""); } catch (IllegalArgumentException e) { - rollback(connectionHandler); + handleError(connectionHandler); throw new SQLException("Invalid input syntax for column \"" + columnName + "\""); } catch (Exception e) { - rollback(connectionHandler); + handleError(connectionHandler); throw e; } } @@ -143,7 +143,7 @@ public void buildMutationList(ConnectionHandler connectionHandler) throws Except this.rowCount++; // Increment the number of COPY rows by one } if (!mutationCountIsWithinLimit()) { - rollback(connectionHandler); + handleError(connectionHandler); throw new SQLException( "Mutation count: " + mutationCount + " has exceeded the limit: " + MUTATION_LIMIT); } @@ -176,7 +176,14 @@ private List parsePayloadData(byte[] payload) throws IOException { } else { parser = CSVParser.parse(copyData, this.format); } - return parser.getRecords(); + // Skip the last record if that is the '\.' end of file indicator. + List records = parser.getRecords(); + if (!records.isEmpty() + && records.get(records.size() - 1).size() == 1 + && "\\.".equals(records.get(records.size() - 1).get(0))) { + return records.subList(0, records.size() - 1); + } + return records; } /** @@ -198,9 +205,7 @@ public int writeToSpanner(ConnectionHandler connectionHandler) throws SQLExcepti return this.rowCount; } - public void rollback(ConnectionHandler connectionHandler) throws Exception { - Connection connection = connectionHandler.getJdbcConnection(); - connection.rollback(); + public void handleError(ConnectionHandler connectionHandler) throws Exception { this.mutations = new ArrayList<>(); this.mutationCount = 0; writeCopyDataToErrorFile(); diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/QueryMessage.java b/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/QueryMessage.java index cf890958a..fc8fbb482 100644 --- a/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/QueryMessage.java +++ b/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/QueryMessage.java @@ -98,6 +98,9 @@ public void handleQuery() throws Exception { copyStatement.getFormatCode()) .send(); this.connection.setStatus(ConnectionStatus.COPY_IN); + + // Return early as we do not respond with CommandComplete after a COPY command. + return; } else if (this.statement.containsResultSet()) { new RowDescriptionResponse( this.outputStream, diff --git a/src/test/java/com/google/cloud/spanner/pgadapter/JdbcMockServerTest.java b/src/test/java/com/google/cloud/spanner/pgadapter/JdbcMockServerTest.java index 8f46b1542..e6d18518d 100644 --- a/src/test/java/com/google/cloud/spanner/pgadapter/JdbcMockServerTest.java +++ b/src/test/java/com/google/cloud/spanner/pgadapter/JdbcMockServerTest.java @@ -16,10 +16,25 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; +import com.google.cloud.spanner.MockSpannerServiceImpl.StatementResult; +import com.google.protobuf.ListValue; +import com.google.protobuf.Value; +import com.google.spanner.v1.CommitRequest; import com.google.spanner.v1.ExecuteBatchDmlRequest; import com.google.spanner.v1.ExecuteSqlRequest; +import com.google.spanner.v1.Mutation; +import com.google.spanner.v1.Mutation.OperationCase; +import com.google.spanner.v1.ResultSetMetadata; +import com.google.spanner.v1.StructType; +import com.google.spanner.v1.StructType.Field; +import com.google.spanner.v1.Type; +import com.google.spanner.v1.TypeCode; +import java.io.File; +import java.io.IOException; +import java.io.StringReader; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; @@ -29,6 +44,8 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +import org.postgresql.copy.CopyManager; +import org.postgresql.core.BaseConnection; @RunWith(JUnit4.class) public class JdbcMockServerTest extends AbstractMockServerTest { @@ -58,6 +75,95 @@ public void testQuery() throws SQLException { } } + @Test + public void testCopyIn() throws SQLException, IOException { + setupCopyInformationSchemaResults(); + + try (Connection connection = DriverManager.getConnection(createUrl())) { + CopyManager copyManager = new CopyManager(connection.unwrap(BaseConnection.class)); + copyManager.copyIn("COPY users FROM STDIN;", new StringReader("5\t5\t5\n6\t6\t6\n7\t7\t7\n")); + } + + List commitRequests = mockSpanner.getRequestsOfType(CommitRequest.class); + assertEquals(1, commitRequests.size()); + CommitRequest commitRequest = commitRequests.get(0); + assertEquals(1, commitRequest.getMutationsCount()); + + Mutation mutation = commitRequest.getMutations(0); + assertEquals(OperationCase.INSERT, mutation.getOperationCase()); + assertEquals(3, mutation.getInsert().getValuesCount()); + } + + @Test + public void testCopyInWithInvalidRow() throws SQLException { + setupCopyInformationSchemaResults(); + + try (Connection connection = DriverManager.getConnection(createUrl())) { + CopyManager copyManager = new CopyManager(connection.unwrap(BaseConnection.class)); + // This row does not contain all the necessary columns. + SQLException exception = + assertThrows( + SQLException.class, + () -> copyManager.copyIn("COPY users FROM STDIN;", new StringReader("5\n"))); + assertTrue( + exception + .getMessage() + .contains("Row length mismatched. Expected 3 columns, but only found 1")); + } finally { + assertTrue(new File("output.txt").delete()); + } + + List commitRequests = mockSpanner.getRequestsOfType(CommitRequest.class); + assertTrue(commitRequests.isEmpty()); + } + + private void setupCopyInformationSchemaResults() { + ResultSetMetadata metadata = + ResultSetMetadata.newBuilder() + .setRowType( + StructType.newBuilder() + .addFields( + Field.newBuilder() + .setName("column_name") + .setType(Type.newBuilder().setCode(TypeCode.STRING).build()) + .build()) + .addFields( + Field.newBuilder() + .setName("spanner_type") + .setType(Type.newBuilder().setCode(TypeCode.STRING).build()) + .build()) + .build()) + .build(); + com.google.spanner.v1.ResultSet resultSet = + com.google.spanner.v1.ResultSet.newBuilder() + .addRows( + ListValue.newBuilder() + .addValues(Value.newBuilder().setStringValue("id").build()) + .addValues(Value.newBuilder().setStringValue("INT64").build()) + .build()) + .addRows( + ListValue.newBuilder() + .addValues(Value.newBuilder().setStringValue("age").build()) + .addValues(Value.newBuilder().setStringValue("INT64").build()) + .build()) + .addRows( + ListValue.newBuilder() + .addValues(Value.newBuilder().setStringValue("name").build()) + .addValues(Value.newBuilder().setStringValue("STRING(MAX)").build()) + .build()) + .setMetadata(metadata) + .build(); + + mockSpanner.putStatementResult( + StatementResult.query( + com.google.cloud.spanner.Statement.newBuilder( + "SELECT column_name, spanner_type FROM information_schema.columns WHERE table_name = $1") + .bind("p1") + .to("users") + .build(), + resultSet)); + } + @Test public void testTwoDmlStatements() throws SQLException { try (Connection connection = DriverManager.getConnection(createUrl())) { From 4b1b5d106402aeddc8a4a7305cc879a23526c789 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?= Date: Wed, 23 Feb 2022 15:47:13 +0100 Subject: [PATCH 08/17] test: update test values to avoid duplicates --- .ci/e2e-expected/copy-from-stdin.txt | 9 ++++++++- .ci/evaluate-with-psql.sh | 12 ++++++------ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/.ci/e2e-expected/copy-from-stdin.txt b/.ci/e2e-expected/copy-from-stdin.txt index 25d9d2e6d..577177143 100644 --- a/.ci/e2e-expected/copy-from-stdin.txt +++ b/.ci/e2e-expected/copy-from-stdin.txt @@ -9,4 +9,11 @@ 8 | 8 | 8 9 | 9 | 9 10 | 10 | 10 -(9 rows) + 11 | 11 | 11 + 12 | 12 | 12 + 13 | 13 | 13 + 14 | 14 | 14 + 15 | 15 | 15 + 16 | 16 | 16 + 17 | 17 | 17 +(16 rows) diff --git a/.ci/evaluate-with-psql.sh b/.ci/evaluate-with-psql.sh index aa1832e49..18439ba9f 100644 --- a/.ci/evaluate-with-psql.sh +++ b/.ci/evaluate-with-psql.sh @@ -113,12 +113,12 @@ RETURN_CODE=$((${RETURN_CODE}||$?)) echo "------Test \"COPY FROM STDIN\"------" /usr/lib/postgresql/"${PSQL_VERSION}"/bin/psql -h localhost -p 4242 -d "${GOOGLE_CLOUD_DATABASE_WITH_VERSION}" -c "COPY users FROM STDIN;" < .ci/e2e-result/copy-from-stdin.txt From 774d3675e56720e7241968a080e9c8b0e330eddf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?= Date: Wed, 23 Feb 2022 15:54:30 +0100 Subject: [PATCH 09/17] fix: remove non-existing rows from expected file --- .ci/e2e-expected/copy-from-stdin.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.ci/e2e-expected/copy-from-stdin.txt b/.ci/e2e-expected/copy-from-stdin.txt index 577177143..e562778f7 100644 --- a/.ci/e2e-expected/copy-from-stdin.txt +++ b/.ci/e2e-expected/copy-from-stdin.txt @@ -3,8 +3,6 @@ 1 | 1 | John 2 | 20 | Joe 3 | 23 | Jack - 5 | 5 | 5 - 6 | 6 | 6 7 | 7 | 7 8 | 8 | 8 9 | 9 | 9 @@ -16,4 +14,4 @@ 15 | 15 | 15 16 | 16 | 16 17 | 17 | 17 -(16 rows) +(14 rows) From 26174ddeae8fc6e3323ac416ada7ea881de44147 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?= Date: Wed, 23 Feb 2022 16:02:06 +0100 Subject: [PATCH 10/17] fix: add missing COPY to response --- .ci/e2e-expected/copy-from-stdin.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/.ci/e2e-expected/copy-from-stdin.txt b/.ci/e2e-expected/copy-from-stdin.txt index e562778f7..d612a0082 100644 --- a/.ci/e2e-expected/copy-from-stdin.txt +++ b/.ci/e2e-expected/copy-from-stdin.txt @@ -15,3 +15,4 @@ 16 | 16 | 16 17 | 17 | 17 (14 rows) +COPY From d99c3c537d9f8fef7873c49079df0af5a829b5f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?= Date: Wed, 23 Feb 2022 16:18:27 +0100 Subject: [PATCH 11/17] fix: copy comes before the select --- .ci/e2e-expected/copy-from-stdin.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.ci/e2e-expected/copy-from-stdin.txt b/.ci/e2e-expected/copy-from-stdin.txt index d612a0082..0ca22ec4b 100644 --- a/.ci/e2e-expected/copy-from-stdin.txt +++ b/.ci/e2e-expected/copy-from-stdin.txt @@ -1,4 +1,5 @@ - id | age | name +COPY + id | age | name ----+-----+------ 1 | 1 | John 2 | 20 | Joe @@ -15,4 +16,3 @@ 16 | 16 | 16 17 | 17 | 17 (14 rows) -COPY From 2a7f121ccc242912ad42e8acdfb4ee2ddfc21403 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?= Date: Wed, 23 Feb 2022 16:24:46 +0100 Subject: [PATCH 12/17] fix: COPY is on stdin, the expected result has an extra empty line --- .ci/e2e-expected/copy-from-stdin.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/e2e-expected/copy-from-stdin.txt b/.ci/e2e-expected/copy-from-stdin.txt index 0ca22ec4b..2ce9e9910 100644 --- a/.ci/e2e-expected/copy-from-stdin.txt +++ b/.ci/e2e-expected/copy-from-stdin.txt @@ -1,4 +1,3 @@ -COPY id | age | name ----+-----+------ 1 | 1 | John @@ -16,3 +15,4 @@ COPY 16 | 16 | 16 17 | 17 | 17 (14 rows) + From 954e9f1cd5b21f077779ef4b13b8629a788812fd Mon Sep 17 00:00:00 2001 From: jsking Date: Wed, 23 Feb 2022 13:50:41 -0800 Subject: [PATCH 13/17] Adding /*GSQL*/ prefix to information schema query to get spanner types instead of PG types. --- .../cloud/spanner/pgadapter/statements/CopyStatement.java | 2 +- .../com/google/cloud/spanner/pgadapter/JdbcMockServerTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/statements/CopyStatement.java b/src/main/java/com/google/cloud/spanner/pgadapter/statements/CopyStatement.java index 939a74566..2f6499d6a 100644 --- a/src/main/java/com/google/cloud/spanner/pgadapter/statements/CopyStatement.java +++ b/src/main/java/com/google/cloud/spanner/pgadapter/statements/CopyStatement.java @@ -169,7 +169,7 @@ private void queryInformationSchema() throws SQLException { Map tableColumns = new LinkedHashMap<>(); PreparedStatement statement = this.connection.prepareStatement( - "SELECT " + "/*GSQL*/SELECT " + COLUMN_NAME + ", " + SPANNER_TYPE diff --git a/src/test/java/com/google/cloud/spanner/pgadapter/JdbcMockServerTest.java b/src/test/java/com/google/cloud/spanner/pgadapter/JdbcMockServerTest.java index e6d18518d..7bb1c7051 100644 --- a/src/test/java/com/google/cloud/spanner/pgadapter/JdbcMockServerTest.java +++ b/src/test/java/com/google/cloud/spanner/pgadapter/JdbcMockServerTest.java @@ -157,7 +157,7 @@ private void setupCopyInformationSchemaResults() { mockSpanner.putStatementResult( StatementResult.query( com.google.cloud.spanner.Statement.newBuilder( - "SELECT column_name, spanner_type FROM information_schema.columns WHERE table_name = $1") + "/*GSQL*/SELECT column_name, spanner_type FROM information_schema.columns WHERE table_name = @p1") .bind("p1") .to("users") .build(), From 1e905ef0c44beebec4b589954e65ad62f9e6698a Mon Sep 17 00:00:00 2001 From: jsking Date: Thu, 24 Feb 2022 12:26:52 -0800 Subject: [PATCH 14/17] Adding comment about /*GSQL*/ information_schema query. --- .../cloud/spanner/pgadapter/statements/CopyStatement.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/statements/CopyStatement.java b/src/main/java/com/google/cloud/spanner/pgadapter/statements/CopyStatement.java index 2f6499d6a..1db150f7f 100644 --- a/src/main/java/com/google/cloud/spanner/pgadapter/statements/CopyStatement.java +++ b/src/main/java/com/google/cloud/spanner/pgadapter/statements/CopyStatement.java @@ -167,6 +167,9 @@ private static TypeCode parseSpannerDataType(String columnType) { private void queryInformationSchema() throws SQLException { Map tableColumns = new LinkedHashMap<>(); + // The mutation API requires GoogleSQL type names instead of PostgreSQL type names for the table + // column types. Issue the information_schema table query as a GoogleSQL query so that it will + // return GoogleSQL type names. PreparedStatement statement = this.connection.prepareStatement( "/*GSQL*/SELECT " From 90c3a66498faf7ef5eff0ecac4788fd8eb17c118 Mon Sep 17 00:00:00 2001 From: jsking Date: Thu, 24 Feb 2022 12:54:36 -0800 Subject: [PATCH 15/17] Update expected test results. --- .ci/e2e-expected/backslash-dn.txt | 3 ++- .ci/e2e-expected/backslash-dt.txt | 20 +++++++++++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/.ci/e2e-expected/backslash-dn.txt b/.ci/e2e-expected/backslash-dn.txt index b7baca6ce..a7873ac39 100644 --- a/.ci/e2e-expected/backslash-dn.txt +++ b/.ci/e2e-expected/backslash-dn.txt @@ -3,6 +3,7 @@ +++++++ testdb_e2e_psql_v?? | public | | | | | | testdb_e2e_psql_v?? | information_schema | | | | | | + testdb_e2e_psql_v?? | pg_catalog | | | | | | testdb_e2e_psql_v?? | spanner_sys | | | | | | -(3 rows) +(4 rows) diff --git a/.ci/e2e-expected/backslash-dt.txt b/.ci/e2e-expected/backslash-dt.txt index dc550365c..34bad72ad 100644 --- a/.ci/e2e-expected/backslash-dt.txt +++ b/.ci/e2e-expected/backslash-dt.txt @@ -19,6 +19,24 @@ testdb_e2e_psql_v?? | information_schema | table_constraints | VIEW | | | | | | | | | | | | | testdb_e2e_psql_v?? | information_schema | tables | VIEW | | | | | | | | | | | | | testdb_e2e_psql_v?? | information_schema | views | VIEW | | | | | | | | | | | | | + testdb_e2e_psql_v?? | pg_catalog | pg_am | VIEW | | | | | | | | | | | | | + testdb_e2e_psql_v?? | pg_catalog | pg_attrdef | VIEW | | | | | | | | | | | | | + testdb_e2e_psql_v?? | pg_catalog | pg_attribute | VIEW | | | | | | | | | | | | | + testdb_e2e_psql_v?? | pg_catalog | pg_class | VIEW | | | | | | | | | | | | | + testdb_e2e_psql_v?? | pg_catalog | pg_collation | VIEW | | | | | | | | | | | | | + testdb_e2e_psql_v?? | pg_catalog | pg_database | VIEW | | | | | | | | | | | | | + testdb_e2e_psql_v?? | pg_catalog | pg_index | VIEW | | | | | | | | | | | | | + testdb_e2e_psql_v?? | pg_catalog | pg_indexes | VIEW | | | | | | | | | | | | | + testdb_e2e_psql_v?? | pg_catalog | pg_inherits | VIEW | | | | | | | | | | | | | + testdb_e2e_psql_v?? | pg_catalog | pg_namespace | VIEW | | | | | | | | | | | | | + testdb_e2e_psql_v?? | pg_catalog | pg_policy | VIEW | | | | | | | | | | | | | + testdb_e2e_psql_v?? | pg_catalog | pg_publication | VIEW | | | | | | | | | | | | | + testdb_e2e_psql_v?? | pg_catalog | pg_publication_rel | VIEW | | | | | | | | | | | | | + testdb_e2e_psql_v?? | pg_catalog | pg_roles | VIEW | | | | | | | | | | | | | + testdb_e2e_psql_v?? | pg_catalog | pg_sequence | VIEW | | | | | | | | | | | | | + testdb_e2e_psql_v?? | pg_catalog | pg_statistic_ext | VIEW | | | | | | | | | | | | | + testdb_e2e_psql_v?? | pg_catalog | pg_tables | VIEW | | | | | | | | | | | | | + testdb_e2e_psql_v?? | pg_catalog | pg_type | VIEW | | | | | | | | | | | | | testdb_e2e_psql_v?? | spanner_sys | active_queries_summary | VIEW | | | | | | | | | | | | | testdb_e2e_psql_v?? | spanner_sys | lock_stats_top_10minute | VIEW | | | | | | | | | | | | | testdb_e2e_psql_v?? | spanner_sys | lock_stats_top_hour | VIEW | | | | | | | | | | | | | @@ -47,5 +65,5 @@ testdb_e2e_psql_v?? | spanner_sys | txn_stats_total_10minute | VIEW | | | | | | | | | | | | | testdb_e2e_psql_v?? | spanner_sys | txn_stats_total_hour | VIEW | | | | | | | | | | | | | testdb_e2e_psql_v?? | spanner_sys | txn_stats_total_minute | VIEW | | | | | | | | | | | | | -(46 rows) +(64 rows) From f08c6649aea28168dc4802933fc937db696f16c0 Mon Sep 17 00:00:00 2001 From: jsking Date: Thu, 24 Feb 2022 13:25:56 -0800 Subject: [PATCH 16/17] Update --- .ci/e2e-expected/backslash-dn.txt | 1 - .ci/e2e-expected/backslash-dt.txt | 17 ----------------- 2 files changed, 18 deletions(-) diff --git a/.ci/e2e-expected/backslash-dn.txt b/.ci/e2e-expected/backslash-dn.txt index a7873ac39..3cf5788da 100644 --- a/.ci/e2e-expected/backslash-dn.txt +++ b/.ci/e2e-expected/backslash-dn.txt @@ -3,7 +3,6 @@ +++++++ testdb_e2e_psql_v?? | public | | | | | | testdb_e2e_psql_v?? | information_schema | | | | | | - testdb_e2e_psql_v?? | pg_catalog | | | | | | testdb_e2e_psql_v?? | spanner_sys | | | | | | (4 rows) diff --git a/.ci/e2e-expected/backslash-dt.txt b/.ci/e2e-expected/backslash-dt.txt index 34bad72ad..1e5acfa3e 100644 --- a/.ci/e2e-expected/backslash-dt.txt +++ b/.ci/e2e-expected/backslash-dt.txt @@ -2,23 +2,6 @@ table_catalog | table_schema | table_name | table_type | self_referencing_column_name | reference_generation | user_defined_type_catalog | user_defined_type_schema | user_defined_type_name | is_insertable_into | is_typed | commit_action | parent_table_name | on_delete_action | spanner_state | interleave_type | row_deletion_policy_expression ++++++++++++++++ testdb_e2e_psql_v?? | public | users | BASE TABLE | | | | | | | | | | | COMMITTED | | - testdb_e2e_psql_v?? | information_schema | check_constraints | VIEW | | | | | | | | | | | | | - testdb_e2e_psql_v?? | information_schema | column_column_usage | VIEW | | | | | | | | | | | | | - testdb_e2e_psql_v?? | information_schema | column_options | VIEW | | | | | | | | | | | | | - testdb_e2e_psql_v?? | information_schema | columns | VIEW | | | | | | | | | | | | | - testdb_e2e_psql_v?? | information_schema | constraint_column_usage | VIEW | | | | | | | | | | | | | - testdb_e2e_psql_v?? | information_schema | constraint_table_usage | VIEW | | | | | | | | | | | | | - testdb_e2e_psql_v?? | information_schema | database_options | VIEW | | | | | | | | | | | | | - testdb_e2e_psql_v?? | information_schema | index_columns | VIEW | | | | | | | | | | | | | - testdb_e2e_psql_v?? | information_schema | indexes | VIEW | | | | | | | | | | | | | - testdb_e2e_psql_v?? | information_schema | information_schema_catalog_name | VIEW | | | | | | | | | | | | | - testdb_e2e_psql_v?? | information_schema | key_column_usage | VIEW | | | | | | | | | | | | | - testdb_e2e_psql_v?? | information_schema | referential_constraints | VIEW | | | | | | | | | | | | | - testdb_e2e_psql_v?? | information_schema | schemata | VIEW | | | | | | | | | | | | | - testdb_e2e_psql_v?? | information_schema | spanner_statistics | VIEW | | | | | | | | | | | | | - testdb_e2e_psql_v?? | information_schema | table_constraints | VIEW | | | | | | | | | | | | | - testdb_e2e_psql_v?? | information_schema | tables | VIEW | | | | | | | | | | | | | - testdb_e2e_psql_v?? | information_schema | views | VIEW | | | | | | | | | | | | | testdb_e2e_psql_v?? | pg_catalog | pg_am | VIEW | | | | | | | | | | | | | testdb_e2e_psql_v?? | pg_catalog | pg_attrdef | VIEW | | | | | | | | | | | | | testdb_e2e_psql_v?? | pg_catalog | pg_attribute | VIEW | | | | | | | | | | | | | From 749265f271e061575565847e8126583b6a4c2680 Mon Sep 17 00:00:00 2001 From: jsking Date: Thu, 24 Feb 2022 13:36:23 -0800 Subject: [PATCH 17/17] Update --- .ci/e2e-expected/backslash-dn.txt | 1 + .ci/e2e-expected/backslash-dt.txt | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/.ci/e2e-expected/backslash-dn.txt b/.ci/e2e-expected/backslash-dn.txt index 3cf5788da..9dc3f9902 100644 --- a/.ci/e2e-expected/backslash-dn.txt +++ b/.ci/e2e-expected/backslash-dn.txt @@ -2,6 +2,7 @@ catalog_name | schema_name | schema_owner | default_character_set_catalog | default_character_set_schema | default_character_set_name | sql_path | effective_timestamp +++++++ testdb_e2e_psql_v?? | public | | | | | | + testdb_e2e_psql_v?? | pg_catalog | | | | | | testdb_e2e_psql_v?? | information_schema | | | | | | testdb_e2e_psql_v?? | spanner_sys | | | | | | (4 rows) diff --git a/.ci/e2e-expected/backslash-dt.txt b/.ci/e2e-expected/backslash-dt.txt index 1e5acfa3e..c0be7f635 100644 --- a/.ci/e2e-expected/backslash-dt.txt +++ b/.ci/e2e-expected/backslash-dt.txt @@ -19,7 +19,24 @@ testdb_e2e_psql_v?? | pg_catalog | pg_sequence | VIEW | | | | | | | | | | | | | testdb_e2e_psql_v?? | pg_catalog | pg_statistic_ext | VIEW | | | | | | | | | | | | | testdb_e2e_psql_v?? | pg_catalog | pg_tables | VIEW | | | | | | | | | | | | | - testdb_e2e_psql_v?? | pg_catalog | pg_type | VIEW | | | | | | | | | | | | | + testdb_e2e_psql_v?? | pg_catalog | pg_type | VIEW | | | | | | | | | | | | | + testdb_e2e_psql_v?? | information_schema | check_constraints | VIEW | | | | | | | | | | | | | + testdb_e2e_psql_v?? | information_schema | column_column_usage | VIEW | | | | | | | | | | | | | + testdb_e2e_psql_v?? | information_schema | column_options | VIEW | | | | | | | | | | | | | + testdb_e2e_psql_v?? | information_schema | columns | VIEW | | | | | | | | | | | | | + testdb_e2e_psql_v?? | information_schema | constraint_column_usage | VIEW | | | | | | | | | | | | | + testdb_e2e_psql_v?? | information_schema | constraint_table_usage | VIEW | | | | | | | | | | | | | + testdb_e2e_psql_v?? | information_schema | database_options | VIEW | | | | | | | | | | | | | + testdb_e2e_psql_v?? | information_schema | index_columns | VIEW | | | | | | | | | | | | | + testdb_e2e_psql_v?? | information_schema | indexes | VIEW | | | | | | | | | | | | | + testdb_e2e_psql_v?? | information_schema | information_schema_catalog_name | VIEW | | | | | | | | | | | | | + testdb_e2e_psql_v?? | information_schema | key_column_usage | VIEW | | | | | | | | | | | | | + testdb_e2e_psql_v?? | information_schema | referential_constraints | VIEW | | | | | | | | | | | | | + testdb_e2e_psql_v?? | information_schema | schemata | VIEW | | | | | | | | | | | | | + testdb_e2e_psql_v?? | information_schema | spanner_statistics | VIEW | | | | | | | | | | | | | + testdb_e2e_psql_v?? | information_schema | table_constraints | VIEW | | | | | | | | | | | | | + testdb_e2e_psql_v?? | information_schema | tables | VIEW | | | | | | | | | | | | | + testdb_e2e_psql_v?? | information_schema | views | VIEW | | | | | | | | | | | | | testdb_e2e_psql_v?? | spanner_sys | active_queries_summary | VIEW | | | | | | | | | | | | | testdb_e2e_psql_v?? | spanner_sys | lock_stats_top_10minute | VIEW | | | | | | | | | | | | | testdb_e2e_psql_v?? | spanner_sys | lock_stats_top_hour | VIEW | | | | | | | | | | | | |