From eea6c9e8901e29ff998d7996c819f3aa793ce2df Mon Sep 17 00:00:00 2001
From: "release-please[bot]"
<55107282+release-please[bot]@users.noreply.github.com>
Date: Sun, 13 Aug 2023 15:08:13 +0000
Subject: [PATCH 1/5] chore(main): release 2.11.8-SNAPSHOT (#1314)
:robot: I have created a release *beep* *boop*
---
### Updating meta-information for bleeding-edge SNAPSHOT release.
---
This PR was generated with [Release Please](https://togithub.com/googleapis/release-please). See [documentation](https://togithub.com/googleapis/release-please#release-please).
---
pom.xml | 2 +-
samples/snapshot/pom.xml | 2 +-
versions.txt | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/pom.xml b/pom.xml
index 4847ce9d5..3378a232f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
google-cloud-spanner-jdbc
- 2.11.7
+ 2.11.8-SNAPSHOT
jar
Google Cloud Spanner JDBC
https://github.com/googleapis/java-spanner-jdbc
diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml
index 74c89c6c2..99c50943d 100644
--- a/samples/snapshot/pom.xml
+++ b/samples/snapshot/pom.xml
@@ -28,7 +28,7 @@
com.google.cloud
google-cloud-spanner-jdbc
- 2.11.7
+ 2.11.8-SNAPSHOT
diff --git a/versions.txt b/versions.txt
index 874269315..5459f1d3b 100644
--- a/versions.txt
+++ b/versions.txt
@@ -1,4 +1,4 @@
# Format:
# module:released-version:current-version
-google-cloud-spanner-jdbc:2.11.7:2.11.7
+google-cloud-spanner-jdbc:2.11.7:2.11.8-SNAPSHOT
From aaf89de2b569cf5f86c456c18fcc79643cc6c94b Mon Sep 17 00:00:00 2001
From: Mend Renovate
Date: Sun, 13 Aug 2023 18:32:12 +0200
Subject: [PATCH 2/5] chore(deps): update dependency
com.google.cloud:google-cloud-spanner-jdbc to v2.11.7 (#1315)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
[](https://renovatebot.com)
This PR contains the following updates:
| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [com.google.cloud:google-cloud-spanner-jdbc](https://togithub.com/googleapis/java-spanner-jdbc) | `2.11.6` -> `2.11.7` | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) |
---
### Release Notes
googleapis/java-spanner-jdbc (com.google.cloud:google-cloud-spanner-jdbc)
### [`v2.11.7`](https://togithub.com/googleapis/java-spanner-jdbc/blob/HEAD/CHANGELOG.md#2117-2023-08-13)
[Compare Source](https://togithub.com/googleapis/java-spanner-jdbc/compare/v2.11.6...v2.11.7)
##### Dependencies
- Update dependency com.google.cloud:google-cloud-spanner-bom to v6.45.1 ([#1312](https://togithub.com/googleapis/java-spanner-jdbc/issues/1312)) ([2e99e35](https://togithub.com/googleapis/java-spanner-jdbc/commit/2e99e357c9688c89881433e77b3167924442abaa))
---
### 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 this update again.
---
- [ ] If you want to rebase/retry this PR, check this box
---
This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://developer.mend.io/github/googleapis/java-spanner-jdbc).
---
samples/install-without-bom/pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/samples/install-without-bom/pom.xml b/samples/install-without-bom/pom.xml
index e5c2fd93b..192263619 100644
--- a/samples/install-without-bom/pom.xml
+++ b/samples/install-without-bom/pom.xml
@@ -29,7 +29,7 @@
com.google.cloud
google-cloud-spanner-jdbc
- 2.11.6
+ 2.11.7
From 1953ea245889f54e3c803d6bf5e739141a48678e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?=
Date: Mon, 14 Aug 2023 21:01:45 +0200
Subject: [PATCH 3/5] chore: method for adding returning clause to statements
(#1311)
* chore: method for adding returning clause to statements
Adds a method to JdbcStatement for appending a THEN RETURN/RETURNING
clause to the statement. This will be used to modify statements that
request generated keys to be returned.
* feat: support return all columns
* fix: only add THEN RETURN * from DML
---
.../cloud/spanner/jdbc/JdbcStatement.java | 81 +++++++++
.../cloud/spanner/jdbc/JdbcStatementTest.java | 156 ++++++++++++++++++
2 files changed, 237 insertions(+)
diff --git a/src/main/java/com/google/cloud/spanner/jdbc/JdbcStatement.java b/src/main/java/com/google/cloud/spanner/jdbc/JdbcStatement.java
index 051ed9d6d..3e7c86f9f 100644
--- a/src/main/java/com/google/cloud/spanner/jdbc/JdbcStatement.java
+++ b/src/main/java/com/google/cloud/spanner/jdbc/JdbcStatement.java
@@ -23,9 +23,11 @@
import com.google.cloud.spanner.Statement;
import com.google.cloud.spanner.Type;
import com.google.cloud.spanner.Type.StructField;
+import com.google.cloud.spanner.connection.AbstractStatementParser.ParsedStatement;
import com.google.cloud.spanner.connection.StatementResult;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
import com.google.rpc.Code;
import java.sql.ResultSet;
import java.sql.SQLException;
@@ -33,9 +35,13 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.stream.Collectors;
+import javax.annotation.Nullable;
/** Implementation of {@link java.sql.Statement} for Google Cloud Spanner. */
class JdbcStatement extends AbstractJdbcStatement {
+ static final ImmutableList ALL_COLUMNS = ImmutableList.of("*");
+
enum BatchType {
NONE,
DML,
@@ -98,6 +104,81 @@ public long executeLargeUpdate(String sql) throws SQLException {
}
}
+ /**
+ * Adds a THEN RETURN/RETURNING clause to the given statement if the following conditions are all
+ * met:
+ *
+ *
+ * - The generatedKeysColumns is not null or empty
+ *
- The statement is a DML statement
+ *
- The DML statement does not already contain a THEN RETURN/RETURNING clause
+ *
+ */
+ Statement addReturningToStatement(
+ Statement statement, @Nullable ImmutableList generatedKeysColumns)
+ throws SQLException {
+ if (generatedKeysColumns == null || generatedKeysColumns.isEmpty()) {
+ return statement;
+ }
+ // Check if the statement is a DML statement or not.
+ ParsedStatement parsedStatement = getConnection().getParser().parse(statement);
+ if (parsedStatement.isUpdate() && !parsedStatement.hasReturningClause()) {
+ if (generatedKeysColumns.size() == 1
+ && ALL_COLUMNS.get(0).equals(generatedKeysColumns.get(0))) {
+ // Add a 'THEN RETURN/RETURNING *' clause to the statement.
+ return statement
+ .toBuilder()
+ .replace(statement.getSql() + getReturningAllColumnsClause())
+ .build();
+ }
+ // Add a 'THEN RETURN/RETURNING col1, col2, ...' to the statement.
+ // The column names will be quoted using the dialect-specific identifier quoting character.
+ return statement
+ .toBuilder()
+ .replace(
+ generatedKeysColumns.stream()
+ .map(this::quoteColumn)
+ .collect(
+ Collectors.joining(
+ ", ", statement.getSql() + getReturningClause() + " ", "")))
+ .build();
+ }
+ return statement;
+ }
+
+ /** Returns the dialect-specific clause for returning values from a DML statement. */
+ String getReturningAllColumnsClause() {
+ switch (getConnection().getDialect()) {
+ case POSTGRESQL:
+ return "\nRETURNING *";
+ case GOOGLE_STANDARD_SQL:
+ default:
+ return "\nTHEN RETURN *";
+ }
+ }
+
+ /** Returns the dialect-specific clause for returning values from a DML statement. */
+ String getReturningClause() {
+ switch (getConnection().getDialect()) {
+ case POSTGRESQL:
+ return "\nRETURNING";
+ case GOOGLE_STANDARD_SQL:
+ default:
+ return "\nTHEN RETURN";
+ }
+ }
+
+ /** Adds dialect-specific quotes to the given column name. */
+ String quoteColumn(String column) {
+ switch (getConnection().getDialect()) {
+ case POSTGRESQL:
+ return "\"" + column + "\"";
+ case GOOGLE_STANDARD_SQL:
+ default:
+ return "`" + column + "`";
+ }
+ }
+
@Override
public boolean execute(String sql) throws SQLException {
checkClosed();
diff --git a/src/test/java/com/google/cloud/spanner/jdbc/JdbcStatementTest.java b/src/test/java/com/google/cloud/spanner/jdbc/JdbcStatementTest.java
index c7b6a6cbd..d44583587 100644
--- a/src/test/java/com/google/cloud/spanner/jdbc/JdbcStatementTest.java
+++ b/src/test/java/com/google/cloud/spanner/jdbc/JdbcStatementTest.java
@@ -21,6 +21,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -39,6 +40,7 @@
import com.google.cloud.spanner.connection.StatementResult;
import com.google.cloud.spanner.connection.StatementResult.ResultType;
import com.google.cloud.spanner.jdbc.JdbcSqlExceptionFactory.JdbcSqlExceptionImpl;
+import com.google.common.collect.ImmutableList;
import com.google.rpc.Code;
import java.sql.ResultSet;
import java.sql.SQLException;
@@ -47,6 +49,7 @@
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
+import javax.annotation.Nullable;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -599,4 +602,157 @@ public void testConvertUpdateCountsToSuccessNoInfo() throws SQLException {
(long) Statement.SUCCESS_NO_INFO);
}
}
+
+ @Test
+ public void testAddReturningToStatement() throws SQLException {
+ JdbcConnection connection = mock(JdbcConnection.class);
+ when(connection.getDialect()).thenReturn(dialect);
+ when(connection.getParser()).thenReturn(AbstractStatementParser.getInstance(dialect));
+ try (JdbcStatement statement = new JdbcStatement(connection)) {
+ assertAddReturningSame(statement, "insert into test (id, value) values (1, 'One')", null);
+ assertAddReturningSame(
+ statement, "insert into test (id, value) values (1, 'One')", ImmutableList.of());
+ assertAddReturningEquals(
+ statement,
+ dialect == Dialect.POSTGRESQL
+ ? "insert into test (id, value) values (1, 'One')\nRETURNING \"id\""
+ : "insert into test (id, value) values (1, 'One')\nTHEN RETURN `id`",
+ "insert into test (id, value) values (1, 'One')",
+ ImmutableList.of("id"));
+ assertAddReturningEquals(
+ statement,
+ dialect == Dialect.POSTGRESQL
+ ? "insert into test (id, value) values (1, 'One')\nRETURNING \"id\", \"value\""
+ : "insert into test (id, value) values (1, 'One')\nTHEN RETURN `id`, `value`",
+ "insert into test (id, value) values (1, 'One')",
+ ImmutableList.of("id", "value"));
+ assertAddReturningEquals(
+ statement,
+ dialect == Dialect.POSTGRESQL
+ ? "insert into test (id, value) values (1, 'One')\nRETURNING *"
+ : "insert into test (id, value) values (1, 'One')\nTHEN RETURN *",
+ "insert into test (id, value) values (1, 'One')",
+ ImmutableList.of("*"));
+ // Requesting generated keys for a DML statement that already contains a returning clause is a
+ // no-op.
+ assertAddReturningSame(
+ statement,
+ "insert into test (id, value) values (1, 'One') "
+ + statement.getReturningClause()
+ + " value",
+ ImmutableList.of("id"));
+ // Requesting generated keys for a query is a no-op.
+ for (ImmutableList keys :
+ ImmutableList.of(
+ ImmutableList.of("id"), ImmutableList.of("id", "value"), ImmutableList.of("*"))) {
+ assertAddReturningSame(statement, "select id, value from test", keys);
+ }
+
+ // Update statements may also request generated keys.
+ assertAddReturningSame(statement, "update test set value='Two' where id=1", null);
+ assertAddReturningSame(
+ statement, "update test set value='Two' where id=1", ImmutableList.of());
+ assertAddReturningEquals(
+ statement,
+ dialect == Dialect.POSTGRESQL
+ ? "update test set value='Two' where id=1\nRETURNING \"value\""
+ : "update test set value='Two' where id=1\nTHEN RETURN `value`",
+ "update test set value='Two' where id=1",
+ ImmutableList.of("value"));
+ assertAddReturningEquals(
+ statement,
+ dialect == Dialect.POSTGRESQL
+ ? "update test set value='Two' where id=1\nRETURNING \"value\", \"id\""
+ : "update test set value='Two' where id=1\nTHEN RETURN `value`, `id`",
+ "update test set value='Two' where id=1",
+ ImmutableList.of("value", "id"));
+ assertAddReturningEquals(
+ statement,
+ dialect == Dialect.POSTGRESQL
+ ? "update test set value='Two' where id=1\nRETURNING *"
+ : "update test set value='Two' where id=1\nTHEN RETURN *",
+ "update test set value='Two' where id=1",
+ ImmutableList.of("*"));
+ // Requesting generated keys for a DML statement that already contains a returning clause is a
+ // no-op.
+ assertAddReturningSame(
+ statement,
+ "update test set value='Two' where id=1 " + statement.getReturningClause() + " value",
+ ImmutableList.of("value"));
+
+ // Delete statements may also request generated keys.
+ assertAddReturningSame(statement, "delete test where id=1", null);
+ assertAddReturningSame(statement, "delete test where id=1", ImmutableList.of());
+ assertAddReturningEquals(
+ statement,
+ dialect == Dialect.POSTGRESQL
+ ? "delete test where id=1\nRETURNING \"value\""
+ : "delete test where id=1\nTHEN RETURN `value`",
+ "delete test where id=1",
+ ImmutableList.of("value"));
+ assertAddReturningEquals(
+ statement,
+ dialect == Dialect.POSTGRESQL
+ ? "delete test where id=1\nRETURNING \"id\", \"value\""
+ : "delete test where id=1\nTHEN RETURN `id`, `value`",
+ "delete test where id=1",
+ ImmutableList.of("id", "value"));
+ assertAddReturningEquals(
+ statement,
+ dialect == Dialect.POSTGRESQL
+ ? "delete test where id=1\nRETURNING *"
+ : "delete test where id=1\nTHEN RETURN *",
+ "delete test where id=1",
+ ImmutableList.of("*"));
+ // Requesting generated keys for a DML statement that already contains a returning clause is a
+ // no-op.
+ for (ImmutableList keys :
+ ImmutableList.of(
+ ImmutableList.of("id"), ImmutableList.of("id", "value"), ImmutableList.of("*"))) {
+ assertAddReturningSame(
+ statement,
+ "delete test where id=1 "
+ + (dialect == Dialect.POSTGRESQL
+ ? "delete test where id=1\nRETURNING"
+ : "delete test where id=1\nTHEN RETURN")
+ + " value",
+ keys);
+ }
+
+ // Requesting generated keys for DDL is a no-op.
+ for (ImmutableList keys :
+ ImmutableList.of(
+ ImmutableList.of("id"), ImmutableList.of("id", "value"), ImmutableList.of("*"))) {
+ assertAddReturningSame(
+ statement,
+ dialect == Dialect.POSTGRESQL
+ ? "create table test (id bigint primary key, value varchar)"
+ : "create table test (id int64, value string(max)) primary key (id)",
+ keys);
+ }
+ }
+ }
+
+ private void assertAddReturningSame(
+ JdbcStatement statement, String sql, @Nullable ImmutableList generatedKeysColumns)
+ throws SQLException {
+ com.google.cloud.spanner.Statement spannerStatement =
+ com.google.cloud.spanner.Statement.of(sql);
+ assertSame(
+ spannerStatement,
+ statement.addReturningToStatement(spannerStatement, generatedKeysColumns));
+ }
+
+ private void assertAddReturningEquals(
+ JdbcStatement statement,
+ String expectedSql,
+ String sql,
+ @Nullable ImmutableList generatedKeysColumns)
+ throws SQLException {
+ com.google.cloud.spanner.Statement spannerStatement =
+ com.google.cloud.spanner.Statement.of(sql);
+ assertEquals(
+ com.google.cloud.spanner.Statement.of(expectedSql),
+ statement.addReturningToStatement(spannerStatement, generatedKeysColumns));
+ }
}
From e9241787b94cb614f658f5e6c977ffc008fd3397 Mon Sep 17 00:00:00 2001
From: Mend Renovate
Date: Tue, 15 Aug 2023 07:08:48 +0200
Subject: [PATCH 4/5] deps: update dependency
com.google.cloud:google-cloud-spanner-bom to v6.45.2 (#1318)
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index 3378a232f..9cc747b20 100644
--- a/pom.xml
+++ b/pom.xml
@@ -62,7 +62,7 @@
com.google.cloud
google-cloud-spanner-bom
- 6.45.1
+ 6.45.2
pom
import
From 4f1472705f8642a8227d9a42bdb2b3285dd942f4 Mon Sep 17 00:00:00 2001
From: "release-please[bot]"
<55107282+release-please[bot]@users.noreply.github.com>
Date: Tue, 15 Aug 2023 06:32:13 +0000
Subject: [PATCH 5/5] chore(main): release 2.11.8 (#1319)
:robot: I have created a release *beep* *boop*
---
## [2.11.8](https://togithub.com/googleapis/java-spanner-jdbc/compare/v2.11.7...v2.11.8) (2023-08-15)
### Dependencies
* Update dependency com.google.cloud:google-cloud-spanner-bom to v6.45.2 ([#1318](https://togithub.com/googleapis/java-spanner-jdbc/issues/1318)) ([e924178](https://togithub.com/googleapis/java-spanner-jdbc/commit/e9241787b94cb614f658f5e6c977ffc008fd3397))
---
This PR was generated with [Release Please](https://togithub.com/googleapis/release-please). See [documentation](https://togithub.com/googleapis/release-please#release-please).
---
CHANGELOG.md | 7 +++++++
pom.xml | 2 +-
samples/snapshot/pom.xml | 2 +-
versions.txt | 2 +-
4 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index af3d292a3..a1f93aa82 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,12 @@
# Changelog
+## [2.11.8](https://github.com/googleapis/java-spanner-jdbc/compare/v2.11.7...v2.11.8) (2023-08-15)
+
+
+### Dependencies
+
+* Update dependency com.google.cloud:google-cloud-spanner-bom to v6.45.2 ([#1318](https://github.com/googleapis/java-spanner-jdbc/issues/1318)) ([e924178](https://github.com/googleapis/java-spanner-jdbc/commit/e9241787b94cb614f658f5e6c977ffc008fd3397))
+
## [2.11.7](https://github.com/googleapis/java-spanner-jdbc/compare/v2.11.6...v2.11.7) (2023-08-13)
diff --git a/pom.xml b/pom.xml
index 9cc747b20..7e85f5a45 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
google-cloud-spanner-jdbc
- 2.11.8-SNAPSHOT
+ 2.11.8
jar
Google Cloud Spanner JDBC
https://github.com/googleapis/java-spanner-jdbc
diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml
index 99c50943d..73e594f27 100644
--- a/samples/snapshot/pom.xml
+++ b/samples/snapshot/pom.xml
@@ -28,7 +28,7 @@
com.google.cloud
google-cloud-spanner-jdbc
- 2.11.8-SNAPSHOT
+ 2.11.8
diff --git a/versions.txt b/versions.txt
index 5459f1d3b..e9d28b678 100644
--- a/versions.txt
+++ b/versions.txt
@@ -1,4 +1,4 @@
# Format:
# module:released-version:current-version
-google-cloud-spanner-jdbc:2.11.7:2.11.8-SNAPSHOT
+google-cloud-spanner-jdbc:2.11.8:2.11.8