Repository: GoogleCloudPlatform/pgadapter (java)

Issue:
copyToArray in PHP PDO fails due to COPY TO STDIN
The PHP PDO driver contains a typo for COPY commands. It executes `COPY table_name TO STDIN` instead of `COPY table_name TO STDOUT`. This typo is ignored by PostgreSQL, and should also be ignored by PGAdapter.

Pull request:
chore: ignore stdin/stdout typo in COPY commands
PostgreSQL accepts both stdin and stdout for COPY TO and COPY FROM. The PHP PDO driver actually contains this typo, and therefore failed when used against PGAdapter.

Fixes #1975

Patch:
diff --git a/samples/python/sqlalchemy2-sample/requirements.txt b/samples/python/sqlalchemy2-sample/requirements.txt
index 3d656c3a..91ee31d8 100644
--- a/samples/python/sqlalchemy2-sample/requirements.txt
+++ b/samples/python/sqlalchemy2-sample/requirements.txt
@@ -1,6 +1,6 @@
 psycopg[binary]==3.1.18
 pytz==2024.1
-sqlalchemy==2.0.27
+sqlalchemy==2.0.31
 google~=3.0.0
 google.auth~=2.30.0
 testcontainers~=4.5.0
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 b6ded551..453e994f 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
@@ -617,13 +617,19 @@ public class CopyStatement extends IntermediatePortalStatement {
     }
     builder.direction = Direction.valueOf(parser.readKeyword().toUpperCase());
     if (builder.direction == Direction.FROM) {
-      if (!parser.eatKeyword("stdin")) {
+      // Silently ignore typo 'copy from stdout'.
+      // See
+      // https://github.com/postgres/postgres/blob/03ec203164119f11f0eab4c83c97a8527e2b108d/src/backend/parser/gram.y#L3463
+      if (!parser.eatKeyword("stdin") && !parser.eatKeyword("stdout")) {
         throw PGExceptionFactory.newPGException(
             "missing 'STDIN' keyword. PGAdapter only supports COPY ... FROM STDIN: " + sql,
             SQLState.SyntaxError);
       }
     } else {
-      if (!parser.eatKeyword("stdout")) {
+      // Silently ignore typo 'copy to stdin'.
+      // See
+      // https://github.com/postgres/postgres/blob/03ec203164119f11f0eab4c83c97a8527e2b108d/src/backend/parser/gram.y#L3463
+      if (!parser.eatKeyword("stdout") && !parser.eatKeyword("stdin")) {
         throw PGExceptionFactory.newPGException(
             "missing 'STDOUT' keyword. PGAdapter only supports COPY ... TO STDOUT: " + sql,
             SQLState.SyntaxError);


Test patch:
diff --git a/src/test/java/com/google/cloud/spanner/pgadapter/statements/CopyStatementTest.java b/src/test/java/com/google/cloud/spanner/pgadapter/statements/CopyStatementTest.java
index 062e84b7..01d77bbf 100644
--- a/src/test/java/com/google/cloud/spanner/pgadapter/statements/CopyStatementTest.java
+++ b/src/test/java/com/google/cloud/spanner/pgadapter/statements/CopyStatementTest.java
@@ -51,6 +51,14 @@ public class CopyStatementTest {
   public void testParseDirection() {
     assertEquals(Direction.FROM, parse("copy my_table from stdin").direction);
     assertEquals(Direction.TO, parse("copy my_table to stdout").direction);
+
+    // PostgreSQL accepts this kind of typo.
+    // See
+    // https://github.com/postgres/postgres/blob/03ec203164119f11f0eab4c83c97a8527e2b108d/src/backend/parser/gram.y#L3463
+    assertEquals(Direction.FROM, parse("copy my_table from stdout").direction);
+    assertEquals(Direction.TO, parse("copy my_table to stdin").direction);
+
+    // COPY BOTH is not supported.
     assertThrows(PGException.class, () -> parse("copy my_table both stdin"));
   }