From ff7ca654eb30df590f704f144e5a0b22b03c94bc Mon Sep 17 00:00:00 2001 From: guangyus <43188686+guangyus@users.noreply.github.com> Date: Wed, 12 Dec 2018 10:19:45 -0800 Subject: [PATCH 1/6] Cloud Spanner Batch DML implementation and integration tests. (#45) --- .../spanner/SpannerBatchUpdateException.java | 21 ++ .../spanner/SpannerExceptionFactory.java | 6 + .../com/google/cloud/spanner/SpannerImpl.java | 57 +++++ .../cloud/spanner/TransactionContext.java | 17 ++ .../cloud/spanner/spi/v1/SpannerRpc.java | 4 + .../cloud/spanner/it/ITBatchDmlTest.java | 202 ++++++++++++++++++ 6 files changed, 307 insertions(+) create mode 100644 google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerBatchUpdateException.java create mode 100644 google-cloud-clients/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITBatchDmlTest.java diff --git a/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerBatchUpdateException.java b/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerBatchUpdateException.java new file mode 100644 index 000000000000..4a82922b6b80 --- /dev/null +++ b/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerBatchUpdateException.java @@ -0,0 +1,21 @@ +package com.google.cloud.spanner; + +public class SpannerBatchUpdateException extends SpannerException { + private long[] updateCounts; + /** Private constructor. Use {@link SpannerExceptionFactory} to create instances. */ + SpannerBatchUpdateException( + DoNotConstructDirectly token, + ErrorCode code, + String message, + long[] counts) { + super(token, code, false, message, null); + updateCounts = counts; + } + + /** + * Returns the number of rows affected by each statement that is successfully run. + */ + public long[] getUpdateCounts() { + return updateCounts; + } +} diff --git a/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerExceptionFactory.java b/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerExceptionFactory.java index f6f6210d85e7..3ff2d6749778 100644 --- a/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerExceptionFactory.java +++ b/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerExceptionFactory.java @@ -82,6 +82,12 @@ public static SpannerException newSpannerException(Throwable cause) { return newSpannerException(null, cause); } + public static SpannerBatchUpdateException newSpannerBatchUpdateException( + ErrorCode code, String message, long[] updateCounts) { + DoNotConstructDirectly token = DoNotConstructDirectly.ALLOWED; + return new SpannerBatchUpdateException(token, code, message, updateCounts); + } + /** * Creates a new exception based on {@code cause}. If {@code cause} indicates cancellation, {@code * context} will be inspected to establish the type of cancellation. diff --git a/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerImpl.java b/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerImpl.java index 2424cf321e16..63801b7bd240 100644 --- a/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerImpl.java +++ b/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerImpl.java @@ -16,6 +16,7 @@ package com.google.cloud.spanner; +import static com.google.cloud.spanner.SpannerExceptionFactory.newSpannerBatchUpdateException; import static com.google.cloud.spanner.SpannerExceptionFactory.newSpannerException; import static com.google.cloud.spanner.SpannerExceptionFactory.newSpannerExceptionForCancellation; import static com.google.common.base.Preconditions.checkArgument; @@ -69,6 +70,7 @@ import com.google.spanner.v1.BeginTransactionRequest; import com.google.spanner.v1.CommitRequest; import com.google.spanner.v1.CommitResponse; +import com.google.spanner.v1.ExecuteBatchDmlRequest; import com.google.spanner.v1.ExecuteSqlRequest; import com.google.spanner.v1.ExecuteSqlRequest.QueryMode; import com.google.spanner.v1.PartialResultSet; @@ -1080,6 +1082,36 @@ ExecuteSqlRequest.Builder getExecuteSqlRequestBuilder( return builder; } + ExecuteBatchDmlRequest.Builder getExecuteBatchDmlRequestBuilder( + Iterable statements) { + ExecuteBatchDmlRequest.Builder builder = + ExecuteBatchDmlRequest.newBuilder() + .setSession(session.name); + int idx = 0; + for (Statement stmt : statements) { + builder.addStatementsBuilder(); + builder.getStatementsBuilder(idx).setSql(stmt.getSql()); + Map stmtParameters = stmt.getParameters(); + if (!stmtParameters.isEmpty()) { + com.google.protobuf.Struct.Builder paramsBuilder = builder.getStatementsBuilder(idx) + .getParamsBuilder(); + for (Map.Entry param : stmtParameters.entrySet()) { + paramsBuilder.putFields(param.getKey(), param.getValue().toProto()); + builder.getStatementsBuilder(idx) + .putParamTypes(param.getKey(), param.getValue().getType().toProto()); + } + } + idx++; + } + + TransactionSelector selector = getTransactionSelector(); + if (selector != null) { + builder.setTransaction(selector); + } + builder.setSeqno(getSeqNo()); + return builder; + } + ResultSet executeQueryInternalWithOptions( Statement statement, com.google.spanner.v1.ExecuteSqlRequest.QueryMode queryMode, @@ -1660,6 +1692,31 @@ public com.google.spanner.v1.ResultSet call() throws Exception { // For standard DML, using the exact row count. return resultSet.getStats().getRowCountExact(); } + + @Override + public long[] batchUpdate(Iterable statements) { + beforeReadOrQuery(); + final ExecuteBatchDmlRequest.Builder builder = + getExecuteBatchDmlRequestBuilder(statements); + com.google.spanner.v1.ExecuteBatchDmlResponse response = + runWithRetries( + new Callable() { + @Override + public com.google.spanner.v1.ExecuteBatchDmlResponse call() throws Exception { + return rpc.executeBatchDml(builder.build(), session.options); + } + }); + long[] results = new long[response.getResultSetsCount()]; + for (int i = 0; i < response.getResultSetsCount(); ++i) { + results[i] = response.getResultSets(i).getStats().getRowCountExact(); + } + if (response.getStatus().getCode() != 0) { + throw newSpannerBatchUpdateException( + ErrorCode.fromRpcStatus( + response.getStatus()), response.getStatus().getMessage(), results); + } + return results; + } } /** diff --git a/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionContext.java b/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionContext.java index 59e4c52c28b7..b68b3674f4e6 100644 --- a/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionContext.java +++ b/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionContext.java @@ -101,4 +101,21 @@ public interface TransactionContext extends ReadContext { * visible to subsequent operations in the transaction. */ long executeUpdate(Statement statement); + + /** + * Executes a list of DML statements in a single request. The statements will be executed in order + * and the semantics is the same as if each statement is executed by {@code executeUpdate} in a + * loop. This method returns an array of long integers, each representing the number of rows + * modified by each statement. + * + * If an individual statement fails, execution stops and a {@code + * SpannerBatchUpdateException} is returned, which includes the error and the number of rows + * affected by the statements that are run prior to the error. + * + * For example, if statements contains 3 statements, and the 2nd one is not a valid DML. This + * method throws a {@code SpannerBatchUpdateException} that contains the error message from the + * 2nd statement, and an array of length 1 that contains the number of rows modified by the 1st + * statement. The 3rd statement will not run. + */ + long[] batchUpdate(Iterable statements); } diff --git a/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/SpannerRpc.java b/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/SpannerRpc.java index 107b92fa24e8..26f16de2bb26 100644 --- a/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/SpannerRpc.java +++ b/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/SpannerRpc.java @@ -34,6 +34,8 @@ import com.google.spanner.v1.BeginTransactionRequest; import com.google.spanner.v1.CommitRequest; import com.google.spanner.v1.CommitResponse; +import com.google.spanner.v1.ExecuteBatchDmlRequest; +import com.google.spanner.v1.ExecuteBatchDmlResponse; import com.google.spanner.v1.ExecuteSqlRequest; import com.google.spanner.v1.PartialResultSet; import com.google.spanner.v1.PartitionQueryRequest; @@ -214,6 +216,8 @@ StreamingCall read( StreamingCall executeQuery( ExecuteSqlRequest request, ResultStreamConsumer consumer, @Nullable Map options); + ExecuteBatchDmlResponse executeBatchDml(ExecuteBatchDmlRequest build, Map options); + Transaction beginTransaction(BeginTransactionRequest request, @Nullable Map options) throws SpannerException; diff --git a/google-cloud-clients/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITBatchDmlTest.java b/google-cloud-clients/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITBatchDmlTest.java new file mode 100644 index 000000000000..0c9d333c3252 --- /dev/null +++ b/google-cloud-clients/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITBatchDmlTest.java @@ -0,0 +1,202 @@ +package com.google.cloud.spanner.it; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.cloud.spanner.ErrorCode; +import com.google.cloud.spanner.Operation; +import com.google.cloud.spanner.SpannerBatchUpdateException; +import com.google.cloud.spanner.SpannerException; +import com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import com.google.cloud.spanner.Database; +import com.google.cloud.spanner.DatabaseClient; +import com.google.cloud.spanner.IntegrationTest; +import com.google.cloud.spanner.IntegrationTestEnv; +import com.google.cloud.spanner.Statement; +import com.google.cloud.spanner.TransactionContext; +import com.google.cloud.spanner.TransactionRunner; +import com.google.cloud.spanner.TransactionRunner.TransactionCallable; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Integration tests for DML. */ +@Category(IntegrationTest.class) +@RunWith(JUnit4.class) +public final class ITBatchDmlTest { + + private static Database db; + @ClassRule + public static IntegrationTestEnv env = new IntegrationTestEnv(); + + private static final String INSERT_DML = + "INSERT INTO T (k, v) VALUES ('boo1', 1), ('boo2', 2), ('boo3', 3), ('boo4', 4);"; + private static final String UPDATE_DML = "UPDATE T SET T.V = 100 WHERE T.K LIKE 'boo%';"; + private static final String DELETE_DML = "DELETE FROM T WHERE T.K like 'boo%';"; + private static DatabaseClient client; + + @BeforeClass + public static void createDatabase() { + db = + env.getTestHelper() + .createTestDatabase(); + client = env.getTestHelper().getDatabaseClient(db); + } + + @Before + public void createTable() { + String ddl = "CREATE TABLE T (" + + " K STRING(MAX) NOT NULL," + + " V INT64," + + ") PRIMARY KEY (K)"; + Operation op = db.updateDdl( + Arrays.asList(ddl), null); + op.waitFor(); + } + + @After + public void dropTable() { + String ddl = "DROP TABLE T"; + Operation op = db.updateDdl( + Arrays.asList(ddl), null); + op.waitFor(); + } + + @Test + public void noStatementsInRequest() { + final TransactionCallable callable = + new TransactionCallable() { + @Override + public long[] run(TransactionContext transaction) { + List stmts = new ArrayList<>(); + long[] rowCounts; + try { + rowCounts = transaction.batchUpdate(stmts); + Assert.fail("Expecting an exception."); + } catch (SpannerException e) { + assertThat(e instanceof SpannerBatchUpdateException).isFalse(); + assertThat(e.getErrorCode()).isEqualTo(ErrorCode.INVALID_ARGUMENT); + assertThat(e.getMessage()) + .contains("No statements in batch DML request."); + rowCounts = new long[0]; + } + return rowCounts; + } + }; + TransactionRunner runner = client.readWriteTransaction(); + long[] rowCounts = runner.run(callable); + assertThat(rowCounts.length).isEqualTo(0); + } + + @Test + public void batchDml() { + final TransactionCallable callable = + new TransactionCallable() { + @Override + public long[] run(TransactionContext transaction) throws Exception { + List stmts = new ArrayList<>(); + stmts.add(Statement.of(INSERT_DML)); + stmts.add(Statement.of(UPDATE_DML)); + stmts.add(Statement.of(DELETE_DML)); + return transaction.batchUpdate(stmts); + } + }; + TransactionRunner runner = client.readWriteTransaction(); + long[] rowCounts = runner.run(callable); + assertThat(rowCounts.length).isEqualTo(3); + for (long rc : rowCounts) { + assertThat(rc).isEqualTo(4); + } + } + + @Test + public void mixedBatchDmlAndDml() { + final TransactionCallable callable = + new TransactionCallable() { + @Override + public long[] run(TransactionContext transaction) throws Exception { + long rowCount = transaction.executeUpdate(Statement.of(INSERT_DML)); + List stmts = new ArrayList<>(); + stmts.add(Statement.of(UPDATE_DML)); + stmts.add(Statement.of(DELETE_DML)); + long[] batchRowCounts = transaction.batchUpdate(stmts); + long[] rowCounts = new long[batchRowCounts.length + 1]; + System.arraycopy(batchRowCounts, 0, rowCounts, 0, batchRowCounts.length); + rowCounts[batchRowCounts.length] = rowCount; + return rowCounts; + } + }; + TransactionRunner runner = client.readWriteTransaction(); + long[] rowCounts = runner.run(callable); + assertThat(rowCounts.length).isEqualTo(3); + for (long rc : rowCounts) { + assertThat(rc).isEqualTo(4); + } + } + + @Test + public void errorBatchDmlIllegalStatement() { + final TransactionCallable callable = + new TransactionCallable() { + @Override + public long[] run(TransactionContext transaction) { + List stmts = new ArrayList<>(); + stmts.add(Statement.of(INSERT_DML)); + stmts.add(Statement.of("some illegal statement")); + stmts.add(Statement.of(UPDATE_DML)); + return transaction.batchUpdate(stmts); + } + }; + TransactionRunner runner = client.readWriteTransaction(); + try { + runner.run(callable); + Assert.fail("Expecting an exception."); + } catch (SpannerBatchUpdateException e) { + assertThat(e.getErrorCode()).isEqualTo(ErrorCode.INVALID_ARGUMENT); + assertThat(e.getMessage()) + .contains("is not valid DML."); + long[] rowCounts = e.getUpdateCounts(); + assertThat(rowCounts.length).isEqualTo(1); + for (long rc : rowCounts) { + assertThat(rc).isEqualTo(4); + } + } + } + + @Test + public void errorBatchDmlAlreadyExist() { + final TransactionCallable callable = + new TransactionCallable() { + @Override + public long[] run(TransactionContext transaction) { + List stmts = new ArrayList<>(); + stmts.add(Statement.of(INSERT_DML)); + stmts.add(Statement.of(INSERT_DML)); // should fail + stmts.add(Statement.of(UPDATE_DML)); + return transaction.batchUpdate(stmts); + } + }; + TransactionRunner runner = client.readWriteTransaction(); + try { + runner.run(callable); + Assert.fail("Expecting an exception."); + } catch (SpannerBatchUpdateException e) { + assertThat(e.getErrorCode()).isEqualTo(ErrorCode.ALREADY_EXISTS); + assertThat(e.getMessage()).contains("already exists"); + long[] rowCounts = e.getUpdateCounts(); + assertThat(rowCounts.length).isEqualTo(1); + for (long rc : rowCounts) { + assertThat(rc).isEqualTo(4); + } + } + } +} + From a4e382f578e8c54e7bb18059a17b4f8bb5f1bb8d Mon Sep 17 00:00:00 2001 From: guangyus <43188686+guangyus@users.noreply.github.com> Date: Wed, 16 Jan 2019 15:49:19 -0800 Subject: [PATCH 2/6] Fix the file header of the newly added classes. (#46) --- .../spanner/SpannerBatchUpdateException.java | 16 ++++++++++++++++ .../google/cloud/spanner/it/ITBatchDmlTest.java | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerBatchUpdateException.java b/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerBatchUpdateException.java index 4a82922b6b80..a2cc026cf16b 100644 --- a/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerBatchUpdateException.java +++ b/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerBatchUpdateException.java @@ -1,3 +1,19 @@ +/* + * Copyright 2017 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.google.cloud.spanner; public class SpannerBatchUpdateException extends SpannerException { diff --git a/google-cloud-clients/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITBatchDmlTest.java b/google-cloud-clients/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITBatchDmlTest.java index 0c9d333c3252..f101a84ea97a 100644 --- a/google-cloud-clients/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITBatchDmlTest.java +++ b/google-cloud-clients/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITBatchDmlTest.java @@ -1,3 +1,19 @@ +/* + * Copyright 2017 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.google.cloud.spanner.it; import static com.google.common.truth.Truth.assertThat; From 75df9bdeaa18fae25fe4d7d61a801f228fdf375d Mon Sep 17 00:00:00 2001 From: Guangyu Shi Date: Wed, 20 Feb 2019 13:57:13 -0800 Subject: [PATCH 3/6] Fix RPC interface mismatch after GAPIC migration. --- .../cloud/spanner/spi/v1/GapicSpannerRpc.java | 10 ++++++++++ .../google/cloud/spanner/it/ITBatchDmlTest.java | 14 +++++++------- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/GapicSpannerRpc.java b/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/GapicSpannerRpc.java index 5469699abd63..262808392537 100644 --- a/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/GapicSpannerRpc.java +++ b/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/GapicSpannerRpc.java @@ -90,6 +90,8 @@ import com.google.spanner.v1.CommitResponse; import com.google.spanner.v1.CreateSessionRequest; import com.google.spanner.v1.DeleteSessionRequest; +import com.google.spanner.v1.ExecuteBatchDmlRequest; +import com.google.spanner.v1.ExecuteBatchDmlResponse; import com.google.spanner.v1.ExecuteSqlRequest; import com.google.spanner.v1.PartialResultSet; import com.google.spanner.v1.PartitionQueryRequest; @@ -514,6 +516,14 @@ public void cancel(String message) { }; } + @Override + public ExecuteBatchDmlResponse executeBatchDml( + ExecuteBatchDmlRequest request, @Nullable Map options) { + + GrpcCallContext context = newCallContext(options, request.getSession()); + return get(spannerStub.executeBatchDmlCallable().futureCall(request, context)); + } + @Override public Transaction beginTransaction( BeginTransactionRequest request, @Nullable Map options) throws SpannerException { diff --git a/google-cloud-clients/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITBatchDmlTest.java b/google-cloud-clients/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITBatchDmlTest.java index f101a84ea97a..0af46bc24179 100644 --- a/google-cloud-clients/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITBatchDmlTest.java +++ b/google-cloud-clients/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITBatchDmlTest.java @@ -18,8 +18,8 @@ import static com.google.common.truth.Truth.assertThat; +import com.google.api.gax.longrunning.OperationFuture; import com.google.cloud.spanner.ErrorCode; -import com.google.cloud.spanner.Operation; import com.google.cloud.spanner.SpannerBatchUpdateException; import com.google.cloud.spanner.SpannerException; import com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata; @@ -68,22 +68,22 @@ public static void createDatabase() { } @Before - public void createTable() { + public void createTable() throws Exception { String ddl = "CREATE TABLE T (" + " K STRING(MAX) NOT NULL," + " V INT64," + ") PRIMARY KEY (K)"; - Operation op = db.updateDdl( + OperationFuture op = db.updateDdl( Arrays.asList(ddl), null); - op.waitFor(); + op.get(); } @After - public void dropTable() { + public void dropTable() throws Exception { String ddl = "DROP TABLE T"; - Operation op = db.updateDdl( + OperationFuture op = db.updateDdl( Arrays.asList(ddl), null); - op.waitFor(); + op.get(); } @Test From 86ad7f855a3403ddcec529180eefad31e22466dc Mon Sep 17 00:00:00 2001 From: Guangyu Shi Date: Thu, 21 Feb 2019 22:13:53 -0800 Subject: [PATCH 4/6] Address review comment. --- .../src/main/java/com/google/cloud/spanner/SpannerImpl.java | 1 + 1 file changed, 1 insertion(+) diff --git a/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerImpl.java b/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerImpl.java index 63801b7bd240..73485c26f0be 100644 --- a/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerImpl.java +++ b/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerImpl.java @@ -1710,6 +1710,7 @@ public com.google.spanner.v1.ExecuteBatchDmlResponse call() throws Exception { for (int i = 0; i < response.getResultSetsCount(); ++i) { results[i] = response.getResultSets(i).getStats().getRowCountExact(); } + if (response.getStatus().getCode() != 0) { throw newSpannerBatchUpdateException( ErrorCode.fromRpcStatus( From 016d96eeb5f7d025cd1b882a18a7744b5a61547a Mon Sep 17 00:00:00 2001 From: Guangyu Shi Date: Wed, 6 Mar 2019 14:22:21 -0800 Subject: [PATCH 5/6] Fix code format with mvn com.coveo:fmt-maven-plugin:format. --- .../spanner/SpannerBatchUpdateException.java | 9 +--- .../com/google/cloud/spanner/SpannerImpl.java | 18 ++++---- .../cloud/spanner/TransactionContext.java | 8 ++-- .../cloud/spanner/spi/v1/SpannerRpc.java | 2 +- .../cloud/spanner/it/ITBatchDmlTest.java | 42 +++++++------------ 5 files changed, 32 insertions(+), 47 deletions(-) diff --git a/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerBatchUpdateException.java b/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerBatchUpdateException.java index a2cc026cf16b..62d738f571d4 100644 --- a/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerBatchUpdateException.java +++ b/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerBatchUpdateException.java @@ -20,17 +20,12 @@ public class SpannerBatchUpdateException extends SpannerException { private long[] updateCounts; /** Private constructor. Use {@link SpannerExceptionFactory} to create instances. */ SpannerBatchUpdateException( - DoNotConstructDirectly token, - ErrorCode code, - String message, - long[] counts) { + DoNotConstructDirectly token, ErrorCode code, String message, long[] counts) { super(token, code, false, message, null); updateCounts = counts; } - /** - * Returns the number of rows affected by each statement that is successfully run. - */ + /** Returns the number of rows affected by each statement that is successfully run. */ public long[] getUpdateCounts() { return updateCounts; } diff --git a/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerImpl.java b/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerImpl.java index 73485c26f0be..c8e3506ca596 100644 --- a/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerImpl.java +++ b/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerImpl.java @@ -1085,19 +1085,19 @@ ExecuteSqlRequest.Builder getExecuteSqlRequestBuilder( ExecuteBatchDmlRequest.Builder getExecuteBatchDmlRequestBuilder( Iterable statements) { ExecuteBatchDmlRequest.Builder builder = - ExecuteBatchDmlRequest.newBuilder() - .setSession(session.name); + ExecuteBatchDmlRequest.newBuilder().setSession(session.name); int idx = 0; for (Statement stmt : statements) { builder.addStatementsBuilder(); builder.getStatementsBuilder(idx).setSql(stmt.getSql()); Map stmtParameters = stmt.getParameters(); if (!stmtParameters.isEmpty()) { - com.google.protobuf.Struct.Builder paramsBuilder = builder.getStatementsBuilder(idx) - .getParamsBuilder(); + com.google.protobuf.Struct.Builder paramsBuilder = + builder.getStatementsBuilder(idx).getParamsBuilder(); for (Map.Entry param : stmtParameters.entrySet()) { paramsBuilder.putFields(param.getKey(), param.getValue().toProto()); - builder.getStatementsBuilder(idx) + builder + .getStatementsBuilder(idx) .putParamTypes(param.getKey(), param.getValue().getType().toProto()); } } @@ -1696,8 +1696,7 @@ public com.google.spanner.v1.ResultSet call() throws Exception { @Override public long[] batchUpdate(Iterable statements) { beforeReadOrQuery(); - final ExecuteBatchDmlRequest.Builder builder = - getExecuteBatchDmlRequestBuilder(statements); + final ExecuteBatchDmlRequest.Builder builder = getExecuteBatchDmlRequestBuilder(statements); com.google.spanner.v1.ExecuteBatchDmlResponse response = runWithRetries( new Callable() { @@ -1713,8 +1712,9 @@ public com.google.spanner.v1.ExecuteBatchDmlResponse call() throws Exception { if (response.getStatus().getCode() != 0) { throw newSpannerBatchUpdateException( - ErrorCode.fromRpcStatus( - response.getStatus()), response.getStatus().getMessage(), results); + ErrorCode.fromRpcStatus(response.getStatus()), + response.getStatus().getMessage(), + results); } return results; } diff --git a/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionContext.java b/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionContext.java index b68b3674f4e6..a529c4c492be 100644 --- a/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionContext.java +++ b/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionContext.java @@ -108,11 +108,11 @@ public interface TransactionContext extends ReadContext { * loop. This method returns an array of long integers, each representing the number of rows * modified by each statement. * - * If an individual statement fails, execution stops and a {@code - * SpannerBatchUpdateException} is returned, which includes the error and the number of rows - * affected by the statements that are run prior to the error. + *

If an individual statement fails, execution stops and a {@code SpannerBatchUpdateException} + * is returned, which includes the error and the number of rows affected by the statements that + * are run prior to the error. * - * For example, if statements contains 3 statements, and the 2nd one is not a valid DML. This + *

For example, if statements contains 3 statements, and the 2nd one is not a valid DML. This * method throws a {@code SpannerBatchUpdateException} that contains the error message from the * 2nd statement, and an array of length 1 that contains the number of rows modified by the 1st * statement. The 3rd statement will not run. diff --git a/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/SpannerRpc.java b/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/SpannerRpc.java index 26f16de2bb26..500f369f67a8 100644 --- a/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/SpannerRpc.java +++ b/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/SpannerRpc.java @@ -216,7 +216,7 @@ StreamingCall read( StreamingCall executeQuery( ExecuteSqlRequest request, ResultStreamConsumer consumer, @Nullable Map options); - ExecuteBatchDmlResponse executeBatchDml(ExecuteBatchDmlRequest build, Map options); + ExecuteBatchDmlResponse executeBatchDml(ExecuteBatchDmlRequest build, Map options); Transaction beginTransaction(BeginTransactionRequest request, @Nullable Map options) throws SpannerException; diff --git a/google-cloud-clients/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITBatchDmlTest.java b/google-cloud-clients/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITBatchDmlTest.java index 0af46bc24179..3c01698e0a8a 100644 --- a/google-cloud-clients/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITBatchDmlTest.java +++ b/google-cloud-clients/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITBatchDmlTest.java @@ -19,21 +19,21 @@ import static com.google.common.truth.Truth.assertThat; import com.google.api.gax.longrunning.OperationFuture; -import com.google.cloud.spanner.ErrorCode; -import com.google.cloud.spanner.SpannerBatchUpdateException; -import com.google.cloud.spanner.SpannerException; -import com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; import com.google.cloud.spanner.Database; import com.google.cloud.spanner.DatabaseClient; +import com.google.cloud.spanner.ErrorCode; import com.google.cloud.spanner.IntegrationTest; import com.google.cloud.spanner.IntegrationTestEnv; +import com.google.cloud.spanner.SpannerBatchUpdateException; +import com.google.cloud.spanner.SpannerException; import com.google.cloud.spanner.Statement; import com.google.cloud.spanner.TransactionContext; import com.google.cloud.spanner.TransactionRunner; import com.google.cloud.spanner.TransactionRunner.TransactionCallable; +import com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -50,8 +50,7 @@ public final class ITBatchDmlTest { private static Database db; - @ClassRule - public static IntegrationTestEnv env = new IntegrationTestEnv(); + @ClassRule public static IntegrationTestEnv env = new IntegrationTestEnv(); private static final String INSERT_DML = "INSERT INTO T (k, v) VALUES ('boo1', 1), ('boo2', 2), ('boo3', 3), ('boo4', 4);"; @@ -61,28 +60,22 @@ public final class ITBatchDmlTest { @BeforeClass public static void createDatabase() { - db = - env.getTestHelper() - .createTestDatabase(); + db = env.getTestHelper().createTestDatabase(); client = env.getTestHelper().getDatabaseClient(db); } @Before public void createTable() throws Exception { - String ddl = "CREATE TABLE T (" - + " K STRING(MAX) NOT NULL," - + " V INT64," - + ") PRIMARY KEY (K)"; - OperationFuture op = db.updateDdl( - Arrays.asList(ddl), null); + String ddl = + "CREATE TABLE T (" + " K STRING(MAX) NOT NULL," + " V INT64," + ") PRIMARY KEY (K)"; + OperationFuture op = db.updateDdl(Arrays.asList(ddl), null); op.get(); } @After public void dropTable() throws Exception { String ddl = "DROP TABLE T"; - OperationFuture op = db.updateDdl( - Arrays.asList(ddl), null); + OperationFuture op = db.updateDdl(Arrays.asList(ddl), null); op.get(); } @@ -100,8 +93,7 @@ public long[] run(TransactionContext transaction) { } catch (SpannerException e) { assertThat(e instanceof SpannerBatchUpdateException).isFalse(); assertThat(e.getErrorCode()).isEqualTo(ErrorCode.INVALID_ARGUMENT); - assertThat(e.getMessage()) - .contains("No statements in batch DML request."); + assertThat(e.getMessage()).contains("No statements in batch DML request."); rowCounts = new long[0]; } return rowCounts; @@ -177,8 +169,7 @@ public long[] run(TransactionContext transaction) { Assert.fail("Expecting an exception."); } catch (SpannerBatchUpdateException e) { assertThat(e.getErrorCode()).isEqualTo(ErrorCode.INVALID_ARGUMENT); - assertThat(e.getMessage()) - .contains("is not valid DML."); + assertThat(e.getMessage()).contains("is not valid DML."); long[] rowCounts = e.getUpdateCounts(); assertThat(rowCounts.length).isEqualTo(1); for (long rc : rowCounts) { @@ -195,7 +186,7 @@ public void errorBatchDmlAlreadyExist() { public long[] run(TransactionContext transaction) { List stmts = new ArrayList<>(); stmts.add(Statement.of(INSERT_DML)); - stmts.add(Statement.of(INSERT_DML)); // should fail + stmts.add(Statement.of(INSERT_DML)); // should fail stmts.add(Statement.of(UPDATE_DML)); return transaction.batchUpdate(stmts); } @@ -215,4 +206,3 @@ public long[] run(TransactionContext transaction) { } } } - From d31e45f504ffcedbc2a78dafed2448a93256377f Mon Sep 17 00:00:00 2001 From: Guangyu Shi Date: Wed, 6 Mar 2019 15:11:14 -0800 Subject: [PATCH 6/6] Update year in file headers. --- .../com/google/cloud/spanner/SpannerBatchUpdateException.java | 2 +- .../test/java/com/google/cloud/spanner/it/ITBatchDmlTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerBatchUpdateException.java b/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerBatchUpdateException.java index 62d738f571d4..0e51c5f91f31 100644 --- a/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerBatchUpdateException.java +++ b/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerBatchUpdateException.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 Google LLC + * Copyright 2019 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/google-cloud-clients/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITBatchDmlTest.java b/google-cloud-clients/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITBatchDmlTest.java index 3c01698e0a8a..c5090d10e4e5 100644 --- a/google-cloud-clients/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITBatchDmlTest.java +++ b/google-cloud-clients/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITBatchDmlTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 Google LLC + * Copyright 2019 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License.