diff --git a/.github/workflows/hermetic_library_generation.yaml b/.github/workflows/hermetic_library_generation.yaml index f7804e9d026..545f190487a 100644 --- a/.github/workflows/hermetic_library_generation.yaml +++ b/.github/workflows/hermetic_library_generation.yaml @@ -37,7 +37,7 @@ jobs: with: fetch-depth: 0 token: ${{ secrets.CLOUD_JAVA_BOT_TOKEN }} - - uses: googleapis/sdk-platform-java/.github/scripts@v2.52.0 + - uses: googleapis/sdk-platform-java/.github/scripts@v2.53.0 if: env.SHOULD_RUN == 'true' with: base_ref: ${{ github.base_ref }} diff --git a/.github/workflows/integration-tests-against-emulator-with-multiplexed-session.yaml b/.github/workflows/integration-tests-against-emulator-with-multiplexed-session.yaml index bd7dfef3972..6dda467b64e 100644 --- a/.github/workflows/integration-tests-against-emulator-with-multiplexed-session.yaml +++ b/.github/workflows/integration-tests-against-emulator-with-multiplexed-session.yaml @@ -39,4 +39,4 @@ jobs: env: JOB_TYPE: test SPANNER_EMULATOR_HOST: localhost:9010 - GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS: true + GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS: true \ No newline at end of file diff --git a/.github/workflows/unmanaged_dependency_check.yaml b/.github/workflows/unmanaged_dependency_check.yaml index 49ae94b5ef1..0f6016e7ca3 100644 --- a/.github/workflows/unmanaged_dependency_check.yaml +++ b/.github/workflows/unmanaged_dependency_check.yaml @@ -17,6 +17,6 @@ jobs: # repository .kokoro/build.sh - name: Unmanaged dependency check - uses: googleapis/sdk-platform-java/java-shared-dependencies/unmanaged-dependency-check@google-cloud-shared-dependencies/v3.42.0 + uses: googleapis/sdk-platform-java/java-shared-dependencies/unmanaged-dependency-check@google-cloud-shared-dependencies/v3.43.0 with: bom-path: google-cloud-spanner-bom/pom.xml diff --git a/.kokoro/presubmit/graalvm-native-17.cfg b/.kokoro/presubmit/graalvm-native-17.cfg index b78c82df571..8d3f11dc47e 100644 --- a/.kokoro/presubmit/graalvm-native-17.cfg +++ b/.kokoro/presubmit/graalvm-native-17.cfg @@ -3,7 +3,7 @@ # Configure the docker image for kokoro-trampoline. env_vars: { key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_b:3.42.0" + value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_b:3.43.0" } env_vars: { diff --git a/.kokoro/presubmit/graalvm-native.cfg b/.kokoro/presubmit/graalvm-native.cfg index f08c4be3e00..f78bafd26ec 100644 --- a/.kokoro/presubmit/graalvm-native.cfg +++ b/.kokoro/presubmit/graalvm-native.cfg @@ -3,7 +3,7 @@ # Configure the docker image for kokoro-trampoline. env_vars: { key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_a:3.42.0" + value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_a:3.43.0" } env_vars: { diff --git a/CHANGELOG.md b/CHANGELOG.md index 97f78c2d0fb..b4b269263e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,28 @@ # Changelog +## [6.87.0](https://github.com/googleapis/java-spanner/compare/v6.86.0...v6.87.0) (2025-02-20) + + +### Features + +* Add AddSplitPoints API ([a5ebcd3](https://github.com/googleapis/java-spanner/commit/a5ebcd343a67c57d61362cfb0ccb4888f5503681)) +* Add option for multiplexed sessions with partitioned operations ([#3635](https://github.com/googleapis/java-spanner/issues/3635)) ([dc89b4d](https://github.com/googleapis/java-spanner/commit/dc89b4d7663f0e40a9169b21243f2d94f2fc5749)) +* Add option to indicate that a statement is the last in a transaction ([#3647](https://github.com/googleapis/java-spanner/issues/3647)) ([b04ea80](https://github.com/googleapis/java-spanner/commit/b04ea804cfa9551b4d7c49cd83f0ef1120942423)) +* Adding gfe_latencies metric to built-in metrics ([#3490](https://github.com/googleapis/java-spanner/issues/3490)) ([314dadc](https://github.com/googleapis/java-spanner/commit/314dadc31f4a5aa798d45886db7231c1bd8b7a91)) +* **spanner:** Support multiplexed session for read-write transactions ([#3608](https://github.com/googleapis/java-spanner/issues/3608)) ([bda78ed](https://github.com/googleapis/java-spanner/commit/bda78edaba827acf974c87c335868a6f8caa38f2)) + + +### Bug Fixes + +* **deps:** Update the Java code generator (gapic-generator-java) to 2.53.0 ([20a3d0d](https://github.com/googleapis/java-spanner/commit/20a3d0da41509ffca66c77de6771fc8080930613)) +* **spanner:** End spans for read-write methods ([#3629](https://github.com/googleapis/java-spanner/issues/3629)) ([4a1f99c](https://github.com/googleapis/java-spanner/commit/4a1f99c6bb872ffc08e60d3843e4cdfc4efa2690)) +* **spanner:** Release resources in TransactionManager ([#3638](https://github.com/googleapis/java-spanner/issues/3638)) ([e0a3e5b](https://github.com/googleapis/java-spanner/commit/e0a3e5bd169e28e349a2dc92f86a2a9b5510f8f6)) + + +### Dependencies + +* Update dependency com.google.cloud:sdk-platform-java-config to v3.43.0 ([#3642](https://github.com/googleapis/java-spanner/issues/3642)) ([c12968a](https://github.com/googleapis/java-spanner/commit/c12968a5f6dad95017d9867d96d4f19a26643a07)) + ## [6.86.0](https://github.com/googleapis/java-spanner/compare/v6.85.0...v6.86.0) (2025-01-31) diff --git a/README.md b/README.md index 5ba1e55e9e1..639b0d9925e 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ If you are using Maven with [BOM][libraries-bom], add this to your pom.xml file: com.google.cloud libraries-bom - 26.53.0 + 26.54.0 pom import @@ -49,20 +49,20 @@ If you are using Maven without the BOM, add this to your dependencies: If you are using Gradle 5.x or later, add this to your dependencies: ```Groovy -implementation platform('com.google.cloud:libraries-bom:26.53.0') +implementation platform('com.google.cloud:libraries-bom:26.54.0') implementation 'com.google.cloud:google-cloud-spanner' ``` If you are using Gradle without BOM, add this to your dependencies: ```Groovy -implementation 'com.google.cloud:google-cloud-spanner:6.86.0' +implementation 'com.google.cloud:google-cloud-spanner:6.87.0' ``` If you are using SBT, add this to your dependencies: ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-spanner" % "6.86.0" +libraryDependencies += "com.google.cloud" % "google-cloud-spanner" % "6.87.0" ``` ## Authentication @@ -727,7 +727,7 @@ Java is a registered trademark of Oracle and/or its affiliates. [kokoro-badge-link-5]: http://storage.googleapis.com/cloud-devrel-public/java/badges/java-spanner/java11.html [stability-image]: https://img.shields.io/badge/stability-stable-green [maven-version-image]: https://img.shields.io/maven-central/v/com.google.cloud/google-cloud-spanner.svg -[maven-version-link]: https://central.sonatype.com/artifact/com.google.cloud/google-cloud-spanner/6.86.0 +[maven-version-link]: https://central.sonatype.com/artifact/com.google.cloud/google-cloud-spanner/6.87.0 [authentication]: https://github.com/googleapis/google-cloud-java#authentication [auth-scopes]: https://developers.google.com/identity/protocols/oauth2/scopes [predefined-iam-roles]: https://cloud.google.com/iam/docs/understanding-roles#predefined_roles diff --git a/benchmarks/pom.xml b/benchmarks/pom.xml index 50172f24c98..255b64175bf 100644 --- a/benchmarks/pom.xml +++ b/benchmarks/pom.xml @@ -24,7 +24,7 @@ com.google.cloud google-cloud-spanner-parent - 6.86.0 + 6.87.0 diff --git a/generation_config.yaml b/generation_config.yaml index a2188111195..257ca065090 100644 --- a/generation_config.yaml +++ b/generation_config.yaml @@ -1,6 +1,6 @@ -gapic_generator_version: 2.52.0 -googleapis_commitish: 2a3db2e19f48a139db9f40278c0217df110add9b -libraries_bom_version: 26.53.0 +gapic_generator_version: 2.53.0 +googleapis_commitish: 9605bff3d36fbdb1227b26bce68258c5f00815e4 +libraries_bom_version: 26.54.0 libraries: - api_shortname: spanner name_pretty: Cloud Spanner diff --git a/google-cloud-spanner-bom/pom.xml b/google-cloud-spanner-bom/pom.xml index 9fa2e2f3b2e..3e3df860023 100644 --- a/google-cloud-spanner-bom/pom.xml +++ b/google-cloud-spanner-bom/pom.xml @@ -3,12 +3,12 @@ 4.0.0 com.google.cloud google-cloud-spanner-bom - 6.86.0 + 6.87.0 pom com.google.cloud sdk-platform-java-config - 3.42.0 + 3.43.0 Google Cloud Spanner BOM @@ -53,43 +53,43 @@ com.google.cloud google-cloud-spanner - 6.86.0 + 6.87.0 com.google.cloud google-cloud-spanner test-jar - 6.86.0 + 6.87.0 com.google.api.grpc grpc-google-cloud-spanner-v1 - 6.86.0 + 6.87.0 com.google.api.grpc grpc-google-cloud-spanner-admin-instance-v1 - 6.86.0 + 6.87.0 com.google.api.grpc grpc-google-cloud-spanner-admin-database-v1 - 6.86.0 + 6.87.0 com.google.api.grpc proto-google-cloud-spanner-admin-instance-v1 - 6.86.0 + 6.87.0 com.google.api.grpc proto-google-cloud-spanner-v1 - 6.86.0 + 6.87.0 com.google.api.grpc proto-google-cloud-spanner-admin-database-v1 - 6.86.0 + 6.87.0 diff --git a/google-cloud-spanner-executor/pom.xml b/google-cloud-spanner-executor/pom.xml index cd596a746e1..ded694843c2 100644 --- a/google-cloud-spanner-executor/pom.xml +++ b/google-cloud-spanner-executor/pom.xml @@ -5,14 +5,14 @@ 4.0.0 com.google.cloud google-cloud-spanner-executor - 6.86.0 + 6.87.0 jar Google Cloud Spanner Executor com.google.cloud google-cloud-spanner-parent - 6.86.0 + 6.87.0 @@ -46,6 +46,16 @@ com.google.cloud.opentelemetry exporter-trace 0.33.0 + + + io.opentelemetry.semconv + opentelemetry-semconv + + + + + io.opentelemetry.semconv + opentelemetry-semconv com.google.cloud @@ -266,7 +276,7 @@ org.apache.maven.plugins maven-dependency-plugin - com.google.api:gax,org.apache.maven.surefire:surefire-junit4 + com.google.api:gax,org.apache.maven.surefire:surefire-junit4,io.opentelemetry.semconv:opentelemetry-semconv diff --git a/google-cloud-spanner-executor/src/main/resources/META-INF/native-image/com.google.cloud.spanner.executor.v1/reflect-config.json b/google-cloud-spanner-executor/src/main/resources/META-INF/native-image/com.google.cloud.spanner.executor.v1/reflect-config.json index 25668dd3de8..1b9c4de2d83 100644 --- a/google-cloud-spanner-executor/src/main/resources/META-INF/native-image/com.google.cloud.spanner.executor.v1/reflect-config.json +++ b/google-cloud-spanner-executor/src/main/resources/META-INF/native-image/com.google.cloud.spanner.executor.v1/reflect-config.json @@ -1700,6 +1700,42 @@ "allDeclaredClasses": true, "allPublicClasses": true }, + { + "name": "com.google.spanner.admin.database.v1.AddSplitPointsRequest", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + }, + { + "name": "com.google.spanner.admin.database.v1.AddSplitPointsRequest$Builder", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + }, + { + "name": "com.google.spanner.admin.database.v1.AddSplitPointsResponse", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + }, + { + "name": "com.google.spanner.admin.database.v1.AddSplitPointsResponse$Builder", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + }, { "name": "com.google.spanner.admin.database.v1.Backup", "queryAllDeclaredConstructors": true, @@ -2618,6 +2654,42 @@ "allDeclaredClasses": true, "allPublicClasses": true }, + { + "name": "com.google.spanner.admin.database.v1.SplitPoints", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + }, + { + "name": "com.google.spanner.admin.database.v1.SplitPoints$Builder", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + }, + { + "name": "com.google.spanner.admin.database.v1.SplitPoints$Key", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + }, + { + "name": "com.google.spanner.admin.database.v1.SplitPoints$Key$Builder", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + }, { "name": "com.google.spanner.admin.database.v1.UpdateBackupRequest", "queryAllDeclaredConstructors": true, diff --git a/google-cloud-spanner/pom.xml b/google-cloud-spanner/pom.xml index 43deca608da..9f7aadb8a15 100644 --- a/google-cloud-spanner/pom.xml +++ b/google-cloud-spanner/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.google.cloud google-cloud-spanner - 6.86.0 + 6.87.0 jar Google Cloud Spanner https://github.com/googleapis/java-spanner @@ -11,7 +11,7 @@ com.google.cloud google-cloud-spanner-parent - 6.86.0 + 6.87.0 google-cloud-spanner @@ -371,7 +371,7 @@ junit test - + com.google.api.grpc 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 ea21266e19d..145ad67f827 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 @@ -698,6 +698,9 @@ ExecuteSqlRequest.Builder getExecuteSqlRequestBuilder( if (!isReadOnly()) { builder.setSeqno(getSeqNo()); } + if (options.hasLastStatement()) { + builder.setLastStatement(options.isLastStatement()); + } builder.setQueryOptions(buildQueryOptions(statement.getQueryOptions())); builder.setRequestOptions(buildRequestOptions(options)); return builder; @@ -743,6 +746,9 @@ ExecuteBatchDmlRequest.Builder getExecuteBatchDmlRequestBuilder( if (selector != null) { builder.setTransaction(selector); } + if (options.hasLastStatement()) { + builder.setLastStatements(options.isLastStatement()); + } builder.setSeqno(getSeqNo()); builder.setRequestOptions(buildRequestOptions(options)); return builder; diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncTransactionManagerImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncTransactionManagerImpl.java index 0057bb15bea..1578de87cdb 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncTransactionManagerImpl.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncTransactionManagerImpl.java @@ -55,7 +55,7 @@ public void setSpan(ISpan span) { @Override public void close() { - closeAsync(); + SpannerApiFutures.get(closeAsync()); } @Override @@ -183,6 +183,10 @@ public ApiFuture rollbackAsync() { @Override public TransactionContextFuture resetForRetryAsync() { + if (txn == null || !txn.isAborted() && txnState != TransactionState.ABORTED) { + throw new IllegalStateException( + "resetForRetry can only be called if the previous attempt aborted"); + } return new TransactionContextFutureImpl(this, internalBeginAsync(false)); } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BatchClientImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BatchClientImpl.java index a250fd5ba39..9cf06164beb 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BatchClientImpl.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BatchClientImpl.java @@ -22,6 +22,7 @@ import com.google.cloud.spanner.Options.QueryOption; import com.google.cloud.spanner.Options.ReadOption; import com.google.cloud.spanner.spi.v1.SpannerRpc; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.protobuf.Struct; @@ -35,6 +36,7 @@ import java.time.Instant; import java.util.List; import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.ReentrantLock; import javax.annotation.Nullable; @@ -59,6 +61,13 @@ public class BatchClientImpl implements BatchClient { @GuardedBy("multiplexedSessionLock") private final AtomicReference multiplexedSessionReference; + /** + * This flag is set to true if the server return UNIMPLEMENTED when partitioned transaction is + * executed on a multiplexed session. TODO: Remove once this is guaranteed to be available. + */ + @VisibleForTesting + static final AtomicBoolean unimplementedForPartitionedOps = new AtomicBoolean(false); + BatchClientImpl(SessionClient sessionClient, boolean isMultiplexedSessionEnabled) { this.sessionClient = checkNotNull(sessionClient); this.isMultiplexedSessionEnabled = isMultiplexedSessionEnabled; @@ -85,7 +94,7 @@ public String getDatabaseRole() { @Override public BatchReadOnlyTransaction batchReadOnlyTransaction(TimestampBound bound) { SessionImpl session; - if (isMultiplexedSessionEnabled) { + if (canUseMultiplexedSession()) { session = getMultiplexedSession(); } else { session = sessionClient.createSession(); @@ -131,6 +140,10 @@ public BatchReadOnlyTransaction batchReadOnlyTransaction(BatchTransactionId batc batchTransactionId); } + private boolean canUseMultiplexedSession() { + return isMultiplexedSessionEnabled && !unimplementedForPartitionedOps.get(); + } + private SessionImpl getMultiplexedSession() { this.multiplexedSessionLock.lock(); try { @@ -216,15 +229,26 @@ public List partitionReadUsingIndex( builder.setPartitionOptions(pbuilder.build()); final PartitionReadRequest request = builder.build(); - PartitionResponse response = rpc.partitionRead(request, options); - ImmutableList.Builder partitions = ImmutableList.builder(); - for (com.google.spanner.v1.Partition p : response.getPartitionsList()) { - Partition partition = - Partition.createReadPartition( - p.getPartitionToken(), partitionOptions, table, index, keys, columns, readOptions); - partitions.add(partition); + try { + PartitionResponse response = rpc.partitionRead(request, options); + ImmutableList.Builder partitions = ImmutableList.builder(); + for (com.google.spanner.v1.Partition p : response.getPartitionsList()) { + Partition partition = + Partition.createReadPartition( + p.getPartitionToken(), + partitionOptions, + table, + index, + keys, + columns, + readOptions); + partitions.add(partition); + } + return partitions.build(); + } catch (SpannerException e) { + maybeMarkUnimplementedForPartitionedOps(e); + throw e; } - return partitions.build(); } @Override @@ -256,15 +280,27 @@ public List partitionQuery( builder.setPartitionOptions(pbuilder.build()); final PartitionQueryRequest request = builder.build(); - PartitionResponse response = rpc.partitionQuery(request, options); - ImmutableList.Builder partitions = ImmutableList.builder(); - for (com.google.spanner.v1.Partition p : response.getPartitionsList()) { - Partition partition = - Partition.createQueryPartition( - p.getPartitionToken(), partitionOptions, statement, queryOptions); - partitions.add(partition); + try { + PartitionResponse response = rpc.partitionQuery(request, options); + ImmutableList.Builder partitions = ImmutableList.builder(); + for (com.google.spanner.v1.Partition p : response.getPartitionsList()) { + Partition partition = + Partition.createQueryPartition( + p.getPartitionToken(), partitionOptions, statement, queryOptions); + partitions.add(partition); + } + return partitions.build(); + } catch (SpannerException e) { + maybeMarkUnimplementedForPartitionedOps(e); + throw e; + } + } + + void maybeMarkUnimplementedForPartitionedOps(SpannerException spannerException) { + if (MultiplexedSessionDatabaseClient.verifyErrorMessage( + spannerException, "Partitioned operations are not supported with multiplexed sessions")) { + unimplementedForPartitionedOps.set(true); } - return partitions.build(); } @Override diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BuiltInMetricsConstant.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BuiltInMetricsConstant.java index 4f8b091d550..4adf53d7e40 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BuiltInMetricsConstant.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BuiltInMetricsConstant.java @@ -34,9 +34,9 @@ public class BuiltInMetricsConstant { public static final String METER_NAME = "spanner.googleapis.com/internal/client"; - public static final String GAX_METER_NAME = OpenTelemetryMetricsRecorder.GAX_METER_NAME; - + static final String SPANNER_METER_NAME = "spanner-java"; + static final String GFE_LATENCIES_NAME = "gfe_latencies"; static final String OPERATION_LATENCIES_NAME = "operation_latencies"; static final String ATTEMPT_LATENCIES_NAME = "attempt_latencies"; static final String OPERATION_LATENCY_NAME = "operation_latency"; @@ -49,7 +49,8 @@ public class BuiltInMetricsConstant { OPERATION_LATENCIES_NAME, ATTEMPT_LATENCIES_NAME, OPERATION_COUNT_NAME, - ATTEMPT_COUNT_NAME) + ATTEMPT_COUNT_NAME, + GFE_LATENCIES_NAME) .stream() .map(m -> METER_NAME + '/' + m) .collect(Collectors.toSet()); @@ -114,6 +115,7 @@ static Map getAllViews() { ImmutableMap.Builder views = ImmutableMap.builder(); defineView( views, + BuiltInMetricsConstant.GAX_METER_NAME, BuiltInMetricsConstant.OPERATION_LATENCY_NAME, BuiltInMetricsConstant.OPERATION_LATENCIES_NAME, BuiltInMetricsConstant.AGGREGATION_WITH_MILLIS_HISTOGRAM, @@ -121,6 +123,7 @@ static Map getAllViews() { "ms"); defineView( views, + BuiltInMetricsConstant.GAX_METER_NAME, BuiltInMetricsConstant.ATTEMPT_LATENCY_NAME, BuiltInMetricsConstant.ATTEMPT_LATENCIES_NAME, BuiltInMetricsConstant.AGGREGATION_WITH_MILLIS_HISTOGRAM, @@ -128,6 +131,15 @@ static Map getAllViews() { "ms"); defineView( views, + BuiltInMetricsConstant.SPANNER_METER_NAME, + BuiltInMetricsConstant.GFE_LATENCIES_NAME, + BuiltInMetricsConstant.GFE_LATENCIES_NAME, + BuiltInMetricsConstant.AGGREGATION_WITH_MILLIS_HISTOGRAM, + InstrumentType.HISTOGRAM, + "ms"); + defineView( + views, + BuiltInMetricsConstant.GAX_METER_NAME, BuiltInMetricsConstant.OPERATION_COUNT_NAME, BuiltInMetricsConstant.OPERATION_COUNT_NAME, Aggregation.sum(), @@ -135,6 +147,7 @@ static Map getAllViews() { "1"); defineView( views, + BuiltInMetricsConstant.GAX_METER_NAME, BuiltInMetricsConstant.ATTEMPT_COUNT_NAME, BuiltInMetricsConstant.ATTEMPT_COUNT_NAME, Aggregation.sum(), @@ -145,6 +158,7 @@ static Map getAllViews() { private static void defineView( ImmutableMap.Builder viewMap, + String meterName, String metricName, String metricViewName, Aggregation aggregation, @@ -153,7 +167,7 @@ private static void defineView( InstrumentSelector selector = InstrumentSelector.builder() .setName(BuiltInMetricsConstant.METER_NAME + '/' + metricName) - .setMeterName(BuiltInMetricsConstant.GAX_METER_NAME) + .setMeterName(meterName) .setType(type) .setUnit(unit) .build(); diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BuiltInOpenTelemetryMetricsProvider.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BuiltInMetricsProvider.java similarity index 95% rename from google-cloud-spanner/src/main/java/com/google/cloud/spanner/BuiltInOpenTelemetryMetricsProvider.java rename to google-cloud-spanner/src/main/java/com/google/cloud/spanner/BuiltInMetricsProvider.java index 4aeb98987d1..f624f310f77 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BuiltInOpenTelemetryMetricsProvider.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BuiltInMetricsProvider.java @@ -46,25 +46,24 @@ import java.util.logging.Logger; import javax.annotation.Nullable; -final class BuiltInOpenTelemetryMetricsProvider { +final class BuiltInMetricsProvider { - static BuiltInOpenTelemetryMetricsProvider INSTANCE = new BuiltInOpenTelemetryMetricsProvider(); + static BuiltInMetricsProvider INSTANCE = new BuiltInMetricsProvider(); - private static final Logger logger = - Logger.getLogger(BuiltInOpenTelemetryMetricsProvider.class.getName()); + private static final Logger logger = Logger.getLogger(BuiltInMetricsProvider.class.getName()); private static String taskId; private OpenTelemetry openTelemetry; - private BuiltInOpenTelemetryMetricsProvider() {} + private BuiltInMetricsProvider() {} OpenTelemetry getOrCreateOpenTelemetry( String projectId, @Nullable Credentials credentials, @Nullable String monitoringHost) { try { if (this.openTelemetry == null) { SdkMeterProviderBuilder sdkMeterProviderBuilder = SdkMeterProvider.builder(); - BuiltInOpenTelemetryMetricsView.registerBuiltinMetrics( + BuiltInMetricsView.registerBuiltinMetrics( SpannerCloudMonitoringExporter.create(projectId, credentials, monitoringHost), sdkMeterProviderBuilder); SdkMeterProvider sdkMeterProvider = sdkMeterProviderBuilder.build(); diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BuiltInMetricsRecorder.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BuiltInMetricsRecorder.java new file mode 100644 index 00000000000..a12da470b61 --- /dev/null +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BuiltInMetricsRecorder.java @@ -0,0 +1,82 @@ +/* + * Copyright 2025 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; + +import com.google.api.gax.core.GaxProperties; +import com.google.api.gax.tracing.OpenTelemetryMetricsRecorder; +import com.google.common.base.Preconditions; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.api.metrics.DoubleHistogram; +import io.opentelemetry.api.metrics.Meter; +import java.util.Map; + +/** + * Implementation for recording built in metrics. + * + *

