diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 6137bef2a25..da6403b346e 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -4,5 +4,9 @@ # For syntax help see: # https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners#codeowners-syntax +# The @googleapis/api-spanner-java is the default owner for changes in this repo +* @googleapis/yoshi-java @googleapis/api-spanner-java +**/*.java @googleapis/api-spanner-java + # The java-samples-reviewers team is the default owner for samples changes samples/**/*.java @googleapis/java-samples-reviewers diff --git a/.github/readme/synth.py b/.github/readme/synth.py new file mode 100644 index 00000000000..7b48cc28d36 --- /dev/null +++ b/.github/readme/synth.py @@ -0,0 +1,19 @@ +# Copyright 2020 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. + +"""This script is used to synthesize generated the README for this library.""" + +from synthtool.languages import java + +java.custom_templates(["java_library/README.md"]) diff --git a/.github/sync-repo-settings.yaml b/.github/sync-repo-settings.yaml new file mode 100644 index 00000000000..6bddd18eac9 --- /dev/null +++ b/.github/sync-repo-settings.yaml @@ -0,0 +1,49 @@ + +# Whether or not rebase-merging is enabled on this repository. +# Defaults to `true` +rebaseMergeAllowed: false + +# Whether or not squash-merging is enabled on this repository. +# Defaults to `true` +squashMergeAllowed: true + +# Whether or not PRs are merged with a merge commit on this repository. +# Defaults to `false` +mergeCommitAllowed: false + +# Rules for master branch protection +branchProtectionRules: +# Identifies the protection rule pattern. Name of the branch to be protected. +# Defaults to `master` +- pattern: master + # Can admins overwrite branch protection. + # Defaults to `true` + isAdminEnforced: true + # Number of approving reviews required to update matching branches. + # Defaults to `1` + requiredApprovingReviewCount: 1 + # Are reviews from code owners required to update matching branches. + # Defaults to `false` + requiresCodeOwnerReviews: true + # Require up to date branches + requiresStrictStatusChecks: false + # List of required status check contexts that must pass for commits to be accepted to matching branches. + requiredStatusCheckContexts: + - "dependencies (8)" + - "dependencies (11)" + - "linkage-monitor" + - "lint" + - "clirr" + - "units (7)" + - "units (8)" + - "units (11)" + - "Kokoro - Test: Integration" + - "cla/google" +# List of explicit permissions to add (additive only) +permissionRules: +- team: yoshi-admins + permission: admin +- team: yoshi-java-admins + permission: admin +- team: yoshi-java + permission: push \ No newline at end of file diff --git a/.github/workflows/auto-release.yaml b/.github/workflows/auto-release.yaml new file mode 100644 index 00000000000..bc1554aecba --- /dev/null +++ b/.github/workflows/auto-release.yaml @@ -0,0 +1,88 @@ +on: + pull_request: +name: auto-release +jobs: + approve: + runs-on: ubuntu-latest + if: contains(github.head_ref, 'release-v') + steps: + - uses: actions/github-script@v3.0.0 + with: + github-token: ${{secrets.YOSHI_APPROVER_TOKEN}} + debug: true + script: | + // only approve PRs from release-please[bot] + if (context.payload.pull_request.user.login !== "release-please[bot]") { + return; + } + + // only approve PRs like "chore: release " + if ( !context.payload.pull_request.title.startsWith("chore: release") ) { + return; + } + + // only approve PRs with pom.xml and versions.txt changes + const filesPromise = github.pulls.listFiles.endpoint({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.payload.pull_request.number, + }); + const changed_files = await github.paginate(filesPromise) + + if ( changed_files.length < 1 ) { + console.log( "Not proceeding since PR is empty!" ) + return; + } + + if ( !changed_files.some(v => v.filename.includes("pom")) || !changed_files.some(v => v.filename.includes("versions.txt")) ) { + console.log( "PR file changes do not have pom.xml or versions.txt -- something is wrong. PTAL!" ) + return; + } + + // trigger auto-release when + // 1) it is a SNAPSHOT release (auto-generated post regular release) + // 2) there are dependency updates only + // 3) there are no open dependency update PRs in this repo (to avoid multiple releases) + if ( + context.payload.pull_request.body.includes("Fix") || + context.payload.pull_request.body.includes("Build") || + context.payload.pull_request.body.includes("Documentation") || + context.payload.pull_request.body.includes("BREAKING CHANGES") || + context.payload.pull_request.body.includes("Features") + ) { + console.log( "Not auto-releasing since it is not a dependency-update-only release." ); + return; + } + + const promise = github.pulls.list.endpoint({ + owner: context.repo.owner, + repo: context.repo.repo, + state: 'open' + }); + const open_pulls = await github.paginate(promise) + + if ( open_pulls.length > 1 && !context.payload.pull_request.title.includes("SNAPSHOT") ) { + for ( const pull of open_pulls ) { + if ( pull.title.startsWith("deps: update dependency") ) { + console.log( "Not auto-releasing yet since there are dependency update PRs open in this repo." ); + return; + } + } + } + + // approve release PR + await github.pulls.createReview({ + owner: context.repo.owner, + repo: context.repo.repo, + body: 'Rubber stamped release!', + pull_number: context.payload.pull_request.number, + event: 'APPROVE' + }); + + // attach kokoro:force-run and automerge labels + await github.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.pull_request.number, + labels: ['kokoro:force-run', 'automerge'] + }); diff --git a/.github/workflows/samples.yaml b/.github/workflows/samples.yaml index a1d50073069..c46230a78c3 100644 --- a/.github/workflows/samples.yaml +++ b/.github/workflows/samples.yaml @@ -2,7 +2,7 @@ on: pull_request: name: samples jobs: - lint: + checkstyle: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 diff --git a/.kokoro/continuous/readme.cfg b/.kokoro/continuous/readme.cfg new file mode 100644 index 00000000000..acd7dd9717e --- /dev/null +++ b/.kokoro/continuous/readme.cfg @@ -0,0 +1,55 @@ +# Copyright 2020 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. + +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "TRAMPOLINE_IMAGE" + value: "gcr.io/cloud-devrel-kokoro-resources/python-multi" +} + +env_vars: { + key: "TRAMPOLINE_BUILD_FILE" + value: "github/java-spanner/.kokoro/readme.sh" +} + +# Build logs will be here +action { + define_artifacts { + regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.log" + } +} + +# The github token is stored here. +before_action { + fetch_keystore { + keystore_resource { + keystore_config_id: 73713 + keyname: "yoshi-automation-github-key" + # TODO(theacodes): remove this after secrets have globally propagated + backend_type: FASTCONFIGPUSH + } + } +} + +# Common env vars for all repositories and builds. +env_vars: { + key: "GITHUB_USER" + value: "yoshi-automation" +} +env_vars: { + key: "GITHUB_EMAIL" + value: "yoshi-automation@google.com" +} diff --git a/.kokoro/readme.sh b/.kokoro/readme.sh new file mode 100755 index 00000000000..9b726a988b0 --- /dev/null +++ b/.kokoro/readme.sh @@ -0,0 +1,36 @@ +#!/bin/bash +# Copyright 2020 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. + +set -eo pipefail + +cd ${KOKORO_ARTIFACTS_DIR}/github/java-spanner + +# Disable buffering, so that the logs stream through. +export PYTHONUNBUFFERED=1 + +# Kokoro exposes this as a file, but the scripts expect just a plain variable. +export GITHUB_TOKEN=$(cat ${KOKORO_KEYSTORE_DIR}/73713_yoshi-automation-github-key) + +# Setup git credentials +echo "https://${GITHUB_TOKEN}:@github.com" >> ~/.git-credentials +git config --global credential.helper 'store --file ~/.git-credentials' + +python3.6 -m pip install git+https://github.com/googleapis/synthtool.git#egg=gcp-synthtool +python3.6 -m autosynth.synth \ + --repository=googleapis/java-spanner \ + --synth-file-name=.github/readme/synth.py \ + --metadata-path=.github/readme/synth.metadata \ + --pr-title="chore: regenerate README" \ + --branch-suffix="readme" \ No newline at end of file diff --git a/.repo-metadata.json b/.repo-metadata.json index c0b5c5b3963..ea1bd1f3780 100644 --- a/.repo-metadata.json +++ b/.repo-metadata.json @@ -12,5 +12,6 @@ "distribution_name": "com.google.cloud:google-cloud-spanner", "api_id": "spanner.googleapis.com", "transport": "grpc", - "requires_billing": true -} \ No newline at end of file + "requires_billing": true, + "codeowner_team": "@googleapis/api-spanner-java" +} diff --git a/CHANGELOG.md b/CHANGELOG.md index d7f478282e7..9e23ca8a48f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +### [2.0.2](https://www.github.com/googleapis/java-spanner/compare/v2.0.1...v2.0.2) (2020-10-02) + + +### Bug Fixes + +* improve numeric range checks ([#424](https://www.github.com/googleapis/java-spanner/issues/424)) ([9f26785](https://www.github.com/googleapis/java-spanner/commit/9f2678568be77e82c14632b1c7ffcaafb71e7679)) +* ResultSet#close() should not throw exceptions from session creation ([#487](https://www.github.com/googleapis/java-spanner/issues/487)) ([60fb986](https://www.github.com/googleapis/java-spanner/commit/60fb986f8b758a65e20c5315faf85fc0a935d0cc)) +* skip failing backup tests for now ([#463](https://www.github.com/googleapis/java-spanner/issues/463)) ([f037f2d](https://www.github.com/googleapis/java-spanner/commit/f037f2d28096cd173ba338a966fd16babe8c697e)) +* use credentials key in pool ([#430](https://www.github.com/googleapis/java-spanner/issues/430)) ([28103fb](https://www.github.com/googleapis/java-spanner/commit/28103fb2d6e293d20399ecdfd680be67d9d62a1c)) + + +### Dependencies + +* update dependency com.google.cloud:google-cloud-shared-dependencies to v0.10.0 ([#453](https://www.github.com/googleapis/java-spanner/issues/453)) ([e05ee0e](https://www.github.com/googleapis/java-spanner/commit/e05ee0eaa16984393b60fc47f94412e560c36ff1)) + ### [2.0.1](https://www.github.com/googleapis/java-spanner/compare/v2.0.0...v2.0.1) (2020-09-18) diff --git a/README.md b/README.md index 79217260c17..5a6157bea33 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ If you are using Maven without BOM, add this to your dependencies: com.google.cloud google-cloud-spanner - 1.60.0 + 2.0.1 ``` @@ -47,11 +47,11 @@ If you are using Maven without BOM, add this to your dependencies: If you are using Gradle, add this to your dependencies ```Groovy -compile 'com.google.cloud:google-cloud-spanner:2.0.1' +compile 'com.google.cloud:google-cloud-spanner:2.0.2' ``` If you are using SBT, add this to your dependencies ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-spanner" % "2.0.1" +libraryDependencies += "com.google.cloud" % "google-cloud-spanner" % "2.0.2" ``` [//]: # ({x-version-update-end}) diff --git a/google-cloud-spanner-bom/pom.xml b/google-cloud-spanner-bom/pom.xml index eb6d87e0488..2ed27bee666 100644 --- a/google-cloud-spanner-bom/pom.xml +++ b/google-cloud-spanner-bom/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.google.cloud google-cloud-spanner-bom - 2.0.1 + 2.0.2 pom com.google.cloud @@ -64,43 +64,43 @@ com.google.api.grpc proto-google-cloud-spanner-admin-instance-v1 - 2.0.1 + 2.0.2 com.google.api.grpc grpc-google-cloud-spanner-v1 - 2.0.1 + 2.0.2 com.google.api.grpc proto-google-cloud-spanner-v1 - 2.0.1 + 2.0.2 com.google.api.grpc proto-google-cloud-spanner-admin-database-v1 - 2.0.1 + 2.0.2 com.google.cloud google-cloud-spanner - 2.0.1 + 2.0.2 com.google.cloud google-cloud-spanner test-jar - 2.0.1 + 2.0.2 com.google.api.grpc grpc-google-cloud-spanner-admin-instance-v1 - 2.0.1 + 2.0.2 com.google.api.grpc grpc-google-cloud-spanner-admin-database-v1 - 2.0.1 + 2.0.2 diff --git a/google-cloud-spanner/pom.xml b/google-cloud-spanner/pom.xml index a135d1ff4e8..326f5e077d5 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 - 2.0.1 + 2.0.2 jar Google Cloud Spanner https://github.com/googleapis/java-spanner @@ -11,7 +11,7 @@ com.google.cloud google-cloud-spanner-parent - 2.0.1 + 2.0.2 google-cloud-spanner diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ForwardingResultSet.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ForwardingResultSet.java index 4cc0ab9b9e7..7d97fad162f 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ForwardingResultSet.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ForwardingResultSet.java @@ -61,7 +61,15 @@ public Struct getCurrentRowAsStruct() { @Override public void close() { - delegate.get().close(); + ResultSet rs; + try { + rs = delegate.get(); + } catch (Exception e) { + // Ignore any exceptions when getting the underlying result set, as that means that there is + // nothing that needs to be closed. + return; + } + rs.close(); } @Override diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPool.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPool.java index 90e399fad69..2512024117c 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPool.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPool.java @@ -1255,7 +1255,10 @@ public void close() { leakedException = null; checkedOutSessions.remove(this); } - get().close(); + PooledSession delegate = getOrNull(); + if (delegate != null) { + delegate.close(); + } } @Override @@ -1264,7 +1267,19 @@ public ApiFuture asyncClose() { leakedException = null; checkedOutSessions.remove(this); } - return get().asyncClose(); + PooledSession delegate = getOrNull(); + if (delegate != null) { + return delegate.asyncClose(); + } + return ApiFutures.immediateFuture(Empty.getDefaultInstance()); + } + + private PooledSession getOrNull() { + try { + return get(); + } catch (Throwable t) { + return null; + } } @Override diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Value.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Value.java index fa0224ff950..5ea25908a8a 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Value.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Value.java @@ -123,11 +123,37 @@ public static Value float64(double v) { } /** - * Returns a {@code NUMERIC} value. + * Returns a {@code NUMERIC} value. The valid value range for the whole component of the {@link + * BigDecimal} is from -9,999,999,999,999,999,999,999,999 to +9,999,999,999,999,999,999,999,999 + * (both inclusive), i.e. the max length of the whole component is 29 digits. The max length of + * the fractional part is 9 digits. Trailing zeros in the fractional part are not considered and + * will be lost, as Cloud Spanner does not preserve the precision of a numeric value. + * + *

