From 3eda847597873566a5ba1f8da53f9f5fd06bb9fa Mon Sep 17 00:00:00 2001 From: Shirdon Gorse Date: Mon, 3 Feb 2025 19:42:27 +0000 Subject: [PATCH 1/2] feat: Add LastStatement DML option --- .../cloud/spanner/AbstractReadContext.java | 6 +++ .../com/google/cloud/spanner/Options.java | 47 ++++++++++++++++++- samples/install-without-bom/pom.xml | 7 ++- samples/snippets/pom.xml | 6 +++ .../com/example/spanner/SpannerSample.java | 20 ++++---- 5 files changed, 74 insertions(+), 12 deletions(-) diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbstractReadContext.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbstractReadContext.java index cecf462bd25..f0ab964b74a 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbstractReadContext.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbstractReadContext.java @@ -696,6 +696,9 @@ ExecuteSqlRequest.Builder getExecuteSqlRequestBuilder( if (!isReadOnly()) { builder.setSeqno(getSeqNo()); } + if (options.hasLastStatementSet()) { + builder.setLastStatement(options.lastStatementSet()); + } builder.setQueryOptions(buildQueryOptions(statement.getQueryOptions())); builder.setRequestOptions(buildRequestOptions(options)); return builder; @@ -741,6 +744,9 @@ ExecuteBatchDmlRequest.Builder getExecuteBatchDmlRequestBuilder( if (selector != null) { builder.setTransaction(selector); } + if (options.hasLastStatementSet()) { + builder.setLastStatements(options.lastStatementSet()); + } builder.setSeqno(getSeqNo()); builder.setRequestOptions(buildRequestOptions(options)); return builder; diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Options.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Options.java index 9c3257586fb..c26a54f6d52 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Options.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Options.java @@ -212,6 +212,20 @@ public static DataBoostQueryOption dataBoostEnabled(Boolean dataBoostEnabled) { return new DataBoostQueryOption(dataBoostEnabled); } + /** + * If set to true, this option marks the end of the transaction. The transaction should be + * committed or aborted after this statement executes, and attempts to execute any other requests + * against this transaction (including reads and queries) will be rejected. Mixing mutations with + * statements that are marked as the last statement is not allowed. + * + *

For DML statements, setting this option may cause some error reporting to be deferred until + * commit time (e.g. validation of unique constraints). Given this, successful execution of a DML + * statement should not be assumed until the transaction commits. + */ + public static LastStatementUpdateOption lastStatementSet(Boolean lastStatementSet) { + return new LastStatementUpdateOption(lastStatementSet); + } + /** * Specifying this will cause the list operation to start fetching the record from this onwards. */ @@ -470,6 +484,8 @@ void appendToOptions(Options options) { private DecodeMode decodeMode; private RpcOrderBy orderBy; + private Boolean lastStatementSet; + // Construction is via factory methods below. private Options() {} @@ -605,6 +621,14 @@ OrderBy orderBy() { return orderBy == null ? null : orderBy.proto; } + boolean hasLastStatementSet() { + return lastStatementSet != null; + } + + Boolean lastStatementSet() { + return lastStatementSet; + } + @Override public String toString() { StringBuilder b = new StringBuilder(); @@ -661,6 +685,9 @@ public String toString() { if (orderBy != null) { b.append("orderBy: ").append(orderBy).append(' '); } + if (lastStatementSet != null) { + b.append("lastStatementSet: ").append(lastStatementSet).append(' '); + } return b.toString(); } @@ -700,7 +727,8 @@ public boolean equals(Object o) { && Objects.equals(withExcludeTxnFromChangeStreams(), that.withExcludeTxnFromChangeStreams()) && Objects.equals(dataBoostEnabled(), that.dataBoostEnabled()) && Objects.equals(directedReadOptions(), that.directedReadOptions()) - && Objects.equals(orderBy(), that.orderBy()); + && Objects.equals(orderBy(), that.orderBy()) + && Objects.equals(lastStatementSet(), that.lastStatementSet()); } @Override @@ -760,6 +788,9 @@ public int hashCode() { if (orderBy != null) { result = 31 * result + orderBy.hashCode(); } + if (lastStatementSet != null) { + result = 31 * result + lastStatementSet.hashCode(); + } return result; } @@ -912,4 +943,18 @@ public boolean equals(Object o) { return Objects.equals(filter, ((FilterOption) o).filter); } } + + static final class LastStatementUpdateOption extends InternalOption implements UpdateOption { + + private final Boolean lastStatementSet; + + LastStatementUpdateOption(Boolean lastStatementSet) { + this.lastStatementSet = lastStatementSet; + } + + @Override + void appendToOptions(Options options) { + options.lastStatementSet = lastStatementSet; + } + } } diff --git a/samples/install-without-bom/pom.xml b/samples/install-without-bom/pom.xml index 0d6406bd321..e312867220c 100644 --- a/samples/install-without-bom/pom.xml +++ b/samples/install-without-bom/pom.xml @@ -33,7 +33,12 @@ com.google.cloud google-cloud-spanner - 6.81.1 + 6.83.1-SNAPSHOT + + + com.google.api.grpc + proto-google-cloud-spanner-v1 + 6.83.1-SNAPSHOT diff --git a/samples/snippets/pom.xml b/samples/snippets/pom.xml index f78c2d3b308..761e582c2b7 100644 --- a/samples/snippets/pom.xml +++ b/samples/snippets/pom.xml @@ -45,6 +45,12 @@ com.google.cloud google-cloud-spanner + 6.83.1-SNAPSHOT + + + com.google.api.grpc + proto-google-cloud-spanner-v1 + 6.83.1-SNAPSHOT diff --git a/samples/snippets/src/main/java/com/example/spanner/SpannerSample.java b/samples/snippets/src/main/java/com/example/spanner/SpannerSample.java index d406225c28b..30f4bd47daa 100644 --- a/samples/snippets/src/main/java/com/example/spanner/SpannerSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/SpannerSample.java @@ -32,6 +32,7 @@ import com.google.cloud.spanner.KeyRange; import com.google.cloud.spanner.KeySet; import com.google.cloud.spanner.Mutation; +import com.google.cloud.spanner.Options; import com.google.cloud.spanner.ReadOnlyTransaction; import com.google.cloud.spanner.ResultSet; import com.google.cloud.spanner.Spanner; @@ -996,15 +997,15 @@ static void queryNestedStructField(DatabaseClient dbClient) { // [START spanner_dml_standard_insert] static void insertUsingDml(DatabaseClient dbClient) { dbClient - .readWriteTransaction() - .run(transaction -> { - String sql = - "INSERT INTO Singers (SingerId, FirstName, LastName) " - + " VALUES (10, 'Virginia', 'Watson')"; - long rowCount = transaction.executeUpdate(Statement.of(sql)); - System.out.printf("%d record inserted.\n", rowCount); - return null; - }); + .readWriteTransaction() + .run(transaction -> { + String sql = + "INSERT INTO Singers (SingerId, FirstName, LastName) " + + " VALUES (10, 'Virginia', 'Watson')"; + long rowCount = transaction.executeUpdate(Statement.of(sql)); + System.out.printf("%d record inserted.\n", rowCount); + return null; + }); } // [END spanner_dml_standard_insert] @@ -2230,7 +2231,6 @@ public static void main(String[] args) { printUsageAndExit(); } // [START init_client] - SpannerOptions options = SpannerOptions.newBuilder().build(); Spanner spanner = options.getService(); DatabaseAdminClient dbAdminClient = null; try { From 8673806ab9f493f83dc5edc37fef89a8e1efe689 Mon Sep 17 00:00:00 2001 From: Shirdon Gorse Date: Mon, 3 Feb 2025 22:09:58 +0000 Subject: [PATCH 2/2] Removing debugging changes --- samples/snippets/pom.xml | 6 ------ .../com/example/spanner/SpannerSample.java | 20 +++++++++---------- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/samples/snippets/pom.xml b/samples/snippets/pom.xml index 8528848c533..7873c36902d 100644 --- a/samples/snippets/pom.xml +++ b/samples/snippets/pom.xml @@ -45,12 +45,6 @@ com.google.cloud google-cloud-spanner - 6.83.1-SNAPSHOT - - - com.google.api.grpc - proto-google-cloud-spanner-v1 - 6.83.1-SNAPSHOT diff --git a/samples/snippets/src/main/java/com/example/spanner/SpannerSample.java b/samples/snippets/src/main/java/com/example/spanner/SpannerSample.java index 30f4bd47daa..d406225c28b 100644 --- a/samples/snippets/src/main/java/com/example/spanner/SpannerSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/SpannerSample.java @@ -32,7 +32,6 @@ import com.google.cloud.spanner.KeyRange; import com.google.cloud.spanner.KeySet; import com.google.cloud.spanner.Mutation; -import com.google.cloud.spanner.Options; import com.google.cloud.spanner.ReadOnlyTransaction; import com.google.cloud.spanner.ResultSet; import com.google.cloud.spanner.Spanner; @@ -997,15 +996,15 @@ static void queryNestedStructField(DatabaseClient dbClient) { // [START spanner_dml_standard_insert] static void insertUsingDml(DatabaseClient dbClient) { dbClient - .readWriteTransaction() - .run(transaction -> { - String sql = - "INSERT INTO Singers (SingerId, FirstName, LastName) " - + " VALUES (10, 'Virginia', 'Watson')"; - long rowCount = transaction.executeUpdate(Statement.of(sql)); - System.out.printf("%d record inserted.\n", rowCount); - return null; - }); + .readWriteTransaction() + .run(transaction -> { + String sql = + "INSERT INTO Singers (SingerId, FirstName, LastName) " + + " VALUES (10, 'Virginia', 'Watson')"; + long rowCount = transaction.executeUpdate(Statement.of(sql)); + System.out.printf("%d record inserted.\n", rowCount); + return null; + }); } // [END spanner_dml_standard_insert] @@ -2231,6 +2230,7 @@ public static void main(String[] args) { printUsageAndExit(); } // [START init_client] + SpannerOptions options = SpannerOptions.newBuilder().build(); Spanner spanner = options.getService(); DatabaseAdminClient dbAdminClient = null; try {