This class extends the {@link OpenTelemetryMetricsRecorder} which implements the * + * measurements related to the lifecyle of an RPC. + */ +class BuiltInMetricsRecorder extends OpenTelemetryMetricsRecorder { + + private final DoubleHistogram gfeLatencyRecorder; + + /** + * Creates the following instruments for the following metrics: + * + *

    + *
  • GFE Latency: Histogram + *
+ * + * @param openTelemetry OpenTelemetry instance + * @param serviceName Service Name + */ + BuiltInMetricsRecorder(OpenTelemetry openTelemetry, String serviceName) { + super(openTelemetry, serviceName); + Meter meter = + openTelemetry + .meterBuilder(BuiltInMetricsConstant.SPANNER_METER_NAME) + .setInstrumentationVersion(GaxProperties.getLibraryVersion(getClass())) + .build(); + this.gfeLatencyRecorder = + meter + .histogramBuilder(serviceName + '/' + BuiltInMetricsConstant.GFE_LATENCIES_NAME) + .setDescription( + "Latency between Google's network receiving an RPC and reading back the first byte of the response") + .setUnit("ms") + .build(); + } + + /** + * Record the latency between Google's network receiving an RPC and reading back the first byte of + * the response. Data is stored in a Histogram. + * + * @param gfeLatency Attempt Latency in ms + * @param attributes Map of the attributes to store + */ + void recordGFELatency(double gfeLatency, Map attributes) { + gfeLatencyRecorder.record(gfeLatency, toOtelAttributes(attributes)); + } + + Attributes toOtelAttributes(Map attributes) { + Preconditions.checkNotNull(attributes, "Attributes map cannot be null"); + AttributesBuilder attributesBuilder = Attributes.builder(); + attributes.forEach(attributesBuilder::put); + return attributesBuilder.build(); + } +} diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BuiltInMetricsTracer.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BuiltInMetricsTracer.java new file mode 100644 index 00000000000..6faff5ad6d7 --- /dev/null +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BuiltInMetricsTracer.java @@ -0,0 +1,156 @@ +/* + * Copyright 2024 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; + +import com.google.api.gax.rpc.ApiException; +import com.google.api.gax.rpc.StatusCode; +import com.google.api.gax.tracing.ApiTracer; +import com.google.api.gax.tracing.MethodName; +import com.google.api.gax.tracing.MetricsTracer; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.CancellationException; +import javax.annotation.Nullable; + +/** + * Implements built-in metrics tracer. + * + *

This class extends the {@link MetricsTracer} which computes generic metrics that can be + * observed in the lifecycle of an RPC operation. + */ +class BuiltInMetricsTracer extends MetricsTracer implements ApiTracer { + + private final BuiltInMetricsRecorder builtInOpenTelemetryMetricsRecorder; + // These are RPC specific attributes and pertain to a specific API Trace + private final Map attributes = new HashMap<>(); + + private Long gfeLatency = null; + + BuiltInMetricsTracer( + MethodName methodName, BuiltInMetricsRecorder builtInOpenTelemetryMetricsRecorder) { + super(methodName, builtInOpenTelemetryMetricsRecorder); + this.builtInOpenTelemetryMetricsRecorder = builtInOpenTelemetryMetricsRecorder; + this.attributes.put(METHOD_ATTRIBUTE, methodName.toString()); + } + + /** + * Adds an annotation that the attempt succeeded. Successful attempt add "OK" value to the status + * attribute key. + */ + @Override + public void attemptSucceeded() { + super.attemptSucceeded(); + if (gfeLatency != null) { + attributes.put(STATUS_ATTRIBUTE, StatusCode.Code.OK.toString()); + builtInOpenTelemetryMetricsRecorder.recordGFELatency(gfeLatency, attributes); + } + } + + /** + * Add an annotation that the attempt was cancelled by the user. Cancelled attempt add "CANCELLED" + * to the status attribute key. + */ + @Override + public void attemptCancelled() { + super.attemptCancelled(); + if (gfeLatency != null) { + attributes.put(STATUS_ATTRIBUTE, StatusCode.Code.CANCELLED.toString()); + builtInOpenTelemetryMetricsRecorder.recordGFELatency(gfeLatency, attributes); + } + } + + /** + * Adds an annotation that the attempt failed, but another attempt will be made after the delay. + * + * @param error the error that caused the attempt to fail. + * @param delay the amount of time to wait before the next attempt will start. + *

Failed attempt extracts the error from the throwable and adds it to the status attribute + * key. + */ + @Override + public void attemptFailedDuration(Throwable error, java.time.Duration delay) { + super.attemptFailedDuration(error, delay); + if (gfeLatency != null) { + attributes.put(STATUS_ATTRIBUTE, extractStatus(error)); + builtInOpenTelemetryMetricsRecorder.recordGFELatency(gfeLatency, attributes); + } + } + + /** + * Adds an annotation that the attempt failed and that no further attempts will be made because + * retry limits have been reached. This extracts the error from the throwable and adds it to the + * status attribute key. + * + * @param error the last error received before retries were exhausted. + */ + @Override + public void attemptFailedRetriesExhausted(Throwable error) { + super.attemptFailedRetriesExhausted(error); + if (gfeLatency != null) { + attributes.put(STATUS_ATTRIBUTE, extractStatus(error)); + builtInOpenTelemetryMetricsRecorder.recordGFELatency(gfeLatency, attributes); + } + } + + /** + * Adds an annotation that the attempt failed and that no further attempts will be made because + * the last error was not retryable. This extracts the error from the throwable and adds it to the + * status attribute key. + * + * @param error the error that caused the final attempt to fail. + */ + @Override + public void attemptPermanentFailure(Throwable error) { + super.attemptPermanentFailure(error); + if (gfeLatency != null) { + attributes.put(STATUS_ATTRIBUTE, extractStatus(error)); + builtInOpenTelemetryMetricsRecorder.recordGFELatency(gfeLatency, attributes); + } + } + + void recordGFELatency(Long gfeLatency) { + this.gfeLatency = gfeLatency; + } + + @Override + public void addAttributes(Map attributes) { + super.addAttributes(attributes); + this.attributes.putAll(attributes); + }; + + @Override + public void addAttributes(String key, String value) { + super.addAttributes(key, value); + this.attributes.put(key, value); + } + + private static String extractStatus(@Nullable Throwable error) { + final String statusString; + + if (error == null) { + return StatusCode.Code.OK.toString(); + } else if (error instanceof CancellationException) { + statusString = StatusCode.Code.CANCELLED.toString(); + } else if (error instanceof ApiException) { + statusString = ((ApiException) error).getStatusCode().getCode().toString(); + } else { + statusString = StatusCode.Code.UNKNOWN.toString(); + } + + return statusString; + } +} diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BuiltInMetricsTracerFactory.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BuiltInMetricsTracerFactory.java new file mode 100644 index 00000000000..42c19dd72a0 --- /dev/null +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BuiltInMetricsTracerFactory.java @@ -0,0 +1,62 @@ +/* + * Copyright 2024 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; + +import com.google.api.gax.tracing.ApiTracer; +import com.google.api.gax.tracing.ApiTracerFactory; +import com.google.api.gax.tracing.MethodName; +import com.google.api.gax.tracing.MetricsTracer; +import com.google.api.gax.tracing.MetricsTracerFactory; +import com.google.api.gax.tracing.SpanName; +import com.google.common.collect.ImmutableMap; +import java.util.Map; + +/** + * A {@link ApiTracerFactory} to build instances of {@link MetricsTracer}. + * + *

This class extends the {@link MetricsTracerFactory} which wraps the {@link + * BuiltInMetricsRecorder} and pass it to {@link BuiltInMetricsTracer}. It will be * used to record + * metrics in {@link BuiltInMetricsTracer}. + * + *

This class is expected to be initialized once during client initialization. + */ +class BuiltInMetricsTracerFactory extends MetricsTracerFactory { + + protected BuiltInMetricsRecorder builtInMetricsRecorder; + private final Map attributes; + + /** + * Pass in a Map of client level attributes which will be added to every single MetricsTracer + * created from the ApiTracerFactory. + */ + public BuiltInMetricsTracerFactory( + BuiltInMetricsRecorder builtInMetricsRecorder, Map attributes) { + super(builtInMetricsRecorder, attributes); + this.builtInMetricsRecorder = builtInMetricsRecorder; + this.attributes = ImmutableMap.copyOf(attributes); + } + + @Override + public ApiTracer newTracer(ApiTracer parent, SpanName spanName, OperationType operationType) { + BuiltInMetricsTracer metricsTracer = + new BuiltInMetricsTracer( + MethodName.of(spanName.getClientName(), spanName.getMethodName()), + builtInMetricsRecorder); + metricsTracer.addAttributes(attributes); + return metricsTracer; + } +} diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BuiltInOpenTelemetryMetricsView.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BuiltInMetricsView.java similarity index 93% rename from google-cloud-spanner/src/main/java/com/google/cloud/spanner/BuiltInOpenTelemetryMetricsView.java rename to google-cloud-spanner/src/main/java/com/google/cloud/spanner/BuiltInMetricsView.java index 4a09c0d856a..e72eeb9425a 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BuiltInOpenTelemetryMetricsView.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BuiltInMetricsView.java @@ -20,9 +20,9 @@ import io.opentelemetry.sdk.metrics.export.MetricExporter; import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; -class BuiltInOpenTelemetryMetricsView { +class BuiltInMetricsView { - private BuiltInOpenTelemetryMetricsView() {} + private BuiltInMetricsView() {} /** Register built-in metrics on the {@link SdkMeterProviderBuilder} with credentials. */ static void registerBuiltinMetrics( diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/CompositeTracer.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/CompositeTracer.java index 60d7081cc1e..5268e9046f8 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/CompositeTracer.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/CompositeTracer.java @@ -190,4 +190,12 @@ public void addAttributes(Map attributes) { } } } + + public void recordGFELatency(Long gfeLatency) { + for (ApiTracer child : children) { + if (child instanceof BuiltInMetricsTracer) { + ((BuiltInMetricsTracer) child).recordGFELatency(gfeLatency); + } + } + } } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DatabaseClientImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DatabaseClientImpl.java index 92971ff320f..ed5b0179349 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DatabaseClientImpl.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DatabaseClientImpl.java @@ -124,6 +124,12 @@ private boolean canUseMultiplexedSessionsForRW() { && this.multiplexedSessionDatabaseClient.isMultiplexedSessionsForRWSupported(); } + private boolean canUseMultiplexedSessionsForPartitionedOps() { + return this.useMultiplexedSessionPartitionedOps + && this.multiplexedSessionDatabaseClient != null + && this.multiplexedSessionDatabaseClient.isMultiplexedSessionsForPartitionedOpsSupported(); + } + @Override public Dialect getDialect() { return pool.getDialect(); @@ -323,8 +329,15 @@ public AsyncTransactionManager transactionManagerAsync(TransactionOption... opti @Override public long executePartitionedUpdate(final Statement stmt, final UpdateOption... options) { - if (useMultiplexedSessionPartitionedOps) { - return getMultiplexedSession().executePartitionedUpdate(stmt, options); + + if (canUseMultiplexedSessionsForPartitionedOps()) { + try { + return getMultiplexedSession().executePartitionedUpdate(stmt, options); + } catch (SpannerException e) { + if (!multiplexedSessionDatabaseClient.maybeMarkUnimplementedForPartitionedOps(e)) { + throw e; + } + } } return executePartitionedUpdateWithPooledSession(stmt, options); } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/MultiplexedSessionDatabaseClient.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/MultiplexedSessionDatabaseClient.java index 33ddcdeb0cb..235d663360d 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/MultiplexedSessionDatabaseClient.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/MultiplexedSessionDatabaseClient.java @@ -104,6 +104,11 @@ void onError(SpannerException spannerException) { // UNIMPLEMENTED with error message "Transaction type read_write not supported with // multiplexed sessions" is returned. this.client.maybeMarkUnimplementedForRW(spannerException); + // Mark multiplexed sessions for Partitioned Ops as unimplemented and fall back to regular + // sessions if + // UNIMPLEMENTED with error message "Partitioned operations are not supported with multiplexed + // sessions". + this.client.maybeMarkUnimplementedForPartitionedOps(spannerException); } @Override @@ -214,6 +219,12 @@ public void close() { */ @VisibleForTesting final AtomicBoolean unimplementedForRW = new AtomicBoolean(false); + /** + * This flag is set to true if the server return UNIMPLEMENTED when partitioned transaction is + * executed on a multiplexed session. TODO: Remove once this is guaranteed to be available. + */ + @VisibleForTesting final AtomicBoolean unimplementedForPartitionedOps = new AtomicBoolean(false); + MultiplexedSessionDatabaseClient(SessionClient sessionClient) { this(sessionClient, Clock.systemUTC()); } @@ -316,7 +327,18 @@ && verifyErrorMessage( } } - private boolean verifyErrorMessage(SpannerException spannerException, String message) { + boolean maybeMarkUnimplementedForPartitionedOps(SpannerException spannerException) { + if (spannerException.getErrorCode() == ErrorCode.UNIMPLEMENTED + && verifyErrorMessage( + spannerException, + "Transaction type partitioned_dml not supported with multiplexed sessions")) { + unimplementedForPartitionedOps.set(true); + return true; + } + return false; + } + + static boolean verifyErrorMessage(SpannerException spannerException, String message) { if (spannerException.getCause() == null) { return false; } @@ -391,6 +413,10 @@ boolean isMultiplexedSessionsForRWSupported() { return !this.unimplementedForRW.get(); } + boolean isMultiplexedSessionsForPartitionedOpsSupported() { + return !this.unimplementedForPartitionedOps.get(); + } + void close() { synchronized (this) { if (!this.isClosed) { 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 c062e89ec2b..c8c588f813a 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 @@ -108,6 +108,9 @@ public interface ReadQueryUpdateTransactionOption /** Marker interface to mark options applicable to Update and Write operations */ public interface UpdateTransactionOption extends UpdateOption, TransactionOption {} + /** Marker interface for options that can be used with both executeQuery and executeUpdate. */ + public interface QueryUpdateOption extends QueryOption, UpdateOption {} + /** * Marker interface to mark options applicable to Create, Update and Delete operations in admin * API. @@ -236,6 +239,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 QueryUpdateOption lastStatement() { + return new LastStatementUpdateOption(); + } + /** * Specifying this will cause the list operation to start fetching the record from this onwards. */ @@ -494,6 +511,7 @@ void appendToOptions(Options options) { private DecodeMode decodeMode; private RpcOrderBy orderBy; private RpcLockHint lockHint; + private Boolean lastStatement; // Construction is via factory methods below. private Options() {} @@ -630,6 +648,14 @@ OrderBy orderBy() { return orderBy == null ? null : orderBy.proto; } + boolean hasLastStatement() { + return lastStatement != null; + } + + Boolean isLastStatement() { + return lastStatement; + } + boolean hasLockHint() { return lockHint != null; } @@ -694,6 +720,9 @@ public String toString() { if (orderBy != null) { b.append("orderBy: ").append(orderBy).append(' '); } + if (lastStatement != null) { + b.append("lastStatement: ").append(lastStatement).append(' '); + } if (lockHint != null) { b.append("lockHint: ").append(lockHint).append(' '); } @@ -737,6 +766,7 @@ public boolean equals(Object o) { && Objects.equals(dataBoostEnabled(), that.dataBoostEnabled()) && Objects.equals(directedReadOptions(), that.directedReadOptions()) && Objects.equals(orderBy(), that.orderBy()) + && Objects.equals(isLastStatement(), that.isLastStatement()) && Objects.equals(lockHint(), that.lockHint()); } @@ -797,6 +827,9 @@ public int hashCode() { if (orderBy != null) { result = 31 * result + orderBy.hashCode(); } + if (lastStatement != null) { + result = 31 * result + lastStatement.hashCode(); + } if (lockHint != null) { result = 31 * result + lockHint.hashCode(); } @@ -965,4 +998,24 @@ public boolean equals(Object o) { return Objects.equals(filter, ((FilterOption) o).filter); } } + + static final class LastStatementUpdateOption extends InternalOption implements QueryUpdateOption { + + LastStatementUpdateOption() {} + + @Override + void appendToOptions(Options options) { + options.lastStatement = true; + } + + @Override + public int hashCode() { + return LastStatementUpdateOption.class.hashCode(); + } + + @Override + public boolean equals(Object o) { + return o instanceof LastStatementUpdateOption; + } + } } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPoolOptions.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPoolOptions.java index 03551640b43..171c10c9c92 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPoolOptions.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPoolOptions.java @@ -360,7 +360,7 @@ public boolean getUseMultiplexedSessionForRW() { @VisibleForTesting @InternalApi public boolean getUseMultiplexedSessionPartitionedOps() { - return useMultiplexedSessionForPartitionedOps; + return getUseMultiplexedSession() && useMultiplexedSessionForPartitionedOps; } private static Boolean getUseMultiplexedSessionFromEnvVariable() { @@ -370,9 +370,7 @@ private static Boolean getUseMultiplexedSessionFromEnvVariable() { @VisibleForTesting @InternalApi protected static Boolean getUseMultiplexedSessionFromEnvVariablePartitionedOps() { - // Checks the value of env, GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS_PARTITIONED_OPS - // This returns null until Partitioned Operations is supported. - return null; + return parseBooleanEnvVariable("GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS_PARTITIONED_OPS"); } private static Boolean parseBooleanEnvVariable(String variableName) { @@ -390,7 +388,7 @@ private static Boolean parseBooleanEnvVariable(String variableName) { private static Boolean getUseMultiplexedSessionForRWFromEnvVariable() { // Checks the value of env, GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS_FOR_RW // This returns null until RW is supported. - return null; + return parseBooleanEnvVariable("GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS_FOR_RW"); } Duration getMultiplexedSessionMaintenanceDuration() { diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerCloudMonitoringExporterUtils.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerCloudMonitoringExporterUtils.java index 21fcba8194d..620430b87df 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerCloudMonitoringExporterUtils.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerCloudMonitoringExporterUtils.java @@ -25,6 +25,7 @@ import static com.google.cloud.spanner.BuiltInMetricsConstant.GAX_METER_NAME; import static com.google.cloud.spanner.BuiltInMetricsConstant.INSTANCE_ID_KEY; import static com.google.cloud.spanner.BuiltInMetricsConstant.PROJECT_ID_KEY; +import static com.google.cloud.spanner.BuiltInMetricsConstant.SPANNER_METER_NAME; import static com.google.cloud.spanner.BuiltInMetricsConstant.SPANNER_PROMOTED_RESOURCE_LABELS; import static com.google.cloud.spanner.BuiltInMetricsConstant.SPANNER_RESOURCE_TYPE; @@ -75,8 +76,9 @@ static List convertToSpannerTimeSeries(List collection) List allTimeSeries = new ArrayList<>(); for (MetricData metricData : collection) { - // Get common metrics data from GAX library - if (!metricData.getInstrumentationScopeInfo().getName().equals(GAX_METER_NAME)) { + // Get metrics data from GAX library and Spanner library + if (!(metricData.getInstrumentationScopeInfo().getName().equals(GAX_METER_NAME) + || metricData.getInstrumentationScopeInfo().getName().equals(SPANNER_METER_NAME))) { // Filter out metric data for instruments that are not part of the spanner metrics list continue; } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerExceptionFactory.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerExceptionFactory.java index 2dd70ce108e..a3f174cda60 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerExceptionFactory.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerExceptionFactory.java @@ -265,6 +265,9 @@ static ErrorDetails extractErrorDetails(Throwable cause) { if (cause instanceof ApiException) { return ((ApiException) cause).getErrorDetails(); } + if (cause instanceof SpannerException) { + return ((SpannerException) cause).getErrorDetails(); + } prevCause = cause; cause = cause.getCause(); } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerOptions.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerOptions.java index 42fc0c2d0bd..5b63ff4fe44 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerOptions.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerOptions.java @@ -33,8 +33,6 @@ import com.google.api.gax.rpc.TransportChannelProvider; import com.google.api.gax.tracing.ApiTracerFactory; import com.google.api.gax.tracing.BaseApiTracerFactory; -import com.google.api.gax.tracing.MetricsTracerFactory; -import com.google.api.gax.tracing.OpenTelemetryMetricsRecorder; import com.google.api.gax.tracing.OpencensusTracerFactory; import com.google.cloud.NoCredentials; import com.google.cloud.ServiceDefaults; @@ -144,8 +142,7 @@ public class SpannerOptions extends ServiceOptions { private final boolean autoThrottleAdministrativeRequests; private final RetrySettings retryAdministrativeRequestsSettings; private final boolean trackTransactionStarter; - private final BuiltInOpenTelemetryMetricsProvider builtInOpenTelemetryMetricsProvider = - BuiltInOpenTelemetryMetricsProvider.INSTANCE; + private final BuiltInMetricsProvider builtInMetricsProvider = BuiltInMetricsProvider.INSTANCE; /** * These are the default {@link QueryOptions} defined by the user on this {@link SpannerOptions}. */ @@ -1910,13 +1907,13 @@ private ApiTracerFactory getDefaultApiTracerFactory() { private ApiTracerFactory createMetricsApiTracerFactory() { OpenTelemetry openTelemetry = - this.builtInOpenTelemetryMetricsProvider.getOrCreateOpenTelemetry( + this.builtInMetricsProvider.getOrCreateOpenTelemetry( this.getProjectId(), getCredentials(), this.monitoringHost); return openTelemetry != null - ? new MetricsTracerFactory( - new OpenTelemetryMetricsRecorder(openTelemetry, BuiltInMetricsConstant.METER_NAME), - builtInOpenTelemetryMetricsProvider.createClientAttributes( + ? new BuiltInMetricsTracerFactory( + new BuiltInMetricsRecorder(openTelemetry, BuiltInMetricsConstant.METER_NAME), + builtInMetricsProvider.createClientAttributes( this.getProjectId(), "spanner-java/" + GaxProperties.getLibraryVersion(getClass()))) : null; } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TraceWrapper.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TraceWrapper.java index 606a54fe8b7..df5874cb3c6 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TraceWrapper.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TraceWrapper.java @@ -47,6 +47,8 @@ class TraceWrapper { private static final AttributeKey> DB_STATEMENT_ARRAY_KEY = AttributeKey.stringArrayKey("db.statement"); private static final AttributeKey DB_TABLE_NAME_KEY = AttributeKey.stringKey("db.table"); + private static final AttributeKey CLOUD_REGION_KEY = + AttributeKey.stringKey("cloud.region"); private static final AttributeKey GCP_CLIENT_SERVICE_KEY = AttributeKey.stringKey("gcp.client.service"); private static final AttributeKey GCP_CLIENT_VERSION_KEY = @@ -214,6 +216,7 @@ Attributes createCommonAttributes(DatabaseId db) { builder.put(GCP_CLIENT_SERVICE_KEY, "spanner"); builder.put(GCP_CLIENT_REPO_KEY, "googleapis/java-spanner"); builder.put(GCP_CLIENT_VERSION_KEY, GaxProperties.getLibraryVersion(TraceWrapper.class)); + builder.put(CLOUD_REGION_KEY, BuiltInMetricsProvider.detectClientLocation()); return builder.build(); } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionManagerImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionManagerImpl.java index b1d37f3e4cd..bbf34ab5c8f 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionManagerImpl.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionManagerImpl.java @@ -80,6 +80,13 @@ public void commit() { } catch (SpannerException e2) { txnState = TransactionState.COMMIT_FAILED; throw e2; + } finally { + // At this point, if the TransactionState is not ABORTED, then the transaction has reached an + // end state. + // We can safely call close() to release resources. + if (getState() != TransactionState.ABORTED) { + close(); + } } } @@ -92,6 +99,9 @@ public void rollback() { txn.rollback(); } finally { txnState = TransactionState.ROLLED_BACK; + // At this point, the TransactionState is ROLLED_BACK which is an end state. + // We can safely call close() to release resources. + close(); } } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/XGoogSpannerRequestId.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/XGoogSpannerRequestId.java new file mode 100644 index 00000000000..4f6c0114750 --- /dev/null +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/XGoogSpannerRequestId.java @@ -0,0 +1,88 @@ +/* + * Copyright 2025 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; + +import com.google.api.core.InternalApi; +import com.google.common.annotations.VisibleForTesting; +import java.math.BigInteger; +import java.security.SecureRandom; +import java.util.Objects; + +@InternalApi +public class XGoogSpannerRequestId { + // 1. Generate the random process Id singleton. + @VisibleForTesting + static final String RAND_PROCESS_ID = XGoogSpannerRequestId.generateRandProcessId(); + + @VisibleForTesting + static final long VERSION = 1; // The version of the specification being implemented. + + private final long nthClientId; + private final long nthChannelId; + private final long nthRequest; + private long attempt; + + XGoogSpannerRequestId(long nthClientId, long nthChannelId, long nthRequest, long attempt) { + this.nthClientId = nthClientId; + this.nthChannelId = nthChannelId; + this.nthRequest = nthRequest; + this.attempt = attempt; + } + + public static XGoogSpannerRequestId of( + long nthClientId, long nthChannelId, long nthRequest, long attempt) { + return new XGoogSpannerRequestId(nthClientId, nthChannelId, nthRequest, attempt); + } + + private static String generateRandProcessId() { + // Expecting to use 64-bits of randomness to avoid clashes. + BigInteger bigInt = new BigInteger(64, new SecureRandom()); + return String.format("%016x", bigInt); + } + + @Override + public String toString() { + return String.format( + "%d.%s.%d.%d.%d.%d", + XGoogSpannerRequestId.VERSION, + XGoogSpannerRequestId.RAND_PROCESS_ID, + this.nthClientId, + this.nthChannelId, + this.nthRequest, + this.attempt); + } + + @Override + public boolean equals(Object other) { + // instanceof for a null object returns false. + if (!(other instanceof XGoogSpannerRequestId)) { + return false; + } + + XGoogSpannerRequestId otherReqId = (XGoogSpannerRequestId) (other); + + return Objects.equals(this.nthClientId, otherReqId.nthClientId) + && Objects.equals(this.nthChannelId, otherReqId.nthChannelId) + && Objects.equals(this.nthRequest, otherReqId.nthRequest) + && Objects.equals(this.attempt, otherReqId.attempt); + } + + @Override + public int hashCode() { + return Objects.hash(this.nthClientId, this.nthChannelId, this.nthRequest, this.attempt); + } +} diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/DatabaseAdminClient.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/DatabaseAdminClient.java index 416913e22f7..b91188619aa 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/DatabaseAdminClient.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/DatabaseAdminClient.java @@ -41,6 +41,8 @@ import com.google.protobuf.Empty; import com.google.protobuf.FieldMask; import com.google.protobuf.Timestamp; +import com.google.spanner.admin.database.v1.AddSplitPointsRequest; +import com.google.spanner.admin.database.v1.AddSplitPointsResponse; import com.google.spanner.admin.database.v1.Backup; import com.google.spanner.admin.database.v1.BackupName; import com.google.spanner.admin.database.v1.BackupSchedule; @@ -78,6 +80,7 @@ import com.google.spanner.admin.database.v1.ListDatabasesResponse; import com.google.spanner.admin.database.v1.RestoreDatabaseMetadata; import com.google.spanner.admin.database.v1.RestoreDatabaseRequest; +import com.google.spanner.admin.database.v1.SplitPoints; import com.google.spanner.admin.database.v1.UpdateBackupRequest; import com.google.spanner.admin.database.v1.UpdateBackupScheduleRequest; import com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata; @@ -524,6 +527,25 @@ * * * + *

AddSplitPoints + *

Adds split points to specified tables, indexes of a database. + * + *

Request object method variants only take one parameter, a request object, which must be constructed before the call.

+ *
    + *
  • addSplitPoints(AddSplitPointsRequest request) + *

+ *

"Flattened" method variants have converted the fields of the request object into function parameters to enable multiple ways to call the same method.

+ *
    + *
  • addSplitPoints(DatabaseName database, List<SplitPoints> splitPoints) + *

  • addSplitPoints(String database, List<SplitPoints> splitPoints) + *

+ *

Callable method variants take no parameters and return an immutable API callable object, which can be used to initiate calls to the service.

+ *
    + *
  • addSplitPointsCallable() + *

+ * + * + * *

CreateBackupSchedule *

Creates a new backup schedule. * @@ -4318,6 +4340,137 @@ public final ListDatabaseRolesPagedResponse listDatabaseRoles(ListDatabaseRolesR return stub.listDatabaseRolesCallable(); } + // AUTO-GENERATED DOCUMENTATION AND METHOD. + /** + * Adds split points to specified tables, indexes of a database. + * + *

Sample code: + * + *

{@code
+   * // This snippet has been automatically generated and should be regarded as a code template only.
+   * // It will require modifications to work:
+   * // - It may require correct/in-range values for request initialization.
+   * // - It may require specifying regional endpoints when creating the service client as shown in
+   * // https://cloud.google.com/java/docs/setup#configure_endpoints_for_the_client_library
+   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
+   *   DatabaseName database = DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]");
+   *   List splitPoints = new ArrayList<>();
+   *   AddSplitPointsResponse response = databaseAdminClient.addSplitPoints(database, splitPoints);
+   * }
+   * }
+ * + * @param database Required. The database on whose tables/indexes split points are to be added. + * Values are of the form + * `projects/<project>/instances/<instance>/databases/<database>`. + * @param splitPoints Required. The split points to add. + * @throws com.google.api.gax.rpc.ApiException if the remote call fails + */ + public final AddSplitPointsResponse addSplitPoints( + DatabaseName database, List splitPoints) { + AddSplitPointsRequest request = + AddSplitPointsRequest.newBuilder() + .setDatabase(database == null ? null : database.toString()) + .addAllSplitPoints(splitPoints) + .build(); + return addSplitPoints(request); + } + + // AUTO-GENERATED DOCUMENTATION AND METHOD. + /** + * Adds split points to specified tables, indexes of a database. + * + *

Sample code: + * + *

{@code
+   * // This snippet has been automatically generated and should be regarded as a code template only.
+   * // It will require modifications to work:
+   * // - It may require correct/in-range values for request initialization.
+   * // - It may require specifying regional endpoints when creating the service client as shown in
+   * // https://cloud.google.com/java/docs/setup#configure_endpoints_for_the_client_library
+   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
+   *   String database = DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]").toString();
+   *   List splitPoints = new ArrayList<>();
+   *   AddSplitPointsResponse response = databaseAdminClient.addSplitPoints(database, splitPoints);
+   * }
+   * }
+ * + * @param database Required. The database on whose tables/indexes split points are to be added. + * Values are of the form + * `projects/<project>/instances/<instance>/databases/<database>`. + * @param splitPoints Required. The split points to add. + * @throws com.google.api.gax.rpc.ApiException if the remote call fails + */ + public final AddSplitPointsResponse addSplitPoints( + String database, List splitPoints) { + AddSplitPointsRequest request = + AddSplitPointsRequest.newBuilder() + .setDatabase(database) + .addAllSplitPoints(splitPoints) + .build(); + return addSplitPoints(request); + } + + // AUTO-GENERATED DOCUMENTATION AND METHOD. + /** + * Adds split points to specified tables, indexes of a database. + * + *

Sample code: + * + *

{@code
+   * // This snippet has been automatically generated and should be regarded as a code template only.
+   * // It will require modifications to work:
+   * // - It may require correct/in-range values for request initialization.
+   * // - It may require specifying regional endpoints when creating the service client as shown in
+   * // https://cloud.google.com/java/docs/setup#configure_endpoints_for_the_client_library
+   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
+   *   AddSplitPointsRequest request =
+   *       AddSplitPointsRequest.newBuilder()
+   *           .setDatabase(DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]").toString())
+   *           .addAllSplitPoints(new ArrayList())
+   *           .setInitiator("initiator-248987089")
+   *           .build();
+   *   AddSplitPointsResponse response = databaseAdminClient.addSplitPoints(request);
+   * }
+   * }
+ * + * @param request The request object containing all of the parameters for the API call. + * @throws com.google.api.gax.rpc.ApiException if the remote call fails + */ + public final AddSplitPointsResponse addSplitPoints(AddSplitPointsRequest request) { + return addSplitPointsCallable().call(request); + } + + // AUTO-GENERATED DOCUMENTATION AND METHOD. + /** + * Adds split points to specified tables, indexes of a database. + * + *

Sample code: + * + *