If you set a numeric value of a record to for example 0.10, Cloud Spanner will return this + * value as 0.1 in subsequent queries. Use {@link BigDecimal#stripTrailingZeros()} to compare + * inserted values with retrieved values if your application might insert numeric values with + * trailing zeros. * * @param v the value, which may be null */ public static Value numeric(@Nullable BigDecimal v) { + if (v != null) { + // Cloud Spanner does not preserve the precision, so 0.1 is considered equal to 0.10. + BigDecimal test = v.stripTrailingZeros(); + if (test.scale() > 9) { + throw SpannerExceptionFactory.newSpannerException( + ErrorCode.OUT_OF_RANGE, + String.format( + "Max scale for a numeric is 9. The requested numeric has scale %d", test.scale())); + } + if (test.precision() - test.scale() > 29) { + throw SpannerExceptionFactory.newSpannerException( + ErrorCode.OUT_OF_RANGE, + String.format( + "Max precision for the whole component of a numeric is 29. The requested numeric has a whole component with precision %d", + test.precision() - test.scale())); + } + } return new NumericImpl(v == null, v); } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionOptions.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionOptions.java index 0b1bc8d21bd..379459884c7 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionOptions.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionOptions.java @@ -384,6 +384,7 @@ public static Builder newBuilder() { private final String uri; private final String credentialsUrl; private final String oauthToken; + private final Credentials fixedCredentials; private final boolean usePlainText; private final String host; @@ -413,6 +414,7 @@ private ConnectionOptions(Builder builder) { builder.credentialsUrl != null ? builder.credentialsUrl : parseCredentials(builder.uri); this.oauthToken = builder.oauthToken != null ? builder.oauthToken : parseOAuthToken(builder.uri); + this.fixedCredentials = builder.credentials; // Check that not both credentials and an OAuth token have been specified. Preconditions.checkArgument( (builder.credentials == null && this.credentialsUrl == null) || this.oauthToken == null, @@ -441,11 +443,10 @@ private ConnectionOptions(Builder builder) { this.credentials = NoCredentials.getInstance(); } else if (this.oauthToken != null) { this.credentials = new GoogleCredentials(new AccessToken(oauthToken, null)); + } else if (this.fixedCredentials != null) { + this.credentials = fixedCredentials; } else { - this.credentials = - builder.credentials == null - ? getCredentialsService().createCredentials(this.credentialsUrl) - : builder.credentials; + this.credentials = getCredentialsService().createCredentials(this.credentialsUrl); } String numChannelsValue = parseNumChannels(builder.uri); if (numChannelsValue != null) { @@ -593,6 +594,14 @@ public String getCredentialsUrl() { return credentialsUrl; } + String getOAuthToken() { + return this.oauthToken; + } + + Credentials getFixedCredentials() { + return this.fixedCredentials; + } + /** The {@link SessionPoolOptions} of this {@link ConnectionOptions}. */ public SessionPoolOptions getSessionPoolOptions() { return sessionPoolOptions; diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/SpannerPool.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/SpannerPool.java index 7116bc17f35..ecf13cd399f 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/SpannerPool.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/SpannerPool.java @@ -17,7 +17,6 @@ package com.google.cloud.spanner.connection; import com.google.api.core.ApiFunction; -import com.google.auth.Credentials; import com.google.cloud.NoCredentials; import com.google.cloud.spanner.ErrorCode; import com.google.cloud.spanner.SessionPoolOptions; @@ -28,8 +27,11 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.base.MoreObjects; import com.google.common.base.Preconditions; +import com.google.common.base.Predicates; +import com.google.common.collect.Iterables; import io.grpc.ManagedChannelBuilder; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -108,10 +110,38 @@ public void run() { } } + static class CredentialsKey { + static final Object DEFAULT_CREDENTIALS_KEY = new Object(); + final Object key; + + static CredentialsKey create(ConnectionOptions options) { + return new CredentialsKey( + Iterables.find( + Arrays.asList( + options.getOAuthToken(), + options.getFixedCredentials(), + options.getCredentialsUrl(), + DEFAULT_CREDENTIALS_KEY), + Predicates.notNull())); + } + + private CredentialsKey(Object key) { + this.key = Preconditions.checkNotNull(key); + } + + public int hashCode() { + return key.hashCode(); + } + + public boolean equals(Object o) { + return (o instanceof CredentialsKey && Objects.equals(((CredentialsKey) o).key, this.key)); + } + } + static class SpannerPoolKey { private final String host; private final String projectId; - private final Credentials credentials; + private final CredentialsKey credentialsKey; private final SessionPoolOptions sessionPoolOptions; private final Integer numChannels; private final boolean usePlainText; @@ -124,7 +154,7 @@ private static SpannerPoolKey of(ConnectionOptions options) { private SpannerPoolKey(ConnectionOptions options) { this.host = options.getHost(); this.projectId = options.getProjectId(); - this.credentials = options.getCredentials(); + this.credentialsKey = CredentialsKey.create(options); this.sessionPoolOptions = options.getSessionPoolOptions(); this.numChannels = options.getNumChannels(); this.usePlainText = options.isUsePlainText(); @@ -139,7 +169,7 @@ public boolean equals(Object o) { SpannerPoolKey other = (SpannerPoolKey) o; return Objects.equals(this.host, other.host) && Objects.equals(this.projectId, other.projectId) - && Objects.equals(this.credentials, other.credentials) + && Objects.equals(this.credentialsKey, other.credentialsKey) && Objects.equals(this.sessionPoolOptions, other.sessionPoolOptions) && Objects.equals(this.numChannels, other.numChannels) && Objects.equals(this.usePlainText, other.usePlainText) @@ -151,7 +181,7 @@ public int hashCode() { return Objects.hash( this.host, this.projectId, - this.credentials, + this.credentialsKey, this.sessionPoolOptions, this.numChannels, this.usePlainText, @@ -240,7 +270,7 @@ Spanner getSpanner(ConnectionOptions options, ConnectionImpl connection) { if (spanners.get(key) != null) { spanner = spanners.get(key); } else { - spanner = createSpanner(key); + spanner = createSpanner(key, options); spanners.put(key, spanner); } List registeredConnectionsForSpanner = connections.get(key); @@ -279,13 +309,13 @@ public Thread newThread(Runnable r) { @SuppressWarnings("rawtypes") @VisibleForTesting - Spanner createSpanner(SpannerPoolKey key) { + Spanner createSpanner(SpannerPoolKey key, ConnectionOptions options) { SpannerOptions.Builder builder = SpannerOptions.newBuilder(); builder .setClientLibToken(MoreObjects.firstNonNull(key.userAgent, CONNECTION_API_CLIENT_LIB_TOKEN)) .setHost(key.host) .setProjectId(key.projectId) - .setCredentials(key.credentials); + .setCredentials(options.getCredentials()); builder.setSessionPoolOption(key.sessionPoolOptions); if (key.numChannels != null) { builder.setNumChannels(key.numChannels); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DatabaseClientImplTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DatabaseClientImplTest.java index bf425556b46..8775fb1b183 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DatabaseClientImplTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DatabaseClientImplTest.java @@ -1599,4 +1599,31 @@ public Long run(TransactionContext transaction) throws Exception { } }); } + + @Test + public void testBatchCreateSessionsFailure_shouldNotPropagateToCloseMethod() { + try { + // Simulate session creation failures on the backend. + mockSpanner.setBatchCreateSessionsExecutionTime( + SimulatedExecutionTime.ofStickyException(Status.RESOURCE_EXHAUSTED.asRuntimeException())); + DatabaseClient client = + spannerWithEmptySessionPool.getDatabaseClient( + DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE)); + // This will not cause any failure as getting a session from the pool is guaranteed to be + // non-blocking, and any exceptions will be delayed until actual query execution. + ResultSet rs = client.singleUse().executeQuery(SELECT1); + try { + while (rs.next()) { + fail("Missing expected exception"); + } + } catch (SpannerException e) { + assertThat(e.getErrorCode()).isEqualTo(ErrorCode.RESOURCE_EXHAUSTED); + } finally { + // This should not cause any failures. + rs.close(); + } + } finally { + mockSpanner.setBatchCreateSessionsExecutionTime(SimulatedExecutionTime.none()); + } + } } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/GrpcResultSetTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/GrpcResultSetTest.java index 4952e179adb..de1cb74c822 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/GrpcResultSetTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/GrpcResultSetTest.java @@ -25,6 +25,7 @@ import com.google.cloud.Timestamp; import com.google.cloud.spanner.spi.v1.SpannerRpc; import com.google.common.base.Function; +import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.protobuf.ByteString; @@ -692,17 +693,26 @@ public void getBigDecimal() { consumer.onPartialResultSet( PartialResultSet.newBuilder() .setMetadata(makeMetadata(Type.struct(Type.StructField.of("f", Type.numeric())))) - .addValues(Value.numeric(BigDecimal.valueOf(Double.MIN_VALUE)).toProto()) - .addValues(Value.numeric(BigDecimal.valueOf(Double.MAX_VALUE)).toProto()) + .addValues( + Value.numeric( + new BigDecimal( + "-" + Strings.repeat("9", 29) + "." + Strings.repeat("9", 9))) + .toProto()) + .addValues( + Value.numeric( + new BigDecimal(Strings.repeat("9", 29) + "." + Strings.repeat("9", 9))) + .toProto()) .addValues(Value.numeric(BigDecimal.ZERO).toProto()) .addValues(Value.numeric(new BigDecimal("1.23456")).toProto()) .build()); consumer.onCompleted(); assertThat(resultSet.next()).isTrue(); - assertThat(resultSet.getBigDecimal(0).doubleValue()).isWithin(0.0).of(Double.MIN_VALUE); + assertThat(resultSet.getBigDecimal(0).toPlainString()) + .isEqualTo("-99999999999999999999999999999.999999999"); assertThat(resultSet.next()).isTrue(); - assertThat(resultSet.getBigDecimal(0).doubleValue()).isWithin(0.0).of(Double.MAX_VALUE); + assertThat(resultSet.getBigDecimal(0).toPlainString()) + .isEqualTo("99999999999999999999999999999.999999999"); assertThat(resultSet.next()).isTrue(); assertThat(resultSet.getBigDecimal(0)).isEqualTo(BigDecimal.ZERO); assertThat(resultSet.next()).isTrue(); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ResumableStreamIteratorTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ResumableStreamIteratorTest.java index 6c387f0d48b..ef744d31a10 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ResumableStreamIteratorTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ResumableStreamIteratorTest.java @@ -21,6 +21,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import com.google.api.client.util.BackOff; import com.google.common.collect.AbstractIterator; import com.google.common.collect.Lists; import com.google.protobuf.ByteString; @@ -29,10 +30,12 @@ import com.google.rpc.RetryInfo; import com.google.spanner.v1.PartialResultSet; import io.grpc.Metadata; +import io.grpc.Status; import io.grpc.StatusRuntimeException; import io.grpc.protobuf.ProtoUtils; import io.opencensus.trace.EndSpanOptions; import io.opencensus.trace.Span; +import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedList; @@ -79,6 +82,11 @@ static class RetryableException extends SpannerException { // OK to instantiate SpannerException directly for this unit test. super(DoNotConstructDirectly.ALLOWED, code, true, message, statusWithRetryInfo(code)); } + + RetryableException(ErrorCode code, @Nullable String message, StatusRuntimeException cause) { + // OK to instantiate SpannerException directly for this unit test. + super(DoNotConstructDirectly.ALLOWED, code, true, message, cause); + } } static class NonRetryableException extends SpannerException { @@ -220,6 +228,30 @@ public void restartWithHoldBackMidStream() { .inOrder(); } + @Test + public void retryableErrorWithoutRetryInfo() throws IOException { + BackOff backOff = mock(BackOff.class); + Mockito.when(backOff.nextBackOffMillis()).thenReturn(1L); + Whitebox.setInternalState(this.resumableStreamIterator, "backOff", backOff); + + ResultSetStream s1 = Mockito.mock(ResultSetStream.class); + Mockito.when(starter.startStream(null)).thenReturn(new ResultSetIterator(s1)); + Mockito.when(s1.next()) + .thenReturn(resultSet(ByteString.copyFromUtf8("r1"), "a")) + .thenThrow( + new RetryableException( + ErrorCode.UNAVAILABLE, "failed by test", Status.UNAVAILABLE.asRuntimeException())); + + ResultSetStream s2 = Mockito.mock(ResultSetStream.class); + Mockito.when(starter.startStream(ByteString.copyFromUtf8("r1"))) + .thenReturn(new ResultSetIterator(s2)); + Mockito.when(s2.next()) + .thenReturn(resultSet(ByteString.copyFromUtf8("r2"), "b")) + .thenReturn(null); + assertThat(consume(resumableStreamIterator)).containsExactly("a", "b").inOrder(); + verify(backOff).nextBackOffMillis(); + } + @Test public void nonRetryableError() { ResultSetStream s1 = Mockito.mock(ResultSetStream.class); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ValueTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ValueTest.java index 0288f177af5..72222f72d01 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ValueTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ValueTest.java @@ -25,6 +25,7 @@ import com.google.cloud.Date; import com.google.cloud.Timestamp; import com.google.cloud.spanner.Type.StructField; +import com.google.common.base.Strings; import com.google.common.collect.ForwardingList; import com.google.common.collect.Lists; import com.google.common.testing.EqualsTester; @@ -265,6 +266,86 @@ public void testNumericFormats() { assertThat(new BigDecimal("1e-01").toString()).isEqualTo("0.1"); } + @Test + public void numericPrecisionAndScale() { + for (long s : new long[] {1L, -1L}) { + BigDecimal sign = new BigDecimal(s); + assertThat(Value.numeric(new BigDecimal(Strings.repeat("9", 29)).multiply(sign)).toString()) + .isEqualTo((s == -1L ? "-" : "") + Strings.repeat("9", 29)); + try { + Value.numeric(new BigDecimal(Strings.repeat("9", 30)).multiply(sign)); + fail("Missing expected exception"); + } catch (SpannerException e) { + assertThat(e.getErrorCode()).isEqualTo(ErrorCode.OUT_OF_RANGE); + } + try { + Value.numeric(new BigDecimal("1" + Strings.repeat("0", 29)).multiply(sign)); + fail("Missing expected exception"); + } catch (SpannerException e) { + assertThat(e.getErrorCode()).isEqualTo(ErrorCode.OUT_OF_RANGE); + } + + assertThat( + Value.numeric(new BigDecimal("0." + Strings.repeat("9", 9)).multiply(sign)) + .toString()) + .isEqualTo((s == -1L ? "-" : "") + "0." + Strings.repeat("9", 9)); + assertThat( + Value.numeric(new BigDecimal("0.1" + Strings.repeat("0", 8)).multiply(sign)) + .toString()) + .isEqualTo((s == -1L ? "-" : "") + "0.1" + Strings.repeat("0", 8)); + // Cloud Spanner does not store precision and considers 0.1 to be equal to 0.10. + // 0.100000000000000000000000000 is therefore also a valid value, as it will be capped to 0.1. + assertThat( + Value.numeric(new BigDecimal("0.1" + Strings.repeat("0", 20)).multiply(sign)) + .toString()) + .isEqualTo((s == -1L ? "-" : "") + "0.1" + Strings.repeat("0", 20)); + try { + Value.numeric(new BigDecimal("0." + Strings.repeat("9", 10)).multiply(sign)); + fail("Missing expected exception"); + } catch (SpannerException e) { + assertThat(e.getErrorCode()).isEqualTo(ErrorCode.OUT_OF_RANGE); + } + + assertThat( + Value.numeric( + new BigDecimal(Strings.repeat("9", 29) + "." + Strings.repeat("9", 9)) + .multiply(sign)) + .toString()) + .isEqualTo( + (s == -1L ? "-" : "") + Strings.repeat("9", 29) + "." + Strings.repeat("9", 9)); + + try { + Value.numeric( + new BigDecimal(Strings.repeat("9", 30) + "." + Strings.repeat("9", 9)).multiply(sign)); + fail("Missing expected exception"); + } catch (SpannerException e) { + assertThat(e.getErrorCode()).isEqualTo(ErrorCode.OUT_OF_RANGE); + } + try { + Value.numeric( + new BigDecimal("1" + Strings.repeat("0", 29) + "." + Strings.repeat("9", 9)) + .multiply(sign)); + fail("Missing expected exception"); + } catch (SpannerException e) { + assertThat(e.getErrorCode()).isEqualTo(ErrorCode.OUT_OF_RANGE); + } + + try { + Value.numeric( + new BigDecimal(Strings.repeat("9", 29) + "." + Strings.repeat("9", 10)).multiply(sign)); + fail("Missing expected exception"); + } catch (SpannerException e) { + assertThat(e.getErrorCode()).isEqualTo(ErrorCode.OUT_OF_RANGE); + } + try { + Value.numeric(new BigDecimal("1." + Strings.repeat("9", 10)).multiply(sign)); + fail("Missing expected exception"); + } catch (SpannerException e) { + assertThat(e.getErrorCode()).isEqualTo(ErrorCode.OUT_OF_RANGE); + } + } + } + @Test public void numericNull() { Value v = Value.numeric(null); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/SpannerPoolTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/SpannerPoolTest.java index c0145203cec..3c0e9cf160e 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/SpannerPoolTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/SpannerPoolTest.java @@ -26,7 +26,6 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import com.google.auth.oauth2.GoogleCredentials; import com.google.cloud.NoCredentials; import com.google.cloud.spanner.ErrorCode; import com.google.cloud.spanner.SessionPoolOptions; @@ -34,6 +33,7 @@ import com.google.cloud.spanner.SpannerException; import com.google.cloud.spanner.connection.ConnectionImpl.LeakedConnectionException; import com.google.cloud.spanner.connection.SpannerPool.CheckAndCloseSpannersMode; +import com.google.cloud.spanner.connection.SpannerPool.SpannerPoolKey; import java.io.ByteArrayOutputStream; import java.io.OutputStream; import java.util.logging.Handler; @@ -51,13 +51,16 @@ public class SpannerPoolTest { private ConnectionImpl connection1 = mock(ConnectionImpl.class); private ConnectionImpl connection2 = mock(ConnectionImpl.class); private ConnectionImpl connection3 = mock(ConnectionImpl.class); - private GoogleCredentials credentials1 = mock(GoogleCredentials.class); - private GoogleCredentials credentials2 = mock(GoogleCredentials.class); + private String credentials1 = "credentials1"; + private String credentials2 = "credentials2"; private ConnectionOptions options1 = mock(ConnectionOptions.class); private ConnectionOptions options2 = mock(ConnectionOptions.class); private ConnectionOptions options3 = mock(ConnectionOptions.class); private ConnectionOptions options4 = mock(ConnectionOptions.class); + private ConnectionOptions options5 = mock(ConnectionOptions.class); + private ConnectionOptions options6 = mock(ConnectionOptions.class); + private SpannerPool createSubjectAndMocks() { return createSubjectAndMocks(0L); } @@ -66,21 +69,25 @@ private SpannerPool createSubjectAndMocks(long closeSpannerAfterMillisecondsUnus SpannerPool pool = new SpannerPool(closeSpannerAfterMillisecondsUnused) { @Override - Spanner createSpanner(SpannerPoolKey key) { + Spanner createSpanner(SpannerPoolKey key, ConnectionOptions options) { return mock(Spanner.class); } }; - when(options1.getCredentials()).thenReturn(credentials1); + when(options1.getCredentialsUrl()).thenReturn(credentials1); when(options1.getProjectId()).thenReturn("test-project-1"); - when(options2.getCredentials()).thenReturn(credentials2); + when(options2.getCredentialsUrl()).thenReturn(credentials2); when(options2.getProjectId()).thenReturn("test-project-1"); - when(options3.getCredentials()).thenReturn(credentials1); + when(options3.getCredentialsUrl()).thenReturn(credentials1); when(options3.getProjectId()).thenReturn("test-project-2"); - when(options4.getCredentials()).thenReturn(credentials2); + when(options4.getCredentialsUrl()).thenReturn(credentials2); when(options4.getProjectId()).thenReturn("test-project-2"); + // ConnectionOptions with no specific credentials. + when(options5.getProjectId()).thenReturn("test-project-3"); + when(options6.getProjectId()).thenReturn("test-project-3"); + return pool; } @@ -108,6 +115,10 @@ public void testGetSpanner() { spanner1 = pool.getSpanner(options4, connection1); spanner2 = pool.getSpanner(options4, connection2); assertThat(spanner1, is(equalTo(spanner2))); + // Options 5 and 6 both use default credentials. + spanner1 = pool.getSpanner(options5, connection1); + spanner2 = pool.getSpanner(options6, connection2); + assertThat(spanner1, is(equalTo(spanner2))); // assert not equal spanner1 = pool.getSpanner(options1, connection1); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITDatabaseAdminTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITDatabaseAdminTest.java index 167ab97ce4f..a683b05ab67 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITDatabaseAdminTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITDatabaseAdminTest.java @@ -63,6 +63,7 @@ import org.junit.After; import org.junit.Before; import org.junit.ClassRule; +import org.junit.Ignore; import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; @@ -263,6 +264,7 @@ public void onClose(Status status, Metadata metadata) { } } + @Ignore("Skipping until list backup operations bug is fixed: b/169431286") @Test public void testRetryNonIdempotentRpcsReturningLongRunningOperations() throws Exception { assumeFalse( diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITQueryTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITQueryTest.java index 4bb788031b9..02b2dac2d8e 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITQueryTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITQueryTest.java @@ -16,7 +16,6 @@ package com.google.cloud.spanner.it; -import static com.google.cloud.spanner.Type.StructField; import static com.google.cloud.spanner.testing.EmulatorSpannerHelper.isUsingEmulator; import static com.google.common.truth.Truth.assertThat; import static java.util.Arrays.asList; @@ -39,10 +38,13 @@ import com.google.cloud.spanner.Struct; import com.google.cloud.spanner.TimestampBound; import com.google.cloud.spanner.Type; +import com.google.cloud.spanner.Type.StructField; import com.google.cloud.spanner.Value; +import com.google.cloud.spanner.testing.EmulatorSpannerHelper; import com.google.common.base.Joiner; import com.google.common.collect.Iterables; import com.google.spanner.v1.ResultSetStats; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -269,6 +271,34 @@ public void bindDateNull() { assertThat(row.isNull(0)).isTrue(); } + @Test + public void bindNumeric() { + assumeFalse("Emulator does not yet support NUMERIC", EmulatorSpannerHelper.isUsingEmulator()); + BigDecimal b = new BigDecimal("1.1"); + Struct row = execute(Statement.newBuilder("SELECT @v").bind("v").to(b), Type.numeric()); + assertThat(row.isNull(0)).isFalse(); + assertThat(row.getBigDecimal(0)).isEqualTo(b); + } + + @Test + public void bindNumericNull() { + assumeFalse("Emulator does not yet support NUMERIC", EmulatorSpannerHelper.isUsingEmulator()); + Struct row = + execute(Statement.newBuilder("SELECT @v").bind("v").to((BigDecimal) null), Type.numeric()); + assertThat(row.isNull(0)).isTrue(); + } + + @Test + public void bindNumeric_doesNotPreservePrecision() { + assumeFalse("Emulator does not yet support NUMERIC", EmulatorSpannerHelper.isUsingEmulator()); + BigDecimal b = new BigDecimal("1.10"); + Struct row = execute(Statement.newBuilder("SELECT @v").bind("v").to(b), Type.numeric()); + assertThat(row.isNull(0)).isFalse(); + // Cloud Spanner does not store precision, and will therefore return 1.10 as 1.1. + assertThat(row.getBigDecimal(0)).isNotEqualTo(b); + assertThat(row.getBigDecimal(0)).isEqualTo(b.stripTrailingZeros()); + } + @Test public void bindBoolArray() { Struct row = @@ -494,6 +524,57 @@ public void bindDateArrayNull() { assertThat(row.isNull(0)).isTrue(); } + @Test + public void bindNumericArray() { + assumeFalse("Emulator does not yet support NUMERIC", EmulatorSpannerHelper.isUsingEmulator()); + BigDecimal b1 = new BigDecimal("3.14"); + BigDecimal b2 = new BigDecimal("6.626"); + + Struct row = + execute( + Statement.newBuilder("SELECT @v").bind("v").toNumericArray(asList(b1, b2, null)), + Type.array(Type.numeric())); + assertThat(row.isNull(0)).isFalse(); + assertThat(row.getBigDecimalList(0)).containsExactly(b1, b2, null).inOrder(); + } + + @Test + public void bindNumericArrayEmpty() { + assumeFalse("Emulator does not yet support NUMERIC", EmulatorSpannerHelper.isUsingEmulator()); + Struct row = + execute( + Statement.newBuilder("SELECT @v").bind("v").toNumericArray(Arrays.asList()), + Type.array(Type.numeric())); + assertThat(row.isNull(0)).isFalse(); + assertThat(row.getBigDecimalList(0)).containsExactly(); + } + + @Test + public void bindNumericArrayNull() { + assumeFalse("Emulator does not yet support NUMERIC", EmulatorSpannerHelper.isUsingEmulator()); + Struct row = + execute( + Statement.newBuilder("SELECT @v").bind("v").toNumericArray(null), + Type.array(Type.numeric())); + assertThat(row.isNull(0)).isTrue(); + } + + @Test + public void bindNumericArray_doesNotPreservePrecision() { + assumeFalse("Emulator does not yet support NUMERIC", EmulatorSpannerHelper.isUsingEmulator()); + BigDecimal b1 = new BigDecimal("3.14"); + BigDecimal b2 = new BigDecimal("6.626070"); + + Struct row = + execute( + Statement.newBuilder("SELECT @v").bind("v").toNumericArray(asList(b1, b2, null)), + Type.array(Type.numeric())); + assertThat(row.isNull(0)).isFalse(); + assertThat(row.getBigDecimalList(0)) + .containsExactly(b1.stripTrailingZeros(), b2.stripTrailingZeros(), null) + .inOrder(); + } + @Test public void unsupportedSelectStructValue() { assumeFalse("The emulator accepts this query", isUsingEmulator()); diff --git a/grpc-google-cloud-spanner-admin-database-v1/pom.xml b/grpc-google-cloud-spanner-admin-database-v1/pom.xml index 2d44f93769b..e845ba03e14 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 - 2.0.1 + 2.0.2 grpc-google-cloud-spanner-admin-database-v1 GRPC library for grpc-google-cloud-spanner-admin-database-v1 com.google.cloud google-cloud-spanner-parent - 2.0.1 + 2.0.2 diff --git a/grpc-google-cloud-spanner-admin-instance-v1/pom.xml b/grpc-google-cloud-spanner-admin-instance-v1/pom.xml index 1467dd6efe3..9f51e34edf6 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 - 2.0.1 + 2.0.2 grpc-google-cloud-spanner-admin-instance-v1 GRPC library for grpc-google-cloud-spanner-admin-instance-v1 com.google.cloud google-cloud-spanner-parent - 2.0.1 + 2.0.2 diff --git a/grpc-google-cloud-spanner-v1/pom.xml b/grpc-google-cloud-spanner-v1/pom.xml index 222d4b65481..af4bfaad72d 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 - 2.0.1 + 2.0.2 grpc-google-cloud-spanner-v1 GRPC library for grpc-google-cloud-spanner-v1 com.google.cloud google-cloud-spanner-parent - 2.0.1 + 2.0.2 diff --git a/pom.xml b/pom.xml index 1a18a6388e8..08df301b8a1 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.google.cloud google-cloud-spanner-parent pom - 2.0.1 + 2.0.2 Google Cloud Spanner Parent https://github.com/googleapis/java-spanner @@ -70,43 +70,43 @@ com.google.api.grpc proto-google-cloud-spanner-admin-instance-v1 - 2.0.1 + 2.0.2 com.google.api.grpc proto-google-cloud-spanner-v1 - 2.0.1 + 2.0.2 com.google.api.grpc proto-google-cloud-spanner-admin-database-v1 - 2.0.1 + 2.0.2 com.google.api.grpc grpc-google-cloud-spanner-v1 - 2.0.1 + 2.0.2 com.google.api.grpc grpc-google-cloud-spanner-admin-instance-v1 - 2.0.1 + 2.0.2 com.google.api.grpc grpc-google-cloud-spanner-admin-database-v1 - 2.0.1 + 2.0.2 com.google.cloud google-cloud-spanner - 2.0.1 + 2.0.2 com.google.cloud google-cloud-shared-dependencies - 0.9.0 + 0.10.0 pom import diff --git a/proto-google-cloud-spanner-admin-database-v1/pom.xml b/proto-google-cloud-spanner-admin-database-v1/pom.xml index f096f9a5fdf..d7105cc7b8c 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 - 2.0.1 + 2.0.2 proto-google-cloud-spanner-admin-database-v1 PROTO library for proto-google-cloud-spanner-admin-database-v1 com.google.cloud google-cloud-spanner-parent - 2.0.1 + 2.0.2 diff --git a/proto-google-cloud-spanner-admin-instance-v1/pom.xml b/proto-google-cloud-spanner-admin-instance-v1/pom.xml index 1687f79c928..4f7e0889dc9 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 - 2.0.1 + 2.0.2 proto-google-cloud-spanner-admin-instance-v1 PROTO library for proto-google-cloud-spanner-admin-instance-v1 com.google.cloud google-cloud-spanner-parent - 2.0.1 + 2.0.2 diff --git a/proto-google-cloud-spanner-v1/pom.xml b/proto-google-cloud-spanner-v1/pom.xml index 04eac082570..fd8dc8dff1a 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 - 2.0.1 + 2.0.2 proto-google-cloud-spanner-v1 PROTO library for proto-google-cloud-spanner-v1 com.google.cloud google-cloud-spanner-parent - 2.0.1 + 2.0.2 diff --git a/samples/install-without-bom/pom.xml b/samples/install-without-bom/pom.xml index c478a9cfe56..d4cca25d3ca 100644 --- a/samples/install-without-bom/pom.xml +++ b/samples/install-without-bom/pom.xml @@ -14,7 +14,7 @@ com.google.cloud.samples shared-configuration - 1.0.18 + 1.0.21 @@ -29,7 +29,7 @@ com.google.cloud google-cloud-spanner - 2.0.0 + 2.0.1 diff --git a/samples/pom.xml b/samples/pom.xml index cee7fc12505..b7ab0b1ca69 100644 --- a/samples/pom.xml +++ b/samples/pom.xml @@ -18,7 +18,7 @@ com.google.cloud.samples shared-configuration - 1.0.18 + 1.0.21 diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml index d085222e6ca..39e2fe08dd2 100644 --- a/samples/snapshot/pom.xml +++ b/samples/snapshot/pom.xml @@ -14,7 +14,7 @@ com.google.cloud.samples shared-configuration - 1.0.18 + 1.0.21 @@ -28,7 +28,7 @@ com.google.cloud google-cloud-spanner - 2.0.1 + 2.0.2 diff --git a/samples/snippets/pom.xml b/samples/snippets/pom.xml index 06c9d0fa37f..bb111adc0ee 100644 --- a/samples/snippets/pom.xml +++ b/samples/snippets/pom.xml @@ -14,7 +14,7 @@ com.google.cloud.samples shared-configuration - 1.0.18 + 1.0.21 @@ -30,7 +30,7 @@ com.google.cloud libraries-bom - 10.1.0 + 11.1.0 pom import diff --git a/synth.metadata b/synth.metadata index cfc89d52e3b..d47379a450e 100644 --- a/synth.metadata +++ b/synth.metadata @@ -4,7 +4,7 @@ "git": { "name": ".", "remote": "https://github.com/googleapis/java-spanner.git", - "sha": "b35304ede5c980c3c042b89247058cc5a4ab1488" + "sha": "81b07e31a6a63a795d904094a72a6b51e593c314" } }, { @@ -19,7 +19,7 @@ "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "019c7168faa0e56619f792693a8acdb30d6de19b" + "sha": "0762e8ee2ec21cdfc4d82020b985a104feb0453b" } } ], @@ -58,8 +58,10 @@ ".github/ISSUE_TEMPLATE/feature_request.md", ".github/ISSUE_TEMPLATE/support_request.md", ".github/PULL_REQUEST_TEMPLATE.md", + ".github/readme/synth.py", ".github/release-please.yml", ".github/trusted-contribution.yml", + ".github/workflows/auto-release.yaml", ".github/workflows/ci.yaml", ".github/workflows/samples.yaml", ".kokoro/build.bat", @@ -68,6 +70,7 @@ ".kokoro/common.cfg", ".kokoro/common.sh", ".kokoro/continuous/java8.cfg", + ".kokoro/continuous/readme.cfg", ".kokoro/dependencies.sh", ".kokoro/linkage-monitor.sh", ".kokoro/nightly/integration.cfg", @@ -89,6 +92,7 @@ ".kokoro/presubmit/linkage-monitor.cfg", ".kokoro/presubmit/lint.cfg", ".kokoro/presubmit/samples.cfg", + ".kokoro/readme.sh", ".kokoro/release/bump_snapshot.cfg", ".kokoro/release/common.cfg", ".kokoro/release/common.sh", @@ -106,7 +110,6 @@ "CODE_OF_CONDUCT.md", "CONTRIBUTING.md", "LICENSE", - "README.md", "codecov.yaml", "google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/DatabaseAdminClient.java", "google-cloud-spanner/src/main/java/com/google/cloud/spanner/admin/database/v1/DatabaseAdminSettings.java", diff --git a/versions.txt b/versions.txt index 56bbc5754f4..fc1a2c984fc 100644 --- a/versions.txt +++ b/versions.txt @@ -1,10 +1,10 @@ # Format: # module:released-version:current-version -proto-google-cloud-spanner-admin-instance-v1:2.0.1:2.0.1 -proto-google-cloud-spanner-v1:2.0.1:2.0.1 -proto-google-cloud-spanner-admin-database-v1:2.0.1:2.0.1 -grpc-google-cloud-spanner-v1:2.0.1:2.0.1 -grpc-google-cloud-spanner-admin-instance-v1:2.0.1:2.0.1 -grpc-google-cloud-spanner-admin-database-v1:2.0.1:2.0.1 -google-cloud-spanner:2.0.1:2.0.1 \ No newline at end of file +proto-google-cloud-spanner-admin-instance-v1:2.0.2:2.0.2 +proto-google-cloud-spanner-v1:2.0.2:2.0.2 +proto-google-cloud-spanner-admin-database-v1:2.0.2:2.0.2 +grpc-google-cloud-spanner-v1:2.0.2:2.0.2 +grpc-google-cloud-spanner-admin-instance-v1:2.0.2:2.0.2 +grpc-google-cloud-spanner-admin-database-v1:2.0.2:2.0.2 +google-cloud-spanner:2.0.2:2.0.2 \ No newline at end of file