Repository: GoogleCloudPlatform/pgadapter
Pull request #2221 (2024-08-23 12:58:43)

deps: update dependency @types/node to v22
This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [@types/node](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/node) ([source](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node)) | [`^20.12.4` -> `^22.0.0`](https://renovatebot.com/diffs/npm/@types%2fnode/20.16.5/22.5.5) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@types%2fnode/22.5.5?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@types%2fnode/22.5.5?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@types%2fnode/20.16.5/22.5.5?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@types%2fnode/20.16.5/22.5.5?slim=true)](https://docs.renovatebot.com/merge-confidence/) |
| [@types/node](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/node) ([source](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node)) | [`^20.1.4` -> `^22.0.0`](https://renovatebot.com/diffs/npm/@types%2fnode/20.16.5/22.5.5) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@types%2fnode/22.5.5?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@types%2fnode/22.5.5?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@types%2fnode/20.16.5/22.5.5?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@types%2fnode/20.16.5/22.5.5?slim=true)](https://docs.renovatebot.com/merge-confidence/) |

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about these updates again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/GoogleCloudPlatform/pgadapter).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOC4yNi4xIiwidXBkYXRlZEluVmVyIjoiMzguODAuMCIsInRhcmdldEJyYW5jaCI6InBvc3RncmVzcWwtZGlhbGVjdCIsImxhYmVscyI6W119-->



Patch:
diff --git a/benchmarks/latency-comparison/nodejs/package.json b/benchmarks/latency-comparison/nodejs/package.json
index c0ad67a2..dd7f16b8 100644
--- a/benchmarks/latency-comparison/nodejs/package.json
+++ b/benchmarks/latency-comparison/nodejs/package.json
@@ -4,7 +4,7 @@
   "description": "Node.js Latency Comparison for PGAdapter and the Spanner client library",
   "type": "commonjs",
   "devDependencies": {
-    "@types/node": "^20.1.4",
+    "@types/node": "^22.0.0",
     "@types/yargs": "^17.0.32",
     "typescript": "^5.4.5"
   },
diff --git a/samples/golang/gorm/go.mod b/samples/golang/gorm/go.mod
index 2d8483bf..95e5bb93 100644
--- a/samples/golang/gorm/go.mod
+++ b/samples/golang/gorm/go.mod
@@ -3,7 +3,7 @@ module gorm
 go 1.21
 
 require (
-	github.com/GoogleCloudPlatform/pgadapter/wrappers/golang v0.0.0-20240912183532-c756559e4665
+	github.com/GoogleCloudPlatform/pgadapter/wrappers/golang v0.0.0-20240919150510-c310ce84d0c9
 	github.com/google/uuid v1.6.0
 	github.com/jackc/pgtype v1.14.3
 	github.com/shopspring/decimal v1.4.0
diff --git a/samples/golang/gorm/go.sum b/samples/golang/gorm/go.sum
index 4845e97e..3b83b6f0 100644
--- a/samples/golang/gorm/go.sum
+++ b/samples/golang/gorm/go.sum
@@ -11,6 +11,8 @@ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg6
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/GoogleCloudPlatform/pgadapter/wrappers/golang v0.0.0-20240912183532-c756559e4665 h1:y2O+wODDmfREw15/LE1jkIziNJLYOVq3cllbu85H6kg=
 github.com/GoogleCloudPlatform/pgadapter/wrappers/golang v0.0.0-20240912183532-c756559e4665/go.mod h1:o0VdiWILEdFOmmWaLjI/CR1cojsHX748rEmqb++PfOU=
+github.com/GoogleCloudPlatform/pgadapter/wrappers/golang v0.0.0-20240919150510-c310ce84d0c9 h1:3JCzAQ/YdZ/wrGq3wgGQq24uG/IHfnlq2TPpGQqWiTE=
+github.com/GoogleCloudPlatform/pgadapter/wrappers/golang v0.0.0-20240919150510-c310ce84d0c9/go.mod h1:o0VdiWILEdFOmmWaLjI/CR1cojsHX748rEmqb++PfOU=
 github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
 github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
 github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
diff --git a/samples/golang/migrate/go.mod b/samples/golang/migrate/go.mod
index d4bf6a30..62da8620 100644
--- a/samples/golang/migrate/go.mod
+++ b/samples/golang/migrate/go.mod
@@ -3,7 +3,7 @@ module migrate
 go 1.22.3
 
 require (
-	github.com/GoogleCloudPlatform/pgadapter/wrappers/golang v0.0.0-20240918132525-a4dc9268921f
+	github.com/GoogleCloudPlatform/pgadapter/wrappers/golang v0.0.0-20240919150510-c310ce84d0c9
 	github.com/golang-migrate/migrate/v4 v4.18.1
 )
 
diff --git a/samples/golang/migrate/go.sum b/samples/golang/migrate/go.sum
index f436c727..17b0c100 100644
--- a/samples/golang/migrate/go.sum
+++ b/samples/golang/migrate/go.sum
@@ -8,6 +8,8 @@ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25
 github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
 github.com/GoogleCloudPlatform/pgadapter/wrappers/golang v0.0.0-20240918132525-a4dc9268921f h1:LAdtwZoHYcWnCqzUKdDkNG7nngy8il3i9eE/fW3oQq0=
 github.com/GoogleCloudPlatform/pgadapter/wrappers/golang v0.0.0-20240918132525-a4dc9268921f/go.mod h1:o0VdiWILEdFOmmWaLjI/CR1cojsHX748rEmqb++PfOU=
+github.com/GoogleCloudPlatform/pgadapter/wrappers/golang v0.0.0-20240919150510-c310ce84d0c9 h1:3JCzAQ/YdZ/wrGq3wgGQq24uG/IHfnlq2TPpGQqWiTE=
+github.com/GoogleCloudPlatform/pgadapter/wrappers/golang v0.0.0-20240919150510-c310ce84d0c9/go.mod h1:o0VdiWILEdFOmmWaLjI/CR1cojsHX748rEmqb++PfOU=
 github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
 github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
 github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
diff --git a/samples/golang/pgx/go.mod b/samples/golang/pgx/go.mod
index 3a61652b..d5f2619e 100644
--- a/samples/golang/pgx/go.mod
+++ b/samples/golang/pgx/go.mod
@@ -3,7 +3,7 @@ module pgadapter-pgx-sample
 go 1.21
 
 require (
-	github.com/GoogleCloudPlatform/pgadapter/wrappers/golang v0.0.0-20240912183532-c756559e4665
+	github.com/GoogleCloudPlatform/pgadapter/wrappers/golang v0.0.0-20240919150510-c310ce84d0c9
 	github.com/jackc/pgx/v5 v5.7.1
 )
 
diff --git a/samples/golang/pgx/go.sum b/samples/golang/pgx/go.sum
index 97923128..fa51af43 100644
--- a/samples/golang/pgx/go.sum
+++ b/samples/golang/pgx/go.sum
@@ -8,6 +8,8 @@ github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOEl
 github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
 github.com/GoogleCloudPlatform/pgadapter/wrappers/golang v0.0.0-20240912183532-c756559e4665 h1:y2O+wODDmfREw15/LE1jkIziNJLYOVq3cllbu85H6kg=
 github.com/GoogleCloudPlatform/pgadapter/wrappers/golang v0.0.0-20240912183532-c756559e4665/go.mod h1:o0VdiWILEdFOmmWaLjI/CR1cojsHX748rEmqb++PfOU=
+github.com/GoogleCloudPlatform/pgadapter/wrappers/golang v0.0.0-20240919150510-c310ce84d0c9 h1:3JCzAQ/YdZ/wrGq3wgGQq24uG/IHfnlq2TPpGQqWiTE=
+github.com/GoogleCloudPlatform/pgadapter/wrappers/golang v0.0.0-20240919150510-c310ce84d0c9/go.mod h1:o0VdiWILEdFOmmWaLjI/CR1cojsHX748rEmqb++PfOU=
 github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
 github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
 github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
diff --git a/samples/nodejs/knex/package.json b/samples/nodejs/knex/package.json
index 21bccd59..4ecdde96 100644
--- a/samples/nodejs/knex/package.json
+++ b/samples/nodejs/knex/package.json
@@ -4,7 +4,7 @@
   "description": "Knex Query Builder Sample",
   "type": "commonjs",
   "devDependencies": {
-    "@types/node": "^20.1.4",
+    "@types/node": "^22.0.0",
     "ts-node": "10.9.2",
     "typescript": "5.6.2"
   },
diff --git a/samples/python/connectorx/requirements.txt b/samples/python/connectorx/requirements.txt
index d3bdd5d5..d5a705a5 100644
--- a/samples/python/connectorx/requirements.txt
+++ b/samples/python/connectorx/requirements.txt
@@ -1,5 +1,5 @@
 connectorx==0.3.3
-pandas==2.2.2
+pandas==2.2.3
 testcontainers~=4.8.0
 requests==2.32.3
 google~=3.0.0
diff --git a/samples/snippets/nodejs-snippets/package.json b/samples/snippets/nodejs-snippets/package.json
index e1396c8c..5b45293d 100644
--- a/samples/snippets/nodejs-snippets/package.json
+++ b/samples/snippets/nodejs-snippets/package.json
@@ -5,7 +5,7 @@
   "type": "commonjs",
   "devDependencies": {
     "@types/jest": "^29.5.12",
-    "@types/node": "^20.12.4",
+    "@types/node": "^22.0.0",
     "@types/pg": "^8.11.4",
     "@types/yargs": "^17.0.32",
     "jest": "^29.7.0",
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/session/SessionState.java b/src/main/java/com/google/cloud/spanner/pgadapter/session/SessionState.java
index e59c3427..692d643e 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/session/SessionState.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/session/SessionState.java
@@ -525,6 +525,26 @@ public class SessionState {
     return getIntegerSetting("spanner", "string_conversion_buffer_size", 0);
   }
 
+  /**
+   * Returns the update count that PGAdapter should return for DML statements that are executed
+   * during a DML batch. The default is 0.
+   *
+   * <p>Example:
+   *
+   * <pre>{@code
+   * begin;
+   * set local spanner.dml_batch_update_count=1;
+   * start batch dml;
+   * insert into my_table (id, value) values (1, 'one'); -- This returns update count 1
+   * insert into my_table (id, value) values (2, 'two'); -- This returns update count 1
+   * run batch; -- This actually executes the DML statements.
+   * commit;
+   * }</pre>
+   */
+  public int getDmlBatchUpdateCount() {
+    return getIntegerSetting("spanner", "dml_batch_update_count", 0);
+  }
+
   private ZoneId cachedZoneId;
 
   /** Returns the {@link ZoneId} of the current timezone for this session. */


Test patch:
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/statements/IntermediateStatement.java b/src/main/java/com/google/cloud/spanner/pgadapter/statements/IntermediateStatement.java
index ee438e6b..50d7baff 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/statements/IntermediateStatement.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/statements/IntermediateStatement.java
@@ -175,6 +175,15 @@ public class IntermediateStatement {
         return -1L;
       case UPDATE:
         long res = this.statementResult.getUpdateCount();
+        // The Connection API returns update count -1 if DML statements that are executed during
+        // DML batches. PostgreSQL-drivers expect this update count to be >= 0.
+        if (res == -1L) {
+          // Sometimes the application that is executing DML statements in a batch want the driver
+          // to return a specific update count. E.g. Hibernate expects all insert statements to
+          // return an update count of 1. This can be achieved by setting the session variable
+          // spanner.dml_batch_update_count to 1.
+          return getDmlBatchUpdateCount();
+        }
         return Math.max(res, 0L);
       case CLIENT_SIDE:
       case DDL:
@@ -184,6 +193,15 @@ public class IntermediateStatement {
     }
   }
 
+  private long getDmlBatchUpdateCount() {
+    // This value comes from the session variable 'spanner.dml_batch_update_count'.
+    return getConnectionHandler()
+        .getExtendedQueryProtocolHandler()
+        .getBackendConnection()
+        .getSessionState()
+        .getDmlBatchUpdateCount();
+  }
+
   /**
    * @return True if at some point in execution an exception was thrown. Fails if execution has not
    *     yet finished.
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 f0e201f2..8d5bb96b 100644
--- a/src/test/java/com/google/cloud/spanner/pgadapter/JdbcMockServerTest.java
+++ b/src/test/java/com/google/cloud/spanner/pgadapter/JdbcMockServerTest.java
@@ -5551,6 +5551,31 @@ public class JdbcMockServerTest extends AbstractMockServerTest {
     assertEquals(TimeUnit.MILLISECONDS.toNanos(20), request.getMaxCommitDelay().getNanos());
   }
 
+  @Test
+  public void testDmlBatchUpdateCount() throws SQLException {
+    String sql = "insert into foo (id) values (1)";
+    mockSpanner.putStatementResult(StatementResult.update(Statement.of(sql), 1L));
+
+    try (Connection connection = DriverManager.getConnection(createUrl());
+        java.sql.Statement statement = connection.createStatement()) {
+      connection.setAutoCommit(false);
+
+      for (Integer updateCount : new Integer[] {null, 1, 2, 10}) {
+        if (updateCount != null) {
+          // 'set local' means that the effect of the statement will be undone by the end of the
+          // transaction.
+          statement.execute("set local spanner.dml_batch_update_count=" + updateCount);
+        }
+        statement.execute("start batch dml");
+        assertEquals(updateCount == null ? 0 : updateCount, statement.executeUpdate(sql));
+        assertEquals(updateCount == null ? 0 : updateCount, statement.executeUpdate(sql));
+        statement.execute("run batch");
+        connection.commit();
+      }
+    }
+    assertEquals(4, mockSpanner.countRequestsOfType(ExecuteBatchDmlRequest.class));
+  }
+
   @Ignore("Only used for manual performance testing")
   @Test
   public void testBasePerformance() throws SQLException {