{@code
+   * // This snippet has been automatically generated and should be regarded as a code template only.
+   * // It will require modifications to work:
+   * // - It may require correct/in-range values for request initialization.
+   * // - It may require specifying regional endpoints when creating the service client as shown in
+   * // https://cloud.google.com/java/docs/setup#configure_endpoints_for_the_client_library
+   * try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
+   *   AddSplitPointsRequest request =
+   *       AddSplitPointsRequest.newBuilder()
+   *           .setDatabase(DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]").toString())
+   *           .addAllSplitPoints(new ArrayList())
+   *           .setInitiator("initiator-248987089")
+   *           .build();
+   *   ApiFuture future =
+   *       databaseAdminClient.addSplitPointsCallable().futureCall(request);
+   *   // Do something.
+   *   AddSplitPointsResponse response = future.get();
+   * }
+   * }
+ */ + public final UnaryCallable + addSplitPointsCallable() { + return stub.addSplitPointsCallable(); + } + // AUTO-GENERATED DOCUMENTATION AND METHOD. /** * Creates a new backup schedule. diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/DatabaseAdminSettings.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/DatabaseAdminSettings.java index 097695ebece..399dcbf38c8 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/DatabaseAdminSettings.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/DatabaseAdminSettings.java @@ -44,6 +44,8 @@ import com.google.iam.v1.TestIamPermissionsResponse; import com.google.longrunning.Operation; import com.google.protobuf.Empty; +import com.google.spanner.admin.database.v1.AddSplitPointsRequest; +import com.google.spanner.admin.database.v1.AddSplitPointsResponse; import com.google.spanner.admin.database.v1.Backup; import com.google.spanner.admin.database.v1.BackupSchedule; import com.google.spanner.admin.database.v1.CopyBackupMetadata; @@ -312,6 +314,11 @@ public UnaryCallSettings restoreDatabaseSetti return ((DatabaseAdminStubSettings) getStubSettings()).listDatabaseRolesSettings(); } + /** Returns the object with the settings used for calls to addSplitPoints. */ + public UnaryCallSettings addSplitPointsSettings() { + return ((DatabaseAdminStubSettings) getStubSettings()).addSplitPointsSettings(); + } + /** Returns the object with the settings used for calls to createBackupSchedule. */ public UnaryCallSettings createBackupScheduleSettings() { @@ -606,6 +613,12 @@ public UnaryCallSettings.Builder restoreDatab return getStubSettingsBuilder().listDatabaseRolesSettings(); } + /** Returns the builder for the settings used for calls to addSplitPoints. */ + public UnaryCallSettings.Builder + addSplitPointsSettings() { + return getStubSettingsBuilder().addSplitPointsSettings(); + } + /** Returns the builder for the settings used for calls to createBackupSchedule. */ public UnaryCallSettings.Builder createBackupScheduleSettings() { diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/gapic_metadata.json b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/gapic_metadata.json index 7d6c894d7b6..96dc31e91d7 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/gapic_metadata.json +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/gapic_metadata.json @@ -10,6 +10,9 @@ "grpc": { "libraryClient": "DatabaseAdminClient", "rpcs": { + "AddSplitPoints": { + "methods": ["addSplitPoints", "addSplitPoints", "addSplitPoints", "addSplitPointsCallable"] + }, "CopyBackup": { "methods": ["copyBackupAsync", "copyBackupAsync", "copyBackupAsync", "copyBackupAsync", "copyBackupAsync", "copyBackupOperationCallable", "copyBackupCallable"] }, diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/stub/DatabaseAdminStub.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/stub/DatabaseAdminStub.java index 7250a8c60f6..7926008bab3 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/stub/DatabaseAdminStub.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/stub/DatabaseAdminStub.java @@ -34,6 +34,8 @@ import com.google.longrunning.Operation; import com.google.longrunning.stub.OperationsStub; import com.google.protobuf.Empty; +import com.google.spanner.admin.database.v1.AddSplitPointsRequest; +import com.google.spanner.admin.database.v1.AddSplitPointsResponse; import com.google.spanner.admin.database.v1.Backup; import com.google.spanner.admin.database.v1.BackupSchedule; import com.google.spanner.admin.database.v1.CopyBackupMetadata; @@ -231,6 +233,10 @@ public UnaryCallable restoreDatabaseCallable( throw new UnsupportedOperationException("Not implemented: listDatabaseRolesCallable()"); } + public UnaryCallable addSplitPointsCallable() { + throw new UnsupportedOperationException("Not implemented: addSplitPointsCallable()"); + } + public UnaryCallable createBackupScheduleCallable() { throw new UnsupportedOperationException("Not implemented: createBackupScheduleCallable()"); } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/stub/DatabaseAdminStubSettings.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/stub/DatabaseAdminStubSettings.java index 246e44438ef..b2624c5a9a9 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/stub/DatabaseAdminStubSettings.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/stub/DatabaseAdminStubSettings.java @@ -64,6 +64,8 @@ import com.google.iam.v1.TestIamPermissionsResponse; import com.google.longrunning.Operation; import com.google.protobuf.Empty; +import com.google.spanner.admin.database.v1.AddSplitPointsRequest; +import com.google.spanner.admin.database.v1.AddSplitPointsResponse; import com.google.spanner.admin.database.v1.Backup; import com.google.spanner.admin.database.v1.BackupSchedule; import com.google.spanner.admin.database.v1.CopyBackupMetadata; @@ -240,6 +242,8 @@ public class DatabaseAdminStubSettings extends StubSettings listDatabaseRolesSettings; + private final UnaryCallSettings + addSplitPointsSettings; private final UnaryCallSettings createBackupScheduleSettings; private final UnaryCallSettings @@ -745,6 +749,11 @@ public UnaryCallSettings restoreDatabaseSetti return listDatabaseRolesSettings; } + /** Returns the object with the settings used for calls to addSplitPoints. */ + public UnaryCallSettings addSplitPointsSettings() { + return addSplitPointsSettings; + } + /** Returns the object with the settings used for calls to createBackupSchedule. */ public UnaryCallSettings createBackupScheduleSettings() { @@ -912,6 +921,7 @@ protected DatabaseAdminStubSettings(Builder settingsBuilder) throws IOException listDatabaseOperationsSettings = settingsBuilder.listDatabaseOperationsSettings().build(); listBackupOperationsSettings = settingsBuilder.listBackupOperationsSettings().build(); listDatabaseRolesSettings = settingsBuilder.listDatabaseRolesSettings().build(); + addSplitPointsSettings = settingsBuilder.addSplitPointsSettings().build(); createBackupScheduleSettings = settingsBuilder.createBackupScheduleSettings().build(); getBackupScheduleSettings = settingsBuilder.getBackupScheduleSettings().build(); updateBackupScheduleSettings = settingsBuilder.updateBackupScheduleSettings().build(); @@ -978,6 +988,8 @@ public static class Builder extends StubSettings.Builder listDatabaseRolesSettings; + private final UnaryCallSettings.Builder + addSplitPointsSettings; private final UnaryCallSettings.Builder createBackupScheduleSettings; private final UnaryCallSettings.Builder @@ -1095,6 +1107,7 @@ protected Builder(ClientContext clientContext) { listBackupOperationsSettings = PagedCallSettings.newBuilder(LIST_BACKUP_OPERATIONS_PAGE_STR_FACT); listDatabaseRolesSettings = PagedCallSettings.newBuilder(LIST_DATABASE_ROLES_PAGE_STR_FACT); + addSplitPointsSettings = UnaryCallSettings.newUnaryCallSettingsBuilder(); createBackupScheduleSettings = UnaryCallSettings.newUnaryCallSettingsBuilder(); getBackupScheduleSettings = UnaryCallSettings.newUnaryCallSettingsBuilder(); updateBackupScheduleSettings = UnaryCallSettings.newUnaryCallSettingsBuilder(); @@ -1124,6 +1137,7 @@ protected Builder(ClientContext clientContext) { listDatabaseOperationsSettings, listBackupOperationsSettings, listDatabaseRolesSettings, + addSplitPointsSettings, createBackupScheduleSettings, getBackupScheduleSettings, updateBackupScheduleSettings, @@ -1161,6 +1175,7 @@ protected Builder(DatabaseAdminStubSettings settings) { listDatabaseOperationsSettings = settings.listDatabaseOperationsSettings.toBuilder(); listBackupOperationsSettings = settings.listBackupOperationsSettings.toBuilder(); listDatabaseRolesSettings = settings.listDatabaseRolesSettings.toBuilder(); + addSplitPointsSettings = settings.addSplitPointsSettings.toBuilder(); createBackupScheduleSettings = settings.createBackupScheduleSettings.toBuilder(); getBackupScheduleSettings = settings.getBackupScheduleSettings.toBuilder(); updateBackupScheduleSettings = settings.updateBackupScheduleSettings.toBuilder(); @@ -1189,6 +1204,7 @@ protected Builder(DatabaseAdminStubSettings settings) { listDatabaseOperationsSettings, listBackupOperationsSettings, listDatabaseRolesSettings, + addSplitPointsSettings, createBackupScheduleSettings, getBackupScheduleSettings, updateBackupScheduleSettings, @@ -1321,6 +1337,11 @@ private static Builder initDefaults(Builder builder) { .setRetryableCodes(RETRYABLE_CODE_DEFINITIONS.get("retry_policy_0_codes")) .setRetrySettings(RETRY_PARAM_DEFINITIONS.get("retry_policy_0_params")); + builder + .addSplitPointsSettings() + .setRetryableCodes(RETRYABLE_CODE_DEFINITIONS.get("retry_policy_0_codes")) + .setRetrySettings(RETRY_PARAM_DEFINITIONS.get("retry_policy_0_params")); + builder .createBackupScheduleSettings() .setRetryableCodes(RETRYABLE_CODE_DEFINITIONS.get("retry_policy_0_codes")) @@ -1661,6 +1682,12 @@ public UnaryCallSettings.Builder restoreDatab return listDatabaseRolesSettings; } + /** Returns the builder for the settings used for calls to addSplitPoints. */ + public UnaryCallSettings.Builder + addSplitPointsSettings() { + return addSplitPointsSettings; + } + /** Returns the builder for the settings used for calls to createBackupSchedule. */ public UnaryCallSettings.Builder createBackupScheduleSettings() { diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/stub/GrpcDatabaseAdminStub.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/stub/GrpcDatabaseAdminStub.java index 5726370d364..9e6270f3c24 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/stub/GrpcDatabaseAdminStub.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/stub/GrpcDatabaseAdminStub.java @@ -39,6 +39,8 @@ import com.google.longrunning.Operation; import com.google.longrunning.stub.GrpcOperationsStub; import com.google.protobuf.Empty; +import com.google.spanner.admin.database.v1.AddSplitPointsRequest; +import com.google.spanner.admin.database.v1.AddSplitPointsResponse; import com.google.spanner.admin.database.v1.Backup; import com.google.spanner.admin.database.v1.BackupSchedule; import com.google.spanner.admin.database.v1.CopyBackupMetadata; @@ -285,6 +287,17 @@ public class GrpcDatabaseAdminStub extends DatabaseAdminStub { ProtoUtils.marshaller(ListDatabaseRolesResponse.getDefaultInstance())) .build(); + private static final MethodDescriptor + addSplitPointsMethodDescriptor = + MethodDescriptor.newBuilder() + .setType(MethodDescriptor.MethodType.UNARY) + .setFullMethodName("google.spanner.admin.database.v1.DatabaseAdmin/AddSplitPoints") + .setRequestMarshaller( + ProtoUtils.marshaller(AddSplitPointsRequest.getDefaultInstance())) + .setResponseMarshaller( + ProtoUtils.marshaller(AddSplitPointsResponse.getDefaultInstance())) + .build(); + private static final MethodDescriptor createBackupScheduleMethodDescriptor = MethodDescriptor.newBuilder() @@ -386,6 +399,7 @@ public class GrpcDatabaseAdminStub extends DatabaseAdminStub { listDatabaseRolesCallable; private final UnaryCallable listDatabaseRolesPagedCallable; + private final UnaryCallable addSplitPointsCallable; private final UnaryCallable createBackupScheduleCallable; private final UnaryCallable getBackupScheduleCallable; @@ -645,6 +659,17 @@ protected GrpcDatabaseAdminStub( return builder.build(); }) .build(); + GrpcCallSettings + addSplitPointsTransportSettings = + GrpcCallSettings.newBuilder() + .setMethodDescriptor(addSplitPointsMethodDescriptor) + .setParamsExtractor( + request -> { + RequestParamsBuilder builder = RequestParamsBuilder.create(); + builder.add("database", String.valueOf(request.getDatabase())); + return builder.build(); + }) + .build(); GrpcCallSettings createBackupScheduleTransportSettings = GrpcCallSettings.newBuilder() @@ -828,6 +853,9 @@ protected GrpcDatabaseAdminStub( listDatabaseRolesTransportSettings, settings.listDatabaseRolesSettings(), clientContext); + this.addSplitPointsCallable = + callableFactory.createUnaryCallable( + addSplitPointsTransportSettings, settings.addSplitPointsSettings(), clientContext); this.createBackupScheduleCallable = callableFactory.createUnaryCallable( createBackupScheduleTransportSettings, @@ -1036,6 +1064,11 @@ public UnaryCallable restoreDatabaseCallable( return listDatabaseRolesPagedCallable; } + @Override + public UnaryCallable addSplitPointsCallable() { + return addSplitPointsCallable; + } + @Override public UnaryCallable createBackupScheduleCallable() { return createBackupScheduleCallable; diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/stub/HttpJsonDatabaseAdminStub.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/stub/HttpJsonDatabaseAdminStub.java index db4f8c7b960..038c51b144e 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/stub/HttpJsonDatabaseAdminStub.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/stub/HttpJsonDatabaseAdminStub.java @@ -48,6 +48,8 @@ import com.google.longrunning.Operation; import com.google.protobuf.Empty; import com.google.protobuf.TypeRegistry; +import com.google.spanner.admin.database.v1.AddSplitPointsRequest; +import com.google.spanner.admin.database.v1.AddSplitPointsResponse; import com.google.spanner.admin.database.v1.Backup; import com.google.spanner.admin.database.v1.BackupSchedule; import com.google.spanner.admin.database.v1.CopyBackupMetadata; @@ -879,6 +881,43 @@ public class HttpJsonDatabaseAdminStub extends DatabaseAdminStub { .build()) .build(); + private static final ApiMethodDescriptor + addSplitPointsMethodDescriptor = + ApiMethodDescriptor.newBuilder() + .setFullMethodName("google.spanner.admin.database.v1.DatabaseAdmin/AddSplitPoints") + .setHttpMethod("POST") + .setType(ApiMethodDescriptor.MethodType.UNARY) + .setRequestFormatter( + ProtoMessageRequestFormatter.newBuilder() + .setPath( + "/v1/{database=projects/*/instances/*/databases/*}:addSplitPoints", + request -> { + Map fields = new HashMap<>(); + ProtoRestSerializer serializer = + ProtoRestSerializer.create(); + serializer.putPathParam(fields, "database", request.getDatabase()); + return fields; + }) + .setQueryParamsExtractor( + request -> { + Map> fields = new HashMap<>(); + ProtoRestSerializer serializer = + ProtoRestSerializer.create(); + serializer.putQueryParam(fields, "$alt", "json;enum-encoding=int"); + return fields; + }) + .setRequestBodyExtractor( + request -> + ProtoRestSerializer.create() + .toBody("*", request.toBuilder().clearDatabase().build(), true)) + .build()) + .setResponseParser( + ProtoMessageResponseParser.newBuilder() + .setDefaultInstance(AddSplitPointsResponse.getDefaultInstance()) + .setDefaultTypeRegistry(typeRegistry) + .build()) + .build(); + private static final ApiMethodDescriptor createBackupScheduleMethodDescriptor = ApiMethodDescriptor.newBuilder() @@ -1113,6 +1152,7 @@ public class HttpJsonDatabaseAdminStub extends DatabaseAdminStub { listDatabaseRolesCallable; private final UnaryCallable listDatabaseRolesPagedCallable; + private final UnaryCallable addSplitPointsCallable; private final UnaryCallable createBackupScheduleCallable; private final UnaryCallable getBackupScheduleCallable; @@ -1474,6 +1514,18 @@ protected HttpJsonDatabaseAdminStub( return builder.build(); }) .build(); + HttpJsonCallSettings + addSplitPointsTransportSettings = + HttpJsonCallSettings.newBuilder() + .setMethodDescriptor(addSplitPointsMethodDescriptor) + .setTypeRegistry(typeRegistry) + .setParamsExtractor( + request -> { + RequestParamsBuilder builder = RequestParamsBuilder.create(); + builder.add("database", String.valueOf(request.getDatabase())); + return builder.build(); + }) + .build(); HttpJsonCallSettings createBackupScheduleTransportSettings = HttpJsonCallSettings.newBuilder() @@ -1664,6 +1716,9 @@ protected HttpJsonDatabaseAdminStub( listDatabaseRolesTransportSettings, settings.listDatabaseRolesSettings(), clientContext); + this.addSplitPointsCallable = + callableFactory.createUnaryCallable( + addSplitPointsTransportSettings, settings.addSplitPointsSettings(), clientContext); this.createBackupScheduleCallable = callableFactory.createUnaryCallable( createBackupScheduleTransportSettings, @@ -1722,6 +1777,7 @@ public static List getMethodDescriptors() { methodDescriptors.add(listDatabaseOperationsMethodDescriptor); methodDescriptors.add(listBackupOperationsMethodDescriptor); methodDescriptors.add(listDatabaseRolesMethodDescriptor); + methodDescriptors.add(addSplitPointsMethodDescriptor); methodDescriptors.add(createBackupScheduleMethodDescriptor); methodDescriptors.add(getBackupScheduleMethodDescriptor); methodDescriptors.add(updateBackupScheduleMethodDescriptor); @@ -1903,6 +1959,11 @@ public UnaryCallable restoreDatabaseCallable( return listDatabaseRolesPagedCallable; } + @Override + public UnaryCallable addSplitPointsCallable() { + return addSplitPointsCallable; + } + @Override public UnaryCallable createBackupScheduleCallable() { return createBackupScheduleCallable; diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/SingleUseTransaction.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/SingleUseTransaction.java index 3c533cb9a7a..a827f82ba36 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/SingleUseTransaction.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/SingleUseTransaction.java @@ -34,6 +34,7 @@ import com.google.cloud.spanner.Mutation; import com.google.cloud.spanner.Options; import com.google.cloud.spanner.Options.QueryOption; +import com.google.cloud.spanner.Options.QueryUpdateOption; import com.google.cloud.spanner.Options.UpdateOption; import com.google.cloud.spanner.PartitionOptions; import com.google.cloud.spanner.ReadOnlyTransaction; @@ -298,7 +299,8 @@ private ApiFuture executeDmlReturningAsync( writeTransaction.run( transaction -> DirectExecuteResultSet.ofResultSet( - transaction.executeQuery(update.getStatement(), options))); + transaction.executeQuery( + update.getStatement(), appendLastStatement(options)))); state = UnitOfWorkState.COMMITTED; return resultSet; } catch (Throwable t) { @@ -554,11 +556,15 @@ private ApiFuture> executeTransactionalUpdateAsync( transaction -> { if (analyzeMode == AnalyzeMode.NONE) { return Tuple.of( - transaction.executeUpdate(update.getStatement(), options), null); + transaction.executeUpdate( + update.getStatement(), appendLastStatement(options)), + null); } ResultSet resultSet = transaction.analyzeUpdateStatement( - update.getStatement(), analyzeMode.getQueryAnalyzeMode(), options); + update.getStatement(), + analyzeMode.getQueryAnalyzeMode(), + appendLastStatement(options)); return Tuple.of(null, resultSet); }); state = UnitOfWorkState.COMMITTED; @@ -582,6 +588,29 @@ private ApiFuture> executeTransactionalUpdateAsync( return transactionalResult; } + private static final QueryUpdateOption[] LAST_STATEMENT_OPTIONS = + new QueryUpdateOption[] {Options.lastStatement()}; + + private static UpdateOption[] appendLastStatement(UpdateOption[] options) { + if (options.length == 0) { + return LAST_STATEMENT_OPTIONS; + } + UpdateOption[] result = new UpdateOption[options.length + 1]; + System.arraycopy(options, 0, result, 0, options.length); + result[result.length - 1] = LAST_STATEMENT_OPTIONS[0]; + return result; + } + + private static QueryOption[] appendLastStatement(QueryOption[] options) { + if (options.length == 0) { + return LAST_STATEMENT_OPTIONS; + } + QueryOption[] result = new QueryOption[options.length + 1]; + System.arraycopy(options, 0, result, 0, options.length); + result[result.length - 1] = LAST_STATEMENT_OPTIONS[0]; + return result; + } + /** * Adds a callback to the given future that retries the update statement using Partitioned DML if * the original statement fails with a {@link TransactionMutationLimitExceededException}. @@ -719,7 +748,8 @@ private ApiFuture executeTransactionalBatchUpdateAsync( try { long[] res = transaction.batchUpdate( - Iterables.transform(updates, ParsedStatement::getStatement), options); + Iterables.transform(updates, ParsedStatement::getStatement), + appendLastStatement(options)); state = UnitOfWorkState.COMMITTED; return res; } catch (Throwable t) { diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/HeaderInterceptor.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/HeaderInterceptor.java index e4eec68b278..dba3b38e92f 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/HeaderInterceptor.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/HeaderInterceptor.java @@ -71,9 +71,11 @@ class HeaderInterceptor implements ClientInterceptor { DatabaseName.of("undefined-project", "undefined-instance", "undefined-database"); private static final Metadata.Key SERVER_TIMING_HEADER_KEY = Metadata.Key.of("server-timing", Metadata.ASCII_STRING_MARSHALLER); - private static final String SERVER_TIMING_HEADER_PREFIX = "gfet4t7; dur="; + private static final String GFE_TIMING_HEADER = "gfet4t7"; private static final Metadata.Key GOOGLE_CLOUD_RESOURCE_PREFIX_KEY = Metadata.Key.of("google-cloud-resource-prefix", Metadata.ASCII_STRING_MARSHALLER); + private static final Pattern SERVER_TIMING_PATTERN = + Pattern.compile("(?[a-zA-Z0-9_-]+);\\s*dur=(?\\d+)"); private static final Pattern GOOGLE_CLOUD_RESOURCE_PREFIX_PATTERN = Pattern.compile( ".*projects/(?\\p{ASCII}[^/]*)(/instances/(?\\p{ASCII}[^/]*))?(/databases/(?\\p{ASCII}[^/]*))?"); @@ -128,7 +130,7 @@ public void onHeaders(Metadata metadata) { Boolean isDirectPathUsed = isDirectPathUsed(getAttributes().get(Grpc.TRANSPORT_ATTR_REMOTE_ADDR)); addDirectPathUsedAttribute(compositeTracer, isDirectPathUsed); - processHeader(metadata, tagContext, attributes, span); + processHeader(metadata, tagContext, attributes, span, compositeTracer); super.onHeaders(metadata); } }, @@ -142,29 +144,61 @@ public void onHeaders(Metadata metadata) { } private void processHeader( - Metadata metadata, TagContext tagContext, Attributes attributes, Span span) { + Metadata metadata, + TagContext tagContext, + Attributes attributes, + Span span, + CompositeTracer compositeTracer) { MeasureMap measureMap = STATS_RECORDER.newMeasureMap(); String serverTiming = metadata.get(SERVER_TIMING_HEADER_KEY); - if (serverTiming != null && serverTiming.startsWith(SERVER_TIMING_HEADER_PREFIX)) { - try { - long latency = Long.parseLong(serverTiming.substring(SERVER_TIMING_HEADER_PREFIX.length())); - measureMap.put(SPANNER_GFE_LATENCY, latency); + try { + // Previous implementation parsed the GFE latency directly using: + // long latency = Long.parseLong(serverTiming.substring("gfet4t7; dur=".length())); + // This approach assumed the serverTiming header contained exactly one metric "gfet4t7". + // If additional metrics were introduced in the header, older versions of the library + // would fail to parse it correctly. To make the parsing more robust, the logic has been + // updated to handle multiple metrics gracefully. + + Map serverTimingMetrics = parseServerTimingHeader(serverTiming); + if (serverTimingMetrics.containsKey(GFE_TIMING_HEADER)) { + long gfeLatency = serverTimingMetrics.get(GFE_TIMING_HEADER); + + measureMap.put(SPANNER_GFE_LATENCY, gfeLatency); measureMap.put(SPANNER_GFE_HEADER_MISSING_COUNT, 0L); measureMap.record(tagContext); - spannerRpcMetrics.recordGfeLatency(latency, attributes); + spannerRpcMetrics.recordGfeLatency(gfeLatency, attributes); spannerRpcMetrics.recordGfeHeaderMissingCount(0L, attributes); + if (compositeTracer != null) { + compositeTracer.recordGFELatency(gfeLatency); + } if (span != null) { - span.setAttribute("gfe_latency", String.valueOf(latency)); + span.setAttribute("gfe_latency", String.valueOf(gfeLatency)); + } + } else { + measureMap.put(SPANNER_GFE_HEADER_MISSING_COUNT, 1L).record(tagContext); + spannerRpcMetrics.recordGfeHeaderMissingCount(1L, attributes); + } + } catch (NumberFormatException e) { + LOGGER.log(LEVEL, "Invalid server-timing object in header: {}", serverTiming); + } + } + + private Map parseServerTimingHeader(String serverTiming) { + Map serverTimingMetrics = new HashMap<>(); + if (serverTiming != null) { + Matcher matcher = SERVER_TIMING_PATTERN.matcher(serverTiming); + while (matcher.find()) { + String metricName = matcher.group("metricName"); + String durationStr = matcher.group("duration"); + + if (metricName != null && durationStr != null) { + serverTimingMetrics.put(metricName, Long.valueOf(durationStr)); } - } catch (NumberFormatException e) { - LOGGER.log(LEVEL, "Invalid server-timing object in header: {}", serverTiming); } - } else { - spannerRpcMetrics.recordGfeHeaderMissingCount(1L, attributes); - measureMap.put(SPANNER_GFE_HEADER_MISSING_COUNT, 1L).record(tagContext); } + return serverTimingMetrics; } private DatabaseName extractDatabaseName(Metadata headers) throws ExecutionException { diff --git a/google-cloud-spanner/src/main/resources/META-INF/native-image/com.google.cloud.spanner.admin.database.v1/reflect-config.json b/google-cloud-spanner/src/main/resources/META-INF/native-image/com.google.cloud.spanner.admin.database.v1/reflect-config.json index 15e53bae299..3b456d976d0 100644 --- a/google-cloud-spanner/src/main/resources/META-INF/native-image/com.google.cloud.spanner.admin.database.v1/reflect-config.json +++ b/google-cloud-spanner/src/main/resources/META-INF/native-image/com.google.cloud.spanner.admin.database.v1/reflect-config.json @@ -1601,6 +1601,51 @@ "allDeclaredClasses": true, "allPublicClasses": true }, + { + "name": "com.google.protobuf.ListValue", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + }, + { + "name": "com.google.protobuf.ListValue$Builder", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + }, + { + "name": "com.google.protobuf.NullValue", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + }, + { + "name": "com.google.protobuf.Struct", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + }, + { + "name": "com.google.protobuf.Struct$Builder", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + }, { "name": "com.google.protobuf.Timestamp", "queryAllDeclaredConstructors": true, @@ -1619,6 +1664,24 @@ "allDeclaredClasses": true, "allPublicClasses": true }, + { + "name": "com.google.protobuf.Value", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + }, + { + "name": "com.google.protobuf.Value$Builder", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + }, { "name": "com.google.rpc.Status", "queryAllDeclaredConstructors": true, @@ -1637,6 +1700,42 @@ "allDeclaredClasses": true, "allPublicClasses": true }, + { + "name": "com.google.spanner.admin.database.v1.AddSplitPointsRequest", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + }, + { + "name": "com.google.spanner.admin.database.v1.AddSplitPointsRequest$Builder", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + }, + { + "name": "com.google.spanner.admin.database.v1.AddSplitPointsResponse", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + }, + { + "name": "com.google.spanner.admin.database.v1.AddSplitPointsResponse$Builder", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + }, { "name": "com.google.spanner.admin.database.v1.Backup", "queryAllDeclaredConstructors": true, @@ -2555,6 +2654,42 @@ "allDeclaredClasses": true, "allPublicClasses": true }, + { + "name": "com.google.spanner.admin.database.v1.SplitPoints", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + }, + { + "name": "com.google.spanner.admin.database.v1.SplitPoints$Builder", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + }, + { + "name": "com.google.spanner.admin.database.v1.SplitPoints$Key", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + }, + { + "name": "com.google.spanner.admin.database.v1.SplitPoints$Key$Builder", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + }, { "name": "com.google.spanner.admin.database.v1.UpdateBackupRequest", "queryAllDeclaredConstructors": true, diff --git a/google-cloud-spanner/src/main/resources/META-INF/native-image/native-image.properties b/google-cloud-spanner/src/main/resources/META-INF/native-image/native-image.properties index 0bcf872e79b..44bcd53941a 100644 --- a/google-cloud-spanner/src/main/resources/META-INF/native-image/native-image.properties +++ b/google-cloud-spanner/src/main/resources/META-INF/native-image/native-image.properties @@ -1,4 +1,5 @@ Args = --initialize-at-build-time=com.google.cloud.spanner.IntegrationTestEnv,\ org.junit.experimental.categories.CategoryValidator,\ - org.junit.validator.AnnotationValidator \ + org.junit.validator.AnnotationValidator,\ + java.lang.annotation.Annotation \ --features=com.google.cloud.spanner.nativeimage.SpannerFeature diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AbstractNettyMockServerTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AbstractNettyMockServerTest.java new file mode 100644 index 00000000000..8e8da054b08 --- /dev/null +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AbstractNettyMockServerTest.java @@ -0,0 +1,114 @@ +/* + * Copyright 2023 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; + +import com.google.api.gax.grpc.testing.LocalChannelProvider; +import com.google.cloud.NoCredentials; +import io.grpc.ForwardingServerCall; +import io.grpc.ManagedChannelBuilder; +import io.grpc.Metadata; +import io.grpc.Server; +import io.grpc.ServerCall; +import io.grpc.ServerCallHandler; +import io.grpc.ServerInterceptor; +import io.grpc.netty.shaded.io.grpc.netty.NettyServerBuilder; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.util.Random; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; + +abstract class AbstractNettyMockServerTest { + protected static MockSpannerServiceImpl mockSpanner; + + protected static Server server; + protected static InetSocketAddress address; + static ExecutorService executor; + protected static LocalChannelProvider channelProvider; + protected static AtomicInteger fakeServerTiming = + new AtomicInteger(new Random().nextInt(1000) + 1); + + protected Spanner spanner; + + @BeforeClass + public static void startMockServer() throws IOException { + mockSpanner = new MockSpannerServiceImpl(); + mockSpanner.setAbortProbability(0.0D); // We don't want any unpredictable aborted transactions. + + address = new InetSocketAddress("localhost", 0); + server = + NettyServerBuilder.forAddress(address) + .addService(mockSpanner) + .intercept( + new ServerInterceptor() { + @Override + public ServerCall.Listener interceptCall( + ServerCall serverCall, + Metadata headers, + ServerCallHandler serverCallHandler) { + return serverCallHandler.startCall( + new ForwardingServerCall.SimpleForwardingServerCall( + serverCall) { + @Override + public void sendHeaders(Metadata headers) { + headers.put( + Metadata.Key.of("server-timing", Metadata.ASCII_STRING_MARSHALLER), + String.format("gfet4t7; dur=%d", fakeServerTiming.get())); + super.sendHeaders(headers); + } + }, + headers); + } + }) + .build() + .start(); + executor = Executors.newSingleThreadExecutor(); + } + + @AfterClass + public static void stopMockServer() throws InterruptedException { + server.shutdown(); + server.awaitTermination(); + executor.shutdown(); + } + + @Before + public void createSpannerInstance() { + String endpoint = address.getHostString() + ":" + server.getPort(); + spanner = + SpannerOptions.newBuilder() + .setProjectId("test-project") + .setChannelConfigurator(ManagedChannelBuilder::usePlaintext) + .setHost("http://" + endpoint) + .setCredentials(NoCredentials.getInstance()) + .setSessionPoolOption(SessionPoolOptions.newBuilder().setFailOnSessionLeak().build()) + .build() + .getService(); + } + + @After + public void cleanup() { + spanner.close(); + mockSpanner.reset(); + mockSpanner.removeAllExecutionTimes(); + } +} diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AbstractReadContextTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AbstractReadContextTest.java index 8b53bd7efff..eea6658d26d 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AbstractReadContextTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AbstractReadContextTest.java @@ -18,6 +18,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -266,6 +267,42 @@ public void testGetExecuteBatchDmlRequestBuilderWithPriority() { assertEquals(Priority.PRIORITY_LOW, request.getRequestOptions().getPriority()); } + @Test + public void testExecuteSqlLastStatement() { + assertFalse( + context + .getExecuteSqlRequestBuilder( + Statement.of("insert into test (id) values (1)"), + QueryMode.NORMAL, + Options.fromUpdateOptions(), + false) + .getLastStatement()); + assertTrue( + context + .getExecuteSqlRequestBuilder( + Statement.of("insert into test (id) values (1)"), + QueryMode.NORMAL, + Options.fromUpdateOptions(Options.lastStatement()), + false) + .getLastStatement()); + } + + @Test + public void testExecuteBatchDmlLastStatement() { + assertFalse( + context + .getExecuteBatchDmlRequestBuilder( + Collections.singleton(Statement.of("insert into test (id) values (1)")), + Options.fromUpdateOptions()) + .getLastStatements()); + assertTrue( + context + .getExecuteBatchDmlRequestBuilder( + Collections.singleton(Statement.of("insert into test (id) values (1)")), + Options.fromUpdateOptions(Options.lastStatement())) + .getLastStatements()); + } + public void executeSqlRequestBuilderWithRequestOptions() { ExecuteSqlRequest request = context diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncRunnerTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncRunnerTest.java index 72e19e0291f..56d33c54878 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncRunnerTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncRunnerTest.java @@ -60,17 +60,51 @@ public void clearRequests() { @Test public void testAsyncRunner_doesNotReturnCommitTimestampBeforeCommit() { AsyncRunner runner = client().runAsync(); - IllegalStateException e = - assertThrows(IllegalStateException.class, () -> runner.getCommitTimestamp()); - assertTrue(e.getMessage().contains("runAsync() has not yet been called")); + if (isMultiplexedSessionsEnabledForRW()) { + Throwable e = assertThrows(Throwable.class, () -> runner.getCommitTimestamp().get()); + // If the error occurs within the future, it gets wrapped in an ExecutionException. + // This happens when DelayedAsyncRunner is invoked while the multiplexed session is not yet + // created. + // If the error occurs before the future is created, it may throw an IllegalStateException + // instead. + assertTrue(e instanceof ExecutionException || e instanceof IllegalStateException); + if (e instanceof ExecutionException) { + Throwable cause = e.getCause(); + assertTrue(cause instanceof IllegalStateException); + assertTrue(cause.getMessage().contains("runAsync() has not yet been called")); + } else { + assertTrue(e.getMessage().contains("runAsync() has not yet been called")); + } + } else { + IllegalStateException e = + assertThrows(IllegalStateException.class, () -> runner.getCommitTimestamp()); + assertTrue(e.getMessage().contains("runAsync() has not yet been called")); + } } @Test public void testAsyncRunner_doesNotReturnCommitResponseBeforeCommit() { AsyncRunner runner = client().runAsync(); - IllegalStateException e = - assertThrows(IllegalStateException.class, () -> runner.getCommitResponse()); - assertTrue(e.getMessage().contains("runAsync() has not yet been called")); + if (isMultiplexedSessionsEnabledForRW()) { + Throwable e = assertThrows(Throwable.class, () -> runner.getCommitResponse().get()); + // If the error occurs within the future, it gets wrapped in an ExecutionException. + // This happens when DelayedAsyncRunner is invoked while the multiplexed session is not yet + // created. + // If the error occurs before the future is created, it may throw an IllegalStateException + // instead. + assertTrue(e instanceof ExecutionException || e instanceof IllegalStateException); + if (e instanceof ExecutionException) { + Throwable cause = e.getCause(); + assertTrue(cause instanceof IllegalStateException); + assertTrue(cause.getMessage().contains("runAsync() has not yet been called")); + } else { + assertTrue(e.getMessage().contains("runAsync() has not yet been called")); + } + } else { + IllegalStateException e = + assertThrows(IllegalStateException.class, () -> runner.getCommitResponse()); + assertTrue(e.getMessage().contains("runAsync() has not yet been called")); + } } @Test @@ -558,7 +592,9 @@ public void closeTransactionBeforeEndOfAsyncQuery() throws Exception { // Wait until at least one row has been fetched. At that moment there should be one session // checked out. dataReceived.await(); - assertThat(clientImpl.pool.getNumberOfSessionsInUse()).isEqualTo(1); + if (!isMultiplexedSessionsEnabledForRW()) { + assertThat(clientImpl.pool.getNumberOfSessionsInUse()).isEqualTo(1); + } assertThat(res.isDone()).isFalse(); dataChecked.countDown(); // Get the data from the transaction. diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncTransactionManagerImplTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncTransactionManagerImplTest.java index 006a926e907..dd13c39abc8 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncTransactionManagerImplTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncTransactionManagerImplTest.java @@ -16,18 +16,14 @@ package com.google.cloud.spanner; -import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.clearInvocations; -import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import com.google.api.core.ApiFutures; import com.google.cloud.Timestamp; -import com.google.protobuf.ByteString; import io.opentelemetry.api.trace.Span; import io.opentelemetry.context.Scope; import org.junit.Test; @@ -60,67 +56,4 @@ public void testCommitReturnsCommitStats() { verify(transaction).commitAsync(); } } - - @Test - public void testRetryUsesPreviousTransactionIdOnMultiplexedSession() { - // Set up mock transaction IDs - final ByteString mockTransactionId = ByteString.copyFromUtf8("mockTransactionId"); - final ByteString mockPreviousTransactionId = - ByteString.copyFromUtf8("mockPreviousTransactionId"); - - Span oTspan = mock(Span.class); - ISpan span = new OpenTelemetrySpan(oTspan); - when(oTspan.makeCurrent()).thenReturn(mock(Scope.class)); - // Mark the session as multiplexed. - when(session.getIsMultiplexed()).thenReturn(true); - - // Initialize a mock transaction with transactionId = null, previousTransactionId = null. - transaction = mock(TransactionRunnerImpl.TransactionContextImpl.class); - when(transaction.ensureTxnAsync()).thenReturn(ApiFutures.immediateFuture(null)); - when(session.newTransaction(eq(Options.fromTransactionOptions(Options.commitStats())), any())) - .thenReturn(transaction); - - // Simulate an ABORTED error being thrown when `commitAsync()` is called. - doThrow(SpannerExceptionFactory.newSpannerException(ErrorCode.ABORTED, "")) - .when(transaction) - .commitAsync(); - - try (AsyncTransactionManagerImpl manager = - new AsyncTransactionManagerImpl(session, span, Options.commitStats())) { - manager.beginAsync(); - - // Verify that for the first transaction attempt, the `previousTransactionId` is - // ByteString.EMPTY. - // This is because no transaction has been previously aborted at this point. - verify(session) - .newTransaction(Options.fromTransactionOptions(Options.commitStats()), ByteString.EMPTY); - assertThrows(AbortedException.class, manager::commitAsync); - clearInvocations(session); - - // Mock the transaction object to contain transactionID=null and - // previousTransactionId=mockPreviousTransactionId - when(transaction.getPreviousTransactionId()).thenReturn(mockPreviousTransactionId); - manager.resetForRetryAsync(); - // Verify that in the first retry attempt, the `previousTransactionId` - // (mockPreviousTransactionId) is passed to the new transaction. - // This allows Spanner to retry the transaction using the ID of the aborted transaction. - verify(session) - .newTransaction( - Options.fromTransactionOptions(Options.commitStats()), mockPreviousTransactionId); - assertThrows(AbortedException.class, manager::commitAsync); - clearInvocations(session); - - // Mock the transaction object to contain transactionID=mockTransactionId and - // previousTransactionId=mockPreviousTransactionId and transactionID = null - transaction.transactionId = mockTransactionId; - manager.resetForRetryAsync(); - // Verify that the latest `transactionId` (mockTransactionId) is used in the retry. - // This ensures the retry logic is working as expected with the latest transaction ID. - verify(session) - .newTransaction(Options.fromTransactionOptions(Options.commitStats()), mockTransactionId); - - when(transaction.rollbackAsync()).thenReturn(ApiFutures.immediateFuture(null)); - manager.closeAsync(); - } - } } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncTransactionManagerTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncTransactionManagerTest.java index 81a94edd980..6a5d77e20e7 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncTransactionManagerTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncTransactionManagerTest.java @@ -28,6 +28,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeFalse; import com.google.api.core.ApiFuture; import com.google.api.core.ApiFutureCallback; @@ -250,6 +251,11 @@ public void asyncTransactionManagerUpdate() throws Exception { @Test public void asyncTransactionManagerIsNonBlocking() throws Exception { + // TODO: Remove this condition once DelayedAsyncTransactionManager is made non-blocking with + // multiplexed sessions. + assumeFalse( + "DelayedAsyncTransactionManager is currently blocking with multiplexed sessions.", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); mockSpanner.freeze(); try (AsyncTransactionManager manager = clientWithEmptySessionPool().transactionManagerAsync()) { TransactionContextFuture transactionContextFuture = manager.beginAsync(); @@ -633,6 +639,11 @@ public void asyncTransactionManagerBatchUpdate() throws Exception { @Test public void asyncTransactionManagerIsNonBlockingWithBatchUpdate() throws Exception { + // TODO: Remove this condition once DelayedAsyncTransactionManager is made non-blocking with + // multiplexed sessions. + assumeFalse( + "DelayedAsyncTransactionManager is currently blocking with multiplexed sessions.", + spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); mockSpanner.freeze(); try (AsyncTransactionManager manager = clientWithEmptySessionPool().transactionManagerAsync()) { TransactionContextFuture transactionContextFuture = manager.beginAsync(); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/BatchClientImplTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/BatchClientImplTest.java index edafc7ddba9..ba508ac8d19 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/BatchClientImplTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/BatchClientImplTest.java @@ -102,6 +102,7 @@ public void setUp() { @SuppressWarnings("resource") SpannerImpl spanner = new SpannerImpl(gapicRpc, spannerOptions); client = new BatchClientImpl(spanner.getSessionClient(db), isMultiplexedSession); + BatchClientImpl.unimplementedForPartitionedOps.set(false); } @SuppressWarnings("unchecked") diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/BuiltInOpenTelemetryMetricsProviderTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/BuiltInOpenTelemetryMetricsProviderTest.java index 43fe97113d0..73185177de1 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/BuiltInOpenTelemetryMetricsProviderTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/BuiltInOpenTelemetryMetricsProviderTest.java @@ -29,31 +29,31 @@ public class BuiltInOpenTelemetryMetricsProviderTest { @Test public void testGenerateClientHashWithSimpleUid() { String clientUid = "testClient"; - verifyHash(BuiltInOpenTelemetryMetricsProvider.generateClientHash(clientUid)); + verifyHash(BuiltInMetricsProvider.generateClientHash(clientUid)); } @Test public void testGenerateClientHashWithEmptyUid() { String clientUid = ""; - verifyHash(BuiltInOpenTelemetryMetricsProvider.generateClientHash(clientUid)); + verifyHash(BuiltInMetricsProvider.generateClientHash(clientUid)); } @Test public void testGenerateClientHashWithNullUid() { String clientUid = null; - verifyHash(BuiltInOpenTelemetryMetricsProvider.generateClientHash(clientUid)); + verifyHash(BuiltInMetricsProvider.generateClientHash(clientUid)); } @Test public void testGenerateClientHashWithLongUid() { String clientUid = "aVeryLongUniqueClientIdentifierThatIsUnusuallyLong"; - verifyHash(BuiltInOpenTelemetryMetricsProvider.generateClientHash(clientUid)); + verifyHash(BuiltInMetricsProvider.generateClientHash(clientUid)); } @Test public void testGenerateClientHashWithSpecialCharacters() { String clientUid = "273d60f2-5604-42f1-b687-f5f1b975fd07@2316645@test#"; - verifyHash(BuiltInOpenTelemetryMetricsProvider.generateClientHash(clientUid)); + verifyHash(BuiltInMetricsProvider.generateClientHash(clientUid)); } private void verifyHash(String hash) { diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MockSpannerServiceImpl.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MockSpannerServiceImpl.java index 35c2d553b08..3443be192e6 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MockSpannerServiceImpl.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MockSpannerServiceImpl.java @@ -2024,14 +2024,6 @@ public void commit(CommitRequest request, StreamObserver respons return; } sessionLastUsed.put(session.getName(), Instant.now()); - if (session.getMultiplexed() - && !request.hasPrecommitToken() - && !request.hasSingleUseTransaction()) { - throw Status.INVALID_ARGUMENT - .withDescription( - "A Commit request for a read-write transaction on a multiplexed session must specify a precommit token.") - .asRuntimeException(); - } try { commitExecutionTime.simulateExecutionTime(exceptions, stickyGlobalExceptions, freezeLock); // Find or start a transaction diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MultiplexedSessionDatabaseClientMockServerTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MultiplexedSessionDatabaseClientMockServerTest.java index d5fa4dd5c37..87877caf21a 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MultiplexedSessionDatabaseClientMockServerTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MultiplexedSessionDatabaseClientMockServerTest.java @@ -46,20 +46,12 @@ import com.google.common.collect.Lists; import com.google.common.util.concurrent.MoreExecutors; import com.google.protobuf.ByteString; -import com.google.spanner.v1.BatchWriteRequest; -import com.google.spanner.v1.BatchWriteResponse; -import com.google.spanner.v1.BeginTransactionRequest; -import com.google.spanner.v1.CommitRequest; -import com.google.spanner.v1.ExecuteSqlRequest; +import com.google.spanner.v1.*; import com.google.spanner.v1.RequestOptions.Priority; import com.google.spanner.v1.Session; -import com.google.spanner.v1.Transaction; import io.grpc.Status; import java.time.Duration; -import java.util.Collections; -import java.util.List; -import java.util.Set; -import java.util.UUID; +import java.util.*; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; @@ -1540,6 +1532,89 @@ public void testInitialBeginTransactionWithRW_receivesUnimplemented_fallsBackToR assertFalse(session2.getMultiplexed()); } + // Tests the behavior of the server-side kill switch for read-write multiplexed sessions. + @Test + public void + testInitialBeginTransactionWithPDML_receivesUnimplemented_fallsBackToRegularSession() { + mockSpanner.setBeginTransactionExecutionTime( + SimulatedExecutionTime.ofExceptions( + Arrays.asList( + Status.UNIMPLEMENTED + .withDescription( + "Transaction type partitioned_dml not supported with multiplexed sessions") + .asRuntimeException(), + Status.UNIMPLEMENTED + .withDescription( + "Transaction type partitioned_dml not supported with multiplexed sessions") + .asRuntimeException()))); + DatabaseClientImpl client = + (DatabaseClientImpl) spanner.getDatabaseClient(DatabaseId.of("p", "i", "d")); + + assertNotNull(client.multiplexedSessionDatabaseClient); + + // Partitioned Ops transaction should fallback to regular sessions + assertEquals(UPDATE_COUNT, client.executePartitionedUpdate(UPDATE_STATEMENT)); + + // Verify that we received one ExecuteSqlRequest, and it uses a regular session due to fallback. + List executeSqlRequests = + mockSpanner.getRequestsOfType(ExecuteSqlRequest.class); + assertEquals(1, executeSqlRequests.size()); + // Verify the requests are not executed using multiplexed sessions + Session session2 = mockSpanner.getSession(executeSqlRequests.get(0).getSession()); + assertNotNull(session2); + assertFalse(session2.getMultiplexed()); + assertTrue(client.multiplexedSessionDatabaseClient.unimplementedForPartitionedOps.get()); + } + + // Tests the behavior of the server-side kill switch for read-write multiplexed sessions. + @Test + public void testPartitionedQuery_receivesUnimplemented_fallsBackToRegularSession() { + mockSpanner.setPartitionQueryExecutionTime( + SimulatedExecutionTime.ofException( + Status.INVALID_ARGUMENT + .withDescription( + "Partitioned operations are not supported with multiplexed sessions") + .asRuntimeException())); + BatchClientImpl client = (BatchClientImpl) spanner.getBatchClient(DatabaseId.of("p", "i", "d")); + + try (BatchReadOnlyTransaction transaction = + client.batchReadOnlyTransaction(TimestampBound.strong())) { + // Partitioned Query should fail + SpannerException spannerException = + assertThrows( + SpannerException.class, + () -> { + transaction.partitionQuery(PartitionOptions.getDefaultInstance(), STATEMENT); + }); + assertEquals(ErrorCode.INVALID_ARGUMENT, spannerException.getErrorCode()); + + // Verify that we received one PartitionQueryRequest. + List partitionQueryRequests = + mockSpanner.getRequestsOfType(PartitionQueryRequest.class); + assertEquals(1, partitionQueryRequests.size()); + // Verify the requests were executed using multiplexed sessions + Session session2 = mockSpanner.getSession(partitionQueryRequests.get(0).getSession()); + assertNotNull(session2); + assertTrue(session2.getMultiplexed()); + assertTrue(client.unimplementedForPartitionedOps.get()); + } + try (BatchReadOnlyTransaction transaction = + client.batchReadOnlyTransaction(TimestampBound.strong())) { + // Partitioned Query should fail + transaction.partitionQuery(PartitionOptions.getDefaultInstance(), STATEMENT); + + // // Verify that we received two PartitionQueryRequest. and it uses a regular session due to + // fallback. + List partitionQueryRequests = + mockSpanner.getRequestsOfType(PartitionQueryRequest.class); + assertEquals(2, partitionQueryRequests.size()); + // Verify the requests are not executed using multiplexed sessions + Session session2 = mockSpanner.getSession(partitionQueryRequests.get(1).getSession()); + assertNotNull(session2); + assertFalse(session2.getMultiplexed()); + } + } + @Test public void testReadWriteUnimplementedErrorDuringInitialBeginTransactionRPC_firstReceivesError_secondFallsBackToRegularSessions() { diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/OpenTelemetryBuiltInMetricsTracerTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/OpenTelemetryBuiltInMetricsTracerTest.java index 1b6d99260fe..f0c13b0f389 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/OpenTelemetryBuiltInMetricsTracerTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/OpenTelemetryBuiltInMetricsTracerTest.java @@ -60,7 +60,7 @@ import org.junit.runners.JUnit4; @RunWith(JUnit4.class) -public class OpenTelemetryBuiltInMetricsTracerTest extends AbstractMockServerTest { +public class OpenTelemetryBuiltInMetricsTracerTest extends AbstractNettyMockServerTest { private static final Statement SELECT_RANDOM = Statement.of("SELECT * FROM random"); @@ -71,7 +71,8 @@ public class OpenTelemetryBuiltInMetricsTracerTest extends AbstractMockServerTes private static Map attributes; - private static Attributes expectedBaseAttributes; + private static Attributes expectedCommonBaseAttributes; + private static Attributes expectedCommonRequestAttributes; private static final long MIN_LATENCY = 0; @@ -81,7 +82,7 @@ public class OpenTelemetryBuiltInMetricsTracerTest extends AbstractMockServerTes public static void setup() { metricReader = InMemoryMetricReader.create(); - BuiltInOpenTelemetryMetricsProvider provider = BuiltInOpenTelemetryMetricsProvider.INSTANCE; + BuiltInMetricsProvider provider = BuiltInMetricsProvider.INSTANCE; SdkMeterProviderBuilder meterProvider = SdkMeterProvider.builder().registerMetricReader(metricReader); @@ -92,17 +93,23 @@ public static void setup() { openTelemetry = OpenTelemetrySdk.builder().setMeterProvider(meterProvider.build()).build(); attributes = provider.createClientAttributes("test-project", client_name); - expectedBaseAttributes = + expectedCommonBaseAttributes = Attributes.builder() .put(BuiltInMetricsConstant.PROJECT_ID_KEY, "test-project") .put(BuiltInMetricsConstant.INSTANCE_CONFIG_ID_KEY, "unknown") .put( BuiltInMetricsConstant.LOCATION_ID_KEY, - BuiltInOpenTelemetryMetricsProvider.detectClientLocation()) + BuiltInMetricsProvider.detectClientLocation()) .put(BuiltInMetricsConstant.CLIENT_NAME_KEY, client_name) .put(BuiltInMetricsConstant.CLIENT_UID_KEY, attributes.get("client_uid")) .put(BuiltInMetricsConstant.CLIENT_HASH_KEY, attributes.get("client_hash")) + .put(BuiltInMetricsConstant.INSTANCE_ID_KEY, "i") + .put(BuiltInMetricsConstant.DATABASE_KEY, "d") + .put(BuiltInMetricsConstant.DIRECT_PATH_ENABLED_KEY, "false") .build(); + + expectedCommonRequestAttributes = + Attributes.builder().put(BuiltInMetricsConstant.DIRECT_PATH_USED_KEY, "false").build(); } @BeforeClass @@ -122,8 +129,8 @@ public void createSpannerInstance() { SpannerOptions.Builder builder = SpannerOptions.newBuilder(); ApiTracerFactory metricsTracerFactory = - new MetricsTracerFactory( - new OpenTelemetryMetricsRecorder(openTelemetry, BuiltInMetricsConstant.METER_NAME), + new BuiltInMetricsTracerFactory( + new BuiltInMetricsRecorder(openTelemetry, BuiltInMetricsConstant.METER_NAME), attributes); // Set a quick polling algorithm to prevent this from slowing down the test unnecessarily. builder @@ -137,10 +144,12 @@ public void createSpannerInstance() { .setRetryDelayMultiplier(1.0) .setTotalTimeoutDuration(Duration.ofMinutes(10L)) .build())); + String endpoint = address.getHostString() + ":" + server.getPort(); spanner = - builder + SpannerOptions.newBuilder() .setProjectId("test-project") - .setChannelProvider(channelProvider) + .setChannelConfigurator(ManagedChannelBuilder::usePlaintext) + .setHost("http://" + endpoint) .setCredentials(NoCredentials.getInstance()) .setSessionPoolOption( SessionPoolOptions.newBuilder() @@ -167,8 +176,9 @@ public void testMetricsSingleUseQuery() { long elapsed = stopwatch.elapsed(TimeUnit.MILLISECONDS); Attributes expectedAttributes = - expectedBaseAttributes + expectedCommonBaseAttributes .toBuilder() + .putAll(expectedCommonRequestAttributes) .put(BuiltInMetricsConstant.STATUS_KEY, "OK") .put(BuiltInMetricsConstant.METHOD_KEY, "Spanner.ExecuteStreamingSql") .build(); @@ -194,6 +204,11 @@ public void testMetricsSingleUseQuery() { getMetricData(metricReader, BuiltInMetricsConstant.ATTEMPT_COUNT_NAME); assertNotNull(attemptCountMetricData); assertThat(getAggregatedValue(attemptCountMetricData, expectedAttributes)).isEqualTo(1); + + MetricData gfeLatencyMetricData = + getMetricData(metricReader, BuiltInMetricsConstant.GFE_LATENCIES_NAME); + long gfeLatencyValue = getAggregatedValue(gfeLatencyMetricData, expectedAttributes); + assertEquals(fakeServerTiming.get(), gfeLatencyValue, 0); } @Test @@ -210,14 +225,15 @@ public void testMetricsWithGaxRetryUnaryRpc() { stopwatch.elapsed(TimeUnit.MILLISECONDS); Attributes expectedAttributesBeginTransactionOK = - expectedBaseAttributes + expectedCommonBaseAttributes .toBuilder() + .putAll(expectedCommonRequestAttributes) .put(BuiltInMetricsConstant.STATUS_KEY, "OK") .put(BuiltInMetricsConstant.METHOD_KEY, "Spanner.BeginTransaction") .build(); Attributes expectedAttributesBeginTransactionFailed = - expectedBaseAttributes + expectedCommonBaseAttributes .toBuilder() .put(BuiltInMetricsConstant.STATUS_KEY, "UNAVAILABLE") .put(BuiltInMetricsConstant.METHOD_KEY, "Spanner.BeginTransaction") @@ -289,7 +305,7 @@ public void testNoNetworkConnection() { .setApiTracerFactory(metricsTracerFactory) .build() .getService(); - String instance = "test-instance"; + String instance = "i"; DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of("test-project", instance, "d")); // Using this client will return UNAVAILABLE, as the server is not reachable and we have @@ -300,29 +316,24 @@ public void testNoNetworkConnection() { assertEquals(ErrorCode.UNAVAILABLE, exception.getErrorCode()); Attributes expectedAttributesCreateSessionOK = - expectedBaseAttributes + expectedCommonBaseAttributes .toBuilder() + .putAll(expectedCommonRequestAttributes) .put(BuiltInMetricsConstant.STATUS_KEY, "OK") .put(BuiltInMetricsConstant.METHOD_KEY, "Spanner.CreateSession") // Include the additional attributes that are added by the HeaderInterceptor in the // filter. Note that the DIRECT_PATH_USED attribute is not added, as the request never // leaves the client. - .put(BuiltInMetricsConstant.INSTANCE_ID_KEY, instance) - .put(BuiltInMetricsConstant.DATABASE_KEY, "d") - .put(BuiltInMetricsConstant.DIRECT_PATH_ENABLED_KEY, "false") .build(); Attributes expectedAttributesCreateSessionFailed = - expectedBaseAttributes + expectedCommonBaseAttributes .toBuilder() .put(BuiltInMetricsConstant.STATUS_KEY, "UNAVAILABLE") .put(BuiltInMetricsConstant.METHOD_KEY, "Spanner.CreateSession") // Include the additional attributes that are added by the HeaderInterceptor in the // filter. Note that the DIRECT_PATH_USED attribute is not added, as the request never // leaves the client. - .put(BuiltInMetricsConstant.INSTANCE_ID_KEY, instance) - .put(BuiltInMetricsConstant.DATABASE_KEY, "d") - .put(BuiltInMetricsConstant.DIRECT_PATH_ENABLED_KEY, "false") .build(); MetricData attemptCountMetricData = @@ -332,8 +343,6 @@ public void testNoNetworkConnection() { // Attempt count should have a failed metric point for CreateSession. assertEquals( 1, getAggregatedValue(attemptCountMetricData, expectedAttributesCreateSessionFailed)); - // There should be no OK metric points for CreateSession. - assertEquals(0, getAggregatedValue(attemptCountMetricData, expectedAttributesCreateSessionOK)); } private MetricData getMetricData(InMemoryMetricReader reader, String metricName) { diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/OptionsTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/OptionsTest.java index f391088589f..17c25558f3b 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/OptionsTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/OptionsTest.java @@ -789,4 +789,22 @@ public void updateOptionsExcludeTxnFromChangeStreams() { assertNull(option3.withExcludeTxnFromChangeStreams()); assertThat(option3.toString()).doesNotContain("withExcludeTxnFromChangeStreams: true"); } + + @Test + public void testLastStatement() { + Options option1 = Options.fromUpdateOptions(Options.lastStatement()); + Options option2 = Options.fromUpdateOptions(Options.lastStatement()); + Options option3 = Options.fromUpdateOptions(); + + assertEquals(option1, option2); + assertEquals(option1.hashCode(), option2.hashCode()); + assertNotEquals(option1, option3); + assertNotEquals(option1.hashCode(), option3.hashCode()); + + assertTrue(option1.isLastStatement()); + assertThat(option1.toString()).contains("lastStatement: true"); + + assertNull(option3.isLastStatement()); + assertThat(option3.toString()).doesNotContain("lastStatement: true"); + } } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/XGoogSpannerRequestIdTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/XGoogSpannerRequestIdTest.java new file mode 100644 index 00000000000..12c9213c7dc --- /dev/null +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/XGoogSpannerRequestIdTest.java @@ -0,0 +1,54 @@ +/* + * Copyright 2025 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 + * + * https://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; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class XGoogSpannerRequestIdTest { + private static final Pattern REGEX_RAND_PROCESS_ID = + Pattern.compile("1.([0-9a-z]{16})(\\.\\d+){3}\\.(\\d+)$"); + + @Test + public void testEquals() { + XGoogSpannerRequestId reqID1 = XGoogSpannerRequestId.of(1, 1, 1, 1); + XGoogSpannerRequestId reqID2 = XGoogSpannerRequestId.of(1, 1, 1, 1); + assertEquals(reqID1, reqID2); + assertEquals(reqID1, reqID1); + assertEquals(reqID2, reqID2); + + XGoogSpannerRequestId reqID3 = XGoogSpannerRequestId.of(1, 1, 1, 2); + assertNotEquals(reqID1, reqID3); + assertNotEquals(reqID3, reqID1); + assertEquals(reqID3, reqID3); + } + + @Test + public void testEnsureHexadecimalFormatForRandProcessID() { + String str = XGoogSpannerRequestId.of(1, 2, 3, 4).toString(); + Matcher m = XGoogSpannerRequestIdTest.REGEX_RAND_PROCESS_ID.matcher(str); + assertTrue(m.matches()); + } +} diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/admin/database/v1/DatabaseAdminClientHttpJsonTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/admin/database/v1/DatabaseAdminClientHttpJsonTest.java index 948d3345143..5d476f914a8 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/admin/database/v1/DatabaseAdminClientHttpJsonTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/admin/database/v1/DatabaseAdminClientHttpJsonTest.java @@ -46,6 +46,7 @@ import com.google.protobuf.Empty; import com.google.protobuf.FieldMask; import com.google.protobuf.Timestamp; +import com.google.spanner.admin.database.v1.AddSplitPointsResponse; import com.google.spanner.admin.database.v1.Backup; import com.google.spanner.admin.database.v1.BackupName; import com.google.spanner.admin.database.v1.BackupSchedule; @@ -67,6 +68,7 @@ import com.google.spanner.admin.database.v1.ListDatabaseRolesResponse; import com.google.spanner.admin.database.v1.ListDatabasesResponse; import com.google.spanner.admin.database.v1.RestoreInfo; +import com.google.spanner.admin.database.v1.SplitPoints; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; @@ -2454,6 +2456,92 @@ public void listDatabaseRolesExceptionTest2() throws Exception { } } + @Test + public void addSplitPointsTest() throws Exception { + AddSplitPointsResponse expectedResponse = AddSplitPointsResponse.newBuilder().build(); + mockService.addResponse(expectedResponse); + + DatabaseName database = DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]"); + List splitPoints = new ArrayList<>(); + + AddSplitPointsResponse actualResponse = client.addSplitPoints(database, splitPoints); + Assert.assertEquals(expectedResponse, actualResponse); + + List actualRequests = mockService.getRequestPaths(); + Assert.assertEquals(1, actualRequests.size()); + + String apiClientHeaderKey = + mockService + .getRequestHeaders() + .get(ApiClientHeaderProvider.getDefaultApiClientHeaderKey()) + .iterator() + .next(); + Assert.assertTrue( + GaxHttpJsonProperties.getDefaultApiClientHeaderPattern() + .matcher(apiClientHeaderKey) + .matches()); + } + + @Test + public void addSplitPointsExceptionTest() throws Exception { + ApiException exception = + ApiExceptionFactory.createException( + new Exception(), FakeStatusCode.of(StatusCode.Code.INVALID_ARGUMENT), false); + mockService.addException(exception); + + try { + DatabaseName database = DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]"); + List splitPoints = new ArrayList<>(); + client.addSplitPoints(database, splitPoints); + Assert.fail("No exception raised"); + } catch (InvalidArgumentException e) { + // Expected exception. + } + } + + @Test + public void addSplitPointsTest2() throws Exception { + AddSplitPointsResponse expectedResponse = AddSplitPointsResponse.newBuilder().build(); + mockService.addResponse(expectedResponse); + + String database = "projects/project-3102/instances/instance-3102/databases/database-3102"; + List splitPoints = new ArrayList<>(); + + AddSplitPointsResponse actualResponse = client.addSplitPoints(database, splitPoints); + Assert.assertEquals(expectedResponse, actualResponse); + + List actualRequests = mockService.getRequestPaths(); + Assert.assertEquals(1, actualRequests.size()); + + String apiClientHeaderKey = + mockService + .getRequestHeaders() + .get(ApiClientHeaderProvider.getDefaultApiClientHeaderKey()) + .iterator() + .next(); + Assert.assertTrue( + GaxHttpJsonProperties.getDefaultApiClientHeaderPattern() + .matcher(apiClientHeaderKey) + .matches()); + } + + @Test + public void addSplitPointsExceptionTest2() throws Exception { + ApiException exception = + ApiExceptionFactory.createException( + new Exception(), FakeStatusCode.of(StatusCode.Code.INVALID_ARGUMENT), false); + mockService.addException(exception); + + try { + String database = "projects/project-3102/instances/instance-3102/databases/database-3102"; + List splitPoints = new ArrayList<>(); + client.addSplitPoints(database, splitPoints); + Assert.fail("No exception raised"); + } catch (InvalidArgumentException e) { + // Expected exception. + } + } + @Test public void createBackupScheduleTest() throws Exception { BackupSchedule expectedResponse = diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/admin/database/v1/DatabaseAdminClientTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/admin/database/v1/DatabaseAdminClientTest.java index 33143d458f6..2424ad7bc27 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/admin/database/v1/DatabaseAdminClientTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/admin/database/v1/DatabaseAdminClientTest.java @@ -48,6 +48,8 @@ import com.google.protobuf.Empty; import com.google.protobuf.FieldMask; import com.google.protobuf.Timestamp; +import com.google.spanner.admin.database.v1.AddSplitPointsRequest; +import com.google.spanner.admin.database.v1.AddSplitPointsResponse; import com.google.spanner.admin.database.v1.Backup; import com.google.spanner.admin.database.v1.BackupName; import com.google.spanner.admin.database.v1.BackupSchedule; @@ -87,6 +89,7 @@ import com.google.spanner.admin.database.v1.ListDatabasesResponse; import com.google.spanner.admin.database.v1.RestoreDatabaseRequest; import com.google.spanner.admin.database.v1.RestoreInfo; +import com.google.spanner.admin.database.v1.SplitPoints; import com.google.spanner.admin.database.v1.UpdateBackupRequest; import com.google.spanner.admin.database.v1.UpdateBackupScheduleRequest; import com.google.spanner.admin.database.v1.UpdateDatabaseDdlRequest; @@ -2250,6 +2253,82 @@ public void listDatabaseRolesExceptionTest2() throws Exception { } } + @Test + public void addSplitPointsTest() throws Exception { + AddSplitPointsResponse expectedResponse = AddSplitPointsResponse.newBuilder().build(); + mockDatabaseAdmin.addResponse(expectedResponse); + + DatabaseName database = DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]"); + List splitPoints = new ArrayList<>(); + + AddSplitPointsResponse actualResponse = client.addSplitPoints(database, splitPoints); + Assert.assertEquals(expectedResponse, actualResponse); + + List actualRequests = mockDatabaseAdmin.getRequests(); + Assert.assertEquals(1, actualRequests.size()); + AddSplitPointsRequest actualRequest = ((AddSplitPointsRequest) actualRequests.get(0)); + + Assert.assertEquals(database.toString(), actualRequest.getDatabase()); + Assert.assertEquals(splitPoints, actualRequest.getSplitPointsList()); + Assert.assertTrue( + channelProvider.isHeaderSent( + ApiClientHeaderProvider.getDefaultApiClientHeaderKey(), + GaxGrpcProperties.getDefaultApiClientHeaderPattern())); + } + + @Test + public void addSplitPointsExceptionTest() throws Exception { + StatusRuntimeException exception = new StatusRuntimeException(io.grpc.Status.INVALID_ARGUMENT); + mockDatabaseAdmin.addException(exception); + + try { + DatabaseName database = DatabaseName.of("[PROJECT]", "[INSTANCE]", "[DATABASE]"); + List splitPoints = new ArrayList<>(); + client.addSplitPoints(database, splitPoints); + Assert.fail("No exception raised"); + } catch (InvalidArgumentException e) { + // Expected exception. + } + } + + @Test + public void addSplitPointsTest2() throws Exception { + AddSplitPointsResponse expectedResponse = AddSplitPointsResponse.newBuilder().build(); + mockDatabaseAdmin.addResponse(expectedResponse); + + String database = "database1789464955"; + List splitPoints = new ArrayList<>(); + + AddSplitPointsResponse actualResponse = client.addSplitPoints(database, splitPoints); + Assert.assertEquals(expectedResponse, actualResponse); + + List actualRequests = mockDatabaseAdmin.getRequests(); + Assert.assertEquals(1, actualRequests.size()); + AddSplitPointsRequest actualRequest = ((AddSplitPointsRequest) actualRequests.get(0)); + + Assert.assertEquals(database, actualRequest.getDatabase()); + Assert.assertEquals(splitPoints, actualRequest.getSplitPointsList()); + Assert.assertTrue( + channelProvider.isHeaderSent( + ApiClientHeaderProvider.getDefaultApiClientHeaderKey(), + GaxGrpcProperties.getDefaultApiClientHeaderPattern())); + } + + @Test + public void addSplitPointsExceptionTest2() throws Exception { + StatusRuntimeException exception = new StatusRuntimeException(io.grpc.Status.INVALID_ARGUMENT); + mockDatabaseAdmin.addException(exception); + + try { + String database = "database1789464955"; + List splitPoints = new ArrayList<>(); + client.addSplitPoints(database, splitPoints); + Assert.fail("No exception raised"); + } catch (InvalidArgumentException e) { + // Expected exception. + } + } + @Test public void createBackupScheduleTest() throws Exception { BackupSchedule expectedResponse = diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/admin/database/v1/MockDatabaseAdminImpl.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/admin/database/v1/MockDatabaseAdminImpl.java index 1d904eed413..57ff0fa3819 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/admin/database/v1/MockDatabaseAdminImpl.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/admin/database/v1/MockDatabaseAdminImpl.java @@ -25,6 +25,8 @@ import com.google.longrunning.Operation; import com.google.protobuf.AbstractMessage; import com.google.protobuf.Empty; +import com.google.spanner.admin.database.v1.AddSplitPointsRequest; +import com.google.spanner.admin.database.v1.AddSplitPointsResponse; import com.google.spanner.admin.database.v1.Backup; import com.google.spanner.admin.database.v1.BackupSchedule; import com.google.spanner.admin.database.v1.CopyBackupRequest; @@ -513,6 +515,27 @@ public void listDatabaseRoles( } } + @Override + public void addSplitPoints( + AddSplitPointsRequest request, StreamObserver responseObserver) { + Object response = responses.poll(); + if (response instanceof AddSplitPointsResponse) { + requests.add(request); + responseObserver.onNext(((AddSplitPointsResponse) response)); + responseObserver.onCompleted(); + } else if (response instanceof Exception) { + responseObserver.onError(((Exception) response)); + } else { + responseObserver.onError( + new IllegalArgumentException( + String.format( + "Unrecognized response type %s for method AddSplitPoints, expected %s or %s", + response == null ? "null" : response.getClass().getName(), + AddSplitPointsResponse.class.getName(), + Exception.class.getName()))); + } + } + @Override public void createBackupSchedule( CreateBackupScheduleRequest request, StreamObserver responseObserver) { diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/AbstractMockServerTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/AbstractMockServerTest.java index ac6a6ecbc77..a9b27f21545 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/AbstractMockServerTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/AbstractMockServerTest.java @@ -328,6 +328,13 @@ boolean isMultiplexedSessionsEnabled(Spanner spanner) { return spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSession(); } + boolean isMultiplexedSessionsEnabledForPartitionedOps(Spanner spanner) { + if (spanner.getOptions() == null || spanner.getOptions().getSessionPoolOptions() == null) { + return false; + } + return spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionPartitionedOps(); + } + boolean isMultiplexedSessionsEnabledForRW(Spanner spanner) { if (spanner.getOptions() == null || spanner.getOptions().getSessionPoolOptions() == null) { return false; diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/AutoCommitMockServerTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/AutoCommitMockServerTest.java new file mode 100644 index 00000000000..c48c6703353 --- /dev/null +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/AutoCommitMockServerTest.java @@ -0,0 +1,166 @@ +/* + * Copyright 2024 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.connection; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import com.google.cloud.spanner.ResultSet; +import com.google.spanner.v1.BeginTransactionRequest; +import com.google.spanner.v1.CommitRequest; +import com.google.spanner.v1.ExecuteBatchDmlRequest; +import com.google.spanner.v1.ExecuteSqlRequest; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class AutoCommitMockServerTest extends AbstractMockServerTest { + + @Test + public void testQuery() { + try (Connection connection = createConnection()) { + connection.setAutocommit(true); + //noinspection EmptyTryBlock + try (ResultSet ignore = connection.executeQuery(SELECT1_STATEMENT)) {} + } + assertEquals(1, mockSpanner.countRequestsOfType(ExecuteSqlRequest.class)); + ExecuteSqlRequest request = mockSpanner.getRequestsOfType(ExecuteSqlRequest.class).get(0); + assertTrue(request.getTransaction().hasSingleUse()); + assertTrue(request.getTransaction().getSingleUse().hasReadOnly()); + assertFalse(request.getLastStatement()); + } + + @Test + public void testDml() { + try (Connection connection = createConnection()) { + connection.setAutocommit(true); + connection.executeUpdate(INSERT_STATEMENT); + } + assertEquals(1, mockSpanner.countRequestsOfType(ExecuteSqlRequest.class)); + ExecuteSqlRequest request = mockSpanner.getRequestsOfType(ExecuteSqlRequest.class).get(0); + assertTrue(request.getTransaction().hasBegin()); + assertTrue(request.getTransaction().getBegin().hasReadWrite()); + assertTrue(request.getLastStatement()); + assertEquals(1, mockSpanner.countRequestsOfType(CommitRequest.class)); + } + + @Test + public void testDmlReturning() { + try (Connection connection = createConnection()) { + connection.setAutocommit(true); + //noinspection EmptyTryBlock + try (ResultSet ignore = connection.executeQuery(INSERT_RETURNING_STATEMENT)) {} + } + assertEquals(1, mockSpanner.countRequestsOfType(ExecuteSqlRequest.class)); + ExecuteSqlRequest request = mockSpanner.getRequestsOfType(ExecuteSqlRequest.class).get(0); + assertTrue(request.getTransaction().hasBegin()); + assertTrue(request.getTransaction().getBegin().hasReadWrite()); + assertTrue(request.getLastStatement()); + assertEquals(1, mockSpanner.countRequestsOfType(CommitRequest.class)); + } + + @Test + public void testBatchDml() { + try (Connection connection = createConnection()) { + connection.setAutocommit(true); + connection.startBatchDml(); + connection.executeUpdate(INSERT_STATEMENT); + connection.executeUpdate(INSERT_STATEMENT); + connection.runBatch(); + } + assertEquals(1, mockSpanner.countRequestsOfType(ExecuteBatchDmlRequest.class)); + ExecuteBatchDmlRequest request = + mockSpanner.getRequestsOfType(ExecuteBatchDmlRequest.class).get(0); + assertTrue(request.getTransaction().hasBegin()); + assertTrue(request.getTransaction().getBegin().hasReadWrite()); + assertTrue(request.getLastStatements()); + assertEquals(1, mockSpanner.countRequestsOfType(CommitRequest.class)); + } + + @Test + public void testPartitionedDml() { + try (Connection connection = createConnection()) { + connection.setAutocommit(true); + connection.setAutocommitDmlMode(AutocommitDmlMode.PARTITIONED_NON_ATOMIC); + connection.executeUpdate(INSERT_STATEMENT); + } + assertEquals(1, mockSpanner.countRequestsOfType(BeginTransactionRequest.class)); + BeginTransactionRequest beginRequest = + mockSpanner.getRequestsOfType(BeginTransactionRequest.class).get(0); + assertTrue(beginRequest.getOptions().hasPartitionedDml()); + assertEquals(1, mockSpanner.countRequestsOfType(ExecuteSqlRequest.class)); + ExecuteSqlRequest request = mockSpanner.getRequestsOfType(ExecuteSqlRequest.class).get(0); + assertTrue(request.getTransaction().hasId()); + assertFalse(request.getLastStatement()); + assertEquals(0, mockSpanner.countRequestsOfType(CommitRequest.class)); + } + + @Test + public void testDmlAborted() { + try (Connection connection = createConnection()) { + connection.setAutocommit(true); + mockSpanner.abortNextTransaction(); + connection.executeUpdate(INSERT_STATEMENT); + } + assertEquals(2, mockSpanner.countRequestsOfType(ExecuteSqlRequest.class)); + for (ExecuteSqlRequest request : mockSpanner.getRequestsOfType(ExecuteSqlRequest.class)) { + assertTrue(request.getTransaction().hasBegin()); + assertTrue(request.getTransaction().getBegin().hasReadWrite()); + assertTrue(request.getLastStatement()); + } + assertEquals(2, mockSpanner.countRequestsOfType(CommitRequest.class)); + } + + @Test + public void testDmlReturningAborted() { + try (Connection connection = createConnection()) { + connection.setAutocommit(true); + mockSpanner.abortNextTransaction(); + //noinspection EmptyTryBlock + try (ResultSet ignore = connection.executeQuery(INSERT_RETURNING_STATEMENT)) {} + } + assertEquals(2, mockSpanner.countRequestsOfType(ExecuteSqlRequest.class)); + for (ExecuteSqlRequest request : mockSpanner.getRequestsOfType(ExecuteSqlRequest.class)) { + assertTrue(request.getTransaction().hasBegin()); + assertTrue(request.getTransaction().getBegin().hasReadWrite()); + assertTrue(request.getLastStatement()); + } + assertEquals(2, mockSpanner.countRequestsOfType(CommitRequest.class)); + } + + @Test + public void testBatchDmlAborted() { + try (Connection connection = createConnection()) { + connection.setAutocommit(true); + mockSpanner.abortNextTransaction(); + connection.startBatchDml(); + connection.executeUpdate(INSERT_STATEMENT); + connection.executeUpdate(INSERT_STATEMENT); + connection.runBatch(); + } + assertEquals(2, mockSpanner.countRequestsOfType(ExecuteBatchDmlRequest.class)); + for (ExecuteBatchDmlRequest request : + mockSpanner.getRequestsOfType(ExecuteBatchDmlRequest.class)) { + assertTrue(request.getTransaction().hasBegin()); + assertTrue(request.getTransaction().getBegin().hasReadWrite()); + assertTrue(request.getLastStatements()); + } + assertEquals(2, mockSpanner.countRequestsOfType(CommitRequest.class)); + } +} diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/AutocommitDmlModeTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/AutocommitDmlModeTest.java index a66d14a8b76..bb845746e13 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/AutocommitDmlModeTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/AutocommitDmlModeTest.java @@ -18,6 +18,9 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -28,6 +31,7 @@ import com.google.cloud.spanner.BatchClient; import com.google.cloud.spanner.DatabaseClient; import com.google.cloud.spanner.Dialect; +import com.google.cloud.spanner.Options; import com.google.cloud.spanner.Spanner; import com.google.cloud.spanner.Statement; import com.google.cloud.spanner.TransactionContext; @@ -82,12 +86,12 @@ public void testAutocommitDmlModeTransactional() { .setCredentials(NoCredentials.getInstance()) .setUri(URI) .build())) { - assertThat(connection.isAutocommit(), is(true)); - assertThat(connection.isReadOnly(), is(false)); - assertThat(connection.getAutocommitDmlMode(), is(AutocommitDmlMode.TRANSACTIONAL)); + assertTrue(connection.isAutocommit()); + assertFalse(connection.isReadOnly()); + assertEquals(AutocommitDmlMode.TRANSACTIONAL, connection.getAutocommitDmlMode()); connection.execute(Statement.of(UPDATE)); - verify(txContext).executeUpdate(Statement.of(UPDATE)); + verify(txContext).executeUpdate(Statement.of(UPDATE), Options.lastStatement()); verify(dbClient, never()).executePartitionedUpdate(Statement.of(UPDATE)); } } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionImplTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionImplTest.java index a81005bbb45..d4b7c035658 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionImplTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionImplTest.java @@ -354,7 +354,8 @@ public TransactionRunner answer(InvocationOnMock invocation) { public T run(TransactionCallable callable) { commitResponse = new CommitResponse(Timestamp.ofTimeSecondsAndNanos(1, 1)); TransactionContext transaction = mock(TransactionContext.class); - when(transaction.executeUpdate(Statement.of(UPDATE))).thenReturn(1L); + when(transaction.executeUpdate(Statement.of(UPDATE), Options.lastStatement())) + .thenReturn(1L); try { return callable.run(transaction); } catch (Exception e) { diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ITAbstractSpannerTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ITAbstractSpannerTest.java index 7bf6a670d9c..988263af9f0 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ITAbstractSpannerTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ITAbstractSpannerTest.java @@ -22,6 +22,7 @@ import com.google.cloud.spanner.GceTestEnvConfig; import com.google.cloud.spanner.IntegrationTestEnv; import com.google.cloud.spanner.ResultSet; +import com.google.cloud.spanner.Spanner; import com.google.cloud.spanner.SpannerExceptionFactory; import com.google.cloud.spanner.SpannerOptions; import com.google.cloud.spanner.Statement; @@ -146,6 +147,9 @@ public void intercept( if (usingMultiplexedsession) { Field stateField = cls.getDeclaredField("txnState"); stateField.setAccessible(true); + if (tx.getState() == null) { + return; + } tx.rollback(); stateField.set(tx, TransactionState.ABORTED); } else { @@ -368,4 +372,11 @@ protected boolean indexExists(Connection connection, String table, String index) } return false; } + + protected boolean isMultiplexedSessionsEnabledForRW(Spanner spanner) { + if (spanner.getOptions() == null || spanner.getOptions().getSessionPoolOptions() == null) { + return false; + } + return spanner.getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW(); + } } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/PartitionedQueryMockServerTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/PartitionedQueryMockServerTest.java index 655ca0de586..cdd8b15a38a 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/PartitionedQueryMockServerTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/PartitionedQueryMockServerTest.java @@ -93,7 +93,9 @@ public void testPartitionQuery() { assertFalse(resultSet.next()); } } - if (isMultiplexedSessionsEnabled(connection.getSpanner())) { + if (isMultiplexedSessionsEnabledForPartitionedOps(connection.getSpanner())) { + assertEquals(2, mockSpanner.countRequestsOfType(CreateSessionRequest.class)); + } else if (isMultiplexedSessionsEnabled(connection.getSpanner())) { assertEquals(3, mockSpanner.countRequestsOfType(CreateSessionRequest.class)); } else { assertEquals(2, mockSpanner.countRequestsOfType(CreateSessionRequest.class)); @@ -155,7 +157,9 @@ public void testMixNormalAndPartitionQueryInReadOnlyTransaction() { readTimestamps.add(connection.getReadTimestamp()); connection.commit(); } - if (isMultiplexedSessionsEnabled(connection.getSpanner())) { + if (isMultiplexedSessionsEnabledForPartitionedOps(connection.getSpanner())) { + assertEquals(2, mockSpanner.countRequestsOfType(CreateSessionRequest.class)); + } else if (isMultiplexedSessionsEnabled(connection.getSpanner())) { assertEquals(3, mockSpanner.countRequestsOfType(CreateSessionRequest.class)); } else { assertEquals(2, mockSpanner.countRequestsOfType(CreateSessionRequest.class)); @@ -228,6 +232,10 @@ public void testRunPartition() { if (createSessionRequestCounts == expectedCreateSessionsRPC + 1) { isMultiplexedSessionCreated = true; } + } else if (isMultiplexedSessionsEnabledForPartitionedOps(connection.getSpanner()) + && isMultiplexedSessionCreated) { + // When multiplexed session will be reused for each iteration. + assertEquals(0, mockSpanner.countRequestsOfType(CreateSessionRequest.class)); } else { assertEquals( expectedCreateSessionsRPC, @@ -261,6 +269,7 @@ public void testRunPartitionUsingSql() { String prefix = dialect == Dialect.POSTGRESQL ? "spanner." : ""; int maxPartitions = 5; + boolean isMultiplexedSessionCreated = false; try (Connection connection = createConnection()) { connection.execute(Statement.of("set autocommit=true")); assertTrue(connection.isAutocommit()); @@ -284,7 +293,6 @@ public void testRunPartitionUsingSql() { assertFalse(resultSet.next()); } - boolean isMultiplexedSessionCreated = false; for (boolean useLiteral : new boolean[] {true, false}) { try (ResultSet partitions = connection.executeQuery( @@ -328,6 +336,10 @@ public void testRunPartitionUsingSql() { if (createSessionRequestCounts == expectedCreateSessionsRPC + 1) { isMultiplexedSessionCreated = true; } + } else if (isMultiplexedSessionsEnabledForPartitionedOps(connection.getSpanner()) + && isMultiplexedSessionCreated) { + // When multiplexed session will be reused for each iteration. + assertEquals(0, mockSpanner.countRequestsOfType(CreateSessionRequest.class)); } else { assertEquals( expectedCreateSessionsRPC, @@ -570,7 +582,9 @@ public void testRunPartitionedQueryUsingSql() { assertEquals(maxPartitions * generatedRowCount, rowCount); } } - if (isMultiplexedSessionsEnabled(connection.getSpanner())) { + if (isMultiplexedSessionsEnabledForPartitionedOps(connection.getSpanner())) { + assertEquals(2, mockSpanner.countRequestsOfType(CreateSessionRequest.class)); + } else if (isMultiplexedSessionsEnabled(connection.getSpanner())) { assertEquals(3, mockSpanner.countRequestsOfType(CreateSessionRequest.class)); } else { assertEquals(2, mockSpanner.countRequestsOfType(CreateSessionRequest.class)); @@ -679,7 +693,9 @@ public void testRunPartitionedQueryWithMaxParallelism() { assertEquals(maxPartitions * generatedRowCount, rowCount); } } - if (isMultiplexedSessionsEnabled(connection.getSpanner())) { + if (isMultiplexedSessionsEnabledForPartitionedOps(connection.getSpanner())) { + assertEquals(2, mockSpanner.countRequestsOfType(CreateSessionRequest.class)); + } else if (isMultiplexedSessionsEnabled(connection.getSpanner())) { assertEquals(6, mockSpanner.countRequestsOfType(CreateSessionRequest.class)); } else { assertEquals(5, mockSpanner.countRequestsOfType(CreateSessionRequest.class)); @@ -758,7 +774,10 @@ public void testAutoPartitionMode() { exception .getMessage() .contains("Partition query is not supported for read/write transaction")); - if (isMultiplexedSessionsEnabled(connection.getSpanner())) { + + if (isMultiplexedSessionsEnabledForPartitionedOps(connection.getSpanner())) { + assertEquals(2, mockSpanner.countRequestsOfType(CreateSessionRequest.class)); + } else if (isMultiplexedSessionsEnabled(connection.getSpanner())) { assertEquals(3, mockSpanner.countRequestsOfType(CreateSessionRequest.class)); } else { assertEquals(2, mockSpanner.countRequestsOfType(CreateSessionRequest.class)); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/RetryDmlAsPartitionedDmlMockServerTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/RetryDmlAsPartitionedDmlMockServerTest.java index 022c9a92f1f..610a1a99cbe 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/RetryDmlAsPartitionedDmlMockServerTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/RetryDmlAsPartitionedDmlMockServerTest.java @@ -83,6 +83,8 @@ public void testTransactionMutationLimitExceeded_isNotRetriedByDefault() { assertEquals(0, exception.getSuppressed().length); } assertEquals(1, mockSpanner.countRequestsOfType(ExecuteSqlRequest.class)); + ExecuteSqlRequest request = mockSpanner.getRequestsOfType(ExecuteSqlRequest.class).get(0); + assertTrue(request.getLastStatement()); assertEquals(0, mockSpanner.countRequestsOfType(CommitRequest.class)); } @@ -108,6 +110,7 @@ public void testTransactionMutationLimitExceeded_canBeRetriedAsPDML() { ExecuteSqlRequest transactionalRequest = mockSpanner.getRequestsOfType(ExecuteSqlRequest.class).get(0); assertTrue(transactionalRequest.getTransaction().getBegin().hasReadWrite()); + assertTrue(transactionalRequest.getLastStatement()); // Partitioned DML uses an explicit BeginTransaction RPC. assertEquals(1, mockSpanner.countRequestsOfType(BeginTransactionRequest.class)); @@ -117,6 +120,7 @@ public void testTransactionMutationLimitExceeded_canBeRetriedAsPDML() { ExecuteSqlRequest partitionedDmlRequest = mockSpanner.getRequestsOfType(ExecuteSqlRequest.class).get(1); assertTrue(partitionedDmlRequest.getTransaction().hasId()); + assertFalse(partitionedDmlRequest.getLastStatement()); // Partitioned DML transactions are not committed. assertEquals(0, mockSpanner.countRequestsOfType(CommitRequest.class)); @@ -163,6 +167,7 @@ public void testTransactionMutationLimitExceeded_retryAsPDMLFails() { ExecuteSqlRequest transactionalRequest = mockSpanner.getRequestsOfType(ExecuteSqlRequest.class).get(0); assertTrue(transactionalRequest.getTransaction().getBegin().hasReadWrite()); + assertTrue(transactionalRequest.getLastStatement()); // Partitioned DML uses an explicit BeginTransaction RPC. assertEquals(1, mockSpanner.countRequestsOfType(BeginTransactionRequest.class)); @@ -172,6 +177,7 @@ public void testTransactionMutationLimitExceeded_retryAsPDMLFails() { ExecuteSqlRequest partitionedDmlRequest = mockSpanner.getRequestsOfType(ExecuteSqlRequest.class).get(1); assertTrue(partitionedDmlRequest.getTransaction().hasId()); + assertFalse(partitionedDmlRequest.getLastStatement()); // Partitioned DML transactions are not committed. assertEquals(0, mockSpanner.countRequestsOfType(CommitRequest.class)); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/SingleUseTransactionTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/SingleUseTransactionTest.java index 6edf46b5623..0e2a322023d 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/SingleUseTransactionTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/SingleUseTransactionTest.java @@ -395,6 +395,8 @@ private SingleUseTransaction createSubject( final TransactionContext txContext = mock(TransactionContext.class); when(txContext.executeUpdate(Statement.of(VALID_UPDATE))).thenReturn(VALID_UPDATE_COUNT); + when(txContext.executeUpdate(Statement.of(VALID_UPDATE), Options.lastStatement())) + .thenReturn(VALID_UPDATE_COUNT); when(txContext.executeUpdate(Statement.of(SLOW_UPDATE))) .thenAnswer( invocation -> { @@ -404,6 +406,9 @@ private SingleUseTransaction createSubject( when(txContext.executeUpdate(Statement.of(INVALID_UPDATE))) .thenThrow( SpannerExceptionFactory.newSpannerException(ErrorCode.UNKNOWN, "invalid update")); + when(txContext.executeUpdate(Statement.of(INVALID_UPDATE), Options.lastStatement())) + .thenThrow( + SpannerExceptionFactory.newSpannerException(ErrorCode.UNKNOWN, "invalid update")); SimpleTransactionManager txManager = new SimpleTransactionManager(txContext, commitBehavior); when(dbClient.transactionManager()).thenReturn(txManager); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/it/ITAsyncTransactionRetryTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/it/ITAsyncTransactionRetryTest.java index 744d7042df4..e25e376ca22 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/it/ITAsyncTransactionRetryTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/it/ITAsyncTransactionRetryTest.java @@ -221,6 +221,8 @@ public void testCommitAborted() { AbortInterceptor interceptor = new AbortInterceptor(0); try (ITConnection connection = createConnection(interceptor, new CountTransactionRetryListener())) { + interceptor.setUsingMultiplexedSession( + isMultiplexedSessionsEnabledForRW(connection.getSpanner())); ApiFuture count = getTestRecordCountAsync(connection); // do an insert ApiFuture updateCount = @@ -253,6 +255,8 @@ public void testInsertAborted() { AbortInterceptor interceptor = new AbortInterceptor(0); try (ITConnection connection = createConnection(interceptor, new CountTransactionRetryListener())) { + interceptor.setUsingMultiplexedSession( + isMultiplexedSessionsEnabledForRW(connection.getSpanner())); ApiFuture count = getTestRecordCountAsync(connection); // indicate that the next statement should abort interceptor.setProbability(1.0); @@ -276,6 +280,8 @@ public void testUpdateAborted() { AbortInterceptor interceptor = new AbortInterceptor(0); try (ITConnection connection = createConnection(interceptor, new CountTransactionRetryListener())) { + interceptor.setUsingMultiplexedSession( + isMultiplexedSessionsEnabledForRW(connection.getSpanner())); ApiFuture count = getTestRecordCountAsync(connection); // insert a test record connection.executeUpdateAsync( @@ -309,6 +315,8 @@ public void testQueryAborted() { AbortInterceptor interceptor = new AbortInterceptor(0); try (ITConnection connection = createConnection(interceptor, new CountTransactionRetryListener())) { + interceptor.setUsingMultiplexedSession( + isMultiplexedSessionsEnabledForRW(connection.getSpanner())); // insert a test record connection.executeUpdateAsync( Statement.of("INSERT INTO TEST (ID, NAME) VALUES (1, 'test aborted')")); @@ -359,6 +367,8 @@ public void testNextCallAborted() { AbortInterceptor interceptor = new AbortInterceptor(0); try (ITConnection connection = createConnection(interceptor, new CountTransactionRetryListener())) { + interceptor.setUsingMultiplexedSession( + isMultiplexedSessionsEnabledForRW(connection.getSpanner())); // insert two test records connection.executeUpdateAsync( Statement.of("INSERT INTO TEST (ID, NAME) VALUES (1, 'test 1')")); @@ -392,6 +402,8 @@ public void testMultipleAborts() { AbortInterceptor interceptor = new AbortInterceptor(0); try (ITConnection connection = createConnection(interceptor, new CountTransactionRetryListener())) { + interceptor.setUsingMultiplexedSession( + isMultiplexedSessionsEnabledForRW(connection.getSpanner())); ApiFuture count = getTestRecordCountAsync(connection); // do three inserts which all will abort and retry interceptor.setProbability(1.0); @@ -428,6 +440,8 @@ public void testAbortAfterSelect() { AbortInterceptor interceptor = new AbortInterceptor(0); try (ITConnection connection = createConnection(interceptor, new CountTransactionRetryListener())) { + interceptor.setUsingMultiplexedSession( + isMultiplexedSessionsEnabledForRW(connection.getSpanner())); ApiFuture count = getTestRecordCountAsync(connection); // insert a test record connection.executeUpdateAsync( @@ -504,6 +518,8 @@ public void testAbortWithResultSetHalfway() { AbortInterceptor interceptor = new AbortInterceptor(0); try (ITConnection connection = createConnection(interceptor, new CountTransactionRetryListener())) { + interceptor.setUsingMultiplexedSession( + isMultiplexedSessionsEnabledForRW(connection.getSpanner())); // insert two test records connection.executeUpdateAsync( Statement.of("INSERT INTO TEST (ID, NAME) VALUES (1, 'test 1')")); @@ -539,6 +555,8 @@ public void testAbortWithResultSetFullyConsumed() { AbortInterceptor interceptor = new AbortInterceptor(0); try (ITConnection connection = createConnection(interceptor, new CountTransactionRetryListener())) { + interceptor.setUsingMultiplexedSession( + isMultiplexedSessionsEnabledForRW(connection.getSpanner())); // insert two test records connection.executeUpdateAsync( Statement.of("INSERT INTO TEST (ID, NAME) VALUES (1, 'test 1')")); @@ -581,6 +599,8 @@ public void testAbortWithConcurrentInsert() { AbortInterceptor interceptor = new AbortInterceptor(0); try (ITConnection connection = createConnection(interceptor, new CountTransactionRetryListener())) { + interceptor.setUsingMultiplexedSession( + isMultiplexedSessionsEnabledForRW(connection.getSpanner())); // insert two test records connection.executeUpdateAsync( Statement.of("INSERT INTO TEST (ID, NAME) VALUES (1, 'test 1')")); @@ -632,6 +652,8 @@ public void testAbortWithConcurrentDelete() { AbortInterceptor interceptor = new AbortInterceptor(0); // first insert two test records try (ITConnection connection = createConnection()) { + interceptor.setUsingMultiplexedSession( + isMultiplexedSessionsEnabledForRW(connection.getSpanner())); connection.executeUpdateAsync( Statement.of("INSERT INTO TEST (ID, NAME) VALUES (1, 'test 1')")); connection.executeUpdateAsync( @@ -641,6 +663,8 @@ public void testAbortWithConcurrentDelete() { // open a new connection and select the two test records try (ITConnection connection = createConnection(interceptor, new CountTransactionRetryListener())) { + interceptor.setUsingMultiplexedSession( + isMultiplexedSessionsEnabledForRW(connection.getSpanner())); // select the test records and consume the entire result set try (AsyncResultSet rs = connection.executeQueryAsync(Statement.of("SELECT * FROM TEST ORDER BY ID"))) { @@ -694,6 +718,8 @@ public void testAbortWithConcurrentUpdate() { // open a new connection and select the two test records try (ITConnection connection = createConnection(interceptor, new CountTransactionRetryListener())) { + interceptor.setUsingMultiplexedSession( + isMultiplexedSessionsEnabledForRW(connection.getSpanner())); // select the test records and consume the entire result set try (AsyncResultSet rs = connection.executeQueryAsync(Statement.of("SELECT * FROM TEST ORDER BY ID"))) { @@ -744,6 +770,8 @@ public void testAbortWithUnseenConcurrentInsert() throws InterruptedException { AbortInterceptor interceptor = new AbortInterceptor(0); try (ITConnection connection = createConnection(interceptor, new CountTransactionRetryListener())) { + interceptor.setUsingMultiplexedSession( + isMultiplexedSessionsEnabledForRW(connection.getSpanner())); // insert three test records connection.executeUpdateAsync( Statement.of("INSERT INTO TEST (ID, NAME) VALUES (1, 'test 1')")); @@ -833,6 +861,8 @@ public void testRetryLargeResultSet() { final long UPDATED_RECORDS = 1000L; AbortInterceptor interceptor = new AbortInterceptor(0); try (ITConnection connection = createConnection()) { + interceptor.setUsingMultiplexedSession( + isMultiplexedSessionsEnabledForRW(connection.getSpanner())); // insert test records for (int i = 0; i < NUMBER_OF_TEST_RECORDS; i++) { connection.bufferedWrite( @@ -845,6 +875,8 @@ public void testRetryLargeResultSet() { } try (ITConnection connection = createConnection(interceptor, new CountTransactionRetryListener())) { + interceptor.setUsingMultiplexedSession( + isMultiplexedSessionsEnabledForRW(connection.getSpanner())); // select the test records and iterate over them try (AsyncResultSet rs = connection.executeQueryAsync(Statement.of("SELECT * FROM TEST ORDER BY ID"))) { @@ -867,6 +899,8 @@ public void testRetryLargeResultSet() { // Wait until the entire result set has been consumed. get(finished); } + interceptor.setUsingMultiplexedSession( + isMultiplexedSessionsEnabledForRW(connection.getSpanner())); // Do an update that will abort and retry. interceptor.setProbability(1.0); interceptor.setOnlyInjectOnce(true); @@ -898,6 +932,8 @@ public void testRetryHighAbortRate() { AbortInterceptor interceptor = new AbortInterceptor(0.25D); try (ITConnection connection = createConnection(interceptor, new CountTransactionRetryListener())) { + interceptor.setUsingMultiplexedSession( + isMultiplexedSessionsEnabledForRW(connection.getSpanner())); // insert test records for (int i = 0; i < NUMBER_OF_TEST_RECORDS; i++) { connection.bufferedWrite( diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/it/ITSqlMusicScriptTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/it/ITSqlMusicScriptTest.java index e7afe957705..745c57cfb2a 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/it/ITSqlMusicScriptTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/it/ITSqlMusicScriptTest.java @@ -71,6 +71,8 @@ public void test02_RunAbortedTest() { long numberOfSongs = 0L; AbortInterceptor interceptor = new AbortInterceptor(0.0D); try (ITConnection connection = createConnection(interceptor)) { + interceptor.setUsingMultiplexedSession( + isMultiplexedSessionsEnabledForRW(connection.getSpanner())); connection.setAutocommit(false); connection.setRetryAbortsInternally(true); // Read all data from the different music tables in the transaction diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/it/ITTransactionRetryTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/it/ITTransactionRetryTest.java index 0cf3abda6bf..54f714a13aa 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/it/ITTransactionRetryTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/it/ITTransactionRetryTest.java @@ -172,6 +172,8 @@ public void testCommitAborted() { AbortInterceptor interceptor = new AbortInterceptor(0); try (ITConnection connection = createConnection(interceptor, new CountTransactionRetryListener())) { + interceptor.setUsingMultiplexedSession( + isMultiplexedSessionsEnabledForRW(connection.getSpanner())); // verify that the there is no test record try (ResultSet rs = connection.executeQuery(Statement.of("SELECT COUNT(*) AS C FROM TEST WHERE ID=1"))) { @@ -216,6 +218,8 @@ public void testInsertAborted() { assertThat(rs.getLong("C"), is(equalTo(0L))); assertThat(rs.next(), is(false)); } + interceptor.setUsingMultiplexedSession( + isMultiplexedSessionsEnabledForRW(connection.getSpanner())); // indicate that the next statement should abort interceptor.setProbability(1.0); interceptor.setOnlyInjectOnce(true); @@ -241,6 +245,8 @@ public void testUpdateAborted() { AbortInterceptor interceptor = new AbortInterceptor(0); try (ITConnection connection = createConnection(interceptor, new CountTransactionRetryListener())) { + interceptor.setUsingMultiplexedSession( + isMultiplexedSessionsEnabledForRW(connection.getSpanner())); // verify that the there is no test record try (ResultSet rs = connection.executeQuery(Statement.of("SELECT COUNT(*) AS C FROM TEST WHERE ID=1"))) { @@ -284,6 +290,8 @@ public void testQueryAborted() { assertThat(rs.getLong("C"), is(equalTo(0L))); assertThat(rs.next(), is(false)); } + interceptor.setUsingMultiplexedSession( + isMultiplexedSessionsEnabledForRW(connection.getSpanner())); // insert a test record connection.executeUpdate( Statement.of("INSERT INTO TEST (ID, NAME) VALUES (1, 'test aborted')")); @@ -321,6 +329,8 @@ public void testNextCallAborted() { connection.executeUpdate(Statement.of("INSERT INTO TEST (ID, NAME) VALUES (2, 'test 2')")); // do a query try (ResultSet rs = connection.executeQuery(Statement.of("SELECT * FROM TEST ORDER BY ID"))) { + interceptor.setUsingMultiplexedSession( + isMultiplexedSessionsEnabledForRW(connection.getSpanner())); // the first record should be accessible without any problems assertThat(rs.next(), is(true)); assertThat(rs.getLong("ID"), is(equalTo(1L))); @@ -358,6 +368,8 @@ public void testMultipleAborts() { assertThat(rs.getLong("C"), is(equalTo(0L))); assertThat(rs.next(), is(false)); } + interceptor.setUsingMultiplexedSession( + isMultiplexedSessionsEnabledForRW(connection.getSpanner())); // do three inserts which all will abort and retry interceptor.setProbability(1.0); interceptor.setOnlyInjectOnce(true); @@ -405,6 +417,8 @@ public void testAbortAfterSelect() { assertThat(rs.getString("NAME"), is(equalTo("test 1"))); assertThat(rs.next(), is(false)); } + interceptor.setUsingMultiplexedSession( + isMultiplexedSessionsEnabledForRW(connection.getSpanner())); // do another insert that will abort and retry interceptor.setProbability(1.0); interceptor.setOnlyInjectOnce(true); @@ -439,6 +453,8 @@ public void testAbortWithResultSetHalfway() { // iterate one step assertThat(rs.next(), is(true)); assertThat(rs.getLong("ID"), is(equalTo(1L))); + interceptor.setUsingMultiplexedSession( + isMultiplexedSessionsEnabledForRW(connection.getSpanner())); // do another insert that will abort and retry interceptor.setProbability(1.0); interceptor.setOnlyInjectOnce(true); @@ -475,6 +491,8 @@ public void testAbortWithResultSetFullyConsumed() { // do nothing, just consume the result set } } + interceptor.setUsingMultiplexedSession( + isMultiplexedSessionsEnabledForRW(connection.getSpanner())); // do another insert that will abort and retry interceptor.setProbability(1.0); interceptor.setOnlyInjectOnce(true); @@ -512,6 +530,8 @@ public void testAbortWithConcurrentInsert() { } // now try to do an insert that will abort. The retry should now fail as there has been a // concurrent modification + interceptor.setUsingMultiplexedSession( + isMultiplexedSessionsEnabledForRW(connection.getSpanner())); interceptor.setProbability(1.0); interceptor.setOnlyInjectOnce(true); boolean expectedException = false; @@ -551,6 +571,8 @@ public void testAbortWithConcurrentDelete() { } // now try to do an insert that will abort. The retry should now fail as there has been a // concurrent modification + interceptor.setUsingMultiplexedSession( + isMultiplexedSessionsEnabledForRW(connection.getSpanner())); interceptor.setProbability(1.0); interceptor.setOnlyInjectOnce(true); boolean expectedException = false; @@ -590,6 +612,8 @@ public void testAbortWithConcurrentUpdate() { } // now try to do an insert that will abort. The retry should now fail as there has been a // concurrent modification + interceptor.setUsingMultiplexedSession( + isMultiplexedSessionsEnabledForRW(connection.getSpanner())); interceptor.setProbability(1.0); interceptor.setOnlyInjectOnce(true); boolean expectedException = false; @@ -629,6 +653,8 @@ public void testAbortWithUnseenConcurrentInsert() { connection2.commit(); } // now try to do an insert that will abort. The retry should still succeed. + interceptor.setUsingMultiplexedSession( + isMultiplexedSessionsEnabledForRW(connection.getSpanner())); interceptor.setProbability(1.0); interceptor.setOnlyInjectOnce(true); int currentRetryCount = RETRY_STATISTICS.totalRetryAttemptsStarted; @@ -714,6 +740,8 @@ private int testAbortWithUnseenConcurrentInsertAbortOnNext(int callsToNext) // First verify that the transaction has not yet retried. int currentRetryCount = RETRY_STATISTICS.totalRetryAttemptsStarted; + interceptor.setUsingMultiplexedSession( + isMultiplexedSessionsEnabledForRW(connection.getSpanner())); interceptor.setProbability(1.0); interceptor.setOnlyInjectOnce(true); @@ -760,6 +788,8 @@ public void testAbortWithConcurrentInsertAndContinue() { } // Now try to do an insert that will abort. The retry should now fail as there has been a // concurrent modification. + interceptor.setUsingMultiplexedSession( + isMultiplexedSessionsEnabledForRW(connection.getSpanner())); interceptor.setProbability(1.0); interceptor.setOnlyInjectOnce(true); boolean expectedException = false; @@ -807,6 +837,8 @@ protected boolean shouldAbort(String statement, ExecutionStep step) { }; try (ITConnection connection = createConnection(interceptor, new CountTransactionRetryListener())) { + interceptor.setUsingMultiplexedSession( + isMultiplexedSessionsEnabledForRW(connection.getSpanner())); connection.executeUpdate( Statement.of("INSERT INTO TEST (ID, NAME) VALUES (1, 'test aborted')")); connection.commit(); @@ -852,6 +884,8 @@ protected boolean shouldAbort(String statement, ExecutionStep step) { }; try (ITConnection connection = createConnection(interceptor, new CountTransactionRetryListener())) { + interceptor.setUsingMultiplexedSession( + isMultiplexedSessionsEnabledForRW(connection.getSpanner())); connection.executeUpdate( Statement.of("INSERT INTO TEST (ID, NAME) VALUES (1, 'test aborted')")); connection.commit(); @@ -906,6 +940,8 @@ protected boolean shouldAbort(String statement, ExecutionStep step) { }; try (ITConnection connection = createConnection(interceptor, new CountTransactionRetryListener())) { + interceptor.setUsingMultiplexedSession( + isMultiplexedSessionsEnabledForRW(connection.getSpanner())); // Insert two test records. connection.executeUpdate(Statement.of("INSERT INTO TEST (ID, NAME) VALUES (1, 'test 1')")); connection.executeUpdate(Statement.of("INSERT INTO TEST (ID, NAME) VALUES (2, 'test 2')")); @@ -986,6 +1022,8 @@ protected boolean shouldAbort(String statement, ExecutionStep step) { } // Now try to do an insert that will abort. The retry should now fail as there has been a // concurrent modification. + interceptor.setUsingMultiplexedSession( + isMultiplexedSessionsEnabledForRW(connection.getSpanner())); interceptor.setProbability(1.0); interceptor.setOnlyInjectOnce(true); boolean expectedException = false; @@ -1034,6 +1072,8 @@ public void testAbortWithDifferentUpdateCount() { } // Now try to do an insert that will abort. The retry should now fail as there has been a // concurrent modification. + interceptor.setUsingMultiplexedSession( + isMultiplexedSessionsEnabledForRW(connection.getSpanner())); interceptor.setProbability(1.0); interceptor.setOnlyInjectOnce(true); boolean expectedException = false; @@ -1089,6 +1129,8 @@ public void testAbortWithExceptionOnSelect() { } } // now try to do an insert that will abort. + interceptor.setUsingMultiplexedSession( + isMultiplexedSessionsEnabledForRW(connection.getSpanner())); interceptor.setProbability(1.0); interceptor.setOnlyInjectOnce(true); connection.executeUpdate(Statement.of("INSERT INTO TEST (ID, NAME) VALUES (3, 'test 3')")); @@ -1147,6 +1189,8 @@ public void testAbortWithExceptionOnSelectAndConcurrentModification() { } // Now try to do an insert that will abort. The subsequent retry will fail as the SELECT * // FROM FOO now returns a result. + interceptor.setUsingMultiplexedSession( + isMultiplexedSessionsEnabledForRW(connection.getSpanner())); interceptor.setProbability(1.0); interceptor.setOnlyInjectOnce(true); try { @@ -1213,6 +1257,8 @@ public void testAbortWithExceptionOnInsertAndConcurrentModification() { } // Now try to do an insert that will abort. The subsequent retry will fail as the INSERT INTO // FOO now succeeds. + interceptor.setUsingMultiplexedSession( + isMultiplexedSessionsEnabledForRW(connection.getSpanner())); interceptor.setProbability(1.0); interceptor.setOnlyInjectOnce(true); try { @@ -1281,6 +1327,8 @@ public void testAbortWithDroppedTableConcurrentModification() { } // Now try to do an insert that will abort. The subsequent retry will fail as the SELECT * // FROM FOO now fails. + interceptor.setUsingMultiplexedSession( + isMultiplexedSessionsEnabledForRW(connection.getSpanner())); interceptor.setProbability(1.0); interceptor.setOnlyInjectOnce(true); try { @@ -1341,6 +1389,8 @@ public void testAbortWithInsertOnDroppedTableConcurrentModification() { } // Now try to do an insert that will abort. The subsequent retry will fail as the INSERT INTO // FOO now fails. + interceptor.setUsingMultiplexedSession( + isMultiplexedSessionsEnabledForRW(connection.getSpanner())); interceptor.setProbability(1.0); interceptor.setOnlyInjectOnce(true); try { @@ -1402,6 +1452,8 @@ public void testAbortWithCursorHalfwayDroppedTableConcurrentModification() { connection2.execute(Statement.of("DROP TABLE FOO")); } // try to continue to consume the result set, but this will now abort. + interceptor.setUsingMultiplexedSession( + isMultiplexedSessionsEnabledForRW(connection.getSpanner())); interceptor.setProbability(1.0); interceptor.setOnlyInjectOnce(true); try { @@ -1443,6 +1495,8 @@ public void testRetryLargeResultSet() { } } // Do an update that will abort and retry. + interceptor.setUsingMultiplexedSession( + isMultiplexedSessionsEnabledForRW(connection.getSpanner())); interceptor.setProbability(1.0); interceptor.setOnlyInjectOnce(true); connection.executeUpdate( @@ -1467,12 +1521,18 @@ public void testRetryLargeResultSet() { /** Test the successful retry of a transaction with a high chance of multiple aborts */ @Test public void testRetryHighAbortRate() { + // TODO(sriharshach): Remove this skip once backend support empty transactions to commit. + assumeFalse( + "Skipping for multiplexed sessions since it does not allow empty transactions to commit", + env.getTestHelper().getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()); final int NUMBER_OF_TEST_RECORDS = 10000; final long UPDATED_RECORDS = 1000L; // abort on 25% of all statements AbortInterceptor interceptor = new AbortInterceptor(0.25D); try (ITConnection connection = createConnection(interceptor, new CountTransactionRetryListener())) { + interceptor.setUsingMultiplexedSession( + isMultiplexedSessionsEnabledForRW(connection.getSpanner())); // insert test records for (int i = 0; i < NUMBER_OF_TEST_RECORDS; i++) { connection.bufferedWrite( @@ -1539,6 +1599,8 @@ public void testAbortWithConcurrentInsertOnEmptyTable() { } // Now try to consume the result set, but the call to next() will throw an AbortedException. // The retry should still succeed. + interceptor.setUsingMultiplexedSession( + isMultiplexedSessionsEnabledForRW(connection.getSpanner())); interceptor.setProbability(1.0); interceptor.setOnlyInjectOnce(true); int currentSuccessfulRetryCount = RETRY_STATISTICS.totalSuccessfulRetries; @@ -1563,6 +1625,8 @@ public void testAbortWithConcurrentInsertOnEmptyTable() { connection2.commit(); } // this time the abort will occur on the call to commit() + interceptor.setUsingMultiplexedSession( + isMultiplexedSessionsEnabledForRW(connection.getSpanner())); interceptor.setProbability(1.0); interceptor.setOnlyInjectOnce(true); boolean expectedException = false; diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITAsyncExamplesTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITAsyncExamplesTest.java index dc5abd77afd..b324e39e43e 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITAsyncExamplesTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITAsyncExamplesTest.java @@ -253,6 +253,12 @@ public void runAsync() throws Exception { }, executor); assertThat(insertCount.get()).isEqualTo(1L); + if (env.getTestHelper().getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()) { + // The runAsync() method should only be called once on the runner. + // However, due to a bug in regular sessions, it can be executed multiple times on the same + // runner. + runner = client.runAsync(); + } ApiFuture deleteCount = runner.runAsync( txn -> @@ -299,6 +305,12 @@ public void runAsyncBatchUpdate() throws Exception { }, executor); assertThat(insertCount.get()).asList().containsExactly(1L, 1L, 1L); + if (env.getTestHelper().getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW()) { + // The runAsync() method should only be called once on the runner. + // However, due to a bug in regular sessions, it can be executed multiple times on the same + // runner. + runner = client.runAsync(); + } ApiFuture deleteCount = runner.runAsync( txn -> diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITBatchDmlTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITBatchDmlTest.java index b11e4f613ce..2decef6158e 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITBatchDmlTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITBatchDmlTest.java @@ -17,6 +17,7 @@ package com.google.cloud.spanner.it; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assume.assumeFalse; import com.google.api.gax.longrunning.OperationFuture; import com.google.cloud.spanner.Database; @@ -84,6 +85,10 @@ public void dropTable() throws Exception { @Test public void noStatementsInRequest() { + // TODO(sriharshach): Remove this skip once backend support empty transactions to commit. + assumeFalse( + "Skipping for multiplexed sessions since it does not allow empty transactions to commit", + isUsingMultiplexedSessionsForRW()); final TransactionCallable callable = transaction -> { List stmts = new ArrayList<>(); @@ -252,4 +257,8 @@ public void largeBatchDml_withNonParameterisedStatements() { assertThat(actualRowCounts.length).isEqualTo(80); assertThat(expectedRowCounts).isEqualTo(actualRowCounts); } + + boolean isUsingMultiplexedSessionsForRW() { + return env.getTestHelper().getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW(); + } } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITBatchReadTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITBatchReadTest.java index f028fbc2b15..4f68949eab5 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITBatchReadTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITBatchReadTest.java @@ -181,7 +181,9 @@ public static void setUpDatabase() throws Exception { totalSize = 0; } } - dbClient.write(mutations); + if (!mutations.isEmpty()) { + dbClient.write(mutations); + } } // Our read/queries are executed with some staleness. Thread.sleep(2 * STALENESS_MILLISEC); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITBuiltInMetricsTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITBuiltInMetricsTest.java index 258c1230709..5bf8e42ccb6 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITBuiltInMetricsTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITBuiltInMetricsTest.java @@ -82,10 +82,14 @@ public void testBuiltinMetricsWithDefaultOTEL() throws Exception { String metricFilter = String.format( - "metric.type=\"spanner.googleapis.com/client/%s\" " - + "AND resource.labels.instance=\"%s\" AND metric.labels.method=\"Spanner.ExecuteStreamingSql\"" + "metric.type=\"spanner.googleapis.com/client/%s\"" + + " AND resource.type=\"spanner_instance\"" + + " AND metric.labels.method=\"Spanner.Commit\"" + + " AND resource.labels.instance_id=\"%s\"" + " AND metric.labels.database=\"%s\"", - "operation_latencies", env.getTestHelper().getInstanceId(), db.getId()); + "operation_latencies", + db.getId().getInstanceId().getInstance(), + db.getId().getDatabase()); ListTimeSeriesRequest.Builder requestBuilder = ListTimeSeriesRequest.newBuilder() diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITJsonWriteReadTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITJsonWriteReadTest.java index e355eaa07a3..026e3649b2e 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITJsonWriteReadTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITJsonWriteReadTest.java @@ -132,7 +132,14 @@ public void testWriteAndReadInvalidJsonValues() throws IOException { .to(Value.json(jsonStr)) .build()))); - assertEquals(ErrorCode.FAILED_PRECONDITION, exception.getErrorCode()); + if (env.getTestHelper() + .getOptions() + .getSessionPoolOptions() + .getUseMultiplexedSessionForRW()) { + assertEquals(ErrorCode.INVALID_ARGUMENT, exception.getErrorCode()); + } else { + assertEquals(ErrorCode.FAILED_PRECONDITION, exception.getErrorCode()); + } } } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITTransactionManagerAsyncTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITTransactionManagerAsyncTest.java index c1e8a903ea5..7ac027bb229 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITTransactionManagerAsyncTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITTransactionManagerAsyncTest.java @@ -161,7 +161,16 @@ public void testInvalidInsert() throws InterruptedException { } catch (ExecutionException e) { assertThat(e.getCause()).isInstanceOf(SpannerException.class); SpannerException se = (SpannerException) e.getCause(); - assertThat(se.getErrorCode()).isEqualTo(ErrorCode.NOT_FOUND); + if (env.getTestHelper() + .getOptions() + .getSessionPoolOptions() + .getUseMultiplexedSessionForRW()) { + // Backend currently returns INVALID_ARGUMENT, however this will be changed to NOT_FOUND + // in future. + assertThat(se.getErrorCode()).isAnyOf(ErrorCode.NOT_FOUND, ErrorCode.INVALID_ARGUMENT); + } else { + assertThat(se.getErrorCode()).isEqualTo(ErrorCode.NOT_FOUND); + } // expected break; } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITTransactionTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITTransactionTest.java index ea60b9fb649..627523f0555 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITTransactionTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITTransactionTest.java @@ -464,7 +464,10 @@ public void nestedSingleUseReadTxnThrows() { @Test public void nestedTxnSucceedsWhenAllowed() { assumeFalse("Emulator does not support multiple parallel transactions", isUsingEmulator()); - + // TODO(sriharshach): Remove this skip once backend support empty transactions to commit. + assumeFalse( + "Skipping for multiplexed sessions since it does not allow empty transactions to commit", + isUsingMultiplexedSessionsForRW()); client .readWriteTransaction() .allowNestedTransaction() @@ -588,4 +591,8 @@ public void testTransactionRunnerReturnsCommitStats() { // MutationCount = 2 (2 columns). assertEquals(2L, runner.getCommitResponse().getCommitStats().getMutationCount()); } + + boolean isUsingMultiplexedSessionsForRW() { + return env.getTestHelper().getOptions().getSessionPoolOptions().getUseMultiplexedSessionForRW(); + } } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITWriteTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITWriteTest.java index c5eb9284479..83d27e18e06 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITWriteTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITWriteTest.java @@ -1043,7 +1043,16 @@ public void tableNotFound() { .build()); fail("Expected exception"); } catch (SpannerException ex) { - assertThat(ex.getErrorCode()).isEqualTo(ErrorCode.NOT_FOUND); + if (env.getTestHelper() + .getOptions() + .getSessionPoolOptions() + .getUseMultiplexedSessionForRW()) { + // Backend currently returns INVALID_ARGUMENT, however this will be changed to NOT_FOUND in + // future. + assertThat(ex.getErrorCode()).isAnyOf(ErrorCode.NOT_FOUND, ErrorCode.INVALID_ARGUMENT); + } else { + assertThat(ex.getErrorCode()).isEqualTo(ErrorCode.NOT_FOUND); + } } } @@ -1053,7 +1062,16 @@ public void columnNotFound() { write(baseInsert().set("ColumnThatDoesNotExist").to("V1").build()); fail("Expected exception"); } catch (SpannerException ex) { - assertThat(ex.getErrorCode()).isEqualTo(ErrorCode.NOT_FOUND); + if (env.getTestHelper() + .getOptions() + .getSessionPoolOptions() + .getUseMultiplexedSessionForRW()) { + // Backend currently returns INVALID_ARGUMENT, however this will be changed to NOT_FOUND in + // future. + assertThat(ex.getErrorCode()).isAnyOf(ErrorCode.NOT_FOUND, ErrorCode.INVALID_ARGUMENT); + } else { + assertThat(ex.getErrorCode()).isEqualTo(ErrorCode.NOT_FOUND); + } } } @@ -1063,8 +1081,15 @@ public void incorrectType() { write(baseInsert().set("StringValue").to(1.234).build()); fail("Expected exception"); } catch (SpannerException ex) { - assertThat(ex.getErrorCode()).isEqualTo(ErrorCode.FAILED_PRECONDITION); - assertThat(ex.getMessage()).contains("STRING"); + if (env.getTestHelper() + .getOptions() + .getSessionPoolOptions() + .getUseMultiplexedSessionForRW()) { + assertThat(ex.getErrorCode()).isEqualTo(ErrorCode.INVALID_ARGUMENT); + } else { + assertThat(ex.getErrorCode()).isEqualTo(ErrorCode.FAILED_PRECONDITION); + assertThat(ex.getMessage()).contains("STRING"); + } } } diff --git a/google-cloud-spanner/src/test/resources/META-INF/native-image/com.google.cloud/google-cloud-spanner/native-image.properties b/google-cloud-spanner/src/test/resources/META-INF/native-image/com.google.cloud/google-cloud-spanner/native-image.properties new file mode 100644 index 00000000000..383f5390d63 --- /dev/null +++ b/google-cloud-spanner/src/test/resources/META-INF/native-image/com.google.cloud/google-cloud-spanner/native-image.properties @@ -0,0 +1,3 @@ +Args=--initialize-at-build-time=org.junit.runner.RunWith \ + --initialize-at-build-time=org.junit.experimental.categories.Category \ + --initialize-at-build-time=org.junit.runners.model.FrameworkField diff --git a/grpc-google-cloud-spanner-admin-database-v1/pom.xml b/grpc-google-cloud-spanner-admin-database-v1/pom.xml index 11f32d329d0..1ceb1073b98 100644 --- a/grpc-google-cloud-spanner-admin-database-v1/pom.xml +++ b/grpc-google-cloud-spanner-admin-database-v1/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc grpc-google-cloud-spanner-admin-database-v1 - 6.86.0 + 6.87.0 grpc-google-cloud-spanner-admin-database-v1 GRPC library for grpc-google-cloud-spanner-admin-database-v1 com.google.cloud google-cloud-spanner-parent - 6.86.0 + 6.87.0 diff --git a/grpc-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/DatabaseAdminGrpc.java b/grpc-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/DatabaseAdminGrpc.java index 0089e1c9345..d8ad23a2bdb 100644 --- a/grpc-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/DatabaseAdminGrpc.java +++ b/grpc-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/DatabaseAdminGrpc.java @@ -943,6 +943,53 @@ private DatabaseAdminGrpc() {} return getListDatabaseRolesMethod; } + private static volatile io.grpc.MethodDescriptor< + com.google.spanner.admin.database.v1.AddSplitPointsRequest, + com.google.spanner.admin.database.v1.AddSplitPointsResponse> + getAddSplitPointsMethod; + + @io.grpc.stub.annotations.RpcMethod( + fullMethodName = SERVICE_NAME + '/' + "AddSplitPoints", + requestType = com.google.spanner.admin.database.v1.AddSplitPointsRequest.class, + responseType = com.google.spanner.admin.database.v1.AddSplitPointsResponse.class, + methodType = io.grpc.MethodDescriptor.MethodType.UNARY) + public static io.grpc.MethodDescriptor< + com.google.spanner.admin.database.v1.AddSplitPointsRequest, + com.google.spanner.admin.database.v1.AddSplitPointsResponse> + getAddSplitPointsMethod() { + io.grpc.MethodDescriptor< + com.google.spanner.admin.database.v1.AddSplitPointsRequest, + com.google.spanner.admin.database.v1.AddSplitPointsResponse> + getAddSplitPointsMethod; + if ((getAddSplitPointsMethod = DatabaseAdminGrpc.getAddSplitPointsMethod) == null) { + synchronized (DatabaseAdminGrpc.class) { + if ((getAddSplitPointsMethod = DatabaseAdminGrpc.getAddSplitPointsMethod) == null) { + DatabaseAdminGrpc.getAddSplitPointsMethod = + getAddSplitPointsMethod = + io.grpc.MethodDescriptor + . + newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.UNARY) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "AddSplitPoints")) + .setSampledToLocalTracing(true) + .setRequestMarshaller( + io.grpc.protobuf.ProtoUtils.marshaller( + com.google.spanner.admin.database.v1.AddSplitPointsRequest + .getDefaultInstance())) + .setResponseMarshaller( + io.grpc.protobuf.ProtoUtils.marshaller( + com.google.spanner.admin.database.v1.AddSplitPointsResponse + .getDefaultInstance())) + .setSchemaDescriptor( + new DatabaseAdminMethodDescriptorSupplier("AddSplitPoints")) + .build(); + } + } + } + return getAddSplitPointsMethod; + } + private static volatile io.grpc.MethodDescriptor< com.google.spanner.admin.database.v1.CreateBackupScheduleRequest, com.google.spanner.admin.database.v1.BackupSchedule> @@ -1657,6 +1704,21 @@ default void listDatabaseRoles( getListDatabaseRolesMethod(), responseObserver); } + /** + * + * + *
+     * Adds split points to specified tables, indexes of a database.
+     * 
+ */ + default void addSplitPoints( + com.google.spanner.admin.database.v1.AddSplitPointsRequest request, + io.grpc.stub.StreamObserver + responseObserver) { + io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall( + getAddSplitPointsMethod(), responseObserver); + } + /** * * @@ -2232,6 +2294,23 @@ public void listDatabaseRoles( responseObserver); } + /** + * + * + *
+     * Adds split points to specified tables, indexes of a database.
+     * 
+ */ + public void addSplitPoints( + com.google.spanner.admin.database.v1.AddSplitPointsRequest request, + io.grpc.stub.StreamObserver + responseObserver) { + io.grpc.stub.ClientCalls.asyncUnaryCall( + getChannel().newCall(getAddSplitPointsMethod(), getCallOptions()), + request, + responseObserver); + } + /** * * @@ -2730,6 +2809,19 @@ public com.google.spanner.admin.database.v1.ListDatabaseRolesResponse listDataba getChannel(), getListDatabaseRolesMethod(), getCallOptions(), request); } + /** + * + * + *
+     * Adds split points to specified tables, indexes of a database.
+     * 
+ */ + public com.google.spanner.admin.database.v1.AddSplitPointsResponse addSplitPoints( + com.google.spanner.admin.database.v1.AddSplitPointsRequest request) { + return io.grpc.stub.ClientCalls.blockingUnaryCall( + getChannel(), getAddSplitPointsMethod(), getCallOptions(), request); + } + /** * * @@ -3221,6 +3313,20 @@ protected DatabaseAdminFutureStub build( getChannel().newCall(getListDatabaseRolesMethod(), getCallOptions()), request); } + /** + * + * + *
+     * Adds split points to specified tables, indexes of a database.
+     * 
+ */ + public com.google.common.util.concurrent.ListenableFuture< + com.google.spanner.admin.database.v1.AddSplitPointsResponse> + addSplitPoints(com.google.spanner.admin.database.v1.AddSplitPointsRequest request) { + return io.grpc.stub.ClientCalls.futureUnaryCall( + getChannel().newCall(getAddSplitPointsMethod(), getCallOptions()), request); + } + /** * * @@ -3315,11 +3421,12 @@ protected DatabaseAdminFutureStub build( private static final int METHODID_LIST_DATABASE_OPERATIONS = 17; private static final int METHODID_LIST_BACKUP_OPERATIONS = 18; private static final int METHODID_LIST_DATABASE_ROLES = 19; - private static final int METHODID_CREATE_BACKUP_SCHEDULE = 20; - private static final int METHODID_GET_BACKUP_SCHEDULE = 21; - private static final int METHODID_UPDATE_BACKUP_SCHEDULE = 22; - private static final int METHODID_DELETE_BACKUP_SCHEDULE = 23; - private static final int METHODID_LIST_BACKUP_SCHEDULES = 24; + private static final int METHODID_ADD_SPLIT_POINTS = 20; + private static final int METHODID_CREATE_BACKUP_SCHEDULE = 21; + private static final int METHODID_GET_BACKUP_SCHEDULE = 22; + private static final int METHODID_UPDATE_BACKUP_SCHEDULE = 23; + private static final int METHODID_DELETE_BACKUP_SCHEDULE = 24; + private static final int METHODID_LIST_BACKUP_SCHEDULES = 25; private static final class MethodHandlers implements io.grpc.stub.ServerCalls.UnaryMethod, @@ -3454,6 +3561,13 @@ public void invoke(Req request, io.grpc.stub.StreamObserver responseObserv com.google.spanner.admin.database.v1.ListDatabaseRolesResponse>) responseObserver); break; + case METHODID_ADD_SPLIT_POINTS: + serviceImpl.addSplitPoints( + (com.google.spanner.admin.database.v1.AddSplitPointsRequest) request, + (io.grpc.stub.StreamObserver< + com.google.spanner.admin.database.v1.AddSplitPointsResponse>) + responseObserver); + break; case METHODID_CREATE_BACKUP_SCHEDULE: serviceImpl.createBackupSchedule( (com.google.spanner.admin.database.v1.CreateBackupScheduleRequest) request, @@ -3627,6 +3741,13 @@ public static final io.grpc.ServerServiceDefinition bindService(AsyncService ser com.google.spanner.admin.database.v1.ListDatabaseRolesRequest, com.google.spanner.admin.database.v1.ListDatabaseRolesResponse>( service, METHODID_LIST_DATABASE_ROLES))) + .addMethod( + getAddSplitPointsMethod(), + io.grpc.stub.ServerCalls.asyncUnaryCall( + new MethodHandlers< + com.google.spanner.admin.database.v1.AddSplitPointsRequest, + com.google.spanner.admin.database.v1.AddSplitPointsResponse>( + service, METHODID_ADD_SPLIT_POINTS))) .addMethod( getCreateBackupScheduleMethod(), io.grpc.stub.ServerCalls.asyncUnaryCall( @@ -3732,6 +3853,7 @@ public static io.grpc.ServiceDescriptor getServiceDescriptor() { .addMethod(getListDatabaseOperationsMethod()) .addMethod(getListBackupOperationsMethod()) .addMethod(getListDatabaseRolesMethod()) + .addMethod(getAddSplitPointsMethod()) .addMethod(getCreateBackupScheduleMethod()) .addMethod(getGetBackupScheduleMethod()) .addMethod(getUpdateBackupScheduleMethod()) diff --git a/grpc-google-cloud-spanner-admin-instance-v1/pom.xml b/grpc-google-cloud-spanner-admin-instance-v1/pom.xml index 1df91aad491..3be48a22c4c 100644 --- a/grpc-google-cloud-spanner-admin-instance-v1/pom.xml +++ b/grpc-google-cloud-spanner-admin-instance-v1/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc grpc-google-cloud-spanner-admin-instance-v1 - 6.86.0 + 6.87.0 grpc-google-cloud-spanner-admin-instance-v1 GRPC library for grpc-google-cloud-spanner-admin-instance-v1 com.google.cloud google-cloud-spanner-parent - 6.86.0 + 6.87.0 diff --git a/grpc-google-cloud-spanner-executor-v1/pom.xml b/grpc-google-cloud-spanner-executor-v1/pom.xml index cf50f14bb8f..a1950b288ec 100644 --- a/grpc-google-cloud-spanner-executor-v1/pom.xml +++ b/grpc-google-cloud-spanner-executor-v1/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc grpc-google-cloud-spanner-executor-v1 - 6.86.0 + 6.87.0 grpc-google-cloud-spanner-executor-v1 GRPC library for google-cloud-spanner com.google.cloud google-cloud-spanner-parent - 6.86.0 + 6.87.0 diff --git a/grpc-google-cloud-spanner-v1/pom.xml b/grpc-google-cloud-spanner-v1/pom.xml index be66f571723..eb81ef2cd5a 100644 --- a/grpc-google-cloud-spanner-v1/pom.xml +++ b/grpc-google-cloud-spanner-v1/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc grpc-google-cloud-spanner-v1 - 6.86.0 + 6.87.0 grpc-google-cloud-spanner-v1 GRPC library for grpc-google-cloud-spanner-v1 com.google.cloud google-cloud-spanner-parent - 6.86.0 + 6.87.0 diff --git a/pom.xml b/pom.xml index 4278401a97d..41909b12055 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.google.cloud google-cloud-spanner-parent pom - 6.86.0 + 6.87.0 Google Cloud Spanner Parent https://github.com/googleapis/java-spanner @@ -14,7 +14,7 @@ com.google.cloud sdk-platform-java-config - 3.42.0 + 3.43.0 @@ -61,47 +61,47 @@ com.google.api.grpc proto-google-cloud-spanner-admin-instance-v1 - 6.86.0 + 6.87.0 com.google.api.grpc proto-google-cloud-spanner-executor-v1 - 6.86.0 + 6.87.0 com.google.api.grpc grpc-google-cloud-spanner-executor-v1 - 6.86.0 + 6.87.0 com.google.api.grpc proto-google-cloud-spanner-v1 - 6.86.0 + 6.87.0 com.google.api.grpc proto-google-cloud-spanner-admin-database-v1 - 6.86.0 + 6.87.0 com.google.api.grpc grpc-google-cloud-spanner-v1 - 6.86.0 + 6.87.0 com.google.api.grpc grpc-google-cloud-spanner-admin-instance-v1 - 6.86.0 + 6.87.0 com.google.api.grpc grpc-google-cloud-spanner-admin-database-v1 - 6.86.0 + 6.87.0 com.google.cloud google-cloud-spanner - 6.86.0 + 6.87.0 diff --git a/proto-google-cloud-spanner-admin-database-v1/pom.xml b/proto-google-cloud-spanner-admin-database-v1/pom.xml index e69f7050359..59979f00a92 100644 --- a/proto-google-cloud-spanner-admin-database-v1/pom.xml +++ b/proto-google-cloud-spanner-admin-database-v1/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc proto-google-cloud-spanner-admin-database-v1 - 6.86.0 + 6.87.0 proto-google-cloud-spanner-admin-database-v1 PROTO library for proto-google-cloud-spanner-admin-database-v1 com.google.cloud google-cloud-spanner-parent - 6.86.0 + 6.87.0 diff --git a/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/AddSplitPointsRequest.java b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/AddSplitPointsRequest.java new file mode 100644 index 00000000000..ebcd880bf0d --- /dev/null +++ b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/AddSplitPointsRequest.java @@ -0,0 +1,1414 @@ +/* + * Copyright 2025 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 + * + * https://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. + */ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: google/spanner/admin/database/v1/spanner_database_admin.proto + +// Protobuf Java Version: 3.25.5 +package com.google.spanner.admin.database.v1; + +/** + * + * + *
+ * The request for
+ * [AddSplitPoints][google.spanner.admin.database.v1.DatabaseAdmin.AddSplitPoints].
+ * 
+ * + * Protobuf type {@code google.spanner.admin.database.v1.AddSplitPointsRequest} + */ +public final class AddSplitPointsRequest extends com.google.protobuf.GeneratedMessageV3 + implements + // @@protoc_insertion_point(message_implements:google.spanner.admin.database.v1.AddSplitPointsRequest) + AddSplitPointsRequestOrBuilder { + private static final long serialVersionUID = 0L; + // Use AddSplitPointsRequest.newBuilder() to construct. + private AddSplitPointsRequest(com.google.protobuf.GeneratedMessageV3.Builder builder) { + super(builder); + } + + private AddSplitPointsRequest() { + database_ = ""; + splitPoints_ = java.util.Collections.emptyList(); + initiator_ = ""; + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance(UnusedPrivateParameter unused) { + return new AddSplitPointsRequest(); + } + + public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { + return com.google.spanner.admin.database.v1.SpannerDatabaseAdminProto + .internal_static_google_spanner_admin_database_v1_AddSplitPointsRequest_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return com.google.spanner.admin.database.v1.SpannerDatabaseAdminProto + .internal_static_google_spanner_admin_database_v1_AddSplitPointsRequest_fieldAccessorTable + .ensureFieldAccessorsInitialized( + com.google.spanner.admin.database.v1.AddSplitPointsRequest.class, + com.google.spanner.admin.database.v1.AddSplitPointsRequest.Builder.class); + } + + public static final int DATABASE_FIELD_NUMBER = 1; + + @SuppressWarnings("serial") + private volatile java.lang.Object database_ = ""; + /** + * + * + *
+   * Required. The database on whose tables/indexes split points are to be
+   * added. Values are of the form
+   * `projects/<project>/instances/<instance>/databases/<database>`.
+   * 
+ * + * + * string database = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { ... } + * + * + * @return The database. + */ + @java.lang.Override + public java.lang.String getDatabase() { + java.lang.Object ref = database_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + database_ = s; + return s; + } + } + /** + * + * + *
+   * Required. The database on whose tables/indexes split points are to be
+   * added. Values are of the form
+   * `projects/<project>/instances/<instance>/databases/<database>`.
+   * 
+ * + * + * string database = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { ... } + * + * + * @return The bytes for database. + */ + @java.lang.Override + public com.google.protobuf.ByteString getDatabaseBytes() { + java.lang.Object ref = database_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8((java.lang.String) ref); + database_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int SPLIT_POINTS_FIELD_NUMBER = 2; + + @SuppressWarnings("serial") + private java.util.List splitPoints_; + /** + * + * + *
+   * Required. The split points to add.
+   * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints split_points = 2 [(.google.api.field_behavior) = REQUIRED]; + * + */ + @java.lang.Override + public java.util.List getSplitPointsList() { + return splitPoints_; + } + /** + * + * + *
+   * Required. The split points to add.
+   * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints split_points = 2 [(.google.api.field_behavior) = REQUIRED]; + * + */ + @java.lang.Override + public java.util.List + getSplitPointsOrBuilderList() { + return splitPoints_; + } + /** + * + * + *
+   * Required. The split points to add.
+   * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints split_points = 2 [(.google.api.field_behavior) = REQUIRED]; + * + */ + @java.lang.Override + public int getSplitPointsCount() { + return splitPoints_.size(); + } + /** + * + * + *
+   * Required. The split points to add.
+   * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints split_points = 2 [(.google.api.field_behavior) = REQUIRED]; + * + */ + @java.lang.Override + public com.google.spanner.admin.database.v1.SplitPoints getSplitPoints(int index) { + return splitPoints_.get(index); + } + /** + * + * + *
+   * Required. The split points to add.
+   * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints split_points = 2 [(.google.api.field_behavior) = REQUIRED]; + * + */ + @java.lang.Override + public com.google.spanner.admin.database.v1.SplitPointsOrBuilder getSplitPointsOrBuilder( + int index) { + return splitPoints_.get(index); + } + + public static final int INITIATOR_FIELD_NUMBER = 3; + + @SuppressWarnings("serial") + private volatile java.lang.Object initiator_ = ""; + /** + * + * + *
+   * Optional. A user-supplied tag associated with the split points.
+   * For example, "intital_data_load", "special_event_1".
+   * Defaults to "CloudAddSplitPointsAPI" if not specified.
+   * The length of the tag must not exceed 50 characters,else will be trimmed.
+   * Only valid UTF8 characters are allowed.
+   * 
+ * + * string initiator = 3 [(.google.api.field_behavior) = OPTIONAL]; + * + * @return The initiator. + */ + @java.lang.Override + public java.lang.String getInitiator() { + java.lang.Object ref = initiator_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + initiator_ = s; + return s; + } + } + /** + * + * + *
+   * Optional. A user-supplied tag associated with the split points.
+   * For example, "intital_data_load", "special_event_1".
+   * Defaults to "CloudAddSplitPointsAPI" if not specified.
+   * The length of the tag must not exceed 50 characters,else will be trimmed.
+   * Only valid UTF8 characters are allowed.
+   * 
+ * + * string initiator = 3 [(.google.api.field_behavior) = OPTIONAL]; + * + * @return The bytes for initiator. + */ + @java.lang.Override + public com.google.protobuf.ByteString getInitiatorBytes() { + java.lang.Object ref = initiator_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8((java.lang.String) ref); + initiator_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + private byte memoizedIsInitialized = -1; + + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException { + if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(database_)) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 1, database_); + } + for (int i = 0; i < splitPoints_.size(); i++) { + output.writeMessage(2, splitPoints_.get(i)); + } + if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(initiator_)) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 3, initiator_); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(database_)) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, database_); + } + for (int i = 0; i < splitPoints_.size(); i++) { + size += com.google.protobuf.CodedOutputStream.computeMessageSize(2, splitPoints_.get(i)); + } + if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(initiator_)) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(3, initiator_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof com.google.spanner.admin.database.v1.AddSplitPointsRequest)) { + return super.equals(obj); + } + com.google.spanner.admin.database.v1.AddSplitPointsRequest other = + (com.google.spanner.admin.database.v1.AddSplitPointsRequest) obj; + + if (!getDatabase().equals(other.getDatabase())) return false; + if (!getSplitPointsList().equals(other.getSplitPointsList())) return false; + if (!getInitiator().equals(other.getInitiator())) return false; + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + DATABASE_FIELD_NUMBER; + hash = (53 * hash) + getDatabase().hashCode(); + if (getSplitPointsCount() > 0) { + hash = (37 * hash) + SPLIT_POINTS_FIELD_NUMBER; + hash = (53 * hash) + getSplitPointsList().hashCode(); + } + hash = (37 * hash) + INITIATOR_FIELD_NUMBER; + hash = (53 * hash) + getInitiator().hashCode(); + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static com.google.spanner.admin.database.v1.AddSplitPointsRequest parseFrom( + java.nio.ByteBuffer data) throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + + public static com.google.spanner.admin.database.v1.AddSplitPointsRequest parseFrom( + java.nio.ByteBuffer data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + + public static com.google.spanner.admin.database.v1.AddSplitPointsRequest parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + + public static com.google.spanner.admin.database.v1.AddSplitPointsRequest parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + + public static com.google.spanner.admin.database.v1.AddSplitPointsRequest parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + + public static com.google.spanner.admin.database.v1.AddSplitPointsRequest parseFrom( + byte[] data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + + public static com.google.spanner.admin.database.v1.AddSplitPointsRequest parseFrom( + java.io.InputStream input) throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input); + } + + public static com.google.spanner.admin.database.v1.AddSplitPointsRequest parseFrom( + java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseWithIOException( + PARSER, input, extensionRegistry); + } + + public static com.google.spanner.admin.database.v1.AddSplitPointsRequest parseDelimitedFrom( + java.io.InputStream input) throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException(PARSER, input); + } + + public static com.google.spanner.admin.database.v1.AddSplitPointsRequest parseDelimitedFrom( + java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException( + PARSER, input, extensionRegistry); + } + + public static com.google.spanner.admin.database.v1.AddSplitPointsRequest parseFrom( + com.google.protobuf.CodedInputStream input) throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input); + } + + public static com.google.spanner.admin.database.v1.AddSplitPointsRequest parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseWithIOException( + PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { + return newBuilder(); + } + + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + + public static Builder newBuilder( + com.google.spanner.admin.database.v1.AddSplitPointsRequest prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType(com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * + * + *
+   * The request for
+   * [AddSplitPoints][google.spanner.admin.database.v1.DatabaseAdmin.AddSplitPoints].
+   * 
+ * + * Protobuf type {@code google.spanner.admin.database.v1.AddSplitPointsRequest} + */ + public static final class Builder extends com.google.protobuf.GeneratedMessageV3.Builder + implements + // @@protoc_insertion_point(builder_implements:google.spanner.admin.database.v1.AddSplitPointsRequest) + com.google.spanner.admin.database.v1.AddSplitPointsRequestOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { + return com.google.spanner.admin.database.v1.SpannerDatabaseAdminProto + .internal_static_google_spanner_admin_database_v1_AddSplitPointsRequest_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return com.google.spanner.admin.database.v1.SpannerDatabaseAdminProto + .internal_static_google_spanner_admin_database_v1_AddSplitPointsRequest_fieldAccessorTable + .ensureFieldAccessorsInitialized( + com.google.spanner.admin.database.v1.AddSplitPointsRequest.class, + com.google.spanner.admin.database.v1.AddSplitPointsRequest.Builder.class); + } + + // Construct using com.google.spanner.admin.database.v1.AddSplitPointsRequest.newBuilder() + private Builder() {} + + private Builder(com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + } + + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + database_ = ""; + if (splitPointsBuilder_ == null) { + splitPoints_ = java.util.Collections.emptyList(); + } else { + splitPoints_ = null; + splitPointsBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000002); + initiator_ = ""; + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor getDescriptorForType() { + return com.google.spanner.admin.database.v1.SpannerDatabaseAdminProto + .internal_static_google_spanner_admin_database_v1_AddSplitPointsRequest_descriptor; + } + + @java.lang.Override + public com.google.spanner.admin.database.v1.AddSplitPointsRequest getDefaultInstanceForType() { + return com.google.spanner.admin.database.v1.AddSplitPointsRequest.getDefaultInstance(); + } + + @java.lang.Override + public com.google.spanner.admin.database.v1.AddSplitPointsRequest build() { + com.google.spanner.admin.database.v1.AddSplitPointsRequest result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public com.google.spanner.admin.database.v1.AddSplitPointsRequest buildPartial() { + com.google.spanner.admin.database.v1.AddSplitPointsRequest result = + new com.google.spanner.admin.database.v1.AddSplitPointsRequest(this); + buildPartialRepeatedFields(result); + if (bitField0_ != 0) { + buildPartial0(result); + } + onBuilt(); + return result; + } + + private void buildPartialRepeatedFields( + com.google.spanner.admin.database.v1.AddSplitPointsRequest result) { + if (splitPointsBuilder_ == null) { + if (((bitField0_ & 0x00000002) != 0)) { + splitPoints_ = java.util.Collections.unmodifiableList(splitPoints_); + bitField0_ = (bitField0_ & ~0x00000002); + } + result.splitPoints_ = splitPoints_; + } else { + result.splitPoints_ = splitPointsBuilder_.build(); + } + } + + private void buildPartial0(com.google.spanner.admin.database.v1.AddSplitPointsRequest result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.database_ = database_; + } + if (((from_bitField0_ & 0x00000004) != 0)) { + result.initiator_ = initiator_; + } + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, java.lang.Object value) { + return super.setField(field, value); + } + + @java.lang.Override + public Builder clearField(com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + + @java.lang.Override + public Builder clearOneof(com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, java.lang.Object value) { + return super.addRepeatedField(field, value); + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof com.google.spanner.admin.database.v1.AddSplitPointsRequest) { + return mergeFrom((com.google.spanner.admin.database.v1.AddSplitPointsRequest) other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(com.google.spanner.admin.database.v1.AddSplitPointsRequest other) { + if (other == com.google.spanner.admin.database.v1.AddSplitPointsRequest.getDefaultInstance()) + return this; + if (!other.getDatabase().isEmpty()) { + database_ = other.database_; + bitField0_ |= 0x00000001; + onChanged(); + } + if (splitPointsBuilder_ == null) { + if (!other.splitPoints_.isEmpty()) { + if (splitPoints_.isEmpty()) { + splitPoints_ = other.splitPoints_; + bitField0_ = (bitField0_ & ~0x00000002); + } else { + ensureSplitPointsIsMutable(); + splitPoints_.addAll(other.splitPoints_); + } + onChanged(); + } + } else { + if (!other.splitPoints_.isEmpty()) { + if (splitPointsBuilder_.isEmpty()) { + splitPointsBuilder_.dispose(); + splitPointsBuilder_ = null; + splitPoints_ = other.splitPoints_; + bitField0_ = (bitField0_ & ~0x00000002); + splitPointsBuilder_ = + com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders + ? getSplitPointsFieldBuilder() + : null; + } else { + splitPointsBuilder_.addAllMessages(other.splitPoints_); + } + } + } + if (!other.getInitiator().isEmpty()) { + initiator_ = other.initiator_; + bitField0_ |= 0x00000004; + onChanged(); + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: + { + database_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; + break; + } // case 10 + case 18: + { + com.google.spanner.admin.database.v1.SplitPoints m = + input.readMessage( + com.google.spanner.admin.database.v1.SplitPoints.parser(), + extensionRegistry); + if (splitPointsBuilder_ == null) { + ensureSplitPointsIsMutable(); + splitPoints_.add(m); + } else { + splitPointsBuilder_.addMessage(m); + } + break; + } // case 18 + case 26: + { + initiator_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000004; + break; + } // case 26 + default: + { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + + private int bitField0_; + + private java.lang.Object database_ = ""; + /** + * + * + *
+     * Required. The database on whose tables/indexes split points are to be
+     * added. Values are of the form
+     * `projects/<project>/instances/<instance>/databases/<database>`.
+     * 
+ * + * + * string database = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { ... } + * + * + * @return The database. + */ + public java.lang.String getDatabase() { + java.lang.Object ref = database_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + database_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * + * + *
+     * Required. The database on whose tables/indexes split points are to be
+     * added. Values are of the form
+     * `projects/<project>/instances/<instance>/databases/<database>`.
+     * 
+ * + * + * string database = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { ... } + * + * + * @return The bytes for database. + */ + public com.google.protobuf.ByteString getDatabaseBytes() { + java.lang.Object ref = database_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8((java.lang.String) ref); + database_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * + * + *
+     * Required. The database on whose tables/indexes split points are to be
+     * added. Values are of the form
+     * `projects/<project>/instances/<instance>/databases/<database>`.
+     * 
+ * + * + * string database = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { ... } + * + * + * @param value The database to set. + * @return This builder for chaining. + */ + public Builder setDatabase(java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + database_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + * + * + *
+     * Required. The database on whose tables/indexes split points are to be
+     * added. Values are of the form
+     * `projects/<project>/instances/<instance>/databases/<database>`.
+     * 
+ * + * + * string database = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { ... } + * + * + * @return This builder for chaining. + */ + public Builder clearDatabase() { + database_ = getDefaultInstance().getDatabase(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + * + * + *
+     * Required. The database on whose tables/indexes split points are to be
+     * added. Values are of the form
+     * `projects/<project>/instances/<instance>/databases/<database>`.
+     * 
+ * + * + * string database = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { ... } + * + * + * @param value The bytes for database to set. + * @return This builder for chaining. + */ + public Builder setDatabaseBytes(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + checkByteStringIsUtf8(value); + database_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + + private java.util.List splitPoints_ = + java.util.Collections.emptyList(); + + private void ensureSplitPointsIsMutable() { + if (!((bitField0_ & 0x00000002) != 0)) { + splitPoints_ = + new java.util.ArrayList(splitPoints_); + bitField0_ |= 0x00000002; + } + } + + private com.google.protobuf.RepeatedFieldBuilderV3< + com.google.spanner.admin.database.v1.SplitPoints, + com.google.spanner.admin.database.v1.SplitPoints.Builder, + com.google.spanner.admin.database.v1.SplitPointsOrBuilder> + splitPointsBuilder_; + + /** + * + * + *
+     * Required. The split points to add.
+     * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints split_points = 2 [(.google.api.field_behavior) = REQUIRED]; + * + */ + public java.util.List getSplitPointsList() { + if (splitPointsBuilder_ == null) { + return java.util.Collections.unmodifiableList(splitPoints_); + } else { + return splitPointsBuilder_.getMessageList(); + } + } + /** + * + * + *
+     * Required. The split points to add.
+     * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints split_points = 2 [(.google.api.field_behavior) = REQUIRED]; + * + */ + public int getSplitPointsCount() { + if (splitPointsBuilder_ == null) { + return splitPoints_.size(); + } else { + return splitPointsBuilder_.getCount(); + } + } + /** + * + * + *
+     * Required. The split points to add.
+     * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints split_points = 2 [(.google.api.field_behavior) = REQUIRED]; + * + */ + public com.google.spanner.admin.database.v1.SplitPoints getSplitPoints(int index) { + if (splitPointsBuilder_ == null) { + return splitPoints_.get(index); + } else { + return splitPointsBuilder_.getMessage(index); + } + } + /** + * + * + *
+     * Required. The split points to add.
+     * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints split_points = 2 [(.google.api.field_behavior) = REQUIRED]; + * + */ + public Builder setSplitPoints( + int index, com.google.spanner.admin.database.v1.SplitPoints value) { + if (splitPointsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureSplitPointsIsMutable(); + splitPoints_.set(index, value); + onChanged(); + } else { + splitPointsBuilder_.setMessage(index, value); + } + return this; + } + /** + * + * + *
+     * Required. The split points to add.
+     * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints split_points = 2 [(.google.api.field_behavior) = REQUIRED]; + * + */ + public Builder setSplitPoints( + int index, com.google.spanner.admin.database.v1.SplitPoints.Builder builderForValue) { + if (splitPointsBuilder_ == null) { + ensureSplitPointsIsMutable(); + splitPoints_.set(index, builderForValue.build()); + onChanged(); + } else { + splitPointsBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + * + * + *
+     * Required. The split points to add.
+     * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints split_points = 2 [(.google.api.field_behavior) = REQUIRED]; + * + */ + public Builder addSplitPoints(com.google.spanner.admin.database.v1.SplitPoints value) { + if (splitPointsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureSplitPointsIsMutable(); + splitPoints_.add(value); + onChanged(); + } else { + splitPointsBuilder_.addMessage(value); + } + return this; + } + /** + * + * + *
+     * Required. The split points to add.
+     * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints split_points = 2 [(.google.api.field_behavior) = REQUIRED]; + * + */ + public Builder addSplitPoints( + int index, com.google.spanner.admin.database.v1.SplitPoints value) { + if (splitPointsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureSplitPointsIsMutable(); + splitPoints_.add(index, value); + onChanged(); + } else { + splitPointsBuilder_.addMessage(index, value); + } + return this; + } + /** + * + * + *
+     * Required. The split points to add.
+     * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints split_points = 2 [(.google.api.field_behavior) = REQUIRED]; + * + */ + public Builder addSplitPoints( + com.google.spanner.admin.database.v1.SplitPoints.Builder builderForValue) { + if (splitPointsBuilder_ == null) { + ensureSplitPointsIsMutable(); + splitPoints_.add(builderForValue.build()); + onChanged(); + } else { + splitPointsBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + * + * + *
+     * Required. The split points to add.
+     * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints split_points = 2 [(.google.api.field_behavior) = REQUIRED]; + * + */ + public Builder addSplitPoints( + int index, com.google.spanner.admin.database.v1.SplitPoints.Builder builderForValue) { + if (splitPointsBuilder_ == null) { + ensureSplitPointsIsMutable(); + splitPoints_.add(index, builderForValue.build()); + onChanged(); + } else { + splitPointsBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + * + * + *
+     * Required. The split points to add.
+     * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints split_points = 2 [(.google.api.field_behavior) = REQUIRED]; + * + */ + public Builder addAllSplitPoints( + java.lang.Iterable values) { + if (splitPointsBuilder_ == null) { + ensureSplitPointsIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll(values, splitPoints_); + onChanged(); + } else { + splitPointsBuilder_.addAllMessages(values); + } + return this; + } + /** + * + * + *
+     * Required. The split points to add.
+     * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints split_points = 2 [(.google.api.field_behavior) = REQUIRED]; + * + */ + public Builder clearSplitPoints() { + if (splitPointsBuilder_ == null) { + splitPoints_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000002); + onChanged(); + } else { + splitPointsBuilder_.clear(); + } + return this; + } + /** + * + * + *
+     * Required. The split points to add.
+     * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints split_points = 2 [(.google.api.field_behavior) = REQUIRED]; + * + */ + public Builder removeSplitPoints(int index) { + if (splitPointsBuilder_ == null) { + ensureSplitPointsIsMutable(); + splitPoints_.remove(index); + onChanged(); + } else { + splitPointsBuilder_.remove(index); + } + return this; + } + /** + * + * + *
+     * Required. The split points to add.
+     * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints split_points = 2 [(.google.api.field_behavior) = REQUIRED]; + * + */ + public com.google.spanner.admin.database.v1.SplitPoints.Builder getSplitPointsBuilder( + int index) { + return getSplitPointsFieldBuilder().getBuilder(index); + } + /** + * + * + *
+     * Required. The split points to add.
+     * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints split_points = 2 [(.google.api.field_behavior) = REQUIRED]; + * + */ + public com.google.spanner.admin.database.v1.SplitPointsOrBuilder getSplitPointsOrBuilder( + int index) { + if (splitPointsBuilder_ == null) { + return splitPoints_.get(index); + } else { + return splitPointsBuilder_.getMessageOrBuilder(index); + } + } + /** + * + * + *
+     * Required. The split points to add.
+     * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints split_points = 2 [(.google.api.field_behavior) = REQUIRED]; + * + */ + public java.util.List + getSplitPointsOrBuilderList() { + if (splitPointsBuilder_ != null) { + return splitPointsBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(splitPoints_); + } + } + /** + * + * + *
+     * Required. The split points to add.
+     * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints split_points = 2 [(.google.api.field_behavior) = REQUIRED]; + * + */ + public com.google.spanner.admin.database.v1.SplitPoints.Builder addSplitPointsBuilder() { + return getSplitPointsFieldBuilder() + .addBuilder(com.google.spanner.admin.database.v1.SplitPoints.getDefaultInstance()); + } + /** + * + * + *
+     * Required. The split points to add.
+     * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints split_points = 2 [(.google.api.field_behavior) = REQUIRED]; + * + */ + public com.google.spanner.admin.database.v1.SplitPoints.Builder addSplitPointsBuilder( + int index) { + return getSplitPointsFieldBuilder() + .addBuilder(index, com.google.spanner.admin.database.v1.SplitPoints.getDefaultInstance()); + } + /** + * + * + *
+     * Required. The split points to add.
+     * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints split_points = 2 [(.google.api.field_behavior) = REQUIRED]; + * + */ + public java.util.List + getSplitPointsBuilderList() { + return getSplitPointsFieldBuilder().getBuilderList(); + } + + private com.google.protobuf.RepeatedFieldBuilderV3< + com.google.spanner.admin.database.v1.SplitPoints, + com.google.spanner.admin.database.v1.SplitPoints.Builder, + com.google.spanner.admin.database.v1.SplitPointsOrBuilder> + getSplitPointsFieldBuilder() { + if (splitPointsBuilder_ == null) { + splitPointsBuilder_ = + new com.google.protobuf.RepeatedFieldBuilderV3< + com.google.spanner.admin.database.v1.SplitPoints, + com.google.spanner.admin.database.v1.SplitPoints.Builder, + com.google.spanner.admin.database.v1.SplitPointsOrBuilder>( + splitPoints_, ((bitField0_ & 0x00000002) != 0), getParentForChildren(), isClean()); + splitPoints_ = null; + } + return splitPointsBuilder_; + } + + private java.lang.Object initiator_ = ""; + /** + * + * + *
+     * Optional. A user-supplied tag associated with the split points.
+     * For example, "intital_data_load", "special_event_1".
+     * Defaults to "CloudAddSplitPointsAPI" if not specified.
+     * The length of the tag must not exceed 50 characters,else will be trimmed.
+     * Only valid UTF8 characters are allowed.
+     * 
+ * + * string initiator = 3 [(.google.api.field_behavior) = OPTIONAL]; + * + * @return The initiator. + */ + public java.lang.String getInitiator() { + java.lang.Object ref = initiator_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + initiator_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * + * + *
+     * Optional. A user-supplied tag associated with the split points.
+     * For example, "intital_data_load", "special_event_1".
+     * Defaults to "CloudAddSplitPointsAPI" if not specified.
+     * The length of the tag must not exceed 50 characters,else will be trimmed.
+     * Only valid UTF8 characters are allowed.
+     * 
+ * + * string initiator = 3 [(.google.api.field_behavior) = OPTIONAL]; + * + * @return The bytes for initiator. + */ + public com.google.protobuf.ByteString getInitiatorBytes() { + java.lang.Object ref = initiator_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8((java.lang.String) ref); + initiator_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * + * + *
+     * Optional. A user-supplied tag associated with the split points.
+     * For example, "intital_data_load", "special_event_1".
+     * Defaults to "CloudAddSplitPointsAPI" if not specified.
+     * The length of the tag must not exceed 50 characters,else will be trimmed.
+     * Only valid UTF8 characters are allowed.
+     * 
+ * + * string initiator = 3 [(.google.api.field_behavior) = OPTIONAL]; + * + * @param value The initiator to set. + * @return This builder for chaining. + */ + public Builder setInitiator(java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + initiator_ = value; + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + /** + * + * + *
+     * Optional. A user-supplied tag associated with the split points.
+     * For example, "intital_data_load", "special_event_1".
+     * Defaults to "CloudAddSplitPointsAPI" if not specified.
+     * The length of the tag must not exceed 50 characters,else will be trimmed.
+     * Only valid UTF8 characters are allowed.
+     * 
+ * + * string initiator = 3 [(.google.api.field_behavior) = OPTIONAL]; + * + * @return This builder for chaining. + */ + public Builder clearInitiator() { + initiator_ = getDefaultInstance().getInitiator(); + bitField0_ = (bitField0_ & ~0x00000004); + onChanged(); + return this; + } + /** + * + * + *
+     * Optional. A user-supplied tag associated with the split points.
+     * For example, "intital_data_load", "special_event_1".
+     * Defaults to "CloudAddSplitPointsAPI" if not specified.
+     * The length of the tag must not exceed 50 characters,else will be trimmed.
+     * Only valid UTF8 characters are allowed.
+     * 
+ * + * string initiator = 3 [(.google.api.field_behavior) = OPTIONAL]; + * + * @param value The bytes for initiator to set. + * @return This builder for chaining. + */ + public Builder setInitiatorBytes(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + checkByteStringIsUtf8(value); + initiator_ = value; + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + + @java.lang.Override + public final Builder setUnknownFields(final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + // @@protoc_insertion_point(builder_scope:google.spanner.admin.database.v1.AddSplitPointsRequest) + } + + // @@protoc_insertion_point(class_scope:google.spanner.admin.database.v1.AddSplitPointsRequest) + private static final com.google.spanner.admin.database.v1.AddSplitPointsRequest DEFAULT_INSTANCE; + + static { + DEFAULT_INSTANCE = new com.google.spanner.admin.database.v1.AddSplitPointsRequest(); + } + + public static com.google.spanner.admin.database.v1.AddSplitPointsRequest getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + @java.lang.Override + public AddSplitPointsRequest parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public com.google.spanner.admin.database.v1.AddSplitPointsRequest getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } +} diff --git a/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/AddSplitPointsRequestOrBuilder.java b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/AddSplitPointsRequestOrBuilder.java new file mode 100644 index 00000000000..d03cb8c611d --- /dev/null +++ b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/AddSplitPointsRequestOrBuilder.java @@ -0,0 +1,154 @@ +/* + * Copyright 2025 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 + * + * https://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. + */ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: google/spanner/admin/database/v1/spanner_database_admin.proto + +// Protobuf Java Version: 3.25.5 +package com.google.spanner.admin.database.v1; + +public interface AddSplitPointsRequestOrBuilder + extends + // @@protoc_insertion_point(interface_extends:google.spanner.admin.database.v1.AddSplitPointsRequest) + com.google.protobuf.MessageOrBuilder { + + /** + * + * + *
+   * Required. The database on whose tables/indexes split points are to be
+   * added. Values are of the form
+   * `projects/<project>/instances/<instance>/databases/<database>`.
+   * 
+ * + * + * string database = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { ... } + * + * + * @return The database. + */ + java.lang.String getDatabase(); + /** + * + * + *
+   * Required. The database on whose tables/indexes split points are to be
+   * added. Values are of the form
+   * `projects/<project>/instances/<instance>/databases/<database>`.
+   * 
+ * + * + * string database = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { ... } + * + * + * @return The bytes for database. + */ + com.google.protobuf.ByteString getDatabaseBytes(); + + /** + * + * + *
+   * Required. The split points to add.
+   * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints split_points = 2 [(.google.api.field_behavior) = REQUIRED]; + * + */ + java.util.List getSplitPointsList(); + /** + * + * + *
+   * Required. The split points to add.
+   * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints split_points = 2 [(.google.api.field_behavior) = REQUIRED]; + * + */ + com.google.spanner.admin.database.v1.SplitPoints getSplitPoints(int index); + /** + * + * + *
+   * Required. The split points to add.
+   * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints split_points = 2 [(.google.api.field_behavior) = REQUIRED]; + * + */ + int getSplitPointsCount(); + /** + * + * + *
+   * Required. The split points to add.
+   * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints split_points = 2 [(.google.api.field_behavior) = REQUIRED]; + * + */ + java.util.List + getSplitPointsOrBuilderList(); + /** + * + * + *
+   * Required. The split points to add.
+   * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints split_points = 2 [(.google.api.field_behavior) = REQUIRED]; + * + */ + com.google.spanner.admin.database.v1.SplitPointsOrBuilder getSplitPointsOrBuilder(int index); + + /** + * + * + *
+   * Optional. A user-supplied tag associated with the split points.
+   * For example, "intital_data_load", "special_event_1".
+   * Defaults to "CloudAddSplitPointsAPI" if not specified.
+   * The length of the tag must not exceed 50 characters,else will be trimmed.
+   * Only valid UTF8 characters are allowed.
+   * 
+ * + * string initiator = 3 [(.google.api.field_behavior) = OPTIONAL]; + * + * @return The initiator. + */ + java.lang.String getInitiator(); + /** + * + * + *
+   * Optional. A user-supplied tag associated with the split points.
+   * For example, "intital_data_load", "special_event_1".
+   * Defaults to "CloudAddSplitPointsAPI" if not specified.
+   * The length of the tag must not exceed 50 characters,else will be trimmed.
+   * Only valid UTF8 characters are allowed.
+   * 
+ * + * string initiator = 3 [(.google.api.field_behavior) = OPTIONAL]; + * + * @return The bytes for initiator. + */ + com.google.protobuf.ByteString getInitiatorBytes(); +} diff --git a/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/AddSplitPointsResponse.java b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/AddSplitPointsResponse.java new file mode 100644 index 00000000000..a78c8b880e4 --- /dev/null +++ b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/AddSplitPointsResponse.java @@ -0,0 +1,435 @@ +/* + * Copyright 2025 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 + * + * https://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. + */ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: google/spanner/admin/database/v1/spanner_database_admin.proto + +// Protobuf Java Version: 3.25.5 +package com.google.spanner.admin.database.v1; + +/** + * + * + *
+ * The response for
+ * [AddSplitPoints][google.spanner.admin.database.v1.DatabaseAdmin.AddSplitPoints].
+ * 
+ * + * Protobuf type {@code google.spanner.admin.database.v1.AddSplitPointsResponse} + */ +public final class AddSplitPointsResponse extends com.google.protobuf.GeneratedMessageV3 + implements + // @@protoc_insertion_point(message_implements:google.spanner.admin.database.v1.AddSplitPointsResponse) + AddSplitPointsResponseOrBuilder { + private static final long serialVersionUID = 0L; + // Use AddSplitPointsResponse.newBuilder() to construct. + private AddSplitPointsResponse(com.google.protobuf.GeneratedMessageV3.Builder builder) { + super(builder); + } + + private AddSplitPointsResponse() {} + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance(UnusedPrivateParameter unused) { + return new AddSplitPointsResponse(); + } + + public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { + return com.google.spanner.admin.database.v1.SpannerDatabaseAdminProto + .internal_static_google_spanner_admin_database_v1_AddSplitPointsResponse_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return com.google.spanner.admin.database.v1.SpannerDatabaseAdminProto + .internal_static_google_spanner_admin_database_v1_AddSplitPointsResponse_fieldAccessorTable + .ensureFieldAccessorsInitialized( + com.google.spanner.admin.database.v1.AddSplitPointsResponse.class, + com.google.spanner.admin.database.v1.AddSplitPointsResponse.Builder.class); + } + + private byte memoizedIsInitialized = -1; + + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException { + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof com.google.spanner.admin.database.v1.AddSplitPointsResponse)) { + return super.equals(obj); + } + com.google.spanner.admin.database.v1.AddSplitPointsResponse other = + (com.google.spanner.admin.database.v1.AddSplitPointsResponse) obj; + + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static com.google.spanner.admin.database.v1.AddSplitPointsResponse parseFrom( + java.nio.ByteBuffer data) throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + + public static com.google.spanner.admin.database.v1.AddSplitPointsResponse parseFrom( + java.nio.ByteBuffer data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + + public static com.google.spanner.admin.database.v1.AddSplitPointsResponse parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + + public static com.google.spanner.admin.database.v1.AddSplitPointsResponse parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + + public static com.google.spanner.admin.database.v1.AddSplitPointsResponse parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + + public static com.google.spanner.admin.database.v1.AddSplitPointsResponse parseFrom( + byte[] data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + + public static com.google.spanner.admin.database.v1.AddSplitPointsResponse parseFrom( + java.io.InputStream input) throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input); + } + + public static com.google.spanner.admin.database.v1.AddSplitPointsResponse parseFrom( + java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseWithIOException( + PARSER, input, extensionRegistry); + } + + public static com.google.spanner.admin.database.v1.AddSplitPointsResponse parseDelimitedFrom( + java.io.InputStream input) throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException(PARSER, input); + } + + public static com.google.spanner.admin.database.v1.AddSplitPointsResponse parseDelimitedFrom( + java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException( + PARSER, input, extensionRegistry); + } + + public static com.google.spanner.admin.database.v1.AddSplitPointsResponse parseFrom( + com.google.protobuf.CodedInputStream input) throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input); + } + + public static com.google.spanner.admin.database.v1.AddSplitPointsResponse parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseWithIOException( + PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { + return newBuilder(); + } + + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + + public static Builder newBuilder( + com.google.spanner.admin.database.v1.AddSplitPointsResponse prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType(com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * + * + *
+   * The response for
+   * [AddSplitPoints][google.spanner.admin.database.v1.DatabaseAdmin.AddSplitPoints].
+   * 
+ * + * Protobuf type {@code google.spanner.admin.database.v1.AddSplitPointsResponse} + */ + public static final class Builder extends com.google.protobuf.GeneratedMessageV3.Builder + implements + // @@protoc_insertion_point(builder_implements:google.spanner.admin.database.v1.AddSplitPointsResponse) + com.google.spanner.admin.database.v1.AddSplitPointsResponseOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { + return com.google.spanner.admin.database.v1.SpannerDatabaseAdminProto + .internal_static_google_spanner_admin_database_v1_AddSplitPointsResponse_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return com.google.spanner.admin.database.v1.SpannerDatabaseAdminProto + .internal_static_google_spanner_admin_database_v1_AddSplitPointsResponse_fieldAccessorTable + .ensureFieldAccessorsInitialized( + com.google.spanner.admin.database.v1.AddSplitPointsResponse.class, + com.google.spanner.admin.database.v1.AddSplitPointsResponse.Builder.class); + } + + // Construct using com.google.spanner.admin.database.v1.AddSplitPointsResponse.newBuilder() + private Builder() {} + + private Builder(com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + } + + @java.lang.Override + public Builder clear() { + super.clear(); + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor getDescriptorForType() { + return com.google.spanner.admin.database.v1.SpannerDatabaseAdminProto + .internal_static_google_spanner_admin_database_v1_AddSplitPointsResponse_descriptor; + } + + @java.lang.Override + public com.google.spanner.admin.database.v1.AddSplitPointsResponse getDefaultInstanceForType() { + return com.google.spanner.admin.database.v1.AddSplitPointsResponse.getDefaultInstance(); + } + + @java.lang.Override + public com.google.spanner.admin.database.v1.AddSplitPointsResponse build() { + com.google.spanner.admin.database.v1.AddSplitPointsResponse result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public com.google.spanner.admin.database.v1.AddSplitPointsResponse buildPartial() { + com.google.spanner.admin.database.v1.AddSplitPointsResponse result = + new com.google.spanner.admin.database.v1.AddSplitPointsResponse(this); + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, java.lang.Object value) { + return super.setField(field, value); + } + + @java.lang.Override + public Builder clearField(com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + + @java.lang.Override + public Builder clearOneof(com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, java.lang.Object value) { + return super.addRepeatedField(field, value); + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof com.google.spanner.admin.database.v1.AddSplitPointsResponse) { + return mergeFrom((com.google.spanner.admin.database.v1.AddSplitPointsResponse) other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(com.google.spanner.admin.database.v1.AddSplitPointsResponse other) { + if (other == com.google.spanner.admin.database.v1.AddSplitPointsResponse.getDefaultInstance()) + return this; + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: + { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + + @java.lang.Override + public final Builder setUnknownFields(final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + // @@protoc_insertion_point(builder_scope:google.spanner.admin.database.v1.AddSplitPointsResponse) + } + + // @@protoc_insertion_point(class_scope:google.spanner.admin.database.v1.AddSplitPointsResponse) + private static final com.google.spanner.admin.database.v1.AddSplitPointsResponse DEFAULT_INSTANCE; + + static { + DEFAULT_INSTANCE = new com.google.spanner.admin.database.v1.AddSplitPointsResponse(); + } + + public static com.google.spanner.admin.database.v1.AddSplitPointsResponse getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + @java.lang.Override + public AddSplitPointsResponse parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public com.google.spanner.admin.database.v1.AddSplitPointsResponse getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } +} diff --git a/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/AddSplitPointsResponseOrBuilder.java b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/AddSplitPointsResponseOrBuilder.java new file mode 100644 index 00000000000..bc6192ab7b7 --- /dev/null +++ b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/AddSplitPointsResponseOrBuilder.java @@ -0,0 +1,25 @@ +/* + * Copyright 2025 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 + * + * https://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. + */ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: google/spanner/admin/database/v1/spanner_database_admin.proto + +// Protobuf Java Version: 3.25.5 +package com.google.spanner.admin.database.v1; + +public interface AddSplitPointsResponseOrBuilder + extends + // @@protoc_insertion_point(interface_extends:google.spanner.admin.database.v1.AddSplitPointsResponse) + com.google.protobuf.MessageOrBuilder {} diff --git a/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/SpannerDatabaseAdminProto.java b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/SpannerDatabaseAdminProto.java index 507aa4a281f..06a130a7002 100644 --- a/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/SpannerDatabaseAdminProto.java +++ b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/SpannerDatabaseAdminProto.java @@ -124,6 +124,22 @@ public static void registerAllExtensions(com.google.protobuf.ExtensionRegistry r internal_static_google_spanner_admin_database_v1_ListDatabaseRolesResponse_descriptor; static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_google_spanner_admin_database_v1_ListDatabaseRolesResponse_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_google_spanner_admin_database_v1_AddSplitPointsRequest_descriptor; + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_google_spanner_admin_database_v1_AddSplitPointsRequest_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_google_spanner_admin_database_v1_AddSplitPointsResponse_descriptor; + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_google_spanner_admin_database_v1_AddSplitPointsResponse_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_google_spanner_admin_database_v1_SplitPoints_descriptor; + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_google_spanner_admin_database_v1_SplitPoints_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_google_spanner_admin_database_v1_SplitPoints_Key_descriptor; + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_google_spanner_admin_database_v1_SplitPoints_Key_fieldAccessorTable; public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() { return descriptor; @@ -142,301 +158,319 @@ public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() { + "roto\032\032google/iam/v1/policy.proto\032#google" + "/longrunning/operations.proto\032\033google/pr" + "otobuf/empty.proto\032 google/protobuf/fiel" - + "d_mask.proto\032\037google/protobuf/timestamp." - + "proto\032-google/spanner/admin/database/v1/" - + "backup.proto\0326google/spanner/admin/datab" - + "ase/v1/backup_schedule.proto\032-google/spa" - + "nner/admin/database/v1/common.proto\"\253\001\n\013" - + "RestoreInfo\022H\n\013source_type\030\001 \001(\01623.googl" - + "e.spanner.admin.database.v1.RestoreSourc" - + "eType\022C\n\013backup_info\030\002 \001(\0132,.google.span" - + "ner.admin.database.v1.BackupInfoH\000B\r\n\013so" - + "urce_info\"\312\006\n\010Database\022\021\n\004name\030\001 \001(\tB\003\340A" - + "\002\022D\n\005state\030\002 \001(\01620.google.spanner.admin." - + "database.v1.Database.StateB\003\340A\003\0224\n\013creat" - + "e_time\030\003 \001(\0132\032.google.protobuf.Timestamp" - + "B\003\340A\003\022H\n\014restore_info\030\004 \001(\0132-.google.spa" - + "nner.admin.database.v1.RestoreInfoB\003\340A\003\022" - + "R\n\021encryption_config\030\005 \001(\01322.google.span" - + "ner.admin.database.v1.EncryptionConfigB\003" - + "\340A\003\022N\n\017encryption_info\030\010 \003(\01320.google.sp" - + "anner.admin.database.v1.EncryptionInfoB\003" - + "\340A\003\022%\n\030version_retention_period\030\006 \001(\tB\003\340" - + "A\003\022>\n\025earliest_version_time\030\007 \001(\0132\032.goog" - + "le.protobuf.TimestampB\003\340A\003\022\033\n\016default_le" - + "ader\030\t \001(\tB\003\340A\003\022P\n\020database_dialect\030\n \001(" - + "\01621.google.spanner.admin.database.v1.Dat" - + "abaseDialectB\003\340A\003\022\036\n\026enable_drop_protect" - + "ion\030\013 \001(\010\022\030\n\013reconciling\030\014 \001(\010B\003\340A\003\"M\n\005S" - + "tate\022\025\n\021STATE_UNSPECIFIED\020\000\022\014\n\010CREATING\020" - + "\001\022\t\n\005READY\020\002\022\024\n\020READY_OPTIMIZING\020\003:b\352A_\n" - + "\037spanner.googleapis.com/Database\022\332A\006parent\202\323\344\223\002/\022-/v1/{parent=projec" - + "ts/*/instances/*}/databases\022\244\002\n\016CreateDa" - + "tabase\0227.google.spanner.admin.database.v" - + "1.CreateDatabaseRequest\032\035.google.longrun" - + "ning.Operation\"\271\001\312Ad\n)google.spanner.adm" - + "in.database.v1.Database\0227google.spanner." - + "admin.database.v1.CreateDatabaseMetadata" - + "\332A\027parent,create_statement\202\323\344\223\0022\"-/v1/{p" - + "arent=projects/*/instances/*}/databases:" - + "\001*\022\255\001\n\013GetDatabase\0224.google.spanner.admi" - + "n.database.v1.GetDatabaseRequest\032*.googl" - + "e.spanner.admin.database.v1.Database\"<\332A" - + "\004name\202\323\344\223\002/\022-/v1/{name=projects/*/instan" - + "ces/*/databases/*}\022\357\001\n\016UpdateDatabase\0227." - + "google.spanner.admin.database.v1.UpdateD" - + "atabaseRequest\032\035.google.longrunning.Oper" - + "ation\"\204\001\312A\"\n\010Database\022\026UpdateDatabaseMet" - + "adata\332A\024database,update_mask\202\323\344\223\002B26/v1/" - + "{database.name=projects/*/instances/*/da" - + "tabases/*}:\010database\022\235\002\n\021UpdateDatabaseD" - + "dl\022:.google.spanner.admin.database.v1.Up" - + "dateDatabaseDdlRequest\032\035.google.longrunn" - + "ing.Operation\"\254\001\312AS\n\025google.protobuf.Emp" - + "ty\022:google.spanner.admin.database.v1.Upd" - + "ateDatabaseDdlMetadata\332A\023database,statem" - + "ents\202\323\344\223\002:25/v1/{database=projects/*/ins" - + "tances/*/databases/*}/ddl:\001*\022\243\001\n\014DropDat" - + "abase\0225.google.spanner.admin.database.v1" - + ".DropDatabaseRequest\032\026.google.protobuf.E" - + "mpty\"D\332A\010database\202\323\344\223\0023*1/v1/{database=p" - + "rojects/*/instances/*/databases/*}\022\315\001\n\016G" - + "etDatabaseDdl\0227.google.spanner.admin.dat" - + "abase.v1.GetDatabaseDdlRequest\0328.google." - + "spanner.admin.database.v1.GetDatabaseDdl" - + "Response\"H\332A\010database\202\323\344\223\0027\0225/v1/{databa" - + "se=projects/*/instances/*/databases/*}/d" - + "dl\022\302\002\n\014SetIamPolicy\022\".google.iam.v1.SetI" - + "amPolicyRequest\032\025.google.iam.v1.Policy\"\366" - + "\001\332A\017resource,policy\202\323\344\223\002\335\001\">/v1/{resourc" - + "e=projects/*/instances/*/databases/*}:se" - + "tIamPolicy:\001*ZA\"/v1/{resource=" - + "projects/*/instances/*/databases/*}:getI" - + "amPolicy:\001*ZA\"\n\025earl" + + "iest_version_time\030\007 \001(\0132\032.google.protobu" + + "f.TimestampB\003\340A\003\022\033\n\016default_leader\030\t \001(\t" + + "B\003\340A\003\022P\n\020database_dialect\030\n \001(\01621.google" + + ".spanner.admin.database.v1.DatabaseDiale" + + "ctB\003\340A\003\022\036\n\026enable_drop_protection\030\013 \001(\010\022" + + "\030\n\013reconciling\030\014 \001(\010B\003\340A\003\"M\n\005State\022\025\n\021ST" + + "ATE_UNSPECIFIED\020\000\022\014\n\010CREATING\020\001\022\t\n\005READY" + + "\020\002\022\024\n\020READY_OPTIMIZING\020\003:b\352A_\n\037spanner.g" + + "oogleapis.com/Database\022.google.spanner.admin.databa" - + "se.v1.ListBackupOperationsResponse\"E\332A\006p" - + "arent\202\323\344\223\0026\0224/v1/{parent=projects/*/inst" - + "ances/*}/backupOperations\022\334\001\n\021ListDataba" - + "seRoles\022:.google.spanner.admin.database." - + "v1.ListDatabaseRolesRequest\032;.google.spa" - + "nner.admin.database.v1.ListDatabaseRoles" - + "Response\"N\332A\006parent\202\323\344\223\002?\022=/v1/{parent=p" - + "rojects/*/instances/*/databases/*}/datab" - + "aseRoles\022\216\002\n\024CreateBackupSchedule\022=.goog" - + "le.spanner.admin.database.v1.CreateBacku" - + "pScheduleRequest\0320.google.spanner.admin." - + "database.v1.BackupSchedule\"\204\001\332A)parent,b" - + "ackup_schedule,backup_schedule_id\202\323\344\223\002R\"" - + "?/v1/{parent=projects/*/instances/*/data" - + "bases/*}/backupSchedules:\017backup_schedul" - + "e\022\321\001\n\021GetBackupSchedule\022:.google.spanner" - + ".admin.database.v1.GetBackupScheduleRequ" - + "est\0320.google.spanner.admin.database.v1.B" - + "ackupSchedule\"N\332A\004name\202\323\344\223\002A\022?/v1/{name=" - + "projects/*/instances/*/databases/*/backu" - + "pSchedules/*}\022\220\002\n\024UpdateBackupSchedule\022=" - + ".google.spanner.admin.database.v1.Update" - + "BackupScheduleRequest\0320.google.spanner.a" - + "dmin.database.v1.BackupSchedule\"\206\001\332A\033bac" - + "kup_schedule,update_mask\202\323\344\223\002b2O/v1/{bac" - + "kup_schedule.name=projects/*/instances/*" - + "/databases/*/backupSchedules/*}:\017backup_" - + "schedule\022\275\001\n\024DeleteBackupSchedule\022=.goog" - + "le.spanner.admin.database.v1.DeleteBacku" - + "pScheduleRequest\032\026.google.protobuf.Empty" - + "\"N\332A\004name\202\323\344\223\002A*?/v1/{name=projects/*/in" - + "stances/*/databases/*/backupSchedules/*}" - + "\022\344\001\n\023ListBackupSchedules\022<.google.spanne" - + "r.admin.database.v1.ListBackupSchedulesR" - + "equest\032=.google.spanner.admin.database.v" - + "1.ListBackupSchedulesResponse\"P\332A\006parent" - + "\202\323\344\223\002A\022?/v1/{parent=projects/*/instances" - + "/*/databases/*}/backupSchedules\032x\312A\026span" - + "ner.googleapis.com\322A\\https://www.googlea" - + "pis.com/auth/cloud-platform,https://www." - + "googleapis.com/auth/spanner.adminB\330\002\n$co" - + "m.google.spanner.admin.database.v1B\031Span" - + "nerDatabaseAdminProtoP\001ZFcloud.google.co" - + "m/go/spanner/admin/database/apiv1/databa" - + "sepb;databasepb\252\002&Google.Cloud.Spanner.A" - + "dmin.Database.V1\312\002&Google\\Cloud\\Spanner\\" - + "Admin\\Database\\V1\352\002+Google::Cloud::Spann" - + "er::Admin::Database::V1\352AJ\n\037spanner.goog" - + "leapis.com/Instance\022\'projects/{project}/" - + "instances/{instance}b\006proto3" + + "tabasesRequest\0327.google.spanner.admin.da" + + "tabase.v1.ListDatabasesResponse\">\332A\006pare" + + "nt\202\323\344\223\002/\022-/v1/{parent=projects/*/instanc" + + "es/*}/databases\022\244\002\n\016CreateDatabase\0227.goo" + + "gle.spanner.admin.database.v1.CreateData" + + "baseRequest\032\035.google.longrunning.Operati" + + "on\"\271\001\312Ad\n)google.spanner.admin.database." + + "v1.Database\0227google.spanner.admin.databa" + + "se.v1.CreateDatabaseMetadata\332A\027parent,cr" + + "eate_statement\202\323\344\223\0022\"-/v1/{parent=projec" + + "ts/*/instances/*}/databases:\001*\022\255\001\n\013GetDa" + + "tabase\0224.google.spanner.admin.database.v" + + "1.GetDatabaseRequest\032*.google.spanner.ad" + + "min.database.v1.Database\"<\332A\004name\202\323\344\223\002/\022" + + "-/v1/{name=projects/*/instances/*/databa" + + "ses/*}\022\357\001\n\016UpdateDatabase\0227.google.spann" + + "er.admin.database.v1.UpdateDatabaseReque" + + "st\032\035.google.longrunning.Operation\"\204\001\312A\"\n" + + "\010Database\022\026UpdateDatabaseMetadata\332A\024data" + + "base,update_mask\202\323\344\223\002B26/v1/{database.na" + + "me=projects/*/instances/*/databases/*}:\010" + + "database\022\235\002\n\021UpdateDatabaseDdl\022:.google." + + "spanner.admin.database.v1.UpdateDatabase" + + "DdlRequest\032\035.google.longrunning.Operatio" + + "n\"\254\001\312AS\n\025google.protobuf.Empty\022:google.s" + + "panner.admin.database.v1.UpdateDatabaseD" + + "dlMetadata\332A\023database,statements\202\323\344\223\002:25" + + "/v1/{database=projects/*/instances/*/dat" + + "abases/*}/ddl:\001*\022\243\001\n\014DropDatabase\0225.goog" + + "le.spanner.admin.database.v1.DropDatabas" + + "eRequest\032\026.google.protobuf.Empty\"D\332A\010dat" + + "abase\202\323\344\223\0023*1/v1/{database=projects/*/in" + + "stances/*/databases/*}\022\315\001\n\016GetDatabaseDd" + + "l\0227.google.spanner.admin.database.v1.Get" + + "DatabaseDdlRequest\0328.google.spanner.admi" + + "n.database.v1.GetDatabaseDdlResponse\"H\332A" + + "\010database\202\323\344\223\0027\0225/v1/{database=projects/" + + "*/instances/*/databases/*}/ddl\022\302\002\n\014SetIa" + + "mPolicy\022\".google.iam.v1.SetIamPolicyRequ" + + "est\032\025.google.iam.v1.Policy\"\366\001\332A\017resource" + + ",policy\202\323\344\223\002\335\001\">/v1/{resource=projects/*" + + "/instances/*/databases/*}:setIamPolicy:\001" + + "*ZA\"/v1/{resource=projects/*/i" + + "nstances/*/databases/*}:getIamPolicy:\001*Z" + + "A\"" + + ".google.spanner.admin.database.v1.ListBa" + + "ckupOperationsResponse\"E\332A\006parent\202\323\344\223\0026\022" + + "4/v1/{parent=projects/*/instances/*}/bac" + + "kupOperations\022\334\001\n\021ListDatabaseRoles\022:.go" + + "ogle.spanner.admin.database.v1.ListDatab" + + "aseRolesRequest\032;.google.spanner.admin.d" + + "atabase.v1.ListDatabaseRolesResponse\"N\332A" + + "\006parent\202\323\344\223\002?\022=/v1/{parent=projects/*/in" + + "stances/*/databases/*}/databaseRoles\022\350\001\n" + + "\016AddSplitPoints\0227.google.spanner.admin.d" + + "atabase.v1.AddSplitPointsRequest\0328.googl" + + "e.spanner.admin.database.v1.AddSplitPoin" + + "tsResponse\"c\332A\025database,split_points\202\323\344\223" + + "\002E\"@/v1/{database=projects/*/instances/*" + + "/databases/*}:addSplitPoints:\001*\022\216\002\n\024Crea" + + "teBackupSchedule\022=.google.spanner.admin." + + "database.v1.CreateBackupScheduleRequest\032" + + "0.google.spanner.admin.database.v1.Backu" + + "pSchedule\"\204\001\332A)parent,backup_schedule,ba" + + "ckup_schedule_id\202\323\344\223\002R\"?/v1/{parent=proj" + + "ects/*/instances/*/databases/*}/backupSc" + + "hedules:\017backup_schedule\022\321\001\n\021GetBackupSc" + + "hedule\022:.google.spanner.admin.database.v" + + "1.GetBackupScheduleRequest\0320.google.span" + + "ner.admin.database.v1.BackupSchedule\"N\332A" + + "\004name\202\323\344\223\002A\022?/v1/{name=projects/*/instan" + + "ces/*/databases/*/backupSchedules/*}\022\220\002\n" + + "\024UpdateBackupSchedule\022=.google.spanner.a" + + "dmin.database.v1.UpdateBackupScheduleReq" + + "uest\0320.google.spanner.admin.database.v1." + + "BackupSchedule\"\206\001\332A\033backup_schedule,upda" + + "te_mask\202\323\344\223\002b2O/v1/{backup_schedule.name" + + "=projects/*/instances/*/databases/*/back" + + "upSchedules/*}:\017backup_schedule\022\275\001\n\024Dele" + + "teBackupSchedule\022=.google.spanner.admin." + + "database.v1.DeleteBackupScheduleRequest\032" + + "\026.google.protobuf.Empty\"N\332A\004name\202\323\344\223\002A*?" + + "/v1/{name=projects/*/instances/*/databas" + + "es/*/backupSchedules/*}\022\344\001\n\023ListBackupSc" + + "hedules\022<.google.spanner.admin.database." + + "v1.ListBackupSchedulesRequest\032=.google.s" + + "panner.admin.database.v1.ListBackupSched" + + "ulesResponse\"P\332A\006parent\202\323\344\223\002A\022?/v1/{pare" + + "nt=projects/*/instances/*/databases/*}/b" + + "ackupSchedules\032x\312A\026spanner.googleapis.co" + + "m\322A\\https://www.googleapis.com/auth/clou" + + "d-platform,https://www.googleapis.com/au" + + "th/spanner.adminB\330\002\n$com.google.spanner." + + "admin.database.v1B\031SpannerDatabaseAdminP" + + "rotoP\001ZFcloud.google.com/go/spanner/admi" + + "n/database/apiv1/databasepb;databasepb\252\002" + + "&Google.Cloud.Spanner.Admin.Database.V1\312" + + "\002&Google\\Cloud\\Spanner\\Admin\\Database\\V1" + + "\352\002+Google::Cloud::Spanner::Admin::Databa" + + "se::V1\352AJ\n\037spanner.googleapis.com/Instan" + + "ce\022\'projects/{project}/instances/{instan" + + "ce}b\006proto3" }; descriptor = com.google.protobuf.Descriptors.FileDescriptor.internalBuildGeneratedFileFrom( @@ -451,6 +485,7 @@ public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() { com.google.longrunning.OperationsProto.getDescriptor(), com.google.protobuf.EmptyProto.getDescriptor(), com.google.protobuf.FieldMaskProto.getDescriptor(), + com.google.protobuf.StructProto.getDescriptor(), com.google.protobuf.TimestampProto.getDescriptor(), com.google.spanner.admin.database.v1.BackupProto.getDescriptor(), com.google.spanner.admin.database.v1.BackupScheduleProto.getDescriptor(), @@ -670,6 +705,38 @@ public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() { new java.lang.String[] { "DatabaseRoles", "NextPageToken", }); + internal_static_google_spanner_admin_database_v1_AddSplitPointsRequest_descriptor = + getDescriptor().getMessageTypes().get(24); + internal_static_google_spanner_admin_database_v1_AddSplitPointsRequest_fieldAccessorTable = + new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_google_spanner_admin_database_v1_AddSplitPointsRequest_descriptor, + new java.lang.String[] { + "Database", "SplitPoints", "Initiator", + }); + internal_static_google_spanner_admin_database_v1_AddSplitPointsResponse_descriptor = + getDescriptor().getMessageTypes().get(25); + internal_static_google_spanner_admin_database_v1_AddSplitPointsResponse_fieldAccessorTable = + new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_google_spanner_admin_database_v1_AddSplitPointsResponse_descriptor, + new java.lang.String[] {}); + internal_static_google_spanner_admin_database_v1_SplitPoints_descriptor = + getDescriptor().getMessageTypes().get(26); + internal_static_google_spanner_admin_database_v1_SplitPoints_fieldAccessorTable = + new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_google_spanner_admin_database_v1_SplitPoints_descriptor, + new java.lang.String[] { + "Table", "Index", "Keys", "ExpireTime", + }); + internal_static_google_spanner_admin_database_v1_SplitPoints_Key_descriptor = + internal_static_google_spanner_admin_database_v1_SplitPoints_descriptor + .getNestedTypes() + .get(0); + internal_static_google_spanner_admin_database_v1_SplitPoints_Key_fieldAccessorTable = + new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_google_spanner_admin_database_v1_SplitPoints_Key_descriptor, + new java.lang.String[] { + "KeyParts", + }); com.google.protobuf.ExtensionRegistry registry = com.google.protobuf.ExtensionRegistry.newInstance(); registry.add(com.google.api.ClientProto.defaultHost); @@ -692,6 +759,7 @@ public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() { com.google.longrunning.OperationsProto.getDescriptor(); com.google.protobuf.EmptyProto.getDescriptor(); com.google.protobuf.FieldMaskProto.getDescriptor(); + com.google.protobuf.StructProto.getDescriptor(); com.google.protobuf.TimestampProto.getDescriptor(); com.google.spanner.admin.database.v1.BackupProto.getDescriptor(); com.google.spanner.admin.database.v1.BackupScheduleProto.getDescriptor(); diff --git a/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/SplitPoints.java b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/SplitPoints.java new file mode 100644 index 00000000000..bd01be8bf1e --- /dev/null +++ b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/SplitPoints.java @@ -0,0 +1,2437 @@ +/* + * Copyright 2025 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 + * + * https://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. + */ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: google/spanner/admin/database/v1/spanner_database_admin.proto + +// Protobuf Java Version: 3.25.5 +package com.google.spanner.admin.database.v1; + +/** + * + * + *
+ * The split points of a table/index.
+ * 
+ * + * Protobuf type {@code google.spanner.admin.database.v1.SplitPoints} + */ +public final class SplitPoints extends com.google.protobuf.GeneratedMessageV3 + implements + // @@protoc_insertion_point(message_implements:google.spanner.admin.database.v1.SplitPoints) + SplitPointsOrBuilder { + private static final long serialVersionUID = 0L; + // Use SplitPoints.newBuilder() to construct. + private SplitPoints(com.google.protobuf.GeneratedMessageV3.Builder builder) { + super(builder); + } + + private SplitPoints() { + table_ = ""; + index_ = ""; + keys_ = java.util.Collections.emptyList(); + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance(UnusedPrivateParameter unused) { + return new SplitPoints(); + } + + public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { + return com.google.spanner.admin.database.v1.SpannerDatabaseAdminProto + .internal_static_google_spanner_admin_database_v1_SplitPoints_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return com.google.spanner.admin.database.v1.SpannerDatabaseAdminProto + .internal_static_google_spanner_admin_database_v1_SplitPoints_fieldAccessorTable + .ensureFieldAccessorsInitialized( + com.google.spanner.admin.database.v1.SplitPoints.class, + com.google.spanner.admin.database.v1.SplitPoints.Builder.class); + } + + public interface KeyOrBuilder + extends + // @@protoc_insertion_point(interface_extends:google.spanner.admin.database.v1.SplitPoints.Key) + com.google.protobuf.MessageOrBuilder { + + /** + * + * + *
+     * Required. The column values making up the split key.
+     * 
+ * + * .google.protobuf.ListValue key_parts = 1 [(.google.api.field_behavior) = REQUIRED]; + * + * + * @return Whether the keyParts field is set. + */ + boolean hasKeyParts(); + /** + * + * + *
+     * Required. The column values making up the split key.
+     * 
+ * + * .google.protobuf.ListValue key_parts = 1 [(.google.api.field_behavior) = REQUIRED]; + * + * + * @return The keyParts. + */ + com.google.protobuf.ListValue getKeyParts(); + /** + * + * + *
+     * Required. The column values making up the split key.
+     * 
+ * + * .google.protobuf.ListValue key_parts = 1 [(.google.api.field_behavior) = REQUIRED]; + * + */ + com.google.protobuf.ListValueOrBuilder getKeyPartsOrBuilder(); + } + /** + * + * + *
+   * A split key.
+   * 
+ * + * Protobuf type {@code google.spanner.admin.database.v1.SplitPoints.Key} + */ + public static final class Key extends com.google.protobuf.GeneratedMessageV3 + implements + // @@protoc_insertion_point(message_implements:google.spanner.admin.database.v1.SplitPoints.Key) + KeyOrBuilder { + private static final long serialVersionUID = 0L; + // Use Key.newBuilder() to construct. + private Key(com.google.protobuf.GeneratedMessageV3.Builder builder) { + super(builder); + } + + private Key() {} + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance(UnusedPrivateParameter unused) { + return new Key(); + } + + public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { + return com.google.spanner.admin.database.v1.SpannerDatabaseAdminProto + .internal_static_google_spanner_admin_database_v1_SplitPoints_Key_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return com.google.spanner.admin.database.v1.SpannerDatabaseAdminProto + .internal_static_google_spanner_admin_database_v1_SplitPoints_Key_fieldAccessorTable + .ensureFieldAccessorsInitialized( + com.google.spanner.admin.database.v1.SplitPoints.Key.class, + com.google.spanner.admin.database.v1.SplitPoints.Key.Builder.class); + } + + private int bitField0_; + public static final int KEY_PARTS_FIELD_NUMBER = 1; + private com.google.protobuf.ListValue keyParts_; + /** + * + * + *
+     * Required. The column values making up the split key.
+     * 
+ * + * .google.protobuf.ListValue key_parts = 1 [(.google.api.field_behavior) = REQUIRED]; + * + * + * @return Whether the keyParts field is set. + */ + @java.lang.Override + public boolean hasKeyParts() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + * + * + *
+     * Required. The column values making up the split key.
+     * 
+ * + * .google.protobuf.ListValue key_parts = 1 [(.google.api.field_behavior) = REQUIRED]; + * + * + * @return The keyParts. + */ + @java.lang.Override + public com.google.protobuf.ListValue getKeyParts() { + return keyParts_ == null ? com.google.protobuf.ListValue.getDefaultInstance() : keyParts_; + } + /** + * + * + *
+     * Required. The column values making up the split key.
+     * 
+ * + * .google.protobuf.ListValue key_parts = 1 [(.google.api.field_behavior) = REQUIRED]; + * + */ + @java.lang.Override + public com.google.protobuf.ListValueOrBuilder getKeyPartsOrBuilder() { + return keyParts_ == null ? com.google.protobuf.ListValue.getDefaultInstance() : keyParts_; + } + + private byte memoizedIsInitialized = -1; + + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException { + if (((bitField0_ & 0x00000001) != 0)) { + output.writeMessage(1, getKeyParts()); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) != 0)) { + size += com.google.protobuf.CodedOutputStream.computeMessageSize(1, getKeyParts()); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof com.google.spanner.admin.database.v1.SplitPoints.Key)) { + return super.equals(obj); + } + com.google.spanner.admin.database.v1.SplitPoints.Key other = + (com.google.spanner.admin.database.v1.SplitPoints.Key) obj; + + if (hasKeyParts() != other.hasKeyParts()) return false; + if (hasKeyParts()) { + if (!getKeyParts().equals(other.getKeyParts())) return false; + } + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + if (hasKeyParts()) { + hash = (37 * hash) + KEY_PARTS_FIELD_NUMBER; + hash = (53 * hash) + getKeyParts().hashCode(); + } + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static com.google.spanner.admin.database.v1.SplitPoints.Key parseFrom( + java.nio.ByteBuffer data) throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + + public static com.google.spanner.admin.database.v1.SplitPoints.Key parseFrom( + java.nio.ByteBuffer data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + + public static com.google.spanner.admin.database.v1.SplitPoints.Key parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + + public static com.google.spanner.admin.database.v1.SplitPoints.Key parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + + public static com.google.spanner.admin.database.v1.SplitPoints.Key parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + + public static com.google.spanner.admin.database.v1.SplitPoints.Key parseFrom( + byte[] data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + + public static com.google.spanner.admin.database.v1.SplitPoints.Key parseFrom( + java.io.InputStream input) throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input); + } + + public static com.google.spanner.admin.database.v1.SplitPoints.Key parseFrom( + java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseWithIOException( + PARSER, input, extensionRegistry); + } + + public static com.google.spanner.admin.database.v1.SplitPoints.Key parseDelimitedFrom( + java.io.InputStream input) throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException(PARSER, input); + } + + public static com.google.spanner.admin.database.v1.SplitPoints.Key parseDelimitedFrom( + java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException( + PARSER, input, extensionRegistry); + } + + public static com.google.spanner.admin.database.v1.SplitPoints.Key parseFrom( + com.google.protobuf.CodedInputStream input) throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input); + } + + public static com.google.spanner.admin.database.v1.SplitPoints.Key parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseWithIOException( + PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { + return newBuilder(); + } + + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + + public static Builder newBuilder( + com.google.spanner.admin.database.v1.SplitPoints.Key prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * + * + *
+     * A split key.
+     * 
+ * + * Protobuf type {@code google.spanner.admin.database.v1.SplitPoints.Key} + */ + public static final class Builder + extends com.google.protobuf.GeneratedMessageV3.Builder + implements + // @@protoc_insertion_point(builder_implements:google.spanner.admin.database.v1.SplitPoints.Key) + com.google.spanner.admin.database.v1.SplitPoints.KeyOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { + return com.google.spanner.admin.database.v1.SpannerDatabaseAdminProto + .internal_static_google_spanner_admin_database_v1_SplitPoints_Key_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return com.google.spanner.admin.database.v1.SpannerDatabaseAdminProto + .internal_static_google_spanner_admin_database_v1_SplitPoints_Key_fieldAccessorTable + .ensureFieldAccessorsInitialized( + com.google.spanner.admin.database.v1.SplitPoints.Key.class, + com.google.spanner.admin.database.v1.SplitPoints.Key.Builder.class); + } + + // Construct using com.google.spanner.admin.database.v1.SplitPoints.Key.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder(com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders) { + getKeyPartsFieldBuilder(); + } + } + + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + keyParts_ = null; + if (keyPartsBuilder_ != null) { + keyPartsBuilder_.dispose(); + keyPartsBuilder_ = null; + } + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor getDescriptorForType() { + return com.google.spanner.admin.database.v1.SpannerDatabaseAdminProto + .internal_static_google_spanner_admin_database_v1_SplitPoints_Key_descriptor; + } + + @java.lang.Override + public com.google.spanner.admin.database.v1.SplitPoints.Key getDefaultInstanceForType() { + return com.google.spanner.admin.database.v1.SplitPoints.Key.getDefaultInstance(); + } + + @java.lang.Override + public com.google.spanner.admin.database.v1.SplitPoints.Key build() { + com.google.spanner.admin.database.v1.SplitPoints.Key result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public com.google.spanner.admin.database.v1.SplitPoints.Key buildPartial() { + com.google.spanner.admin.database.v1.SplitPoints.Key result = + new com.google.spanner.admin.database.v1.SplitPoints.Key(this); + if (bitField0_ != 0) { + buildPartial0(result); + } + onBuilt(); + return result; + } + + private void buildPartial0(com.google.spanner.admin.database.v1.SplitPoints.Key result) { + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.keyParts_ = keyPartsBuilder_ == null ? keyParts_ : keyPartsBuilder_.build(); + to_bitField0_ |= 0x00000001; + } + result.bitField0_ |= to_bitField0_; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, java.lang.Object value) { + return super.setField(field, value); + } + + @java.lang.Override + public Builder clearField(com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + + @java.lang.Override + public Builder clearOneof(com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, + java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, java.lang.Object value) { + return super.addRepeatedField(field, value); + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof com.google.spanner.admin.database.v1.SplitPoints.Key) { + return mergeFrom((com.google.spanner.admin.database.v1.SplitPoints.Key) other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(com.google.spanner.admin.database.v1.SplitPoints.Key other) { + if (other == com.google.spanner.admin.database.v1.SplitPoints.Key.getDefaultInstance()) + return this; + if (other.hasKeyParts()) { + mergeKeyParts(other.getKeyParts()); + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: + { + input.readMessage(getKeyPartsFieldBuilder().getBuilder(), extensionRegistry); + bitField0_ |= 0x00000001; + break; + } // case 10 + default: + { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + + private int bitField0_; + + private com.google.protobuf.ListValue keyParts_; + private com.google.protobuf.SingleFieldBuilderV3< + com.google.protobuf.ListValue, + com.google.protobuf.ListValue.Builder, + com.google.protobuf.ListValueOrBuilder> + keyPartsBuilder_; + /** + * + * + *
+       * Required. The column values making up the split key.
+       * 
+ * + * .google.protobuf.ListValue key_parts = 1 [(.google.api.field_behavior) = REQUIRED]; + * + * + * @return Whether the keyParts field is set. + */ + public boolean hasKeyParts() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + * + * + *
+       * Required. The column values making up the split key.
+       * 
+ * + * .google.protobuf.ListValue key_parts = 1 [(.google.api.field_behavior) = REQUIRED]; + * + * + * @return The keyParts. + */ + public com.google.protobuf.ListValue getKeyParts() { + if (keyPartsBuilder_ == null) { + return keyParts_ == null ? com.google.protobuf.ListValue.getDefaultInstance() : keyParts_; + } else { + return keyPartsBuilder_.getMessage(); + } + } + /** + * + * + *
+       * Required. The column values making up the split key.
+       * 
+ * + * .google.protobuf.ListValue key_parts = 1 [(.google.api.field_behavior) = REQUIRED]; + * + */ + public Builder setKeyParts(com.google.protobuf.ListValue value) { + if (keyPartsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + keyParts_ = value; + } else { + keyPartsBuilder_.setMessage(value); + } + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + * + * + *
+       * Required. The column values making up the split key.
+       * 
+ * + * .google.protobuf.ListValue key_parts = 1 [(.google.api.field_behavior) = REQUIRED]; + * + */ + public Builder setKeyParts(com.google.protobuf.ListValue.Builder builderForValue) { + if (keyPartsBuilder_ == null) { + keyParts_ = builderForValue.build(); + } else { + keyPartsBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + * + * + *
+       * Required. The column values making up the split key.
+       * 
+ * + * .google.protobuf.ListValue key_parts = 1 [(.google.api.field_behavior) = REQUIRED]; + * + */ + public Builder mergeKeyParts(com.google.protobuf.ListValue value) { + if (keyPartsBuilder_ == null) { + if (((bitField0_ & 0x00000001) != 0) + && keyParts_ != null + && keyParts_ != com.google.protobuf.ListValue.getDefaultInstance()) { + getKeyPartsBuilder().mergeFrom(value); + } else { + keyParts_ = value; + } + } else { + keyPartsBuilder_.mergeFrom(value); + } + if (keyParts_ != null) { + bitField0_ |= 0x00000001; + onChanged(); + } + return this; + } + /** + * + * + *
+       * Required. The column values making up the split key.
+       * 
+ * + * .google.protobuf.ListValue key_parts = 1 [(.google.api.field_behavior) = REQUIRED]; + * + */ + public Builder clearKeyParts() { + bitField0_ = (bitField0_ & ~0x00000001); + keyParts_ = null; + if (keyPartsBuilder_ != null) { + keyPartsBuilder_.dispose(); + keyPartsBuilder_ = null; + } + onChanged(); + return this; + } + /** + * + * + *
+       * Required. The column values making up the split key.
+       * 
+ * + * .google.protobuf.ListValue key_parts = 1 [(.google.api.field_behavior) = REQUIRED]; + * + */ + public com.google.protobuf.ListValue.Builder getKeyPartsBuilder() { + bitField0_ |= 0x00000001; + onChanged(); + return getKeyPartsFieldBuilder().getBuilder(); + } + /** + * + * + *
+       * Required. The column values making up the split key.
+       * 
+ * + * .google.protobuf.ListValue key_parts = 1 [(.google.api.field_behavior) = REQUIRED]; + * + */ + public com.google.protobuf.ListValueOrBuilder getKeyPartsOrBuilder() { + if (keyPartsBuilder_ != null) { + return keyPartsBuilder_.getMessageOrBuilder(); + } else { + return keyParts_ == null ? com.google.protobuf.ListValue.getDefaultInstance() : keyParts_; + } + } + /** + * + * + *
+       * Required. The column values making up the split key.
+       * 
+ * + * .google.protobuf.ListValue key_parts = 1 [(.google.api.field_behavior) = REQUIRED]; + * + */ + private com.google.protobuf.SingleFieldBuilderV3< + com.google.protobuf.ListValue, + com.google.protobuf.ListValue.Builder, + com.google.protobuf.ListValueOrBuilder> + getKeyPartsFieldBuilder() { + if (keyPartsBuilder_ == null) { + keyPartsBuilder_ = + new com.google.protobuf.SingleFieldBuilderV3< + com.google.protobuf.ListValue, + com.google.protobuf.ListValue.Builder, + com.google.protobuf.ListValueOrBuilder>( + getKeyParts(), getParentForChildren(), isClean()); + keyParts_ = null; + } + return keyPartsBuilder_; + } + + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + // @@protoc_insertion_point(builder_scope:google.spanner.admin.database.v1.SplitPoints.Key) + } + + // @@protoc_insertion_point(class_scope:google.spanner.admin.database.v1.SplitPoints.Key) + private static final com.google.spanner.admin.database.v1.SplitPoints.Key DEFAULT_INSTANCE; + + static { + DEFAULT_INSTANCE = new com.google.spanner.admin.database.v1.SplitPoints.Key(); + } + + public static com.google.spanner.admin.database.v1.SplitPoints.Key getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + @java.lang.Override + public Key parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException() + .setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public com.google.spanner.admin.database.v1.SplitPoints.Key getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + } + + private int bitField0_; + public static final int TABLE_FIELD_NUMBER = 1; + + @SuppressWarnings("serial") + private volatile java.lang.Object table_ = ""; + /** + * + * + *
+   * The table to split.
+   * 
+ * + * string table = 1; + * + * @return The table. + */ + @java.lang.Override + public java.lang.String getTable() { + java.lang.Object ref = table_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + table_ = s; + return s; + } + } + /** + * + * + *
+   * The table to split.
+   * 
+ * + * string table = 1; + * + * @return The bytes for table. + */ + @java.lang.Override + public com.google.protobuf.ByteString getTableBytes() { + java.lang.Object ref = table_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8((java.lang.String) ref); + table_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int INDEX_FIELD_NUMBER = 2; + + @SuppressWarnings("serial") + private volatile java.lang.Object index_ = ""; + /** + * + * + *
+   * The index to split.
+   * If specified, the `table` field must refer to the index's base table.
+   * 
+ * + * string index = 2; + * + * @return The index. + */ + @java.lang.Override + public java.lang.String getIndex() { + java.lang.Object ref = index_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + index_ = s; + return s; + } + } + /** + * + * + *
+   * The index to split.
+   * If specified, the `table` field must refer to the index's base table.
+   * 
+ * + * string index = 2; + * + * @return The bytes for index. + */ + @java.lang.Override + public com.google.protobuf.ByteString getIndexBytes() { + java.lang.Object ref = index_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8((java.lang.String) ref); + index_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int KEYS_FIELD_NUMBER = 3; + + @SuppressWarnings("serial") + private java.util.List keys_; + /** + * + * + *
+   * Required. The list of split keys, i.e., the split boundaries.
+   * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints.Key keys = 3 [(.google.api.field_behavior) = REQUIRED]; + * + */ + @java.lang.Override + public java.util.List getKeysList() { + return keys_; + } + /** + * + * + *
+   * Required. The list of split keys, i.e., the split boundaries.
+   * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints.Key keys = 3 [(.google.api.field_behavior) = REQUIRED]; + * + */ + @java.lang.Override + public java.util.List + getKeysOrBuilderList() { + return keys_; + } + /** + * + * + *
+   * Required. The list of split keys, i.e., the split boundaries.
+   * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints.Key keys = 3 [(.google.api.field_behavior) = REQUIRED]; + * + */ + @java.lang.Override + public int getKeysCount() { + return keys_.size(); + } + /** + * + * + *
+   * Required. The list of split keys, i.e., the split boundaries.
+   * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints.Key keys = 3 [(.google.api.field_behavior) = REQUIRED]; + * + */ + @java.lang.Override + public com.google.spanner.admin.database.v1.SplitPoints.Key getKeys(int index) { + return keys_.get(index); + } + /** + * + * + *
+   * Required. The list of split keys, i.e., the split boundaries.
+   * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints.Key keys = 3 [(.google.api.field_behavior) = REQUIRED]; + * + */ + @java.lang.Override + public com.google.spanner.admin.database.v1.SplitPoints.KeyOrBuilder getKeysOrBuilder(int index) { + return keys_.get(index); + } + + public static final int EXPIRE_TIME_FIELD_NUMBER = 5; + private com.google.protobuf.Timestamp expireTime_; + /** + * + * + *
+   * Optional. The expiration timestamp of the split points.
+   * A timestamp in the past means immediate expiration.
+   * The maximum value can be 30 days in the future.
+   * Defaults to 10 days in the future if not specified.
+   * 
+ * + * .google.protobuf.Timestamp expire_time = 5 [(.google.api.field_behavior) = OPTIONAL]; + * + * + * @return Whether the expireTime field is set. + */ + @java.lang.Override + public boolean hasExpireTime() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + * + * + *
+   * Optional. The expiration timestamp of the split points.
+   * A timestamp in the past means immediate expiration.
+   * The maximum value can be 30 days in the future.
+   * Defaults to 10 days in the future if not specified.
+   * 
+ * + * .google.protobuf.Timestamp expire_time = 5 [(.google.api.field_behavior) = OPTIONAL]; + * + * + * @return The expireTime. + */ + @java.lang.Override + public com.google.protobuf.Timestamp getExpireTime() { + return expireTime_ == null ? com.google.protobuf.Timestamp.getDefaultInstance() : expireTime_; + } + /** + * + * + *
+   * Optional. The expiration timestamp of the split points.
+   * A timestamp in the past means immediate expiration.
+   * The maximum value can be 30 days in the future.
+   * Defaults to 10 days in the future if not specified.
+   * 
+ * + * .google.protobuf.Timestamp expire_time = 5 [(.google.api.field_behavior) = OPTIONAL]; + * + */ + @java.lang.Override + public com.google.protobuf.TimestampOrBuilder getExpireTimeOrBuilder() { + return expireTime_ == null ? com.google.protobuf.Timestamp.getDefaultInstance() : expireTime_; + } + + private byte memoizedIsInitialized = -1; + + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException { + if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(table_)) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 1, table_); + } + if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(index_)) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 2, index_); + } + for (int i = 0; i < keys_.size(); i++) { + output.writeMessage(3, keys_.get(i)); + } + if (((bitField0_ & 0x00000001) != 0)) { + output.writeMessage(5, getExpireTime()); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(table_)) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, table_); + } + if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(index_)) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, index_); + } + for (int i = 0; i < keys_.size(); i++) { + size += com.google.protobuf.CodedOutputStream.computeMessageSize(3, keys_.get(i)); + } + if (((bitField0_ & 0x00000001) != 0)) { + size += com.google.protobuf.CodedOutputStream.computeMessageSize(5, getExpireTime()); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof com.google.spanner.admin.database.v1.SplitPoints)) { + return super.equals(obj); + } + com.google.spanner.admin.database.v1.SplitPoints other = + (com.google.spanner.admin.database.v1.SplitPoints) obj; + + if (!getTable().equals(other.getTable())) return false; + if (!getIndex().equals(other.getIndex())) return false; + if (!getKeysList().equals(other.getKeysList())) return false; + if (hasExpireTime() != other.hasExpireTime()) return false; + if (hasExpireTime()) { + if (!getExpireTime().equals(other.getExpireTime())) return false; + } + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + TABLE_FIELD_NUMBER; + hash = (53 * hash) + getTable().hashCode(); + hash = (37 * hash) + INDEX_FIELD_NUMBER; + hash = (53 * hash) + getIndex().hashCode(); + if (getKeysCount() > 0) { + hash = (37 * hash) + KEYS_FIELD_NUMBER; + hash = (53 * hash) + getKeysList().hashCode(); + } + if (hasExpireTime()) { + hash = (37 * hash) + EXPIRE_TIME_FIELD_NUMBER; + hash = (53 * hash) + getExpireTime().hashCode(); + } + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static com.google.spanner.admin.database.v1.SplitPoints parseFrom(java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + + public static com.google.spanner.admin.database.v1.SplitPoints parseFrom( + java.nio.ByteBuffer data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + + public static com.google.spanner.admin.database.v1.SplitPoints parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + + public static com.google.spanner.admin.database.v1.SplitPoints parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + + public static com.google.spanner.admin.database.v1.SplitPoints parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + + public static com.google.spanner.admin.database.v1.SplitPoints parseFrom( + byte[] data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + + public static com.google.spanner.admin.database.v1.SplitPoints parseFrom( + java.io.InputStream input) throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input); + } + + public static com.google.spanner.admin.database.v1.SplitPoints parseFrom( + java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseWithIOException( + PARSER, input, extensionRegistry); + } + + public static com.google.spanner.admin.database.v1.SplitPoints parseDelimitedFrom( + java.io.InputStream input) throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException(PARSER, input); + } + + public static com.google.spanner.admin.database.v1.SplitPoints parseDelimitedFrom( + java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException( + PARSER, input, extensionRegistry); + } + + public static com.google.spanner.admin.database.v1.SplitPoints parseFrom( + com.google.protobuf.CodedInputStream input) throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input); + } + + public static com.google.spanner.admin.database.v1.SplitPoints parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseWithIOException( + PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { + return newBuilder(); + } + + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + + public static Builder newBuilder(com.google.spanner.admin.database.v1.SplitPoints prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType(com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * + * + *
+   * The split points of a table/index.
+   * 
+ * + * Protobuf type {@code google.spanner.admin.database.v1.SplitPoints} + */ + public static final class Builder extends com.google.protobuf.GeneratedMessageV3.Builder + implements + // @@protoc_insertion_point(builder_implements:google.spanner.admin.database.v1.SplitPoints) + com.google.spanner.admin.database.v1.SplitPointsOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { + return com.google.spanner.admin.database.v1.SpannerDatabaseAdminProto + .internal_static_google_spanner_admin_database_v1_SplitPoints_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return com.google.spanner.admin.database.v1.SpannerDatabaseAdminProto + .internal_static_google_spanner_admin_database_v1_SplitPoints_fieldAccessorTable + .ensureFieldAccessorsInitialized( + com.google.spanner.admin.database.v1.SplitPoints.class, + com.google.spanner.admin.database.v1.SplitPoints.Builder.class); + } + + // Construct using com.google.spanner.admin.database.v1.SplitPoints.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder(com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders) { + getKeysFieldBuilder(); + getExpireTimeFieldBuilder(); + } + } + + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + table_ = ""; + index_ = ""; + if (keysBuilder_ == null) { + keys_ = java.util.Collections.emptyList(); + } else { + keys_ = null; + keysBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000004); + expireTime_ = null; + if (expireTimeBuilder_ != null) { + expireTimeBuilder_.dispose(); + expireTimeBuilder_ = null; + } + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor getDescriptorForType() { + return com.google.spanner.admin.database.v1.SpannerDatabaseAdminProto + .internal_static_google_spanner_admin_database_v1_SplitPoints_descriptor; + } + + @java.lang.Override + public com.google.spanner.admin.database.v1.SplitPoints getDefaultInstanceForType() { + return com.google.spanner.admin.database.v1.SplitPoints.getDefaultInstance(); + } + + @java.lang.Override + public com.google.spanner.admin.database.v1.SplitPoints build() { + com.google.spanner.admin.database.v1.SplitPoints result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public com.google.spanner.admin.database.v1.SplitPoints buildPartial() { + com.google.spanner.admin.database.v1.SplitPoints result = + new com.google.spanner.admin.database.v1.SplitPoints(this); + buildPartialRepeatedFields(result); + if (bitField0_ != 0) { + buildPartial0(result); + } + onBuilt(); + return result; + } + + private void buildPartialRepeatedFields( + com.google.spanner.admin.database.v1.SplitPoints result) { + if (keysBuilder_ == null) { + if (((bitField0_ & 0x00000004) != 0)) { + keys_ = java.util.Collections.unmodifiableList(keys_); + bitField0_ = (bitField0_ & ~0x00000004); + } + result.keys_ = keys_; + } else { + result.keys_ = keysBuilder_.build(); + } + } + + private void buildPartial0(com.google.spanner.admin.database.v1.SplitPoints result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.table_ = table_; + } + if (((from_bitField0_ & 0x00000002) != 0)) { + result.index_ = index_; + } + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000008) != 0)) { + result.expireTime_ = expireTimeBuilder_ == null ? expireTime_ : expireTimeBuilder_.build(); + to_bitField0_ |= 0x00000001; + } + result.bitField0_ |= to_bitField0_; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, java.lang.Object value) { + return super.setField(field, value); + } + + @java.lang.Override + public Builder clearField(com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + + @java.lang.Override + public Builder clearOneof(com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, java.lang.Object value) { + return super.addRepeatedField(field, value); + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof com.google.spanner.admin.database.v1.SplitPoints) { + return mergeFrom((com.google.spanner.admin.database.v1.SplitPoints) other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(com.google.spanner.admin.database.v1.SplitPoints other) { + if (other == com.google.spanner.admin.database.v1.SplitPoints.getDefaultInstance()) + return this; + if (!other.getTable().isEmpty()) { + table_ = other.table_; + bitField0_ |= 0x00000001; + onChanged(); + } + if (!other.getIndex().isEmpty()) { + index_ = other.index_; + bitField0_ |= 0x00000002; + onChanged(); + } + if (keysBuilder_ == null) { + if (!other.keys_.isEmpty()) { + if (keys_.isEmpty()) { + keys_ = other.keys_; + bitField0_ = (bitField0_ & ~0x00000004); + } else { + ensureKeysIsMutable(); + keys_.addAll(other.keys_); + } + onChanged(); + } + } else { + if (!other.keys_.isEmpty()) { + if (keysBuilder_.isEmpty()) { + keysBuilder_.dispose(); + keysBuilder_ = null; + keys_ = other.keys_; + bitField0_ = (bitField0_ & ~0x00000004); + keysBuilder_ = + com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders + ? getKeysFieldBuilder() + : null; + } else { + keysBuilder_.addAllMessages(other.keys_); + } + } + } + if (other.hasExpireTime()) { + mergeExpireTime(other.getExpireTime()); + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: + { + table_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; + break; + } // case 10 + case 18: + { + index_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000002; + break; + } // case 18 + case 26: + { + com.google.spanner.admin.database.v1.SplitPoints.Key m = + input.readMessage( + com.google.spanner.admin.database.v1.SplitPoints.Key.parser(), + extensionRegistry); + if (keysBuilder_ == null) { + ensureKeysIsMutable(); + keys_.add(m); + } else { + keysBuilder_.addMessage(m); + } + break; + } // case 26 + case 42: + { + input.readMessage(getExpireTimeFieldBuilder().getBuilder(), extensionRegistry); + bitField0_ |= 0x00000008; + break; + } // case 42 + default: + { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + + private int bitField0_; + + private java.lang.Object table_ = ""; + /** + * + * + *
+     * The table to split.
+     * 
+ * + * string table = 1; + * + * @return The table. + */ + public java.lang.String getTable() { + java.lang.Object ref = table_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + table_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * + * + *
+     * The table to split.
+     * 
+ * + * string table = 1; + * + * @return The bytes for table. + */ + public com.google.protobuf.ByteString getTableBytes() { + java.lang.Object ref = table_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8((java.lang.String) ref); + table_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * + * + *
+     * The table to split.
+     * 
+ * + * string table = 1; + * + * @param value The table to set. + * @return This builder for chaining. + */ + public Builder setTable(java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + table_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + * + * + *
+     * The table to split.
+     * 
+ * + * string table = 1; + * + * @return This builder for chaining. + */ + public Builder clearTable() { + table_ = getDefaultInstance().getTable(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + * + * + *
+     * The table to split.
+     * 
+ * + * string table = 1; + * + * @param value The bytes for table to set. + * @return This builder for chaining. + */ + public Builder setTableBytes(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + checkByteStringIsUtf8(value); + table_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + + private java.lang.Object index_ = ""; + /** + * + * + *
+     * The index to split.
+     * If specified, the `table` field must refer to the index's base table.
+     * 
+ * + * string index = 2; + * + * @return The index. + */ + public java.lang.String getIndex() { + java.lang.Object ref = index_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + index_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * + * + *
+     * The index to split.
+     * If specified, the `table` field must refer to the index's base table.
+     * 
+ * + * string index = 2; + * + * @return The bytes for index. + */ + public com.google.protobuf.ByteString getIndexBytes() { + java.lang.Object ref = index_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8((java.lang.String) ref); + index_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * + * + *
+     * The index to split.
+     * If specified, the `table` field must refer to the index's base table.
+     * 
+ * + * string index = 2; + * + * @param value The index to set. + * @return This builder for chaining. + */ + public Builder setIndex(java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + index_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + /** + * + * + *
+     * The index to split.
+     * If specified, the `table` field must refer to the index's base table.
+     * 
+ * + * string index = 2; + * + * @return This builder for chaining. + */ + public Builder clearIndex() { + index_ = getDefaultInstance().getIndex(); + bitField0_ = (bitField0_ & ~0x00000002); + onChanged(); + return this; + } + /** + * + * + *
+     * The index to split.
+     * If specified, the `table` field must refer to the index's base table.
+     * 
+ * + * string index = 2; + * + * @param value The bytes for index to set. + * @return This builder for chaining. + */ + public Builder setIndexBytes(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + checkByteStringIsUtf8(value); + index_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + + private java.util.List keys_ = + java.util.Collections.emptyList(); + + private void ensureKeysIsMutable() { + if (!((bitField0_ & 0x00000004) != 0)) { + keys_ = + new java.util.ArrayList(keys_); + bitField0_ |= 0x00000004; + } + } + + private com.google.protobuf.RepeatedFieldBuilderV3< + com.google.spanner.admin.database.v1.SplitPoints.Key, + com.google.spanner.admin.database.v1.SplitPoints.Key.Builder, + com.google.spanner.admin.database.v1.SplitPoints.KeyOrBuilder> + keysBuilder_; + + /** + * + * + *
+     * Required. The list of split keys, i.e., the split boundaries.
+     * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints.Key keys = 3 [(.google.api.field_behavior) = REQUIRED]; + * + */ + public java.util.List getKeysList() { + if (keysBuilder_ == null) { + return java.util.Collections.unmodifiableList(keys_); + } else { + return keysBuilder_.getMessageList(); + } + } + /** + * + * + *
+     * Required. The list of split keys, i.e., the split boundaries.
+     * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints.Key keys = 3 [(.google.api.field_behavior) = REQUIRED]; + * + */ + public int getKeysCount() { + if (keysBuilder_ == null) { + return keys_.size(); + } else { + return keysBuilder_.getCount(); + } + } + /** + * + * + *
+     * Required. The list of split keys, i.e., the split boundaries.
+     * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints.Key keys = 3 [(.google.api.field_behavior) = REQUIRED]; + * + */ + public com.google.spanner.admin.database.v1.SplitPoints.Key getKeys(int index) { + if (keysBuilder_ == null) { + return keys_.get(index); + } else { + return keysBuilder_.getMessage(index); + } + } + /** + * + * + *
+     * Required. The list of split keys, i.e., the split boundaries.
+     * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints.Key keys = 3 [(.google.api.field_behavior) = REQUIRED]; + * + */ + public Builder setKeys(int index, com.google.spanner.admin.database.v1.SplitPoints.Key value) { + if (keysBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureKeysIsMutable(); + keys_.set(index, value); + onChanged(); + } else { + keysBuilder_.setMessage(index, value); + } + return this; + } + /** + * + * + *
+     * Required. The list of split keys, i.e., the split boundaries.
+     * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints.Key keys = 3 [(.google.api.field_behavior) = REQUIRED]; + * + */ + public Builder setKeys( + int index, com.google.spanner.admin.database.v1.SplitPoints.Key.Builder builderForValue) { + if (keysBuilder_ == null) { + ensureKeysIsMutable(); + keys_.set(index, builderForValue.build()); + onChanged(); + } else { + keysBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + * + * + *
+     * Required. The list of split keys, i.e., the split boundaries.
+     * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints.Key keys = 3 [(.google.api.field_behavior) = REQUIRED]; + * + */ + public Builder addKeys(com.google.spanner.admin.database.v1.SplitPoints.Key value) { + if (keysBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureKeysIsMutable(); + keys_.add(value); + onChanged(); + } else { + keysBuilder_.addMessage(value); + } + return this; + } + /** + * + * + *
+     * Required. The list of split keys, i.e., the split boundaries.
+     * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints.Key keys = 3 [(.google.api.field_behavior) = REQUIRED]; + * + */ + public Builder addKeys(int index, com.google.spanner.admin.database.v1.SplitPoints.Key value) { + if (keysBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureKeysIsMutable(); + keys_.add(index, value); + onChanged(); + } else { + keysBuilder_.addMessage(index, value); + } + return this; + } + /** + * + * + *
+     * Required. The list of split keys, i.e., the split boundaries.
+     * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints.Key keys = 3 [(.google.api.field_behavior) = REQUIRED]; + * + */ + public Builder addKeys( + com.google.spanner.admin.database.v1.SplitPoints.Key.Builder builderForValue) { + if (keysBuilder_ == null) { + ensureKeysIsMutable(); + keys_.add(builderForValue.build()); + onChanged(); + } else { + keysBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + * + * + *
+     * Required. The list of split keys, i.e., the split boundaries.
+     * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints.Key keys = 3 [(.google.api.field_behavior) = REQUIRED]; + * + */ + public Builder addKeys( + int index, com.google.spanner.admin.database.v1.SplitPoints.Key.Builder builderForValue) { + if (keysBuilder_ == null) { + ensureKeysIsMutable(); + keys_.add(index, builderForValue.build()); + onChanged(); + } else { + keysBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + * + * + *
+     * Required. The list of split keys, i.e., the split boundaries.
+     * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints.Key keys = 3 [(.google.api.field_behavior) = REQUIRED]; + * + */ + public Builder addAllKeys( + java.lang.Iterable values) { + if (keysBuilder_ == null) { + ensureKeysIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll(values, keys_); + onChanged(); + } else { + keysBuilder_.addAllMessages(values); + } + return this; + } + /** + * + * + *
+     * Required. The list of split keys, i.e., the split boundaries.
+     * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints.Key keys = 3 [(.google.api.field_behavior) = REQUIRED]; + * + */ + public Builder clearKeys() { + if (keysBuilder_ == null) { + keys_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000004); + onChanged(); + } else { + keysBuilder_.clear(); + } + return this; + } + /** + * + * + *
+     * Required. The list of split keys, i.e., the split boundaries.
+     * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints.Key keys = 3 [(.google.api.field_behavior) = REQUIRED]; + * + */ + public Builder removeKeys(int index) { + if (keysBuilder_ == null) { + ensureKeysIsMutable(); + keys_.remove(index); + onChanged(); + } else { + keysBuilder_.remove(index); + } + return this; + } + /** + * + * + *
+     * Required. The list of split keys, i.e., the split boundaries.
+     * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints.Key keys = 3 [(.google.api.field_behavior) = REQUIRED]; + * + */ + public com.google.spanner.admin.database.v1.SplitPoints.Key.Builder getKeysBuilder(int index) { + return getKeysFieldBuilder().getBuilder(index); + } + /** + * + * + *
+     * Required. The list of split keys, i.e., the split boundaries.
+     * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints.Key keys = 3 [(.google.api.field_behavior) = REQUIRED]; + * + */ + public com.google.spanner.admin.database.v1.SplitPoints.KeyOrBuilder getKeysOrBuilder( + int index) { + if (keysBuilder_ == null) { + return keys_.get(index); + } else { + return keysBuilder_.getMessageOrBuilder(index); + } + } + /** + * + * + *
+     * Required. The list of split keys, i.e., the split boundaries.
+     * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints.Key keys = 3 [(.google.api.field_behavior) = REQUIRED]; + * + */ + public java.util.List + getKeysOrBuilderList() { + if (keysBuilder_ != null) { + return keysBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(keys_); + } + } + /** + * + * + *
+     * Required. The list of split keys, i.e., the split boundaries.
+     * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints.Key keys = 3 [(.google.api.field_behavior) = REQUIRED]; + * + */ + public com.google.spanner.admin.database.v1.SplitPoints.Key.Builder addKeysBuilder() { + return getKeysFieldBuilder() + .addBuilder(com.google.spanner.admin.database.v1.SplitPoints.Key.getDefaultInstance()); + } + /** + * + * + *
+     * Required. The list of split keys, i.e., the split boundaries.
+     * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints.Key keys = 3 [(.google.api.field_behavior) = REQUIRED]; + * + */ + public com.google.spanner.admin.database.v1.SplitPoints.Key.Builder addKeysBuilder(int index) { + return getKeysFieldBuilder() + .addBuilder( + index, com.google.spanner.admin.database.v1.SplitPoints.Key.getDefaultInstance()); + } + /** + * + * + *
+     * Required. The list of split keys, i.e., the split boundaries.
+     * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints.Key keys = 3 [(.google.api.field_behavior) = REQUIRED]; + * + */ + public java.util.List + getKeysBuilderList() { + return getKeysFieldBuilder().getBuilderList(); + } + + private com.google.protobuf.RepeatedFieldBuilderV3< + com.google.spanner.admin.database.v1.SplitPoints.Key, + com.google.spanner.admin.database.v1.SplitPoints.Key.Builder, + com.google.spanner.admin.database.v1.SplitPoints.KeyOrBuilder> + getKeysFieldBuilder() { + if (keysBuilder_ == null) { + keysBuilder_ = + new com.google.protobuf.RepeatedFieldBuilderV3< + com.google.spanner.admin.database.v1.SplitPoints.Key, + com.google.spanner.admin.database.v1.SplitPoints.Key.Builder, + com.google.spanner.admin.database.v1.SplitPoints.KeyOrBuilder>( + keys_, ((bitField0_ & 0x00000004) != 0), getParentForChildren(), isClean()); + keys_ = null; + } + return keysBuilder_; + } + + private com.google.protobuf.Timestamp expireTime_; + private com.google.protobuf.SingleFieldBuilderV3< + com.google.protobuf.Timestamp, + com.google.protobuf.Timestamp.Builder, + com.google.protobuf.TimestampOrBuilder> + expireTimeBuilder_; + /** + * + * + *
+     * Optional. The expiration timestamp of the split points.
+     * A timestamp in the past means immediate expiration.
+     * The maximum value can be 30 days in the future.
+     * Defaults to 10 days in the future if not specified.
+     * 
+ * + * .google.protobuf.Timestamp expire_time = 5 [(.google.api.field_behavior) = OPTIONAL]; + * + * + * @return Whether the expireTime field is set. + */ + public boolean hasExpireTime() { + return ((bitField0_ & 0x00000008) != 0); + } + /** + * + * + *
+     * Optional. The expiration timestamp of the split points.
+     * A timestamp in the past means immediate expiration.
+     * The maximum value can be 30 days in the future.
+     * Defaults to 10 days in the future if not specified.
+     * 
+ * + * .google.protobuf.Timestamp expire_time = 5 [(.google.api.field_behavior) = OPTIONAL]; + * + * + * @return The expireTime. + */ + public com.google.protobuf.Timestamp getExpireTime() { + if (expireTimeBuilder_ == null) { + return expireTime_ == null + ? com.google.protobuf.Timestamp.getDefaultInstance() + : expireTime_; + } else { + return expireTimeBuilder_.getMessage(); + } + } + /** + * + * + *
+     * Optional. The expiration timestamp of the split points.
+     * A timestamp in the past means immediate expiration.
+     * The maximum value can be 30 days in the future.
+     * Defaults to 10 days in the future if not specified.
+     * 
+ * + * .google.protobuf.Timestamp expire_time = 5 [(.google.api.field_behavior) = OPTIONAL]; + * + */ + public Builder setExpireTime(com.google.protobuf.Timestamp value) { + if (expireTimeBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + expireTime_ = value; + } else { + expireTimeBuilder_.setMessage(value); + } + bitField0_ |= 0x00000008; + onChanged(); + return this; + } + /** + * + * + *
+     * Optional. The expiration timestamp of the split points.
+     * A timestamp in the past means immediate expiration.
+     * The maximum value can be 30 days in the future.
+     * Defaults to 10 days in the future if not specified.
+     * 
+ * + * .google.protobuf.Timestamp expire_time = 5 [(.google.api.field_behavior) = OPTIONAL]; + * + */ + public Builder setExpireTime(com.google.protobuf.Timestamp.Builder builderForValue) { + if (expireTimeBuilder_ == null) { + expireTime_ = builderForValue.build(); + } else { + expireTimeBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000008; + onChanged(); + return this; + } + /** + * + * + *
+     * Optional. The expiration timestamp of the split points.
+     * A timestamp in the past means immediate expiration.
+     * The maximum value can be 30 days in the future.
+     * Defaults to 10 days in the future if not specified.
+     * 
+ * + * .google.protobuf.Timestamp expire_time = 5 [(.google.api.field_behavior) = OPTIONAL]; + * + */ + public Builder mergeExpireTime(com.google.protobuf.Timestamp value) { + if (expireTimeBuilder_ == null) { + if (((bitField0_ & 0x00000008) != 0) + && expireTime_ != null + && expireTime_ != com.google.protobuf.Timestamp.getDefaultInstance()) { + getExpireTimeBuilder().mergeFrom(value); + } else { + expireTime_ = value; + } + } else { + expireTimeBuilder_.mergeFrom(value); + } + if (expireTime_ != null) { + bitField0_ |= 0x00000008; + onChanged(); + } + return this; + } + /** + * + * + *
+     * Optional. The expiration timestamp of the split points.
+     * A timestamp in the past means immediate expiration.
+     * The maximum value can be 30 days in the future.
+     * Defaults to 10 days in the future if not specified.
+     * 
+ * + * .google.protobuf.Timestamp expire_time = 5 [(.google.api.field_behavior) = OPTIONAL]; + * + */ + public Builder clearExpireTime() { + bitField0_ = (bitField0_ & ~0x00000008); + expireTime_ = null; + if (expireTimeBuilder_ != null) { + expireTimeBuilder_.dispose(); + expireTimeBuilder_ = null; + } + onChanged(); + return this; + } + /** + * + * + *
+     * Optional. The expiration timestamp of the split points.
+     * A timestamp in the past means immediate expiration.
+     * The maximum value can be 30 days in the future.
+     * Defaults to 10 days in the future if not specified.
+     * 
+ * + * .google.protobuf.Timestamp expire_time = 5 [(.google.api.field_behavior) = OPTIONAL]; + * + */ + public com.google.protobuf.Timestamp.Builder getExpireTimeBuilder() { + bitField0_ |= 0x00000008; + onChanged(); + return getExpireTimeFieldBuilder().getBuilder(); + } + /** + * + * + *
+     * Optional. The expiration timestamp of the split points.
+     * A timestamp in the past means immediate expiration.
+     * The maximum value can be 30 days in the future.
+     * Defaults to 10 days in the future if not specified.
+     * 
+ * + * .google.protobuf.Timestamp expire_time = 5 [(.google.api.field_behavior) = OPTIONAL]; + * + */ + public com.google.protobuf.TimestampOrBuilder getExpireTimeOrBuilder() { + if (expireTimeBuilder_ != null) { + return expireTimeBuilder_.getMessageOrBuilder(); + } else { + return expireTime_ == null + ? com.google.protobuf.Timestamp.getDefaultInstance() + : expireTime_; + } + } + /** + * + * + *
+     * Optional. The expiration timestamp of the split points.
+     * A timestamp in the past means immediate expiration.
+     * The maximum value can be 30 days in the future.
+     * Defaults to 10 days in the future if not specified.
+     * 
+ * + * .google.protobuf.Timestamp expire_time = 5 [(.google.api.field_behavior) = OPTIONAL]; + * + */ + private com.google.protobuf.SingleFieldBuilderV3< + com.google.protobuf.Timestamp, + com.google.protobuf.Timestamp.Builder, + com.google.protobuf.TimestampOrBuilder> + getExpireTimeFieldBuilder() { + if (expireTimeBuilder_ == null) { + expireTimeBuilder_ = + new com.google.protobuf.SingleFieldBuilderV3< + com.google.protobuf.Timestamp, + com.google.protobuf.Timestamp.Builder, + com.google.protobuf.TimestampOrBuilder>( + getExpireTime(), getParentForChildren(), isClean()); + expireTime_ = null; + } + return expireTimeBuilder_; + } + + @java.lang.Override + public final Builder setUnknownFields(final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + // @@protoc_insertion_point(builder_scope:google.spanner.admin.database.v1.SplitPoints) + } + + // @@protoc_insertion_point(class_scope:google.spanner.admin.database.v1.SplitPoints) + private static final com.google.spanner.admin.database.v1.SplitPoints DEFAULT_INSTANCE; + + static { + DEFAULT_INSTANCE = new com.google.spanner.admin.database.v1.SplitPoints(); + } + + public static com.google.spanner.admin.database.v1.SplitPoints getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + @java.lang.Override + public SplitPoints parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public com.google.spanner.admin.database.v1.SplitPoints getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } +} diff --git a/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/SplitPointsOrBuilder.java b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/SplitPointsOrBuilder.java new file mode 100644 index 00000000000..5da2a9c837c --- /dev/null +++ b/proto-google-cloud-spanner-admin-database-v1/src/main/java/com/google/spanner/admin/database/v1/SplitPointsOrBuilder.java @@ -0,0 +1,187 @@ +/* + * Copyright 2025 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 + * + * https://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. + */ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: google/spanner/admin/database/v1/spanner_database_admin.proto + +// Protobuf Java Version: 3.25.5 +package com.google.spanner.admin.database.v1; + +public interface SplitPointsOrBuilder + extends + // @@protoc_insertion_point(interface_extends:google.spanner.admin.database.v1.SplitPoints) + com.google.protobuf.MessageOrBuilder { + + /** + * + * + *
+   * The table to split.
+   * 
+ * + * string table = 1; + * + * @return The table. + */ + java.lang.String getTable(); + /** + * + * + *
+   * The table to split.
+   * 
+ * + * string table = 1; + * + * @return The bytes for table. + */ + com.google.protobuf.ByteString getTableBytes(); + + /** + * + * + *
+   * The index to split.
+   * If specified, the `table` field must refer to the index's base table.
+   * 
+ * + * string index = 2; + * + * @return The index. + */ + java.lang.String getIndex(); + /** + * + * + *
+   * The index to split.
+   * If specified, the `table` field must refer to the index's base table.
+   * 
+ * + * string index = 2; + * + * @return The bytes for index. + */ + com.google.protobuf.ByteString getIndexBytes(); + + /** + * + * + *
+   * Required. The list of split keys, i.e., the split boundaries.
+   * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints.Key keys = 3 [(.google.api.field_behavior) = REQUIRED]; + * + */ + java.util.List getKeysList(); + /** + * + * + *
+   * Required. The list of split keys, i.e., the split boundaries.
+   * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints.Key keys = 3 [(.google.api.field_behavior) = REQUIRED]; + * + */ + com.google.spanner.admin.database.v1.SplitPoints.Key getKeys(int index); + /** + * + * + *
+   * Required. The list of split keys, i.e., the split boundaries.
+   * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints.Key keys = 3 [(.google.api.field_behavior) = REQUIRED]; + * + */ + int getKeysCount(); + /** + * + * + *
+   * Required. The list of split keys, i.e., the split boundaries.
+   * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints.Key keys = 3 [(.google.api.field_behavior) = REQUIRED]; + * + */ + java.util.List + getKeysOrBuilderList(); + /** + * + * + *
+   * Required. The list of split keys, i.e., the split boundaries.
+   * 
+ * + * + * repeated .google.spanner.admin.database.v1.SplitPoints.Key keys = 3 [(.google.api.field_behavior) = REQUIRED]; + * + */ + com.google.spanner.admin.database.v1.SplitPoints.KeyOrBuilder getKeysOrBuilder(int index); + + /** + * + * + *
+   * Optional. The expiration timestamp of the split points.
+   * A timestamp in the past means immediate expiration.
+   * The maximum value can be 30 days in the future.
+   * Defaults to 10 days in the future if not specified.
+   * 
+ * + * .google.protobuf.Timestamp expire_time = 5 [(.google.api.field_behavior) = OPTIONAL]; + * + * + * @return Whether the expireTime field is set. + */ + boolean hasExpireTime(); + /** + * + * + *
+   * Optional. The expiration timestamp of the split points.
+   * A timestamp in the past means immediate expiration.
+   * The maximum value can be 30 days in the future.
+   * Defaults to 10 days in the future if not specified.
+   * 
+ * + * .google.protobuf.Timestamp expire_time = 5 [(.google.api.field_behavior) = OPTIONAL]; + * + * + * @return The expireTime. + */ + com.google.protobuf.Timestamp getExpireTime(); + /** + * + * + *
+   * Optional. The expiration timestamp of the split points.
+   * A timestamp in the past means immediate expiration.
+   * The maximum value can be 30 days in the future.
+   * Defaults to 10 days in the future if not specified.
+   * 
+ * + * .google.protobuf.Timestamp expire_time = 5 [(.google.api.field_behavior) = OPTIONAL]; + * + */ + com.google.protobuf.TimestampOrBuilder getExpireTimeOrBuilder(); +} diff --git a/proto-google-cloud-spanner-admin-database-v1/src/main/proto/google/spanner/admin/database/v1/spanner_database_admin.proto b/proto-google-cloud-spanner-admin-database-v1/src/main/proto/google/spanner/admin/database/v1/spanner_database_admin.proto index 5df142403e6..27e3206293e 100644 --- a/proto-google-cloud-spanner-admin-database-v1/src/main/proto/google/spanner/admin/database/v1/spanner_database_admin.proto +++ b/proto-google-cloud-spanner-admin-database-v1/src/main/proto/google/spanner/admin/database/v1/spanner_database_admin.proto @@ -25,6 +25,7 @@ import "google/iam/v1/policy.proto"; import "google/longrunning/operations.proto"; import "google/protobuf/empty.proto"; import "google/protobuf/field_mask.proto"; +import "google/protobuf/struct.proto"; import "google/protobuf/timestamp.proto"; import "google/spanner/admin/database/v1/backup.proto"; import "google/spanner/admin/database/v1/backup_schedule.proto"; @@ -425,6 +426,15 @@ service DatabaseAdmin { option (google.api.method_signature) = "parent"; } + // Adds split points to specified tables, indexes of a database. + rpc AddSplitPoints(AddSplitPointsRequest) returns (AddSplitPointsResponse) { + option (google.api.http) = { + post: "/v1/{database=projects/*/instances/*/databases/*}:addSplitPoints" + body: "*" + }; + option (google.api.method_signature) = "database,split_points"; + } + // Creates a new backup schedule. rpc CreateBackupSchedule(CreateBackupScheduleRequest) returns (BackupSchedule) { @@ -1207,3 +1217,59 @@ message ListDatabaseRolesResponse { // call to fetch more of the matching roles. string next_page_token = 2; } + +// The request for +// [AddSplitPoints][google.spanner.admin.database.v1.DatabaseAdmin.AddSplitPoints]. +message AddSplitPointsRequest { + // Required. The database on whose tables/indexes split points are to be + // added. Values are of the form + // `projects//instances//databases/`. + string database = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "spanner.googleapis.com/Database" + } + ]; + + // Required. The split points to add. + repeated SplitPoints split_points = 2 + [(google.api.field_behavior) = REQUIRED]; + + // Optional. A user-supplied tag associated with the split points. + // For example, "intital_data_load", "special_event_1". + // Defaults to "CloudAddSplitPointsAPI" if not specified. + // The length of the tag must not exceed 50 characters,else will be trimmed. + // Only valid UTF8 characters are allowed. + string initiator = 3 [(google.api.field_behavior) = OPTIONAL]; +} + +// The response for +// [AddSplitPoints][google.spanner.admin.database.v1.DatabaseAdmin.AddSplitPoints]. +message AddSplitPointsResponse {} + +// The split points of a table/index. +message SplitPoints { + // A split key. + message Key { + // Required. The column values making up the split key. + google.protobuf.ListValue key_parts = 1 + [(google.api.field_behavior) = REQUIRED]; + } + + // The table to split. + string table = 1; + + // The index to split. + // If specified, the `table` field must refer to the index's base table. + string index = 2; + + // Required. The list of split keys, i.e., the split boundaries. + repeated Key keys = 3 [(google.api.field_behavior) = REQUIRED]; + + // Optional. The expiration timestamp of the split points. + // A timestamp in the past means immediate expiration. + // The maximum value can be 30 days in the future. + // Defaults to 10 days in the future if not specified. + google.protobuf.Timestamp expire_time = 5 + [(google.api.field_behavior) = OPTIONAL]; +} diff --git a/proto-google-cloud-spanner-admin-instance-v1/pom.xml b/proto-google-cloud-spanner-admin-instance-v1/pom.xml index cc71996744e..1c8d7b9c0a9 100644 --- a/proto-google-cloud-spanner-admin-instance-v1/pom.xml +++ b/proto-google-cloud-spanner-admin-instance-v1/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc proto-google-cloud-spanner-admin-instance-v1 - 6.86.0 + 6.87.0 proto-google-cloud-spanner-admin-instance-v1 PROTO library for proto-google-cloud-spanner-admin-instance-v1 com.google.cloud google-cloud-spanner-parent - 6.86.0 + 6.87.0 diff --git a/proto-google-cloud-spanner-executor-v1/pom.xml b/proto-google-cloud-spanner-executor-v1/pom.xml index 7ad2f03742b..31dd6136825 100644 --- a/proto-google-cloud-spanner-executor-v1/pom.xml +++ b/proto-google-cloud-spanner-executor-v1/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc proto-google-cloud-spanner-executor-v1 - 6.86.0 + 6.87.0 proto-google-cloud-spanner-executor-v1 Proto library for google-cloud-spanner com.google.cloud google-cloud-spanner-parent - 6.86.0 + 6.87.0 diff --git a/proto-google-cloud-spanner-v1/pom.xml b/proto-google-cloud-spanner-v1/pom.xml index f1b3ca9f1bd..fbe27a10d50 100644 --- a/proto-google-cloud-spanner-v1/pom.xml +++ b/proto-google-cloud-spanner-v1/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc proto-google-cloud-spanner-v1 - 6.86.0 + 6.87.0 proto-google-cloud-spanner-v1 PROTO library for proto-google-cloud-spanner-v1 com.google.cloud google-cloud-spanner-parent - 6.86.0 + 6.87.0 diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml index c8d127e2c6c..4140f77781c 100644 --- a/samples/snapshot/pom.xml +++ b/samples/snapshot/pom.xml @@ -32,7 +32,7 @@ com.google.cloud google-cloud-spanner - 6.86.0 + 6.87.0 diff --git a/samples/snippets/pom.xml b/samples/snippets/pom.xml index 7873c36902d..7b617e29306 100644 --- a/samples/snippets/pom.xml +++ b/samples/snippets/pom.xml @@ -34,7 +34,7 @@ com.google.cloud libraries-bom - 26.53.0 + 26.54.0 pom import diff --git a/samples/snippets/src/main/java/com/example/spanner/CreateInstanceWithAutoscalingConfigExample.java b/samples/snippets/src/main/java/com/example/spanner/CreateInstanceWithAutoscalingConfigExample.java index 0a6e21ea620..4d0793820af 100644 --- a/samples/snippets/src/main/java/com/example/spanner/CreateInstanceWithAutoscalingConfigExample.java +++ b/samples/snippets/src/main/java/com/example/spanner/CreateInstanceWithAutoscalingConfigExample.java @@ -24,6 +24,7 @@ import com.google.spanner.admin.instance.v1.AutoscalingConfig; import com.google.spanner.admin.instance.v1.CreateInstanceRequest; import com.google.spanner.admin.instance.v1.Instance; +import com.google.spanner.admin.instance.v1.Instance.Edition; import com.google.spanner.admin.instance.v1.InstanceConfigName; import com.google.spanner.admin.instance.v1.ProjectName; import java.util.concurrent.ExecutionException; @@ -66,6 +67,7 @@ static void createInstance(String projectId, String instanceId) { .setDisplayName(displayName) .setConfig( InstanceConfigName.of(projectId, configId).toString()) + .setEdition(Edition.ENTERPRISE) .build(); // Creates a new instance diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/archived/CreateInstanceWithAutoscalingConfigExample.java b/samples/snippets/src/main/java/com/example/spanner/admin/archived/CreateInstanceWithAutoscalingConfigExample.java index f8a683865ac..0502fba5eda 100644 --- a/samples/snippets/src/main/java/com/example/spanner/admin/archived/CreateInstanceWithAutoscalingConfigExample.java +++ b/samples/snippets/src/main/java/com/example/spanner/admin/archived/CreateInstanceWithAutoscalingConfigExample.java @@ -28,6 +28,7 @@ import com.google.cloud.spanner.SpannerOptions; import com.google.spanner.admin.instance.v1.AutoscalingConfig; import com.google.spanner.admin.instance.v1.CreateInstanceMetadata; +import com.google.spanner.admin.instance.v1.Instance.Edition; import java.util.concurrent.ExecutionException; class CreateInstanceWithAutoscalingConfigExample { @@ -62,6 +63,7 @@ static void createInstance(String projectId, String instanceId) { .setInstanceConfigId(InstanceConfigId.of(projectId, configId)) .setAutoscalingConfig(autoscalingConfig) .setDisplayName("Descriptive name") + .setEdition(Edition.ENTERPRISE) .build(); OperationFuture operation = instanceAdminClient.createInstance(instanceInfo); diff --git a/versions.txt b/versions.txt index 644d7810dcb..de3c4756dd0 100644 --- a/versions.txt +++ b/versions.txt @@ -1,13 +1,13 @@ # Format: # module:released-version:current-version -proto-google-cloud-spanner-admin-instance-v1:6.86.0:6.86.0 -proto-google-cloud-spanner-v1:6.86.0:6.86.0 -proto-google-cloud-spanner-admin-database-v1:6.86.0:6.86.0 -grpc-google-cloud-spanner-v1:6.86.0:6.86.0 -grpc-google-cloud-spanner-admin-instance-v1:6.86.0:6.86.0 -grpc-google-cloud-spanner-admin-database-v1:6.86.0:6.86.0 -google-cloud-spanner:6.86.0:6.86.0 -google-cloud-spanner-executor:6.86.0:6.86.0 -proto-google-cloud-spanner-executor-v1:6.86.0:6.86.0 -grpc-google-cloud-spanner-executor-v1:6.86.0:6.86.0 +proto-google-cloud-spanner-admin-instance-v1:6.87.0:6.87.0 +proto-google-cloud-spanner-v1:6.87.0:6.87.0 +proto-google-cloud-spanner-admin-database-v1:6.87.0:6.87.0 +grpc-google-cloud-spanner-v1:6.87.0:6.87.0 +grpc-google-cloud-spanner-admin-instance-v1:6.87.0:6.87.0 +grpc-google-cloud-spanner-admin-database-v1:6.87.0:6.87.0 +google-cloud-spanner:6.87.0:6.87.0 +google-cloud-spanner-executor:6.87.0:6.87.0 +proto-google-cloud-spanner-executor-v1:6.87.0:6.87.0 +grpc-google-cloud-spanner-executor-v1:6.87.0:6.87.0