From d44e468b7f29cb48bc3e12d12e00ff27a64713a7 Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Wed, 25 Jan 2023 17:38:38 +0000 Subject: [PATCH 01/10] chore(main): release 6.35.3-SNAPSHOT (#2253) :robot: I have created a release *beep* *boop* --- ### Updating meta-information for bleeding-edge SNAPSHOT release. --- This PR was generated with [Release Please](https://togithub.com/googleapis/release-please). See [documentation](https://togithub.com/googleapis/release-please#release-please). --- google-cloud-spanner-bom/pom.xml | 20 +++++++++---------- google-cloud-spanner-executor/pom.xml | 4 ++-- google-cloud-spanner/pom.xml | 4 ++-- .../pom.xml | 4 ++-- .../pom.xml | 4 ++-- grpc-google-cloud-spanner-v1/pom.xml | 4 ++-- pom.xml | 16 +++++++-------- .../pom.xml | 4 ++-- .../pom.xml | 4 ++-- proto-google-cloud-spanner-v1/pom.xml | 4 ++-- samples/snapshot/pom.xml | 2 +- versions.txt | 16 +++++++-------- 12 files changed, 43 insertions(+), 43 deletions(-) diff --git a/google-cloud-spanner-bom/pom.xml b/google-cloud-spanner-bom/pom.xml index b16915735f5..5124d6d773c 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 - 6.35.2 + 6.35.3-SNAPSHOT pom com.google.cloud @@ -53,48 +53,48 @@ com.google.cloud google-cloud-spanner - 6.35.2 + 6.35.3-SNAPSHOT com.google.cloud google-cloud-spanner-executor - 6.35.2 + 6.35.3-SNAPSHOT com.google.cloud google-cloud-spanner test-jar - 6.35.2 + 6.35.3-SNAPSHOT com.google.api.grpc grpc-google-cloud-spanner-v1 - 6.35.2 + 6.35.3-SNAPSHOT com.google.api.grpc grpc-google-cloud-spanner-admin-instance-v1 - 6.35.2 + 6.35.3-SNAPSHOT com.google.api.grpc grpc-google-cloud-spanner-admin-database-v1 - 6.35.2 + 6.35.3-SNAPSHOT com.google.api.grpc proto-google-cloud-spanner-admin-instance-v1 - 6.35.2 + 6.35.3-SNAPSHOT com.google.api.grpc proto-google-cloud-spanner-v1 - 6.35.2 + 6.35.3-SNAPSHOT com.google.api.grpc proto-google-cloud-spanner-admin-database-v1 - 6.35.2 + 6.35.3-SNAPSHOT diff --git a/google-cloud-spanner-executor/pom.xml b/google-cloud-spanner-executor/pom.xml index 05fda84f4b5..053c865e490 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.35.2 + 6.35.3-SNAPSHOT jar Google Cloud Spanner Executor com.google.cloud google-cloud-spanner-parent - 6.35.2 + 6.35.3-SNAPSHOT diff --git a/google-cloud-spanner/pom.xml b/google-cloud-spanner/pom.xml index d978d5fdeec..f54abea2d2d 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.35.2 + 6.35.3-SNAPSHOT jar Google Cloud Spanner https://github.com/googleapis/java-spanner @@ -11,7 +11,7 @@ com.google.cloud google-cloud-spanner-parent - 6.35.2 + 6.35.3-SNAPSHOT google-cloud-spanner diff --git a/grpc-google-cloud-spanner-admin-database-v1/pom.xml b/grpc-google-cloud-spanner-admin-database-v1/pom.xml index 21cc37f52c9..5c678cacc65 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.35.2 + 6.35.3-SNAPSHOT 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.35.2 + 6.35.3-SNAPSHOT diff --git a/grpc-google-cloud-spanner-admin-instance-v1/pom.xml b/grpc-google-cloud-spanner-admin-instance-v1/pom.xml index 4c628120daf..16936b0fd00 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.35.2 + 6.35.3-SNAPSHOT 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.35.2 + 6.35.3-SNAPSHOT diff --git a/grpc-google-cloud-spanner-v1/pom.xml b/grpc-google-cloud-spanner-v1/pom.xml index 2f1f41e3456..a176356829d 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.35.2 + 6.35.3-SNAPSHOT grpc-google-cloud-spanner-v1 GRPC library for grpc-google-cloud-spanner-v1 com.google.cloud google-cloud-spanner-parent - 6.35.2 + 6.35.3-SNAPSHOT diff --git a/pom.xml b/pom.xml index e8d1c8ba61a..b56c36dd17e 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.google.cloud google-cloud-spanner-parent pom - 6.35.2 + 6.35.3-SNAPSHOT Google Cloud Spanner Parent https://github.com/googleapis/java-spanner @@ -62,37 +62,37 @@ com.google.api.grpc proto-google-cloud-spanner-admin-instance-v1 - 6.35.2 + 6.35.3-SNAPSHOT com.google.api.grpc proto-google-cloud-spanner-v1 - 6.35.2 + 6.35.3-SNAPSHOT com.google.api.grpc proto-google-cloud-spanner-admin-database-v1 - 6.35.2 + 6.35.3-SNAPSHOT com.google.api.grpc grpc-google-cloud-spanner-v1 - 6.35.2 + 6.35.3-SNAPSHOT com.google.api.grpc grpc-google-cloud-spanner-admin-instance-v1 - 6.35.2 + 6.35.3-SNAPSHOT com.google.api.grpc grpc-google-cloud-spanner-admin-database-v1 - 6.35.2 + 6.35.3-SNAPSHOT com.google.cloud google-cloud-spanner - 6.35.2 + 6.35.3-SNAPSHOT diff --git a/proto-google-cloud-spanner-admin-database-v1/pom.xml b/proto-google-cloud-spanner-admin-database-v1/pom.xml index 70db4cda10b..2ffc6a76586 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.35.2 + 6.35.3-SNAPSHOT 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.35.2 + 6.35.3-SNAPSHOT diff --git a/proto-google-cloud-spanner-admin-instance-v1/pom.xml b/proto-google-cloud-spanner-admin-instance-v1/pom.xml index 67b1d1904e6..e761e4821c3 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.35.2 + 6.35.3-SNAPSHOT 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.35.2 + 6.35.3-SNAPSHOT diff --git a/proto-google-cloud-spanner-v1/pom.xml b/proto-google-cloud-spanner-v1/pom.xml index dbd76a80f25..c745d556ee3 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.35.2 + 6.35.3-SNAPSHOT proto-google-cloud-spanner-v1 PROTO library for proto-google-cloud-spanner-v1 com.google.cloud google-cloud-spanner-parent - 6.35.2 + 6.35.3-SNAPSHOT diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml index ecf69698d78..3bd15402b84 100644 --- a/samples/snapshot/pom.xml +++ b/samples/snapshot/pom.xml @@ -31,7 +31,7 @@ com.google.cloud google-cloud-spanner - 6.35.2 + 6.35.3-SNAPSHOT diff --git a/versions.txt b/versions.txt index ee89a89f575..788edb83139 100644 --- a/versions.txt +++ b/versions.txt @@ -1,11 +1,11 @@ # Format: # module:released-version:current-version -proto-google-cloud-spanner-admin-instance-v1:6.35.2:6.35.2 -proto-google-cloud-spanner-v1:6.35.2:6.35.2 -proto-google-cloud-spanner-admin-database-v1:6.35.2:6.35.2 -grpc-google-cloud-spanner-v1:6.35.2:6.35.2 -grpc-google-cloud-spanner-admin-instance-v1:6.35.2:6.35.2 -grpc-google-cloud-spanner-admin-database-v1:6.35.2:6.35.2 -google-cloud-spanner:6.35.2:6.35.2 -google-cloud-spanner-executor:6.35.2:6.35.2 +proto-google-cloud-spanner-admin-instance-v1:6.35.2:6.35.3-SNAPSHOT +proto-google-cloud-spanner-v1:6.35.2:6.35.3-SNAPSHOT +proto-google-cloud-spanner-admin-database-v1:6.35.2:6.35.3-SNAPSHOT +grpc-google-cloud-spanner-v1:6.35.2:6.35.3-SNAPSHOT +grpc-google-cloud-spanner-admin-instance-v1:6.35.2:6.35.3-SNAPSHOT +grpc-google-cloud-spanner-admin-database-v1:6.35.2:6.35.3-SNAPSHOT +google-cloud-spanner:6.35.2:6.35.3-SNAPSHOT +google-cloud-spanner-executor:6.35.2:6.35.3-SNAPSHOT From fc721c4d30de6ed9e5bc4fbbe0e1e7b79a5c7490 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?= Date: Thu, 2 Feb 2023 11:10:55 +0100 Subject: [PATCH 02/10] feat: support UNRECOGNIZED types + decode BYTES columns lazily (#2219) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * perf: decode BYTES columns lazily BYTES columns are encoded as Base64 strings. Decoding these are relatively CPU-heavy, especially for large values. Decoding them is not always necessary if the user only needs the Base64 string. Also, the internally used Guava decoder is less efficient than JDK implementations that are available from Java 8 and onwards. This change therefore delays the decoding of BYTES columns until it is actually necessary, and then uses the JDK implementation instead of the Guava version. The JDK implementation in OpenJDK 17 uses approx 1/3 of the CPU cycles of the Guava version. * feat: support unrecognized types * feat: add support for lazy bytes arrays * chore: cleanup * fix: unrecognized types with array element type should be considered unrecognized * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * chore: address review comments * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --------- Co-authored-by: Owl Bot --- README.md | 6 +- .../cloud/spanner/AbstractResultSet.java | 123 +++- .../cloud/spanner/AbstractStructReader.java | 1 - .../java/com/google/cloud/spanner/Type.java | 58 +- .../java/com/google/cloud/spanner/Value.java | 242 +++++++- .../com/google/cloud/spanner/ValueBinder.java | 15 +- .../cloud/spanner/DatabaseClientImplTest.java | 575 +++++++++++++++++- .../cloud/spanner/GrpcResultSetTest.java | 13 + .../cloud/spanner/LazyByteArrayTest.java | 52 ++ .../com/google/cloud/spanner/TypeTest.java | 81 ++- .../google/cloud/spanner/ValueBinderTest.java | 17 + .../com/google/cloud/spanner/ValueTest.java | 154 ++++- .../connection/RandomResultSetGenerator.java | 8 +- .../google/cloud/spanner/it/ITWriteTest.java | 53 ++ 14 files changed, 1341 insertions(+), 57 deletions(-) create mode 100644 google-cloud-spanner/src/test/java/com/google/cloud/spanner/LazyByteArrayTest.java diff --git a/README.md b/README.md index 1086d0b69af..93619201701 100644 --- a/README.md +++ b/README.md @@ -49,20 +49,20 @@ If you are using Maven without 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.4.0') +implementation platform('com.google.cloud:libraries-bom:26.5.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.35.1' +implementation 'com.google.cloud:google-cloud-spanner:6.35.2' ``` If you are using SBT, add this to your dependencies: ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-spanner" % "6.35.1" +libraryDependencies += "com.google.cloud" % "google-cloud-spanner" % "6.35.2" ``` ## Authentication diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbstractResultSet.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbstractResultSet.java index 789e0945e17..4d9ec1cda04 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbstractResultSet.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbstractResultSet.java @@ -32,12 +32,14 @@ import com.google.cloud.spanner.spi.v1.SpannerRpc; import com.google.cloud.spanner.v1.stub.SpannerStubSettings; import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; import com.google.common.collect.AbstractIterator; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.google.common.util.concurrent.Uninterruptibles; import com.google.protobuf.ByteString; import com.google.protobuf.ListValue; +import com.google.protobuf.NullValue; import com.google.protobuf.Value.KindCase; import com.google.spanner.v1.PartialResultSet; import com.google.spanner.v1.ResultSetMetadata; @@ -55,11 +57,13 @@ import java.math.BigDecimal; import java.util.AbstractList; import java.util.ArrayList; +import java.util.Base64; import java.util.BitSet; import java.util.Collections; import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import java.util.Objects; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; @@ -67,11 +71,15 @@ import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; import javax.annotation.Nullable; /** Implementation of {@link ResultSet}. */ abstract class AbstractResultSet extends AbstractStructReader implements ResultSet { private static final Tracer tracer = Tracing.getTracer(); + private static final com.google.protobuf.Value NULL_VALUE = + com.google.protobuf.Value.newBuilder().setNullValue(NullValue.NULL_VALUE).build(); interface Listener { /** @@ -353,6 +361,79 @@ private boolean isMergeable(KindCase kind) { } } + static final class LazyByteArray implements Serializable { + private static final Base64.Encoder ENCODER = Base64.getEncoder(); + private static final Base64.Decoder DECODER = Base64.getDecoder(); + private final String base64String; + private transient AbstractLazyInitializer byteArray; + + LazyByteArray(@Nonnull String base64String) { + this.base64String = Preconditions.checkNotNull(base64String); + this.byteArray = defaultInitializer(); + } + + LazyByteArray(@Nonnull ByteArray byteArray) { + this.base64String = + ENCODER.encodeToString(Preconditions.checkNotNull(byteArray).toByteArray()); + this.byteArray = + new AbstractLazyInitializer() { + @Override + protected ByteArray initialize() { + return byteArray; + } + }; + } + + private AbstractLazyInitializer defaultInitializer() { + return new AbstractLazyInitializer() { + @Override + protected ByteArray initialize() { + return ByteArray.copyFrom(DECODER.decode(base64String)); + } + }; + } + + private void readObject(java.io.ObjectInputStream in) + throws IOException, ClassNotFoundException { + in.defaultReadObject(); + byteArray = defaultInitializer(); + } + + ByteArray getByteArray() { + try { + return byteArray.get(); + } catch (Throwable t) { + throw SpannerExceptionFactory.asSpannerException(t); + } + } + + String getBase64String() { + return base64String; + } + + @Override + public String toString() { + return getBase64String(); + } + + @Override + public int hashCode() { + return base64String.hashCode(); + } + + @Override + public boolean equals(Object o) { + if (o instanceof LazyByteArray) { + return lazyByteArraysEqual((LazyByteArray) o); + } + return false; + } + + private boolean lazyByteArraysEqual(LazyByteArray other) { + return Objects.equals(getBase64String(), other.getBase64String()); + } + } + static class GrpcStruct extends Struct implements Serializable { private final Type type; private final List rowData; @@ -395,7 +476,11 @@ private Object writeReplace() { builder.set(fieldName).to(Value.pgJsonb((String) value)); break; case BYTES: - builder.set(fieldName).to((ByteArray) value); + builder + .set(fieldName) + .to( + Value.bytesFromBase64( + value == null ? null : ((LazyByteArray) value).getBase64String())); break; case TIMESTAMP: builder.set(fieldName).to((Timestamp) value); @@ -431,7 +516,17 @@ private Object writeReplace() { builder.set(fieldName).toPgJsonbArray((Iterable) value); break; case BYTES: - builder.set(fieldName).toBytesArray((Iterable) value); + builder + .set(fieldName) + .toBytesArrayFromBase64( + value == null + ? null + : ((List) value) + .stream() + .map( + element -> + element == null ? null : element.getBase64String()) + .collect(Collectors.toList())); break; case TIMESTAMP: builder.set(fieldName).toTimestampArray((Iterable) value); @@ -511,7 +606,7 @@ private static Object decodeValue(Type fieldType, com.google.protobuf.Value prot return proto.getStringValue(); case BYTES: checkType(fieldType, proto, KindCase.STRING_VALUE); - return ByteArray.fromBase64(proto.getStringValue()); + return new LazyByteArray(proto.getStringValue()); case TIMESTAMP: checkType(fieldType, proto, KindCase.STRING_VALUE); return Timestamp.parseTimestamp(proto.getStringValue()); @@ -526,6 +621,8 @@ private static Object decodeValue(Type fieldType, com.google.protobuf.Value prot checkType(fieldType, proto, KindCase.LIST_VALUE); ListValue structValue = proto.getListValue(); return decodeStructValue(fieldType, structValue); + case UNRECOGNIZED: + return proto; default: throw new AssertionError("Unhandled type code: " + fieldType.getCode()); } @@ -634,7 +731,11 @@ protected String getPgJsonbInternal(int columnIndex) { @Override protected ByteArray getBytesInternal(int columnIndex) { - return (ByteArray) rowData.get(columnIndex); + return getLazyBytesInternal(columnIndex).getByteArray(); + } + + LazyByteArray getLazyBytesInternal(int columnIndex) { + return (LazyByteArray) rowData.get(columnIndex); } @Override @@ -647,6 +748,10 @@ protected Date getDateInternal(int columnIndex) { return (Date) rowData.get(columnIndex); } + protected com.google.protobuf.Value getProtoValueInternal(int columnIndex) { + return (com.google.protobuf.Value) rowData.get(columnIndex); + } + @Override protected Value getValueInternal(int columnIndex) { final List structFields = getType().getStructFields(); @@ -671,13 +776,16 @@ protected Value getValueInternal(int columnIndex) { case PG_JSONB: return Value.pgJsonb(isNull ? null : getPgJsonbInternal(columnIndex)); case BYTES: - return Value.bytes(isNull ? null : getBytesInternal(columnIndex)); + return Value.internalBytes(isNull ? null : getLazyBytesInternal(columnIndex)); case TIMESTAMP: return Value.timestamp(isNull ? null : getTimestampInternal(columnIndex)); case DATE: return Value.date(isNull ? null : getDateInternal(columnIndex)); case STRUCT: return Value.struct(isNull ? null : getStructInternal(columnIndex)); + case UNRECOGNIZED: + return Value.unrecognized( + isNull ? NULL_VALUE : getProtoValueInternal(columnIndex), columnType); case ARRAY: final Type elementType = columnType.getArrayElementType(); switch (elementType.getCode()) { @@ -785,9 +893,10 @@ protected List getPgJsonbListInternal(int columnIndex) { } @Override - @SuppressWarnings("unchecked") // We know ARRAY produces a List. + @SuppressWarnings("unchecked") // We know ARRAY produces a List. protected List getBytesListInternal(int columnIndex) { - return Collections.unmodifiableList((List) rowData.get(columnIndex)); + return Lists.transform( + (List) rowData.get(columnIndex), l -> l == null ? null : l.getByteArray()); } @Override diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbstractStructReader.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbstractStructReader.java index 1e897636245..c868678f109 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbstractStructReader.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbstractStructReader.java @@ -251,7 +251,6 @@ public Date getDate(String columnName) { @Override public Value getValue(int columnIndex) { - checkNonNull(columnIndex, columnIndex); return getValueInternal(columnIndex); } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Type.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Type.java index 7ba6b9a41e4..24a81f09c6c 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Type.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Type.java @@ -33,6 +33,7 @@ import java.util.Map.Entry; import java.util.Objects; import java.util.TreeMap; +import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; @@ -71,6 +72,10 @@ public final class Type implements Serializable { private static final int AMBIGUOUS_FIELD = -1; private static final long serialVersionUID = -3076152125004114582L; + static Type unrecognized(com.google.spanner.v1.Type proto) { + return new Type(proto); + } + /** Returns the descriptor for the {@code BOOL type}. */ public static Type bool() { return TYPE_BOOL; @@ -190,6 +195,7 @@ public static Type struct(StructField... fields) { return new Type(Code.STRUCT, null, ImmutableList.copyOf(fields)); } + private final com.google.spanner.v1.Type proto; private final Code code; private final Type arrayElementType; private final ImmutableList structFields; @@ -201,9 +207,26 @@ public static Type struct(StructField... fields) { private Map fieldsByName; private Type( - Code code, + @Nonnull Code code, @Nullable Type arrayElementType, @Nullable ImmutableList structFields) { + this(null, Preconditions.checkNotNull(code), arrayElementType, structFields); + } + + private Type(@Nonnull com.google.spanner.v1.Type proto) { + this( + Preconditions.checkNotNull(proto), + Code.UNRECOGNIZED, + proto.hasArrayElementType() ? new Type(proto.getArrayElementType()) : null, + null); + } + + private Type( + com.google.spanner.v1.Type proto, + Code code, + Type arrayElementType, + ImmutableList structFields) { + this.proto = proto; this.code = code; this.arrayElementType = arrayElementType; this.structFields = structFields; @@ -211,6 +234,7 @@ private Type( /** Enumerates the categories of types. */ public enum Code { + UNRECOGNIZED(TypeCode.UNRECOGNIZED), BOOL(TypeCode.BOOL), INT64(TypeCode.INT64), NUMERIC(TypeCode.NUMERIC), @@ -258,8 +282,7 @@ TypeAnnotationCode getTypeAnnotationCode() { static Code fromProto(TypeCode typeCode, TypeAnnotationCode typeAnnotationCode) { Code code = protoToCode.get(new SimpleEntry<>(typeCode, typeAnnotationCode)); - checkArgument(code != null, "Invalid code: %s<%s>", typeCode, typeAnnotationCode); - return code; + return code == null ? Code.UNRECOGNIZED : code; } @Override @@ -325,7 +348,7 @@ public Code getCode() { * @throws IllegalStateException if {@code code() != Code.ARRAY} */ public Type getArrayElementType() { - Preconditions.checkState(code == Code.ARRAY, "Illegal call for non-ARRAY type"); + Preconditions.checkState(arrayElementType != null, "Illegal call for non-ARRAY type"); return arrayElementType; } @@ -378,8 +401,14 @@ public int getFieldIndex(String fieldName) { } void toString(StringBuilder b) { - if (code == Code.ARRAY) { - b.append("ARRAY<"); + if (code == Code.ARRAY || (proto != null && proto.hasArrayElementType())) { + if (code == Code.ARRAY) { + b.append("ARRAY<"); + } else { + // This is very unlikely to happen. It would mean that we have introduced a type that + // is not an ARRAY, but does have an array element type. + b.append("UNRECOGNIZED<"); + } arrayElementType.toString(b); b.append('>'); } else if (code == Code.STRUCT) { @@ -393,6 +422,11 @@ void toString(StringBuilder b) { f.getType().toString(b); } b.append('>'); + } else if (proto != null) { + b.append(proto.getCode().name()); + if (proto.getTypeAnnotation() != TYPE_ANNOTATION_CODE_UNSPECIFIED) { + b.append("<").append(proto.getTypeAnnotation().name()).append(">"); + } } else { b.append(code.toString()); } @@ -414,6 +448,9 @@ public boolean equals(Object o) { return false; } Type that = (Type) o; + if (proto != null) { + return Objects.equals(proto, that.proto); + } return code == that.code && Objects.equals(arrayElementType, that.arrayElementType) && Objects.equals(structFields, that.structFields); @@ -421,10 +458,16 @@ public boolean equals(Object o) { @Override public int hashCode() { + if (proto != null) { + return proto.hashCode(); + } return Objects.hash(code, arrayElementType, structFields); } com.google.spanner.v1.Type toProto() { + if (proto != null) { + return proto; + } com.google.spanner.v1.Type.Builder proto = com.google.spanner.v1.Type.newBuilder(); proto.setCode(code.getTypeCode()); proto.setTypeAnnotation(code.getTypeAnnotationCode()); @@ -490,8 +533,9 @@ static Type fromProto(com.google.spanner.v1.Type proto) { fields.add(StructField.of(name, fromProto(field.getType()))); } return struct(fields); + case UNRECOGNIZED: default: - throw new AssertionError("Unimplemented case: " + type); + return unrecognized(proto); } } } 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 82f03e859e1..c30847d6fe3 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 @@ -19,13 +19,17 @@ import com.google.cloud.ByteArray; import com.google.cloud.Date; import com.google.cloud.Timestamp; +import com.google.cloud.spanner.AbstractResultSet.LazyByteArray; import com.google.cloud.spanner.Type.Code; import com.google.common.base.Joiner; import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.protobuf.ListValue; import com.google.protobuf.NullValue; +import com.google.protobuf.Value.KindCase; +import java.io.IOException; import java.io.Serializable; import java.math.BigDecimal; import java.util.ArrayList; @@ -36,6 +40,7 @@ import java.util.List; import java.util.Objects; import java.util.stream.Collectors; +import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; @@ -95,7 +100,16 @@ public abstract class Value implements Serializable { * @param value the non-null proto value (a {@link NullValue} is allowed) */ public static Value untyped(com.google.protobuf.Value value) { - return new UntypedValueImpl(Preconditions.checkNotNull(value)); + return new ProtoBackedValueImpl(Preconditions.checkNotNull(value), null); + } + + /** Returns a generic Value backed by a protobuf value. This is used for unrecognized types. */ + static Value unrecognized(com.google.protobuf.Value value, Type type) { + Preconditions.checkArgument( + type.getCode() == Code.UNRECOGNIZED + || type.getCode() == Code.ARRAY + && type.getArrayElementType().getCode() == Code.UNRECOGNIZED); + return new ProtoBackedValueImpl(Preconditions.checkNotNull(value), type); } /** @@ -223,7 +237,22 @@ public static Value pgJsonb(@Nullable String v) { * @param v the value, which may be null */ public static Value bytes(@Nullable ByteArray v) { - return new BytesImpl(v == null, v); + return new LazyBytesImpl(v == null, v); + } + + /** + * Returns a {@code BYTES} value. + * + * @param base64String the value in Base64 encoding, which may be null. This value must be a valid + * base64 string. + */ + public static Value bytesFromBase64(@Nullable String base64String) { + return new LazyBytesImpl( + base64String == null, base64String == null ? null : new LazyByteArray(base64String)); + } + + static Value internalBytes(@Nullable LazyByteArray bytes) { + return new LazyBytesImpl(bytes == null, bytes); } /** Returns a {@code TIMESTAMP} value. */ @@ -428,7 +457,37 @@ public static Value pgJsonbArray(@Nullable Iterable v) { * {@code isNull()} is {@code true}. Individual elements may also be {@code null}. */ public static Value bytesArray(@Nullable Iterable v) { - return new BytesArrayImpl(v == null, v == null ? null : immutableCopyOf(v)); + return new LazyBytesArrayImpl(v == null, v == null ? null : byteArraysToLazyByteArrayList(v)); + } + + private static List byteArraysToLazyByteArrayList(Iterable byteArrays) { + List list = new ArrayList<>(); + for (ByteArray byteArray : byteArrays) { + list.add(byteArray == null ? null : new LazyByteArray(byteArray)); + } + return Collections.unmodifiableList(list); + } + + /** + * Returns an {@code ARRAY} value. + * + * @param base64Strings the source of element values. This may be {@code null} to produce a value + * for which {@code isNull()} is {@code true}. Individual elements may also be {@code null}. + * Non-null values must be a valid Base64 string. + */ + public static Value bytesArrayFromBase64(@Nullable Iterable base64Strings) { + return new LazyBytesArrayImpl( + base64Strings == null, + base64Strings == null ? null : base64StringsToLazyByteArrayList(base64Strings)); + } + + private static List base64StringsToLazyByteArrayList( + Iterable base64Strings) { + List list = new ArrayList<>(); + for (String base64 : base64Strings) { + list.add(base64 == null ? null : new LazyByteArray(base64)); + } + return Collections.unmodifiableList(list); } /** @@ -672,12 +731,41 @@ public String toString() { return b.toString(); } + /** + * Returns this value as a raw string representation. This is guaranteed to work for all values, + * regardless of the underlying data type, and is guaranteed not to be truncated. + * + *

Returns the string "NULL" for null values. + */ + @Nonnull + public String getAsString() { + return toString(); + } + + /** + * Returns this value as a list of raw string representations. This is guaranteed to work for all + * values, regardless of the underlying data type, and the strings are guaranteed not to be + * truncated. The method returns a singleton list for non-array values and a list containing as + * many elements as there are in the array for array values. This method can be used instead of + * the {@link #getAsString()} method if you need to quote the individual elements in an array. + * + *

Returns the string "NULL" for null values. + */ + @Nonnull + public ImmutableList getAsStringList() { + return ImmutableList.of(toString()); + } + // END OF PUBLIC API. static com.google.protobuf.Value toProto(Value value) { return value == null ? NULL_PROTO : value.toProto(); } + /** + * Appends a string representation of this value to the given builder. The string representation + * can be truncated. + */ abstract void toString(StringBuilder b); abstract com.google.protobuf.Value toProto(); @@ -948,7 +1036,7 @@ final void toString(StringBuilder b) { /** * Appends a representation of {@code this} to {@code b}. {@code this} is guaranteed to - * represent a non-null value. + * represent a non-null value. This value could be truncated if the underlying value is long. */ abstract void valueToString(StringBuilder b); @@ -1022,11 +1110,16 @@ final void checkNotNull() { } } - private static class UntypedValueImpl extends AbstractValue { + /** + * This {@link Value} implementation is backed by a generic protobuf Value instance. It is used + * for untyped Values that are created by users, and for values with an unrecognized types that + * coming from the backend. + */ + private static class ProtoBackedValueImpl extends AbstractValue { private final com.google.protobuf.Value value; - private UntypedValueImpl(com.google.protobuf.Value value) { - super(value.hasNullValue(), null); + private ProtoBackedValueImpl(com.google.protobuf.Value value, @Nullable Type type) { + super(value.hasNullValue(), type); this.value = value; } @@ -1053,6 +1146,44 @@ public double getFloat64() { return value.getNumberValue(); } + @Nonnull + @Override + public String getAsString() { + switch (value.getKindCase()) { + case NULL_VALUE: + return NULL_STRING; + case NUMBER_VALUE: + return Double.toString(value.getNumberValue()); + case STRING_VALUE: + return value.getStringValue(); + case BOOL_VALUE: + return Boolean.toString(value.getBoolValue()); + case LIST_VALUE: + return value.getListValue().getValuesList().stream() + .map(element -> Value.untyped(element).getAsString()) + .collect(Collectors.joining(",", "[", "]")); + case STRUCT_VALUE: + throw new IllegalArgumentException( + "Struct value with unrecognized type is not supported"); + case KIND_NOT_SET: + default: + throw new IllegalArgumentException("Kind of value is not set or unknown"); + } + } + + @Nonnull + @Override + public ImmutableList getAsStringList() { + if (value.getKindCase() == KindCase.LIST_VALUE) { + ImmutableList.Builder builder = ImmutableList.builder(); + value.getListValue().getValuesList().stream() + .map(v -> Value.untyped(v).getAsString()) + .forEach(builder::add); + return builder.build(); + } + return ImmutableList.of(getAsString()); + } + @Override void valueToString(StringBuilder b) { b.append(value); @@ -1065,7 +1196,7 @@ com.google.protobuf.Value valueToProto() { @Override boolean valueEquals(Value v) { - return ((UntypedValueImpl) v).value.equals(value); + return ((ProtoBackedValueImpl) v).value.equals(value); } @Override @@ -1234,6 +1365,12 @@ public String getString() { return value; } + @Nonnull + @Override + public String getAsString() { + return isNull() ? NULL_STRING : value; + } + @Override void valueToString(StringBuilder b) { if (value.length() > MAX_DEBUG_STRING_LENGTH) { @@ -1256,6 +1393,12 @@ public String getJson() { return value; } + @Nonnull + @Override + public String getAsString() { + return isNull() ? NULL_STRING : value; + } + @Override public String getString() { return getJson(); @@ -1283,6 +1426,12 @@ public String getPgJsonb() { return value; } + @Nonnull + @Override + public String getAsString() { + return isNull() ? NULL_STRING : value; + } + @Override public String getString() { return getPgJsonb(); @@ -1298,26 +1447,36 @@ void valueToString(StringBuilder b) { } } - private static class BytesImpl extends AbstractObjectValue { + private static class LazyBytesImpl extends AbstractObjectValue { - private BytesImpl(boolean isNull, ByteArray value) { + private LazyBytesImpl(boolean isNull, LazyByteArray value) { super(isNull, Type.bytes(), value); } + private LazyBytesImpl(boolean isNull, ByteArray value) { + super(isNull, Type.bytes(), value == null ? null : new LazyByteArray(value)); + } + @Override public ByteArray getBytes() { checkNotNull(); - return value; + return value.getByteArray(); } @Override com.google.protobuf.Value valueToProto() { - return com.google.protobuf.Value.newBuilder().setStringValue(value.toBase64()).build(); + return com.google.protobuf.Value.newBuilder().setStringValue(value.getBase64String()).build(); + } + + @Nonnull + @Override + public String getAsString() { + return value == null ? NULL_STRING : value.getBase64String(); } @Override void valueToString(StringBuilder b) { - b.append(value.toString()); + b.append(value == null ? null : value.toString()); } } @@ -1483,6 +1642,16 @@ List getArray() { abstract com.google.protobuf.Value getValueAsProto(int i); + @Nonnull + @Override + public ImmutableList getAsStringList() { + ImmutableList.Builder builder = ImmutableList.builder(); + for (int i = 0; i < size(); i++) { + builder.add(isElementNull(i) ? NULL_STRING : String.valueOf(getValue(i))); + } + return builder.build(); + } + @Override void valueToString(StringBuilder b) { b.append(LIST_OPEN); @@ -1661,6 +1830,16 @@ com.google.protobuf.Value valueToProto() { return com.google.protobuf.Value.newBuilder().setListValue(list).build(); } + @Nonnull + @Override + public ImmutableList getAsStringList() { + ImmutableList.Builder builder = ImmutableList.builder(); + for (T element : value) { + builder.add(element == null ? NULL_STRING : elementToString(element)); + } + return builder.build(); + } + String elementToString(T element) { return element.toString(); } @@ -1749,25 +1928,48 @@ void appendElement(StringBuilder b, String element) { } } - private static class BytesArrayImpl extends AbstractArrayValue { - private BytesArrayImpl(boolean isNull, @Nullable List values) { + private static class LazyBytesArrayImpl extends AbstractArrayValue { + private transient AbstractLazyInitializer> bytesArray = defaultInitializer(); + + private LazyBytesArrayImpl(boolean isNull, @Nullable List values) { super(isNull, Type.bytes(), values); } + private AbstractLazyInitializer> defaultInitializer() { + return new AbstractLazyInitializer>() { + @Override + protected List initialize() { + return value.stream() + .map(element -> element == null ? null : element.getByteArray()) + .collect(Collectors.toList()); + } + }; + } + + private void readObject(java.io.ObjectInputStream in) + throws IOException, ClassNotFoundException { + in.defaultReadObject(); + bytesArray = defaultInitializer(); + } + @Override public List getBytesArray() { checkNotNull(); - return value; + try { + return bytesArray.get(); + } catch (Exception e) { + throw SpannerExceptionFactory.asSpannerException(e); + } } @Override - String elementToString(ByteArray element) { - return element.toBase64(); + String elementToString(LazyByteArray element) { + return element.getBase64String(); } @Override - void appendElement(StringBuilder b, ByteArray element) { - b.append(element.toString()); + void appendElement(StringBuilder b, LazyByteArray element) { + b.append(elementToString(element)); } } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ValueBinder.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ValueBinder.java index ec9e5a43d8f..e16b858faa8 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ValueBinder.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ValueBinder.java @@ -95,7 +95,11 @@ public R to(@Nullable String value) { return handle(Value.string(value)); } - /** Binds to {@code Value.bytes(value)} */ + /** + * Binds to {@code Value.bytes(value)}. Use {@link #to(Value)} in combination with {@link + * Value#bytesFromBase64(String)} if you already have the value that you want to bind in base64 + * format. This prevents unnecessary decoding and encoding of base64 strings. + */ public R to(@Nullable ByteArray value) { return handle(Value.bytes(value)); } @@ -198,6 +202,15 @@ public R toBytesArray(@Nullable Iterable values) { return handle(Value.bytesArray(values)); } + /** + * Binds to {@code Value.bytesArray(values)}. The given strings must be valid base64 encoded + * strings. Use this method instead of {@link #toBytesArray(Iterable)} if you already have the + * values in base64 format to prevent unnecessary decoding and encoding to/from base64. + */ + public R toBytesArrayFromBase64(@Nullable Iterable valuesAsBase64Strings) { + return handle(Value.bytesArrayFromBase64(valuesAsBase64Strings)); + } + /** Binds to {@code Value.timestampArray(values)} */ public R toTimestampArray(@Nullable Iterable values) { return handle(Value.timestampArray(values)); 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 666152eeca4..ac0f0a98d29 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 @@ -37,6 +37,7 @@ import com.google.api.core.ApiFutures; import com.google.api.gax.grpc.testing.LocalChannelProvider; import com.google.api.gax.retrying.RetrySettings; +import com.google.cloud.ByteArray; import com.google.cloud.NoCredentials; import com.google.cloud.Timestamp; import com.google.cloud.spanner.AbstractResultSet.GrpcStreamIterator; @@ -50,10 +51,15 @@ import com.google.cloud.spanner.SessionPool.PooledSessionFuture; import com.google.cloud.spanner.SpannerException.ResourceNotFoundException; import com.google.cloud.spanner.SpannerOptions.SpannerCallContextTimeoutConfigurator; +import com.google.cloud.spanner.Type.Code; +import com.google.cloud.spanner.connection.RandomResultSetGenerator; import com.google.common.base.Stopwatch; import com.google.common.collect.ImmutableList; +import com.google.common.io.BaseEncoding; import com.google.common.util.concurrent.SettableFuture; import com.google.protobuf.AbstractMessage; +import com.google.protobuf.ListValue; +import com.google.protobuf.NullValue; import com.google.spanner.v1.CommitRequest; import com.google.spanner.v1.DeleteSessionRequest; import com.google.spanner.v1.ExecuteBatchDmlRequest; @@ -67,6 +73,7 @@ import com.google.spanner.v1.StructType; import com.google.spanner.v1.StructType.Field; import com.google.spanner.v1.Type; +import com.google.spanner.v1.TypeAnnotationCode; import com.google.spanner.v1.TypeCode; import io.grpc.Context; import io.grpc.Server; @@ -74,9 +81,12 @@ import io.grpc.StatusRuntimeException; import io.grpc.inprocess.InProcessServerBuilder; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Base64; import java.util.Collections; import java.util.List; +import java.util.Random; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; @@ -87,6 +97,7 @@ import java.util.function.Function; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.stream.Collectors; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; @@ -1661,7 +1672,8 @@ public void testBackendPartitionQueryOptions() { @Test public void testAsyncQuery() throws Exception { final int EXPECTED_ROW_COUNT = 10; - RandomResultSetGenerator generator = new RandomResultSetGenerator(EXPECTED_ROW_COUNT); + com.google.cloud.spanner.connection.RandomResultSetGenerator generator = + new RandomResultSetGenerator(EXPECTED_ROW_COUNT); com.google.spanner.v1.ResultSet resultSet = generator.generate(); mockSpanner.putStatementResult( StatementResult.query(Statement.of("SELECT * FROM RANDOM"), resultSet)); @@ -2381,4 +2393,565 @@ public void testAnalyzeUpdateStatement() { ExecuteSqlRequest request = mockSpanner.getRequestsOfType(ExecuteSqlRequest.class).get(0); assertEquals(QueryMode.PLAN, request.getQueryMode()); } + + @Test + public void testByteArray() { + Random random = new Random(); + byte[] bytes = new byte[random.nextInt(200)]; + int numRows = 5; + List rows = new ArrayList<>(numRows); + for (int i = 0; i < numRows; i++) { + random.nextBytes(bytes); + rows.add( + ListValue.newBuilder() + .addValues( + com.google.protobuf.Value.newBuilder() + .setStringValue( + // Use both the Guava and the JDK encoder to encode the values to ensure + // that encoding/decoding using both of them works. + i % 2 == 0 + ? Base64.getEncoder().encodeToString(bytes) + : BaseEncoding.base64().encode(bytes)) + .build()) + .build()); + } + Statement statement = Statement.of("select * from foo"); + mockSpanner.putStatementResult( + StatementResult.query( + statement, + com.google.spanner.v1.ResultSet.newBuilder() + .setMetadata( + ResultSetMetadata.newBuilder() + .setRowType( + StructType.newBuilder() + .addFields( + Field.newBuilder() + .setType(Type.newBuilder().setCode(TypeCode.BYTES).build()) + .setName("f1") + .build()) + .build()) + .build()) + .addAllRows(rows) + .build())); + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE)); + try (ResultSet resultSet = client.singleUse().executeQuery(statement)) { + while (resultSet.next()) { + String base64String = resultSet.getValue(0).getAsString(); + ByteArray byteArray = resultSet.getBytes(0); + // Use the 'old' ByteArray.fromBase64(..) method that uses the Guava encoder to ensure that + // the two encoders (JDK and Guava) return the same values. + assertEquals(ByteArray.fromBase64(base64String), byteArray); + } + } + } + + @Test + public void testGetAllTypesAsString() { + for (Dialect dialect : Dialect.values()) { + Statement statement = Statement.of("select * from all_types"); + mockSpanner.putStatementResult( + StatementResult.query( + statement, + com.google.spanner.v1.ResultSet.newBuilder() + .setMetadata( + RandomResultSetGenerator.generateAllTypesMetadata( + RandomResultSetGenerator.generateAllTypes(dialect))) + .addRows( + ListValue.newBuilder() + .addValues( + com.google.protobuf.Value.newBuilder().setBoolValue(true).build()) + .addValues( + com.google.protobuf.Value.newBuilder().setStringValue("100").build()) + .addValues( + com.google.protobuf.Value.newBuilder().setNumberValue(3.14d).build()) + .addValues( + com.google.protobuf.Value.newBuilder() + .setStringValue("6.626") + .build()) + .addValues( + com.google.protobuf.Value.newBuilder() + .setStringValue("test-string") + .build()) + .addValues( + com.google.protobuf.Value.newBuilder() + .setStringValue("{\"key1\": \"value1\"}") + .build()) + .addValues( + com.google.protobuf.Value.newBuilder() + .setStringValue( + Base64.getEncoder() + .encodeToString( + "test-bytes".getBytes(StandardCharsets.UTF_8))) + .build()) + .addValues( + com.google.protobuf.Value.newBuilder() + .setStringValue("2023-01-11") + .build()) + .addValues( + com.google.protobuf.Value.newBuilder() + .setStringValue("2023-01-11T11:55:18.123456789Z") + .build()) + .addValues( + com.google.protobuf.Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addValues( + com.google.protobuf.Value.newBuilder() + .setBoolValue(true) + .build()) + .addValues( + com.google.protobuf.Value.newBuilder() + .setNullValue(NullValue.NULL_VALUE) + .build()) + .addValues( + com.google.protobuf.Value.newBuilder() + .setBoolValue(false) + .build()) + .build())) + .addValues( + com.google.protobuf.Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addValues( + com.google.protobuf.Value.newBuilder() + .setStringValue(String.valueOf(Long.MAX_VALUE)) + .build()) + .addValues( + com.google.protobuf.Value.newBuilder() + .setStringValue(String.valueOf(Long.MIN_VALUE)) + .build()) + .addValues( + com.google.protobuf.Value.newBuilder() + .setNullValue(NullValue.NULL_VALUE) + .build()) + .build())) + .addValues( + com.google.protobuf.Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addValues( + com.google.protobuf.Value.newBuilder() + .setNullValue(NullValue.NULL_VALUE) + .build()) + .addValues( + com.google.protobuf.Value.newBuilder() + .setNumberValue(-12345.6789d) + .build()) + .addValues( + com.google.protobuf.Value.newBuilder() + .setNumberValue(3.14d) + .build()) + .build())) + .addValues( + com.google.protobuf.Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addValues( + com.google.protobuf.Value.newBuilder() + .setStringValue("6.626") + .build()) + .addValues( + com.google.protobuf.Value.newBuilder() + .setNullValue(NullValue.NULL_VALUE) + .build()) + .addValues( + com.google.protobuf.Value.newBuilder() + .setStringValue("-8.9123") + .build()) + .build())) + .addValues( + com.google.protobuf.Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addValues( + com.google.protobuf.Value.newBuilder() + .setStringValue("test-string1") + .build()) + .addValues( + com.google.protobuf.Value.newBuilder() + .setNullValue(NullValue.NULL_VALUE) + .build()) + .addValues( + com.google.protobuf.Value.newBuilder() + .setStringValue("test-string2") + .build()) + .build())) + .addValues( + com.google.protobuf.Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addValues( + com.google.protobuf.Value.newBuilder() + .setStringValue("{\"key\": \"value1\"}") + .build()) + .addValues( + com.google.protobuf.Value.newBuilder() + .setStringValue("{\"key\": \"value2\"}") + .build()) + .addValues( + com.google.protobuf.Value.newBuilder() + .setNullValue(NullValue.NULL_VALUE) + .build()) + .build())) + .addValues( + com.google.protobuf.Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addValues( + com.google.protobuf.Value.newBuilder() + .setStringValue( + Base64.getEncoder() + .encodeToString( + "test-bytes1" + .getBytes( + StandardCharsets.UTF_8))) + .build()) + .addValues( + com.google.protobuf.Value.newBuilder() + .setStringValue( + Base64.getEncoder() + .encodeToString( + "test-bytes2" + .getBytes( + StandardCharsets.UTF_8))) + .build()) + .addValues( + com.google.protobuf.Value.newBuilder() + .setNullValue(NullValue.NULL_VALUE) + .build()) + .build())) + .addValues( + com.google.protobuf.Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addValues( + com.google.protobuf.Value.newBuilder() + .setStringValue("2000-02-29") + .build()) + .addValues( + com.google.protobuf.Value.newBuilder() + .setNullValue(NullValue.NULL_VALUE) + .build()) + .addValues( + com.google.protobuf.Value.newBuilder() + .setStringValue("2000-01-01") + .build()) + .build())) + .addValues( + com.google.protobuf.Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addValues( + com.google.protobuf.Value.newBuilder() + .setStringValue("2023-01-11T11:55:18.123456789Z") + .build()) + .addValues( + com.google.protobuf.Value.newBuilder() + .setNullValue(NullValue.NULL_VALUE) + .build()) + .addValues( + com.google.protobuf.Value.newBuilder() + .setStringValue("2023-01-12T11:55:18Z") + .build()) + .build())) + .build()) + .build())); + + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE)); + try (ResultSet resultSet = client.singleUse().executeQuery(statement)) { + assertTrue(resultSet.next()); + int col = 0; + assertAsString("true", resultSet, col++); + assertAsString("100", resultSet, col++); + assertAsString("3.14", resultSet, col++); + assertAsString("6.626", resultSet, col++); + assertAsString("test-string", resultSet, col++); + assertAsString("{\"key1\": \"value1\"}", resultSet, col++); + assertAsString( + Base64.getEncoder().encodeToString("test-bytes".getBytes(StandardCharsets.UTF_8)), + resultSet, + col++); + assertAsString("2023-01-11", resultSet, col++); + assertAsString("2023-01-11T11:55:18.123456789Z", resultSet, col++); + + assertAsString(ImmutableList.of("true", "NULL", "false"), resultSet, col++); + assertAsString( + ImmutableList.of( + String.format("%d", Long.MAX_VALUE), String.format("%d", Long.MIN_VALUE), "NULL"), + resultSet, + col++); + assertAsString(ImmutableList.of("NULL", "-12345.6789", "3.14"), resultSet, col++); + assertAsString(ImmutableList.of("6.626", "NULL", "-8.9123"), resultSet, col++); + assertAsString(ImmutableList.of("test-string1", "NULL", "test-string2"), resultSet, col++); + assertAsString( + ImmutableList.of("{\"key\": \"value1\"}", "{\"key\": \"value2\"}", "NULL"), + resultSet, + col++); + assertAsString( + ImmutableList.of( + String.format( + "%s", + Base64.getEncoder() + .encodeToString("test-bytes1".getBytes(StandardCharsets.UTF_8))), + String.format( + "%s", + Base64.getEncoder() + .encodeToString("test-bytes2".getBytes(StandardCharsets.UTF_8))), + "NULL"), + resultSet, + col++); + assertAsString(ImmutableList.of("2000-02-29", "NULL", "2000-01-01"), resultSet, col++); + assertAsString( + ImmutableList.of("2023-01-11T11:55:18.123456789Z", "NULL", "2023-01-12T11:55:18Z"), + resultSet, + col++); + + assertFalse(resultSet.next()); + } + } + } + + @Test + public void testSelectUnknownType() { + mockSpanner.putStatementResult( + StatementResult.query( + Statement.of("SELECT * FROM foo"), + com.google.spanner.v1.ResultSet.newBuilder() + .setMetadata( + ResultSetMetadata.newBuilder() + .setRowType( + StructType.newBuilder() + .addFields( + Field.newBuilder() + .setName("c") + .setType( + Type.newBuilder() + .setCodeValue(Integer.MAX_VALUE) + .build()) + .build()) + .build()) + .build()) + .addRows( + ListValue.newBuilder() + .addValues( + com.google.protobuf.Value.newBuilder().setStringValue("bar").build()) + .build()) + .addRows( + ListValue.newBuilder() + .addValues( + com.google.protobuf.Value.newBuilder().setBoolValue(true).build()) + .build()) + .addRows( + ListValue.newBuilder() + .addValues( + com.google.protobuf.Value.newBuilder().setNumberValue(3.14d).build()) + .build()) + .addRows( + ListValue.newBuilder() + .addValues( + com.google.protobuf.Value.newBuilder() + .setNullValue(NullValue.NULL_VALUE) + .build()) + .build()) + .addRows( + ListValue.newBuilder() + .addValues( + com.google.protobuf.Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addValues( + com.google.protobuf.Value.newBuilder() + .setStringValue("baz") + .build()) + .addValues( + com.google.protobuf.Value.newBuilder() + .setBoolValue(false) + .build()) + .addValues( + com.google.protobuf.Value.newBuilder() + .setNumberValue(6.626) + .build()) + .addValues( + com.google.protobuf.Value.newBuilder() + .setNullValue(NullValue.NULL_VALUE) + .build()) + .build()) + .build()) + .build()) + .build())); + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE)); + try (ResultSet resultSet = client.singleUse().executeQuery(Statement.of("SELECT * FROM foo"))) { + assertTrue(resultSet.next()); + assertAsString("bar", resultSet, 0); + + assertTrue(resultSet.next()); + assertAsString("true", resultSet, 0); + + assertTrue(resultSet.next()); + assertAsString("3.14", resultSet, 0); + + assertTrue(resultSet.next()); + assertAsString("NULL", resultSet, 0); + + assertTrue(resultSet.next()); + assertAsString(ImmutableList.of("baz", "false", "6.626", "NULL"), resultSet, 0); + + assertFalse(resultSet.next()); + } + } + + @Test + public void testMetadataUnknownTypes() { + mockSpanner.putStatementResult( + StatementResult.query( + Statement.of("SELECT * FROM foo"), + com.google.spanner.v1.ResultSet.newBuilder() + .setMetadata( + ResultSetMetadata.newBuilder() + .setRowType( + StructType.newBuilder() + .addFields( + Field.newBuilder() + .setName("c1") + .setType( + Type.newBuilder() + .setCodeValue(Integer.MAX_VALUE) + .build()) + .build()) + .addFields( + Field.newBuilder() + .setName("c2") + .setType( + Type.newBuilder() + .setCode(TypeCode.STRING) + .setTypeAnnotationValue(Integer.MAX_VALUE) + .build()) + .build()) + .addFields( + Field.newBuilder() + .setName("c3") + .setType( + Type.newBuilder() + .setCodeValue(Integer.MAX_VALUE) + .setTypeAnnotation(TypeAnnotationCode.PG_NUMERIC) + .build()) + .build()) + .addFields( + Field.newBuilder() + .setName("c4") + .setType( + Type.newBuilder() + .setCode(TypeCode.ARRAY) + .setArrayElementType( + Type.newBuilder() + .setCodeValue(Integer.MAX_VALUE) + .build()) + .build()) + .build()) + .addFields( + Field.newBuilder() + .setName("c5") + .setType( + Type.newBuilder() + .setCode(TypeCode.ARRAY) + .setArrayElementType( + Type.newBuilder() + .setCode(TypeCode.STRING) + .setTypeAnnotationValue(Integer.MAX_VALUE) + .build()) + .build()) + .build()) + .addFields( + Field.newBuilder() + .setName("c6") + .setType( + Type.newBuilder() + // Set an unrecognized type with an array element + // type. The client should recognize this as an + // array. + .setCodeValue(Integer.MAX_VALUE) + .setArrayElementType( + Type.newBuilder() + .setCodeValue(Integer.MAX_VALUE) + .build()) + .build()) + .build()) + .addFields( + Field.newBuilder() + .setName("c7") + .setType( + Type.newBuilder() + .setCode(TypeCode.ARRAY) + .setArrayElementType( + Type.newBuilder() + .setCodeValue(Integer.MAX_VALUE) + .setTypeAnnotation( + TypeAnnotationCode.PG_NUMERIC) + .build()) + .build()) + .build()) + .build()) + .build()) + .build())); + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE)); + try (ResultSet resultSet = client.singleUse().executeQuery(Statement.of("SELECT * FROM foo"))) { + // There are no rows, but we need to call resultSet.next() before we can get the metadata. + assertFalse(resultSet.next()); + assertEquals( + "STRUCT, c3 UNRECOGNIZED, c4 ARRAY, c5 ARRAY>, c6 UNRECOGNIZED, c7 ARRAY>>", + resultSet.getType().toString()); + assertEquals( + "UNRECOGNIZED", resultSet.getType().getStructFields().get(0).getType().toString()); + assertEquals( + "STRING", + resultSet.getType().getStructFields().get(1).getType().toString()); + assertEquals( + "UNRECOGNIZED", + resultSet.getType().getStructFields().get(2).getType().toString()); + assertEquals( + "ARRAY", resultSet.getType().getStructFields().get(3).getType().toString()); + assertEquals(Code.ARRAY, resultSet.getType().getStructFields().get(3).getType().getCode()); + assertEquals( + Code.UNRECOGNIZED, + resultSet.getType().getStructFields().get(3).getType().getArrayElementType().getCode()); + assertEquals( + "ARRAY>", + resultSet.getType().getStructFields().get(4).getType().toString()); + assertEquals(Code.ARRAY, resultSet.getType().getStructFields().get(4).getType().getCode()); + assertEquals( + Code.UNRECOGNIZED, + resultSet.getType().getStructFields().get(4).getType().getArrayElementType().getCode()); + assertEquals( + "UNRECOGNIZED", + resultSet.getType().getStructFields().get(5).getType().toString()); + assertEquals( + Code.UNRECOGNIZED, resultSet.getType().getStructFields().get(5).getType().getCode()); + assertEquals( + Code.UNRECOGNIZED, + resultSet.getType().getStructFields().get(5).getType().getArrayElementType().getCode()); + assertEquals( + "ARRAY>", + resultSet.getType().getStructFields().get(6).getType().toString()); + assertEquals(Code.ARRAY, resultSet.getType().getStructFields().get(6).getType().getCode()); + assertEquals( + Code.UNRECOGNIZED, + resultSet.getType().getStructFields().get(6).getType().getArrayElementType().getCode()); + } + } + + static void assertAsString(String expected, ResultSet resultSet, int col) { + assertEquals(expected, resultSet.getValue(col).getAsString()); + assertEquals(ImmutableList.of(expected), resultSet.getValue(col).getAsStringList()); + } + + static void assertAsString(ImmutableList expected, ResultSet resultSet, int col) { + assertEquals(expected, resultSet.getValue(col).getAsStringList()); + assertEquals( + expected.stream().collect(Collectors.joining(",", "[", "]")), + resultSet.getValue(col).getAsString()); + } } 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 ff4e92a5215..490eff3aca8 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 @@ -38,8 +38,10 @@ import com.google.spanner.v1.ResultSetStats; import com.google.spanner.v1.Transaction; import java.math.BigDecimal; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; +import java.util.Base64; import java.util.Collections; import java.util.List; import java.util.Map; @@ -517,7 +519,10 @@ public void serialization() { Value.float64(1.0), Value.float64(null), Value.bytes(ByteArray.fromBase64("abcd")), + Value.bytesFromBase64( + Base64.getEncoder().encodeToString("test".getBytes(StandardCharsets.UTF_8))), Value.bytes(null), + Value.bytesFromBase64(null), Value.timestamp(Timestamp.ofTimeSecondsAndNanos(1, 2)), Value.timestamp(null), Value.date(Date.fromYearMonthDay(2017, 4, 17)), @@ -528,6 +533,14 @@ public void serialization() { Value.boolArray((boolean[]) null), Value.int64Array(new long[] {1, 2, 3}), Value.int64Array((long[]) null), + Value.float64Array(new double[] {1.1, 2.2, 3.3}), + Value.float64Array((double[]) null), + Value.bytesArray(Arrays.asList(ByteArray.fromBase64("abcd"), null)), + Value.bytesArrayFromBase64( + Arrays.asList( + Base64.getEncoder().encodeToString("test".getBytes(StandardCharsets.UTF_8)), null)), + Value.bytesArray(null), + Value.bytesArrayFromBase64(null), Value.timestampArray(ImmutableList.of(Timestamp.MAX_VALUE, Timestamp.MAX_VALUE)), Value.timestampArray(null), Value.dateArray( diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/LazyByteArrayTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/LazyByteArrayTest.java new file mode 100644 index 00000000000..a36b3463439 --- /dev/null +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/LazyByteArrayTest.java @@ -0,0 +1,52 @@ +/* + * 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 static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +import com.google.cloud.spanner.AbstractResultSet.LazyByteArray; +import java.util.Base64; +import java.util.Random; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class LazyByteArrayTest { + + @Test + public void testEqualsAndHashCode() { + Random random = new Random(); + byte[] bytes1 = new byte[random.nextInt(300) + 300]; + // Make sure the second byte array has a different length than the first to be absolutely sure + // that they can never contain the same value. + byte[] bytes2 = new byte[bytes1.length + 1]; + random.nextBytes(bytes1); + random.nextBytes(bytes2); + + LazyByteArray lazyByteArray1 = new LazyByteArray(Base64.getEncoder().encodeToString(bytes1)); + LazyByteArray lazyByteArray2 = new LazyByteArray(Base64.getEncoder().encodeToString(bytes2)); + LazyByteArray lazyByteArray3 = new LazyByteArray(Base64.getEncoder().encodeToString(bytes1)); + + assertEquals(lazyByteArray1, lazyByteArray3); + assertNotEquals(lazyByteArray1, lazyByteArray2); + assertNotEquals(lazyByteArray2, lazyByteArray3); + + assertEquals(lazyByteArray1.hashCode(), lazyByteArray3.hashCode()); + } +} diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TypeTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TypeTest.java index 3ed6fc6c577..eb17a7a8c62 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TypeTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TypeTest.java @@ -19,6 +19,8 @@ import static com.google.cloud.spanner.Type.StructField; import static com.google.common.testing.SerializableTester.reserializeAndAssert; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThrows; @@ -428,9 +430,7 @@ public void structFieldIndexAmbiguous() { @Test public void parseErrorMissingTypeCode() { com.google.spanner.v1.Type proto = com.google.spanner.v1.Type.newBuilder().build(); - IllegalArgumentException e = - assertThrows(IllegalArgumentException.class, () -> Type.fromProto(proto)); - assertNotNull(e.getMessage()); + assertEquals(Code.UNRECOGNIZED, Type.fromProto(proto).getCode()); } @Test @@ -442,6 +442,81 @@ public void parseErrorMissingArrayElementTypeProto() { assertNotNull(e.getMessage()); } + @Test + public void testUnrecognized() { + Type unrecognized = Type.fromProto(com.google.spanner.v1.Type.newBuilder().build()); + assertEquals("TYPE_CODE_UNSPECIFIED", unrecognized.toString()); + assertEquals(unrecognized, Type.fromProto(com.google.spanner.v1.Type.newBuilder().build())); + assertNotEquals(unrecognized, Type.int64()); + } + + @Test + public void testUnrecognizedWithAnnotation() { + Type unrecognized = + Type.fromProto( + com.google.spanner.v1.Type.newBuilder() + .setTypeAnnotation(TypeAnnotationCode.PG_NUMERIC) + .build()); + assertEquals("TYPE_CODE_UNSPECIFIED", unrecognized.toString()); + assertEquals( + unrecognized, + Type.fromProto( + com.google.spanner.v1.Type.newBuilder() + .setTypeAnnotation(TypeAnnotationCode.PG_NUMERIC) + .build())); + assertNotEquals( + unrecognized, + Type.fromProto( + com.google.spanner.v1.Type.newBuilder() + .setTypeAnnotation(TypeAnnotationCode.PG_JSONB) + .build())); + assertNotEquals(unrecognized, Type.int64()); + } + + @Test + public void testUnrecognizedArray() { + Type unrecognizedArray = + Type.fromProto( + com.google.spanner.v1.Type.newBuilder() + .setCode(TypeCode.ARRAY) + .setArrayElementType(com.google.spanner.v1.Type.newBuilder().build()) + .build()); + assertEquals("ARRAY", unrecognizedArray.toString()); + assertEquals( + unrecognizedArray, + Type.fromProto( + com.google.spanner.v1.Type.newBuilder() + .setCode(TypeCode.ARRAY) + .setArrayElementType(com.google.spanner.v1.Type.newBuilder().build()) + .build())); + assertNotEquals(unrecognizedArray, Type.array(Type.int64())); + } + + @Test + public void testUnrecognizedArrayWithAnnotation() { + Type unrecognizedArray = + Type.fromProto( + com.google.spanner.v1.Type.newBuilder() + .setCode(TypeCode.ARRAY) + .setArrayElementType( + com.google.spanner.v1.Type.newBuilder() + .setTypeAnnotation(TypeAnnotationCode.PG_NUMERIC) + .build()) + .build()); + assertEquals("ARRAY>", unrecognizedArray.toString()); + assertEquals( + unrecognizedArray, + Type.fromProto( + com.google.spanner.v1.Type.newBuilder() + .setCode(TypeCode.ARRAY) + .setArrayElementType( + com.google.spanner.v1.Type.newBuilder() + .setTypeAnnotation(TypeAnnotationCode.PG_NUMERIC) + .build()) + .build())); + assertNotEquals(unrecognizedArray, Type.array(Type.int64())); + } + private static void assertProtoEquals(com.google.spanner.v1.Type proto, String expected) { MatcherAssert.assertThat( proto, SpannerMatchers.matchesProto(com.google.spanner.v1.Type.class, expected)); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ValueBinderTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ValueBinderTest.java index 91263457baf..8da5c0322dc 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ValueBinderTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ValueBinderTest.java @@ -16,6 +16,7 @@ package com.google.cloud.spanner; +import static com.google.cloud.spanner.ValueBinderTest.DefaultValues.defaultBytesBase64; import static com.google.cloud.spanner.ValueBinderTest.DefaultValues.defaultJson; import static com.google.cloud.spanner.ValueBinderTest.DefaultValues.defaultPgJsonb; import static com.google.common.truth.Truth.assertThat; @@ -28,7 +29,9 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.math.BigDecimal; +import java.nio.charset.StandardCharsets; import java.util.Arrays; +import java.util.Base64; import java.util.Collections; import org.junit.Test; import org.junit.runner.RunWith; @@ -40,6 +43,7 @@ public class ValueBinderTest { private static final String JSON_METHOD_NAME = "json"; private static final String PG_JSONB_METHOD_NAME = "pgJsonb"; private static final String PG_NUMERIC_METHOD_NAME = "pgNumeric"; + private static final String BYTES_BASE64_METHOD_NAME = "bytesFromBase64"; public static final String DEFAULT_PG_NUMERIC = "1.23"; private Value lastValue; @@ -134,6 +138,10 @@ public void reflection() binderMethod = ValueBinder.class.getMethod("to", Value.class); assertThat(binderMethod.invoke(binder, Value.pgNumeric(null))) .isEqualTo(lastReturnValue); + } else if (method.getName().equalsIgnoreCase(BYTES_BASE64_METHOD_NAME)) { + binderMethod = ValueBinder.class.getMethod("to", Value.class); + assertThat(binderMethod.invoke(binder, Value.bytesFromBase64(null))) + .isEqualTo(lastReturnValue); } else { assertThat(binderMethod.invoke(binder, (Object) null)).isEqualTo(lastReturnValue); } @@ -160,6 +168,11 @@ public void reflection() binderMethod = ValueBinder.class.getMethod("to", Value.class); assertThat(binderMethod.invoke(binder, Value.pgNumeric(DEFAULT_PG_NUMERIC))) .isEqualTo(lastReturnValue); + } else if (method.getName().equalsIgnoreCase(BYTES_BASE64_METHOD_NAME)) { + defaultObject = defaultBytesBase64(); + binderMethod = ValueBinder.class.getMethod("to", Value.class); + assertThat(binderMethod.invoke(binder, Value.bytesFromBase64(defaultBytesBase64()))) + .isEqualTo(lastReturnValue); } else { defaultObject = DefaultValues.getDefault(method.getGenericParameterTypes()[0]); assertThat(binderMethod.invoke(binder, defaultObject)).isEqualTo(lastReturnValue); @@ -246,6 +259,10 @@ public static String defaultPgJsonb() { return "{\"color\":\"red\",\"value\":\"#f00\"}"; } + public static String defaultBytesBase64() { + return Base64.getEncoder().encodeToString("test-bytes".getBytes(StandardCharsets.UTF_8)); + } + public static ByteArray defaultByteArray() { return ByteArray.copyFrom(new byte[] {'x'}); } 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 a466fab1ab4..bf806795fa6 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 @@ -32,6 +32,7 @@ import com.google.cloud.ByteArray; import com.google.cloud.Date; import com.google.cloud.Timestamp; +import com.google.cloud.spanner.AbstractResultSet.LazyByteArray; import com.google.cloud.spanner.Type.StructField; import com.google.common.base.Strings; import com.google.common.collect.ForwardingList; @@ -41,11 +42,15 @@ import com.google.protobuf.NullValue; import java.io.Serializable; import java.math.BigDecimal; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; +import java.util.Base64; import java.util.Collections; import java.util.List; +import java.util.Random; import java.util.function.Supplier; +import java.util.stream.Collectors; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -91,6 +96,8 @@ public void untyped() { assertNotEquals( Value.untyped(com.google.protobuf.Value.newBuilder().setBoolValue(false).build()), Value.untyped(com.google.protobuf.Value.newBuilder().setBoolValue(true).build())); + + assertEquals("test", v.getAsString()); } @Test @@ -100,6 +107,7 @@ public void bool() { assertThat(v.isNull()).isFalse(); assertThat(v.getBool()).isTrue(); assertThat(v.toString()).isEqualTo("true"); + assertEquals("true", v.getAsString()); } @Test @@ -109,6 +117,7 @@ public void boolWrapper() { assertThat(v.isNull()).isFalse(); assertThat(v.getBool()).isFalse(); assertThat(v.toString()).isEqualTo("false"); + assertEquals("false", v.getAsString()); } @Test @@ -119,6 +128,7 @@ public void boolWrapperNull() { assertThat(v.toString()).isEqualTo(NULL_STRING); IllegalStateException e = assertThrows(IllegalStateException.class, v::getBool); assertThat(e.getMessage()).contains("null value"); + assertEquals("NULL", v.getAsString()); } @Test @@ -128,6 +138,7 @@ public void int64() { assertThat(v.isNull()).isFalse(); assertThat(v.getInt64()).isEqualTo(123); assertThat(v.toString()).isEqualTo("123"); + assertEquals("123", v.getAsString()); } @Test @@ -158,6 +169,7 @@ public void int64Wrapper() { assertThat(v.isNull()).isFalse(); assertThat(v.getInt64()).isEqualTo(123); assertThat(v.toString()).isEqualTo("123"); + assertEquals("123", v.getAsString()); } @Test @@ -168,6 +180,7 @@ public void int64WrapperNull() { assertThat(v.toString()).isEqualTo(NULL_STRING); IllegalStateException e = assertThrows(IllegalStateException.class, v::getInt64); assertThat(e.getMessage()).contains("null value"); + assertEquals("NULL", v.getAsString()); } @Test @@ -177,6 +190,7 @@ public void float64() { assertThat(v.isNull()).isFalse(); assertThat(v.getFloat64()).isWithin(0.0001).of(1.23); assertThat(v.toString()).isEqualTo("1.23"); + assertEquals("1.23", v.getAsString()); } @Test @@ -186,6 +200,7 @@ public void float64Wrapper() { assertThat(v.isNull()).isFalse(); assertThat(v.getFloat64()).isWithin(0.0001).of(1.23); assertThat(v.toString()).isEqualTo("1.23"); + assertEquals("1.23", v.getAsString()); } @Test @@ -196,6 +211,7 @@ public void float64WrapperNull() { assertThat(v.toString()).isEqualTo(NULL_STRING); IllegalStateException e = assertThrows(IllegalStateException.class, v::getFloat64); assertThat(e.getMessage()).contains("null value"); + assertEquals("NULL", v.getAsString()); } @Test @@ -205,6 +221,7 @@ public void numeric() { assertThat(v.isNull()).isFalse(); assertThat(v.getNumeric()).isEqualTo(BigDecimal.valueOf(123, 2)); assertThat(v.toString()).isEqualTo("1.23"); + assertEquals("1.23", v.getAsString()); } @Test @@ -216,6 +233,7 @@ public void pgNumeric() { assertEquals(BigDecimal.valueOf(12345678, 4), value.getNumeric()); assertEquals(1234.5678D, value.getFloat64(), 0.00001); assertEquals("1234.5678", value.toString()); + assertEquals("1234.5678", value.getAsString()); } @Test @@ -227,6 +245,7 @@ public void pgNumericNaN() { assertThrows(NumberFormatException.class, value::getNumeric); assertEquals(Double.NaN, value.getFloat64(), 0.00001); assertEquals("NaN", value.toString()); + assertEquals("NaN", value.getAsString()); } @Test @@ -384,6 +403,7 @@ public void numericNull() { IllegalStateException e = assertThrows(IllegalStateException.class, v::getNumeric); assertThat(e.getMessage()).contains("null value"); + assertEquals("NULL", v.getAsString()); } @Test @@ -399,6 +419,7 @@ public void pgNumericNull() { assertTrue("exception should mention value is null", e2.getMessage().contains("null value")); final IllegalStateException e3 = assertThrows(IllegalStateException.class, value::getFloat64); assertTrue("exception should mention value is null", e3.getMessage().contains("null value")); + assertEquals("NULL", value.getAsString()); } @Test @@ -411,6 +432,7 @@ public void pgNumericInvalid() { assertEquals("INVALID", value.getString()); assertThrows(NumberFormatException.class, value::getNumeric); assertThrows(NumberFormatException.class, value::getFloat64); + assertEquals("INVALID", value.getAsString()); } @Test @@ -419,6 +441,7 @@ public void string() { assertThat(v.getType()).isEqualTo(Type.string()); assertThat(v.isNull()).isFalse(); assertThat(v.getString()).isEqualTo("abc"); + assertEquals("abc", v.getAsString()); } @Test @@ -429,6 +452,7 @@ public void stringNull() { assertThat(v.toString()).isEqualTo(NULL_STRING); IllegalStateException e = assertThrows(IllegalStateException.class, v::getString); assertThat(e.getMessage()).contains("null value"); + assertEquals("NULL", v.getAsString()); } @Test @@ -439,6 +463,7 @@ public void stringLong() { assertThat(v.toString()).hasLength(36); assertThat(v.toString()).startsWith(str.substring(0, 36 - 3)); assertThat(v.toString()).endsWith("..."); + assertEquals(str, v.getAsString()); } @Test @@ -449,6 +474,7 @@ public void json() { assertFalse(v.isNull()); assertEquals(json, v.getJson()); assertEquals(json, v.getString()); + assertEquals(json, v.getAsString()); } @Test @@ -459,6 +485,7 @@ public void jsonNull() { assertEquals(NULL_STRING, v.toString()); assertThrowsWithMessage(v::getJson, "null value"); assertThrowsWithMessage(v::getString, "null value"); + assertEquals("NULL", v.getAsString()); } @Test @@ -466,6 +493,7 @@ public void jsonEmpty() { String json = "{}"; Value v = Value.json(json); assertEquals(json, v.getJson()); + assertEquals(json, v.getAsString()); } @Test @@ -473,6 +501,7 @@ public void jsonWithEmptyArray() { String json = "[]"; Value v = Value.json(json); assertEquals(json, v.getJson()); + assertEquals(json, v.getAsString()); } @Test @@ -481,6 +510,7 @@ public void jsonWithArray() { "[{\"color\":\"red\",\"value\":\"#f00\"},{\"color\":\"green\",\"value\":\"#0f0\"},{\"color\":\"blue\",\"value\":\"#00f\"},{\"color\":\"cyan\",\"value\":\"#0ff\"},{\"color\":\"magenta\",\"value\":\"#f0f\"},{\"color\":\"yellow\",\"value\":\"#ff0\"},{\"color\":\"black\",\"value\":\"#000\"}]"; Value v = Value.json(json); assertEquals(json, v.getJson()); + assertEquals(json, v.getAsString()); } @Test @@ -489,6 +519,7 @@ public void jsonNested() { "[{\"id\":\"0001\",\"type\":\"donut\",\"name\":\"Cake\",\"ppu\":0.55,\"batters\":{\"batter\":[{\"id\":\"1001\",\"type\":\"Regular\"},{\"id\":\"1002\",\"type\":\"Chocolate\"},{\"id\":\"1003\",\"type\":\"Blueberry\"},{\"id\":\"1004\",\"type\":\"Devil's Food\"}]},\"topping\":[{\"id\":\"5001\",\"type\":\"None\"},{\"id\":\"5002\",\"type\":\"Glazed\"},{\"id\":\"5005\",\"type\":\"Sugar\"},{\"id\":\"5007\",\"type\":\"Powdered Sugar\"},{\"id\":\"5006\",\"type\":\"Chocolate with Sprinkles\"},{\"id\":\"5003\",\"type\":\"Chocolate\"},{\"id\":\"5004\",\"type\":\"Maple\"}]},{\"id\":\"0002\",\"type\":\"donut\",\"name\":\"Raised\",\"ppu\":0.55,\"batters\":{\"batter\":[{\"id\":\"1001\",\"type\":\"Regular\"}]},\"topping\":[{\"id\":\"5001\",\"type\":\"None\"},{\"id\":\"5002\",\"type\":\"Glazed\"},{\"id\":\"5005\",\"type\":\"Sugar\"},{\"id\":\"5003\",\"type\":\"Chocolate\"},{\"id\":\"5004\",\"type\":\"Maple\"}]},{\"id\":\"0003\",\"type\":\"donut\",\"name\":\"Old Fashioned\",\"ppu\":0.55,\"batters\":{\"batter\":[{\"id\":\"1001\",\"type\":\"Regular\"},{\"id\":\"1002\",\"type\":\"Chocolate\"}]},\"topping\":[{\"id\":\"5001\",\"type\":\"None\"},{\"id\":\"5002\",\"type\":\"Glazed\"},{\"id\":\"5003\",\"type\":\"Chocolate\"},{\"id\":\"5004\",\"type\":\"Maple\"}]}]"; Value v = Value.json(json); assertEquals(json, v.getJson()); + assertEquals(json, v.getAsString()); } @Test @@ -499,6 +530,7 @@ public void testPgJsonb() { assertFalse(v.isNull()); assertEquals(json, v.getPgJsonb()); assertEquals(json, v.getString()); + assertEquals(json, v.getAsString()); } @Test @@ -509,6 +541,7 @@ public void testPgJsonbNull() { assertEquals(NULL_STRING, v.toString()); assertThrowsWithMessage(v::getPgJsonb, "null value"); assertThrowsWithMessage(v::getString, "null value"); + assertEquals("NULL", v.getAsString()); } @Test @@ -516,6 +549,7 @@ public void testPgJsonbEmpty() { String json = "{}"; Value v = Value.pgJsonb(json); assertEquals(json, v.getPgJsonb()); + assertEquals(json, v.getAsString()); } @Test @@ -523,6 +557,7 @@ public void testPgJsonbWithEmptyArray() { String json = "[]"; Value v = Value.pgJsonb(json); assertEquals(json, v.getPgJsonb()); + assertEquals(json, v.getAsString()); } @Test @@ -531,6 +566,7 @@ public void testPgJsonbWithArray() { "[{\"color\":\"red\",\"value\":\"#f00\"},{\"color\":\"green\",\"value\":\"#0f0\"},{\"color\":\"blue\",\"value\":\"#00f\"},{\"color\":\"cyan\",\"value\":\"#0ff\"},{\"color\":\"magenta\",\"value\":\"#f0f\"},{\"color\":\"yellow\",\"value\":\"#ff0\"},{\"color\":\"black\",\"value\":\"#000\"}]"; Value v = Value.pgJsonb(json); assertEquals(json, v.getPgJsonb()); + assertEquals(json, v.getAsString()); } @Test @@ -539,6 +575,7 @@ public void testPgJsonbNested() { "[{\"id\":\"0001\",\"type\":\"donut\",\"name\":\"Cake\",\"ppu\":0.55,\"batters\":{\"batter\":[{\"id\":\"1001\",\"type\":\"Regular\"},{\"id\":\"1002\",\"type\":\"Chocolate\"},{\"id\":\"1003\",\"type\":\"Blueberry\"},{\"id\":\"1004\",\"type\":\"Devil's Food\"}]},\"topping\":[{\"id\":\"5001\",\"type\":\"None\"},{\"id\":\"5002\",\"type\":\"Glazed\"},{\"id\":\"5005\",\"type\":\"Sugar\"},{\"id\":\"5007\",\"type\":\"Powdered Sugar\"},{\"id\":\"5006\",\"type\":\"Chocolate with Sprinkles\"},{\"id\":\"5003\",\"type\":\"Chocolate\"},{\"id\":\"5004\",\"type\":\"Maple\"}]},{\"id\":\"0002\",\"type\":\"donut\",\"name\":\"Raised\",\"ppu\":0.55,\"batters\":{\"batter\":[{\"id\":\"1001\",\"type\":\"Regular\"}]},\"topping\":[{\"id\":\"5001\",\"type\":\"None\"},{\"id\":\"5002\",\"type\":\"Glazed\"},{\"id\":\"5005\",\"type\":\"Sugar\"},{\"id\":\"5003\",\"type\":\"Chocolate\"},{\"id\":\"5004\",\"type\":\"Maple\"}]},{\"id\":\"0003\",\"type\":\"donut\",\"name\":\"Old Fashioned\",\"ppu\":0.55,\"batters\":{\"batter\":[{\"id\":\"1001\",\"type\":\"Regular\"},{\"id\":\"1002\",\"type\":\"Chocolate\"}]},\"topping\":[{\"id\":\"5001\",\"type\":\"None\"},{\"id\":\"5002\",\"type\":\"Glazed\"},{\"id\":\"5003\",\"type\":\"Chocolate\"},{\"id\":\"5004\",\"type\":\"Maple\"}]}]"; Value v = Value.pgJsonb(json); assertEquals(json, v.getPgJsonb()); + assertEquals(json, v.getAsString()); } @Test @@ -548,7 +585,8 @@ public void bytes() { assertThat(v.getType()).isEqualTo(Type.bytes()); assertThat(v.isNull()).isFalse(); assertThat(v.getBytes()).isSameInstanceAs(bytes); - assertThat(v.toString()).isEqualTo(bytes.toString()); + assertThat(v.toString()).isEqualTo(bytes.toBase64()); + assertEquals(Base64.getEncoder().encodeToString(bytes.toByteArray()), v.getAsString()); } @Test @@ -556,7 +594,8 @@ public void bytesUnprintable() { ByteArray bytes = ByteArray.copyFrom(new byte[] {'a', 0, 15, -1, 'e'}); Value v = Value.bytes(bytes); assertThat(v.getBytes()).isSameInstanceAs(bytes); - assertThat(v.toString()).isEqualTo(bytes.toString()); + assertThat(v.toString()).isEqualTo(bytes.toBase64()); + assertEquals(Base64.getEncoder().encodeToString(bytes.toByteArray()), v.getAsString()); } @Test @@ -567,6 +606,7 @@ public void bytesNull() { assertThat(v.toString()).isEqualTo(NULL_STRING); IllegalStateException e = assertThrows(IllegalStateException.class, v::getBytes); assertThat(e.getMessage()).contains("null value"); + assertEquals("NULL", v.getAsString()); } @Test @@ -579,6 +619,7 @@ public void timestamp() { assertThat(v.isCommitTimestamp()).isFalse(); assertThat(v.getTimestamp()).isSameInstanceAs(t); assertThat(v.toString()).isEqualTo(timestamp); + assertEquals(timestamp, v.getAsString()); } @Test @@ -590,6 +631,7 @@ public void timestampNull() { assertThat(v.isCommitTimestamp()).isFalse(); IllegalStateException e = assertThrows(IllegalStateException.class, v::getTimestamp); assertThat(e.getMessage()).contains("null value"); + assertEquals("NULL", v.getAsString()); } @Test @@ -606,6 +648,7 @@ public void commitTimestamp() { .build()); IllegalStateException e = assertThrows(IllegalStateException.class, v::getTimestamp); assertThat(e.getMessage()).contains("Commit timestamp value"); + assertEquals("spanner.commit_timestamp()", v.getAsString()); } @Test @@ -617,6 +660,7 @@ public void date() { assertThat(v.isNull()).isFalse(); assertThat(v.getDate()).isSameInstanceAs(t); assertThat(v.toString()).isEqualTo(date); + assertEquals(date, v.getAsString()); } @Test @@ -627,6 +671,7 @@ public void dateNull() { assertThat(v.toString()).isEqualTo(NULL_STRING); IllegalStateException e = assertThrows(IllegalStateException.class, v::getDate); assertThat(e.getMessage()).contains("null value"); + assertEquals("NULL", v.getAsString()); } @Test @@ -635,6 +680,7 @@ public void boolArray() { assertThat(v.isNull()).isFalse(); assertThat(v.getBoolArray()).containsExactly(true, false).inOrder(); assertThat(v.toString()).isEqualTo("[true,false]"); + assertEquals("[true,false]", v.getAsString()); } @Test @@ -643,6 +689,7 @@ public void boolArrayRange() { assertThat(v.isNull()).isFalse(); assertThat(v.getBoolArray()).containsExactly(false, false, true).inOrder(); assertThat(v.toString()).isEqualTo("[false,false,true]"); + assertEquals("[false,false,true]", v.getAsString()); } @Test @@ -652,6 +699,7 @@ public void boolArrayNull() { assertThat(v.toString()).isEqualTo(NULL_STRING); IllegalStateException e = assertThrows(IllegalStateException.class, v::getBoolArray); assertThat(e.getMessage()).contains("null value"); + assertEquals("NULL", v.getAsString()); } @Test @@ -660,6 +708,7 @@ public void boolArrayFromList() { assertThat(v.isNull()).isFalse(); assertThat(v.getBoolArray()).containsExactly(true, null, false).inOrder(); assertThat(v.toString()).isEqualTo("[true,NULL,false]"); + assertEquals("[true,NULL,false]", v.getAsString()); } @Test @@ -669,6 +718,7 @@ public void boolArrayFromListNull() { assertThat(v.toString()).isEqualTo(NULL_STRING); IllegalStateException e = assertThrows(IllegalStateException.class, v::getBoolArray); assertThat(e.getMessage()).contains("null value"); + assertEquals("NULL", v.getAsString()); } @Test @@ -685,6 +735,11 @@ public void boolArrayFromPlainIterable() { Value v = Value.boolArray(plainIterable(data)); assertWithMessage(name).that(v.isNull()).isFalse(); assertWithMessage(name).that(v.getBoolArray()).containsExactly((Object[]) data).inOrder(); + assertEquals( + Arrays.stream(data) + .map(element -> String.valueOf(element).replace("null", "NULL")) + .collect(Collectors.joining(",", "[", "]")), + v.getAsString()); } } @@ -701,6 +756,7 @@ public void int64Array() { assertThat(v.isNull()).isFalse(); assertThat(v.getInt64Array()).containsExactly(1L, 2L).inOrder(); assertThat(v.toString()).isEqualTo("[1,2]"); + assertEquals("[1,2]", v.getAsString()); } @Test @@ -709,6 +765,7 @@ public void int64ArrayRange() { assertThat(v.isNull()).isFalse(); assertThat(v.getInt64Array()).containsExactly(2L, 3L, 4L).inOrder(); assertThat(v.toString()).isEqualTo("[2,3,4]"); + assertEquals("[2,3,4]", v.getAsString()); } @Test @@ -718,6 +775,7 @@ public void int64ArrayNull() { assertThat(v.toString()).isEqualTo(NULL_STRING); IllegalStateException e = assertThrows(IllegalStateException.class, v::getInt64Array); assertThat(e.getMessage()).contains("null value"); + assertEquals("NULL", v.getAsString()); } @Test @@ -726,6 +784,7 @@ public void int64ArrayWrapper() { assertThat(v.isNull()).isFalse(); assertThat(v.getInt64Array()).containsExactly(1L, null, 3L).inOrder(); assertThat(v.toString()).isEqualTo("[1,NULL,3]"); + assertEquals("[1,NULL,3]", v.getAsString()); } @Test @@ -735,6 +794,7 @@ public void int64ArrayWrapperNull() { assertThat(v.toString()).isEqualTo(NULL_STRING); IllegalStateException e = assertThrows(IllegalStateException.class, v::getInt64Array); assertThat(e.getMessage()).contains("null value"); + assertEquals("NULL", v.getAsString()); } @Test @@ -757,6 +817,7 @@ public void float64Array() { assertThat(v.isNull()).isFalse(); assertThat(v.getFloat64Array()).containsExactly(.1d, .2d).inOrder(); assertThat(v.toString()).isEqualTo("[0.1,0.2]"); + assertEquals("[0.1,0.2]", v.getAsString()); } @Test @@ -765,6 +826,7 @@ public void float64ArrayRange() { assertThat(v.isNull()).isFalse(); assertThat(v.getFloat64Array()).containsExactly(.2d, .3d, .4d).inOrder(); assertThat(v.toString()).isEqualTo("[0.2,0.3,0.4]"); + assertEquals("[0.2,0.3,0.4]", v.getAsString()); } @Test @@ -774,6 +836,7 @@ public void float64ArrayNull() { assertThat(v.toString()).isEqualTo(NULL_STRING); IllegalStateException e = assertThrows(IllegalStateException.class, v::getFloat64Array); assertThat(e.getMessage()).contains("null value"); + assertEquals("NULL", v.getAsString()); } @Test @@ -782,6 +845,7 @@ public void float64ArrayWrapper() { assertThat(v.isNull()).isFalse(); assertThat(v.getFloat64Array()).containsExactly(.1d, null, .3d).inOrder(); assertThat(v.toString()).isEqualTo("[0.1,NULL,0.3]"); + assertEquals("[0.1,NULL,0.3]", v.getAsString()); } @Test @@ -791,6 +855,7 @@ public void float64ArrayWrapperNull() { assertThat(v.toString()).isEqualTo(NULL_STRING); IllegalStateException e = assertThrows(IllegalStateException.class, v::getFloat64Array); assertThat(e.getMessage()).contains("null value"); + assertEquals("NULL", v.getAsString()); } @Test @@ -809,6 +874,7 @@ public void numericArray() { .containsExactly(new BigDecimal("0.1"), null, new BigDecimal("0.3")) .inOrder(); assertThat(v.toString()).isEqualTo("[0.1,NULL,0.3]"); + assertEquals("[0.1,NULL,0.3]", v.getAsString()); } @Test @@ -823,6 +889,7 @@ public void pgNumericArray() { assertEquals(1.23D, float64Array.get(0), 0.001); assertNull(float64Array.get(1)); assertEquals(1.24D, float64Array.get(2), 0.001); + assertEquals("[1.23,NULL,1.24]", value.getAsString()); } @Test @@ -835,6 +902,7 @@ public void pgNumericArrayWithNaNs() { assertEquals(1.23D, float64Array.get(0), 0.001); assertNull(float64Array.get(1)); assertEquals(Double.NaN, float64Array.get(2), 0.001); + assertEquals("[1.23,NULL,NaN]", value.getAsString()); } @Test @@ -845,6 +913,7 @@ public void numericArrayNull() { IllegalStateException e = assertThrows(IllegalStateException.class, v::getNumericArray); assertThat(e.getMessage()).contains("null value"); + assertEquals("NULL", v.getAsString()); } @Test @@ -862,6 +931,7 @@ public void pgNumericArrayNull() { final IllegalStateException e3 = assertThrows(IllegalStateException.class, value::getFloat64Array); assertTrue("exception should mention value is null", e3.getMessage().contains("null value")); + assertEquals("NULL", value.getAsString()); } @Test @@ -888,6 +958,7 @@ public void stringArray() { assertThat(v.isNull()).isFalse(); assertThat(v.getStringArray()).containsExactly("a", null, "c").inOrder(); assertThat(v.toString()).isEqualTo("[a,NULL,c]"); + assertEquals("[a,NULL,c]", v.getAsString()); } @Test @@ -897,6 +968,7 @@ public void stringArrayNull() { assertThat(v.toString()).isEqualTo(NULL_STRING); IllegalStateException e = assertThrows(IllegalStateException.class, v::getStringArray); assertThat(e.getMessage()).contains("null value"); + assertEquals("NULL", v.getAsString()); } @Test @@ -916,6 +988,7 @@ public void jsonArray() { assertArrayEquals(new String[] {one, two, three}, v.getJsonArray().toArray()); assertEquals("[{},NULL,{\"color\":\"red\",\"value\":\"#f00\"}]", v.toString()); assertArrayEquals(new String[] {one, two, three}, v.getStringArray().toArray()); + assertEquals("[{},NULL,{\"color\":\"red\",\"value\":\"#f00\"}]", v.getAsString()); } @Test @@ -925,17 +998,13 @@ public void jsonArrayNull() { assertEquals(NULL_STRING, v.toString()); assertThrowsWithMessage(v::getJsonArray, "null value"); assertThrowsWithMessage(v::getStringArray, "null value"); + assertEquals("NULL", v.getAsString()); } @Test public void jsonArrayTryGetBytesArray() { - Value value = Value.jsonArray(Arrays.asList("{}")); - try { - value.getBytesArray(); - fail("Expected exception"); - } catch (IllegalStateException e) { - assertThat(e.getMessage().contains("Expected: ARRAY actual: ARRAY")); - } + Value value = Value.jsonArray(Collections.singletonList("{}")); + assertThrowsWithMessage(value::getBytesArray, "Expected: ARRAY actual: ARRAY"); } @Test @@ -954,6 +1023,7 @@ public void testPgJsonbArray() { assertArrayEquals(new String[] {one, two, three}, v.getPgJsonbArray().toArray()); assertEquals("[{},NULL,{\"color\":\"red\",\"value\":\"#f00\"}]", v.toString()); assertArrayEquals(new String[] {one, two, three}, v.getStringArray().toArray()); + assertEquals("[{},NULL,{\"color\":\"red\",\"value\":\"#f00\"}]", v.getAsString()); } @Test @@ -963,6 +1033,7 @@ public void testPgJsonbArrayNull() { assertEquals(NULL_STRING, v.toString()); assertThrowsWithMessage(v::getPgJsonbArray, "null value"); assertThrowsWithMessage(v::getStringArray, "null value"); + assertEquals("NULL", v.getAsString()); } @Test @@ -986,7 +1057,18 @@ public void bytesArray() { Value v = Value.bytesArray(Arrays.asList(a, null, c)); assertThat(v.isNull()).isFalse(); assertThat(v.getBytesArray()).containsExactly(a, null, c).inOrder(); - assertThat(v.toString()).isEqualTo(String.format("[%s,NULL,%s]", a, c)); + assertThat(v.toString()) + .isEqualTo( + String.format( + "[%s,NULL,%s]", + Base64.getEncoder().encodeToString("a".getBytes(StandardCharsets.UTF_8)), + Base64.getEncoder().encodeToString("c".getBytes(StandardCharsets.UTF_8)))); + assertEquals( + String.format( + "[%s,NULL,%s]", + Base64.getEncoder().encodeToString("a".getBytes(StandardCharsets.UTF_8)), + Base64.getEncoder().encodeToString("c".getBytes(StandardCharsets.UTF_8))), + v.getAsString()); } @Test @@ -996,6 +1078,7 @@ public void bytesArrayNull() { assertThat(v.toString()).isEqualTo(NULL_STRING); IllegalStateException e = assertThrows(IllegalStateException.class, v::getBytesArray); assertThat(e.getMessage()).contains("null value"); + assertEquals("NULL", v.getAsString()); } @Test @@ -1017,6 +1100,7 @@ public void timestampArray() { .containsExactly(Timestamp.parseTimestamp(t1), null, Timestamp.parseTimestamp(t2)) .inOrder(); assertThat(v.toString()).isEqualTo("[" + t1 + ",NULL," + t2 + "]"); + assertEquals(String.format("[%s,NULL,%s]", t1, t2), v.getAsString()); } @Test @@ -1026,6 +1110,7 @@ public void timestampArrayNull() { assertThat(v.toString()).isEqualTo(NULL_STRING); IllegalStateException e = assertThrows(IllegalStateException.class, v::getTimestampArray); assertThat(e.getMessage()).contains("null value"); + assertEquals("NULL", v.getAsString()); } @Test @@ -1039,6 +1124,7 @@ public void dateArray() { .containsExactly(Date.parseDate(d1), null, Date.parseDate(d2)) .inOrder(); assertThat(v.toString()).isEqualTo("[" + d1 + ",NULL," + d2 + "]"); + assertEquals(String.format("[%s,NULL,%s]", d1, d2), v.getAsString()); } @Test @@ -1048,6 +1134,7 @@ public void dateArrayNull() { assertThat(v.toString()).isEqualTo(NULL_STRING); IllegalStateException e = assertThrows(IllegalStateException.class, v::getDateArray); assertThat(e.getMessage()).contains("null value"); + assertEquals("NULL", v.getAsString()); } @Test @@ -1058,6 +1145,7 @@ public void struct() { assertThat(v1.isNull()).isFalse(); assertThat(v1.getStruct()).isEqualTo(struct); assertThat(v1.toString()).isEqualTo("[v1, 30]"); + assertEquals("[v1, 30]", v1.getAsString()); Value v2 = Value.struct(struct.getType(), struct); assertThat(v2).isEqualTo(v1); @@ -1083,6 +1171,7 @@ public void nullStruct() { assertThat(v.toString()).isEqualTo(NULL_STRING); NullPointerException e = assertThrows(NullPointerException.class, () -> Value.struct(null)); assertThat(e.getMessage()).contains("Illegal call to create a NULL struct value."); + assertEquals("NULL", v.getAsString()); } @Test @@ -1095,6 +1184,7 @@ public void nullStructGetter() { assertThat(v.isNull()).isTrue(); IllegalStateException e = assertThrows(IllegalStateException.class, v::getStruct); assertThat(e.getMessage()).contains("Illegal call to getter of null value."); + assertEquals("NULL", v.getAsString()); } @Test @@ -1163,6 +1253,7 @@ public void structArray() { assertThat(v.getType().getArrayElementType()).isEqualTo(elementType); assertThat(v.getStructArray()).isEqualTo(arrayElements); assertThat(v.toString()).isEqualTo("[[v1, 1],NULL,NULL,[v3, 3]]"); + assertEquals("[[v1, 1],NULL,NULL,[v3, 3]]", v.getAsString()); } @Test @@ -1178,6 +1269,7 @@ public void structArrayNull() { assertThat(v.toString()).isEqualTo(NULL_STRING); IllegalStateException e = assertThrows(IllegalStateException.class, v::getStructArray); assertThat(e.getMessage()).contains("Illegal call to getter of null value"); + assertEquals("NULL", v.getAsString()); } @Test @@ -1792,6 +1884,48 @@ public void testEqualsHashCode() { tester.testEquals(); } + @Test + public void testGetAsString() { + assertEquals("true", Value.bool(true).getAsString()); + assertEquals("false", Value.bool(false).getAsString()); + + assertEquals("1", Value.int64(1L).getAsString()); + assertEquals(String.valueOf(Long.MAX_VALUE), Value.int64(Long.MAX_VALUE).getAsString()); + assertEquals(String.valueOf(Long.MIN_VALUE), Value.int64(Long.MIN_VALUE).getAsString()); + + assertEquals("3.14", Value.float64(3.14d).getAsString()); + assertEquals("NaN", Value.float64(Double.NaN).getAsString()); + assertEquals(String.valueOf(Double.MIN_VALUE), Value.float64(Double.MIN_VALUE).getAsString()); + assertEquals(String.valueOf(Double.MAX_VALUE), Value.float64(Double.MAX_VALUE).getAsString()); + + assertEquals("3.14", Value.numeric(new BigDecimal("3.14")).getAsString()); + assertEquals( + "123456789.123456789", Value.numeric(new BigDecimal("123456789.123456789")).getAsString()); + + assertEquals("3.14", Value.pgNumeric("3.14").getAsString()); + assertEquals("123456789.123456789", Value.pgNumeric("123456789.123456789").getAsString()); + assertEquals("NaN", Value.pgNumeric("NaN").getAsString()); + + assertEquals(Strings.repeat("foo", 36), Value.string(Strings.repeat("foo", 36)).getAsString()); + assertEquals(Strings.repeat("foo", 36), Value.json(Strings.repeat("foo", 36)).getAsString()); + assertEquals(Strings.repeat("foo", 36), Value.pgJsonb(Strings.repeat("foo", 36)).getAsString()); + + assertEquals( + "2023-01-10T18:59:00Z", + Value.timestamp(Timestamp.parseTimestamp("2023-01-10T18:59:00Z")).getAsString()); + assertEquals("2023-01-10", Value.date(Date.parseDate("2023-01-10")).getAsString()); + + Random random = new Random(); + byte[] bytes = new byte[random.nextInt(256)]; + assertEquals( + Base64.getEncoder().encodeToString(bytes), + Value.bytes(ByteArray.copyFrom(bytes)).getAsString()); + assertEquals( + Base64.getEncoder().encodeToString(bytes), + Value.internalBytes(new LazyByteArray(Base64.getEncoder().encodeToString(bytes))) + .getAsString()); + } + @Test public void serialization() { diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/RandomResultSetGenerator.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/RandomResultSetGenerator.java index c3ac655a40e..f8415fb00eb 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/RandomResultSetGenerator.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/RandomResultSetGenerator.java @@ -39,7 +39,7 @@ * of Cloud Spanner filled with random data. */ public class RandomResultSetGenerator { - private static Type[] generateTypes(Dialect dialect) { + public static Type[] generateAllTypes(Dialect dialect) { return new Type[] { Type.newBuilder().setCode(TypeCode.BOOL).build(), Type.newBuilder().setCode(TypeCode.INT64).build(), @@ -109,7 +109,7 @@ private static Type[] generateTypes(Dialect dialect) { }; } - private static ResultSetMetadata generateMetadata(Type[] types) { + public static ResultSetMetadata generateAllTypesMetadata(Type[] types) { StructType.Builder rowTypeBuilder = StructType.newBuilder(); for (int col = 0; col < types.length; col++) { rowTypeBuilder.addFields(Field.newBuilder().setName("COL" + col).setType(types[col])).build(); @@ -132,8 +132,8 @@ public RandomResultSetGenerator(int rowCount) { public RandomResultSetGenerator(int rowCount, Dialect dialect) { this.rowCount = rowCount; this.dialect = dialect; - this.types = generateTypes(dialect); - this.metadata = generateMetadata(types); + this.types = generateAllTypes(dialect); + this.metadata = generateAllTypesMetadata(types); } public ResultSet generate() { 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 2428e75c994..3179093139e 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 @@ -21,7 +21,9 @@ import static com.google.cloud.spanner.Type.json; import static com.google.cloud.spanner.testing.EmulatorSpannerHelper.isUsingEmulator; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; import static org.junit.Assume.assumeFalse; @@ -42,6 +44,7 @@ import com.google.cloud.spanner.ParallelIntegrationTest; import com.google.cloud.spanner.ResultSet; import com.google.cloud.spanner.SpannerException; +import com.google.cloud.spanner.Statement; import com.google.cloud.spanner.Struct; import com.google.cloud.spanner.TimestampBound; import com.google.cloud.spanner.Value; @@ -52,6 +55,7 @@ import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; +import java.util.Base64; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -431,6 +435,55 @@ public void writeBytes() { assertThat(row.getBytes(0)).isEqualTo(data); } + @Test + public void writeBytesAsString() { + Random random = new Random(); + byte[] data = new byte[256]; + random.nextBytes(data); + String base64 = Base64.getEncoder().encodeToString(data); + write(baseInsert().set("BytesValue").to(base64).build()); + Struct row = readLastRow("BytesValue"); + assertFalse(row.isNull(0)); + assertArrayEquals(data, row.getBytes(0).toByteArray()); + assertEquals(base64, row.getValue(0).getAsString()); + } + + @Test + public void writeBytesAsStringUsingDml() { + Random random = new Random(); + byte[] data = new byte[256]; + random.nextBytes(data); + String base64 = Base64.getEncoder().encodeToString(data); + Long updateCount = + client + .readWriteTransaction() + .run( + transaction -> + transaction.executeUpdate( + Statement.newBuilder( + "insert into T (BytesValue, K) values (" + + queryParamString(1) + + ", " + + queryParamString(2) + + ")") + .bind("p1") + .to(Value.bytesFromBase64(base64)) + .bind("p2") + .to(lastKey = uniqueString()) + .build())); + assertNotNull(updateCount); + assertEquals(1L, updateCount.longValue()); + + Struct row = readLastRow("BytesValue"); + assertFalse(row.isNull(0)); + assertArrayEquals(data, row.getBytes(0).toByteArray()); + assertEquals(base64, row.getValue(0).getAsString()); + } + + String queryParamString(int index) { + return dialect.dialect == Dialect.GOOGLE_STANDARD_SQL ? "@p" + index : "$" + index; + } + @Test public void writeBytesRandom() { // Pseudo-random test for byte encoding. We explicitly set a random seed so that multiple From b5fdbc0accdaaf1f63c62c1837d72bb378dc8f43 Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Mon, 6 Feb 2023 05:12:18 +0000 Subject: [PATCH 03/10] deps: update dependency com.google.cloud:google-cloud-shared-dependencies to v3.2.0 (#2264) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * deps: update dependency com.google.cloud:google-cloud-shared-dependencies to v3.2.0 * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --------- Co-authored-by: Owl Bot --- README.md | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 93619201701..d5db9eb8177 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ If you are using Maven without 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.5.0') +implementation platform('com.google.cloud:libraries-bom:26.6.0') implementation 'com.google.cloud:google-cloud-spanner' ``` diff --git a/pom.xml b/pom.xml index b56c36dd17e..db45c17d5fa 100644 --- a/pom.xml +++ b/pom.xml @@ -54,7 +54,7 @@ UTF-8 github google-cloud-spanner-parent - 3.1.2 + 3.2.0 From d566613442217bdfc69caea7242464fba2647519 Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Mon, 6 Feb 2023 05:52:11 +0000 Subject: [PATCH 04/10] deps: update dependency com.google.cloud:google-cloud-monitoring to v3.11.0 (#2262) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com) This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [com.google.cloud:google-cloud-monitoring](https://togithub.com/googleapis/google-cloud-java) | `3.10.0` -> `3.11.0` | [![age](https://badges.renovateapi.com/packages/maven/com.google.cloud:google-cloud-monitoring/3.11.0/age-slim)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://badges.renovateapi.com/packages/maven/com.google.cloud:google-cloud-monitoring/3.11.0/adoption-slim)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://badges.renovateapi.com/packages/maven/com.google.cloud:google-cloud-monitoring/3.11.0/compatibility-slim/3.10.0)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://badges.renovateapi.com/packages/maven/com.google.cloud:google-cloud-monitoring/3.11.0/confidence-slim/3.10.0)](https://docs.renovatebot.com/merge-confidence/) | --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://app.renovatebot.com/dashboard#github/googleapis/java-spanner). --- samples/install-without-bom/pom.xml | 2 +- samples/snapshot/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/install-without-bom/pom.xml b/samples/install-without-bom/pom.xml index 25e43d89b15..2d56c213fe6 100644 --- a/samples/install-without-bom/pom.xml +++ b/samples/install-without-bom/pom.xml @@ -23,7 +23,7 @@ UTF-8 0.31.1 2.9.0 - 3.10.0 + 3.11.0 diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml index 3bd15402b84..95e73a87ccb 100644 --- a/samples/snapshot/pom.xml +++ b/samples/snapshot/pom.xml @@ -23,7 +23,7 @@ UTF-8 0.31.1 2.9.0 - 3.10.0 + 3.11.0 From 3767b443d034b9dbf1fc186a2379c96619c01a31 Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Tue, 7 Feb 2023 15:27:21 +0000 Subject: [PATCH 05/10] chore(deps): update dependency com.google.cloud:google-cloud-spanner to v6.35.2 (#2255) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore(deps): update dependency com.google.cloud:google-cloud-spanner to v6.35.2 * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --------- Co-authored-by: Owl Bot --- README.md | 2 +- samples/install-without-bom/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d5db9eb8177..e9bf19444e9 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ If you are using Maven without BOM, add this to your dependencies: com.google.cloud google-cloud-spanner - 6.35.1 + 6.35.2 ``` diff --git a/samples/install-without-bom/pom.xml b/samples/install-without-bom/pom.xml index 2d56c213fe6..7b9741fcbf6 100644 --- a/samples/install-without-bom/pom.xml +++ b/samples/install-without-bom/pom.xml @@ -32,7 +32,7 @@ com.google.cloud google-cloud-spanner - 6.35.1 + 6.35.2 From e7f4b4016f8c4c7e4fac0b822f5af2cffd181134 Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Tue, 7 Feb 2023 16:32:11 +0000 Subject: [PATCH 06/10] fix(java): skip fixing poms for special modules (#1744) (#2244) fix(java): handle empty modules Fixes https://togithub.com/googleapis/synthtool/issues/1743 Source-Link: https://togithub.com/googleapis/synthtool/commit/482d649d5d705c18028076985f64b214f6b6c14e Post-Processor: gcr.io/cloud-devrel-public-resources/owlbot-java:latest@sha256:bd5071596a47614d1fe15eb766c4255bae330f823b606e1196a3b0c8d2e96fd1 --- .github/.OwlBot.lock.yaml | 2 +- .kokoro/requirements.txt | 48 +++++++++++++++++++-------------------- 2 files changed, 24 insertions(+), 26 deletions(-) diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index 2b48c81e372..f8a8575314c 100644 --- a/.github/.OwlBot.lock.yaml +++ b/.github/.OwlBot.lock.yaml @@ -13,4 +13,4 @@ # limitations under the License. docker: image: gcr.io/cloud-devrel-public-resources/owlbot-java:latest - digest: sha256:4566cf7fcd0eece77705330267b2b61a70165711c7829d049b60bc2f1bbcd74e + digest: sha256:bd5071596a47614d1fe15eb766c4255bae330f823b606e1196a3b0c8d2e96fd1 diff --git a/.kokoro/requirements.txt b/.kokoro/requirements.txt index 1dfe630412d..4895c9631fd 100644 --- a/.kokoro/requirements.txt +++ b/.kokoro/requirements.txt @@ -307,31 +307,29 @@ packaging==21.3 \ --hash=sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb \ --hash=sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522 # via gcp-releasetool -protobuf==3.20.1 \ - --hash=sha256:06059eb6953ff01e56a25cd02cca1a9649a75a7e65397b5b9b4e929ed71d10cf \ - --hash=sha256:097c5d8a9808302fb0da7e20edf0b8d4703274d140fd25c5edabddcde43e081f \ - --hash=sha256:284f86a6207c897542d7e956eb243a36bb8f9564c1742b253462386e96c6b78f \ - --hash=sha256:32ca378605b41fd180dfe4e14d3226386d8d1b002ab31c969c366549e66a2bb7 \ - --hash=sha256:3cc797c9d15d7689ed507b165cd05913acb992d78b379f6014e013f9ecb20996 \ - --hash=sha256:62f1b5c4cd6c5402b4e2d63804ba49a327e0c386c99b1675c8a0fefda23b2067 \ - --hash=sha256:69ccfdf3657ba59569c64295b7d51325f91af586f8d5793b734260dfe2e94e2c \ - --hash=sha256:6f50601512a3d23625d8a85b1638d914a0970f17920ff39cec63aaef80a93fb7 \ - --hash=sha256:7403941f6d0992d40161aa8bb23e12575637008a5a02283a930addc0508982f9 \ - --hash=sha256:755f3aee41354ae395e104d62119cb223339a8f3276a0cd009ffabfcdd46bb0c \ - --hash=sha256:77053d28427a29987ca9caf7b72ccafee011257561259faba8dd308fda9a8739 \ - --hash=sha256:7e371f10abe57cee5021797126c93479f59fccc9693dafd6bd5633ab67808a91 \ - --hash=sha256:9016d01c91e8e625141d24ec1b20fed584703e527d28512aa8c8707f105a683c \ - --hash=sha256:9be73ad47579abc26c12024239d3540e6b765182a91dbc88e23658ab71767153 \ - --hash=sha256:adc31566d027f45efe3f44eeb5b1f329da43891634d61c75a5944e9be6dd42c9 \ - --hash=sha256:adfc6cf69c7f8c50fd24c793964eef18f0ac321315439d94945820612849c388 \ - --hash=sha256:af0ebadc74e281a517141daad9d0f2c5d93ab78e9d455113719a45a49da9db4e \ - --hash=sha256:cb29edb9eab15742d791e1025dd7b6a8f6fcb53802ad2f6e3adcb102051063ab \ - --hash=sha256:cd68be2559e2a3b84f517fb029ee611546f7812b1fdd0aa2ecc9bc6ec0e4fdde \ - --hash=sha256:cdee09140e1cd184ba9324ec1df410e7147242b94b5f8b0c64fc89e38a8ba531 \ - --hash=sha256:db977c4ca738dd9ce508557d4fce0f5aebd105e158c725beec86feb1f6bc20d8 \ - --hash=sha256:dd5789b2948ca702c17027c84c2accb552fc30f4622a98ab5c51fcfe8c50d3e7 \ - --hash=sha256:e250a42f15bf9d5b09fe1b293bdba2801cd520a9f5ea2d7fb7536d4441811d20 \ - --hash=sha256:ff8d8fa42675249bb456f5db06c00de6c2f4c27a065955917b28c4f15978b9c3 +protobuf==3.20.2 \ + --hash=sha256:03d76b7bd42ac4a6e109742a4edf81ffe26ffd87c5993126d894fe48a120396a \ + --hash=sha256:09e25909c4297d71d97612f04f41cea8fa8510096864f2835ad2f3b3df5a5559 \ + --hash=sha256:18e34a10ae10d458b027d7638a599c964b030c1739ebd035a1dfc0e22baa3bfe \ + --hash=sha256:291fb4307094bf5ccc29f424b42268640e00d5240bf0d9b86bf3079f7576474d \ + --hash=sha256:2c0b040d0b5d5d207936ca2d02f00f765906622c07d3fa19c23a16a8ca71873f \ + --hash=sha256:384164994727f274cc34b8abd41a9e7e0562801361ee77437099ff6dfedd024b \ + --hash=sha256:3cb608e5a0eb61b8e00fe641d9f0282cd0eedb603be372f91f163cbfbca0ded0 \ + --hash=sha256:5d9402bf27d11e37801d1743eada54372f986a372ec9679673bfcc5c60441151 \ + --hash=sha256:712dca319eee507a1e7df3591e639a2b112a2f4a62d40fe7832a16fd19151750 \ + --hash=sha256:7a5037af4e76c975b88c3becdf53922b5ffa3f2cddf657574a4920a3b33b80f3 \ + --hash=sha256:8228e56a865c27163d5d1d1771d94b98194aa6917bcfb6ce139cbfa8e3c27334 \ + --hash=sha256:84a1544252a933ef07bb0b5ef13afe7c36232a774affa673fc3636f7cee1db6c \ + --hash=sha256:84fe5953b18a383fd4495d375fe16e1e55e0a3afe7b4f7b4d01a3a0649fcda9d \ + --hash=sha256:9c673c8bfdf52f903081816b9e0e612186684f4eb4c17eeb729133022d6032e3 \ + --hash=sha256:a9e5ae5a8e8985c67e8944c23035a0dff2c26b0f5070b2f55b217a1c33bbe8b1 \ + --hash=sha256:b4fdb29c5a7406e3f7ef176b2a7079baa68b5b854f364c21abe327bbeec01cdb \ + --hash=sha256:c184485e0dfba4dfd451c3bd348c2e685d6523543a0f91b9fd4ae90eb09e8422 \ + --hash=sha256:c9cdf251c582c16fd6a9f5e95836c90828d51b0069ad22f463761d27c6c19019 \ + --hash=sha256:e39cf61bb8582bda88cdfebc0db163b774e7e03364bbf9ce1ead13863e81e359 \ + --hash=sha256:e8fbc522303e09036c752a0afcc5c0603e917222d8bedc02813fd73b4b4ed804 \ + --hash=sha256:f34464ab1207114e73bba0794d1257c150a2b89b7a9faf504e00af7c9fd58978 \ + --hash=sha256:f52dabc96ca99ebd2169dadbe018824ebda08a795c7684a0b7d203a290f3adb0 # via # gcp-docuploader # gcp-releasetool From 96f0c8181aeb8ca75647a783d8b163f371ad937e Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Tue, 7 Feb 2023 16:34:11 +0000 Subject: [PATCH 07/10] deps: update dependency com.google.cloud:google-cloud-trace to v2.10.0 (#2263) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com) This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [com.google.cloud:google-cloud-trace](https://togithub.com/googleapis/google-cloud-java) | `2.9.0` -> `2.10.0` | [![age](https://badges.renovateapi.com/packages/maven/com.google.cloud:google-cloud-trace/2.10.0/age-slim)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://badges.renovateapi.com/packages/maven/com.google.cloud:google-cloud-trace/2.10.0/adoption-slim)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://badges.renovateapi.com/packages/maven/com.google.cloud:google-cloud-trace/2.10.0/compatibility-slim/2.9.0)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://badges.renovateapi.com/packages/maven/com.google.cloud:google-cloud-trace/2.10.0/confidence-slim/2.9.0)](https://docs.renovatebot.com/merge-confidence/) | --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://app.renovatebot.com/dashboard#github/googleapis/java-spanner). --- samples/install-without-bom/pom.xml | 2 +- samples/snapshot/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/install-without-bom/pom.xml b/samples/install-without-bom/pom.xml index 7b9741fcbf6..5f8a8e8fcd6 100644 --- a/samples/install-without-bom/pom.xml +++ b/samples/install-without-bom/pom.xml @@ -22,7 +22,7 @@ 1.8 UTF-8 0.31.1 - 2.9.0 + 2.10.0 3.11.0 diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml index 95e73a87ccb..e626cf71fc0 100644 --- a/samples/snapshot/pom.xml +++ b/samples/snapshot/pom.xml @@ -22,7 +22,7 @@ 1.8 UTF-8 0.31.1 - 2.9.0 + 2.10.0 3.11.0 From 510d0fb4b24426cc170dbb921a2486476e71291c Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Wed, 8 Feb 2023 05:34:13 +0000 Subject: [PATCH 08/10] chore(deps): update dependency com.google.cloud:libraries-bom to v26.6.0 (#2257) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com) This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [com.google.cloud:libraries-bom](https://cloud.google.com/java/docs/bom) ([source](https://togithub.com/googleapis/java-cloud-bom)) | `26.4.0` -> `26.6.0` | [![age](https://badges.renovateapi.com/packages/maven/com.google.cloud:libraries-bom/26.6.0/age-slim)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://badges.renovateapi.com/packages/maven/com.google.cloud:libraries-bom/26.6.0/adoption-slim)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://badges.renovateapi.com/packages/maven/com.google.cloud:libraries-bom/26.6.0/compatibility-slim/26.4.0)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://badges.renovateapi.com/packages/maven/com.google.cloud:libraries-bom/26.6.0/confidence-slim/26.4.0)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes

googleapis/java-cloud-bom ### [`v26.5.0`](https://togithub.com/googleapis/java-cloud-bom/blob/HEAD/CHANGELOG.md#​2650-httpsgithubcomgoogleapisjava-cloud-bomcomparev2640v2650-2023-01-26) [Compare Source](https://togithub.com/googleapis/java-cloud-bom/compare/v26.4.0...v26.5.0) ##### Bug Fixes - **java:** skip fixing poms for special modules ([#​1744](https://togithub.com/googleapis/java-cloud-bom/issues/1744)) ([#​5751](https://togithub.com/googleapis/java-cloud-bom/issues/5751)) ([38e6519](https://togithub.com/googleapis/java-cloud-bom/commit/38e6519614590673a360d658635d42f15fb11bb2)) ##### Dependencies - update dependency com.google.cloud:first-party-dependencies to v3.1.2 ([#​5752](https://togithub.com/googleapis/java-cloud-bom/issues/5752)) ([d2158e0](https://togithub.com/googleapis/java-cloud-bom/commit/d2158e0a03c6b1a59aec053abd95999b9ef5d19b)) - update dependency com.google.cloud:gapic-libraries-bom to v1.3.0 ([#​5765](https://togithub.com/googleapis/java-cloud-bom/issues/5765)) ([c338517](https://togithub.com/googleapis/java-cloud-bom/commit/c3385174ca221561460e2d7930f04034f7e57def)) - update dependency com.google.cloud:google-cloud-bigquery to v2.21.0 ([#​5767](https://togithub.com/googleapis/java-cloud-bom/issues/5767)) ([b15c09f](https://togithub.com/googleapis/java-cloud-bom/commit/b15c09f10bb27b831d69ca812015f8a668d532c8)) - update dependency com.google.cloud:google-cloud-bigquerystorage-bom to v2.28.2 ([#​5748](https://togithub.com/googleapis/java-cloud-bom/issues/5748)) ([1d2314c](https://togithub.com/googleapis/java-cloud-bom/commit/1d2314c07b6d76d867dd453646933d65afcfe97a)) - update dependency com.google.cloud:google-cloud-bigquerystorage-bom to v2.28.3 ([#​5755](https://togithub.com/googleapis/java-cloud-bom/issues/5755)) ([7497a81](https://togithub.com/googleapis/java-cloud-bom/commit/7497a818650a8c31eb03137d217792d9cd36384f)) - update dependency com.google.cloud:google-cloud-bigquerystorage-bom to v2.28.4 ([#​5778](https://togithub.com/googleapis/java-cloud-bom/issues/5778)) ([7709bde](https://togithub.com/googleapis/java-cloud-bom/commit/7709bdeeab818da69c60e3b9f50e415c1a3afb25)) - update dependency com.google.cloud:google-cloud-bigtable-bom to v2.18.2 ([#​5768](https://togithub.com/googleapis/java-cloud-bom/issues/5768)) ([c7afa6d](https://togithub.com/googleapis/java-cloud-bom/commit/c7afa6d6410ba7c5b2d12f4bb20a250743fb074d)) - update dependency com.google.cloud:google-cloud-bigtable-bom to v2.18.3 ([#​5775](https://togithub.com/googleapis/java-cloud-bom/issues/5775)) ([f6650d4](https://togithub.com/googleapis/java-cloud-bom/commit/f6650d4086db89d48c72e4e3ffc4d75440cee3d2)) - update dependency com.google.cloud:google-cloud-datastore-bom to v2.13.3 ([#​5764](https://togithub.com/googleapis/java-cloud-bom/issues/5764)) ([1f3cbf0](https://togithub.com/googleapis/java-cloud-bom/commit/1f3cbf05df8445eefd67cadf36e0238b95417975)) - update dependency com.google.cloud:google-cloud-firestore-bom to v3.7.7 ([#​5746](https://togithub.com/googleapis/java-cloud-bom/issues/5746)) ([1bff51d](https://togithub.com/googleapis/java-cloud-bom/commit/1bff51dcc5aea760db8fa320f58ea7b74737a18e)) - update dependency com.google.cloud:google-cloud-firestore-bom to v3.7.8 ([#​5753](https://togithub.com/googleapis/java-cloud-bom/issues/5753)) ([46de6d4](https://togithub.com/googleapis/java-cloud-bom/commit/46de6d4bd340de28dd2522be89ab83dd84fdf2e3)) - update dependency com.google.cloud:google-cloud-logging-bom to v3.14.2 ([#​5773](https://togithub.com/googleapis/java-cloud-bom/issues/5773)) ([70de9fa](https://togithub.com/googleapis/java-cloud-bom/commit/70de9faa54c64a7536ea1e8588cf51247e16f2f4)) - update dependency com.google.cloud:google-cloud-logging-logback to v0.130.1-alpha ([#​5772](https://togithub.com/googleapis/java-cloud-bom/issues/5772)) ([b3d314b](https://togithub.com/googleapis/java-cloud-bom/commit/b3d314b791b1a7f648c679a48b1684a93ff8c8c3)) - update dependency com.google.cloud:google-cloud-nio to v0.126.2 ([#​5754](https://togithub.com/googleapis/java-cloud-bom/issues/5754)) ([3968ea1](https://togithub.com/googleapis/java-cloud-bom/commit/3968ea16998404de3611282d1a9c28d90125a3ec)) - update dependency com.google.cloud:google-cloud-nio to v0.126.3 ([#​5763](https://togithub.com/googleapis/java-cloud-bom/issues/5763)) ([360ac45](https://togithub.com/googleapis/java-cloud-bom/commit/360ac45e87dde64796a175e75984d0bacfe16e96)) - update dependency com.google.cloud:google-cloud-pubsub-bom to v1.123.1 ([#​5756](https://togithub.com/googleapis/java-cloud-bom/issues/5756)) ([347291e](https://togithub.com/googleapis/java-cloud-bom/commit/347291e84f227a690e0311fdf21db40d23b261c8)) - update dependency com.google.cloud:google-cloud-pubsublite-bom to v1.9.4 ([#​5774](https://togithub.com/googleapis/java-cloud-bom/issues/5774)) ([7f7ce27](https://togithub.com/googleapis/java-cloud-bom/commit/7f7ce277d47fa365ce46ec70c1e309365ad89d55)) - update dependency com.google.cloud:google-cloud-spanner-bom to v6.35.1 ([#​5747](https://togithub.com/googleapis/java-cloud-bom/issues/5747)) ([1b7a888](https://togithub.com/googleapis/java-cloud-bom/commit/1b7a888688528165954c093ebedbe36593db61a7)) - update dependency com.google.cloud:google-cloud-spanner-bom to v6.35.2 ([#​5780](https://togithub.com/googleapis/java-cloud-bom/issues/5780)) ([5363ef9](https://togithub.com/googleapis/java-cloud-bom/commit/5363ef9a09ee5df0a26831ddf79fb4a4020b5785)) - update dependency com.google.cloud:google-cloud-spanner-jdbc to v2.9.3 ([#​5779](https://togithub.com/googleapis/java-cloud-bom/issues/5779)) ([bdc5bac](https://togithub.com/googleapis/java-cloud-bom/commit/bdc5bac03ef0db90dc9e987e065e2fd032bfecae)) - update dependency com.google.cloud:google-cloud-storage-bom to v2.17.2 ([#​5762](https://togithub.com/googleapis/java-cloud-bom/issues/5762)) ([d94c669](https://togithub.com/googleapis/java-cloud-bom/commit/d94c6698789a7e0de0266900140bd6d4c4c00a05))
--- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://app.renovatebot.com/dashboard#github/googleapis/java-spanner). --- README.md | 2 +- samples/native-image/pom.xml | 2 +- samples/snippets/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e9bf19444e9..36d133e20f4 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.4.0 + 26.6.0 pom import diff --git a/samples/native-image/pom.xml b/samples/native-image/pom.xml index 99f3776ae7f..08aac76c543 100644 --- a/samples/native-image/pom.xml +++ b/samples/native-image/pom.xml @@ -28,7 +28,7 @@ com.google.cloud libraries-bom - 26.4.0 + 26.6.0 pom import diff --git a/samples/snippets/pom.xml b/samples/snippets/pom.xml index 929db126e8e..311bcc42321 100644 --- a/samples/snippets/pom.xml +++ b/samples/snippets/pom.xml @@ -33,7 +33,7 @@ com.google.cloud libraries-bom - 26.4.0 + 26.6.0 pom import From fa3bfd3d93e5b3ed33da94229f7dc473effda4e9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Feb 2023 19:20:57 +0530 Subject: [PATCH 09/10] build(deps): bump protobuf from 3.20.1 to 3.20.2 in /.kokoro (#2252) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * build(deps): bump protobuf from 3.20.1 to 3.20.2 in /.kokoro Bumps [protobuf](https://github.com/protocolbuffers/protobuf) from 3.20.1 to 3.20.2. - [Release notes](https://github.com/protocolbuffers/protobuf/releases) - [Changelog](https://github.com/protocolbuffers/protobuf/blob/main/generate_changelog.py) - [Commits](https://github.com/protocolbuffers/protobuf/compare/v3.20.1...v3.20.2) --- updated-dependencies: - dependency-name: protobuf dependency-type: indirect ... Signed-off-by: dependabot[bot] * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Owl Bot From 1ee11e579bae63e7b988e3b4740262c26ef004b9 Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Wed, 8 Feb 2023 15:46:19 +0000 Subject: [PATCH 10/10] chore(main): release 6.36.0 (#2260) :robot: I have created a release *beep* *boop* --- ## [6.36.0](https://togithub.com/googleapis/java-spanner/compare/v6.35.2...v6.36.0) (2023-02-08) ### Features * Support UNRECOGNIZED types + decode BYTES columns lazily ([#2219](https://togithub.com/googleapis/java-spanner/issues/2219)) ([fc721c4](https://togithub.com/googleapis/java-spanner/commit/fc721c4d30de6ed9e5bc4fbbe0e1e7b79a5c7490)) ### Bug Fixes * **java:** Skip fixing poms for special modules ([#1744](https://togithub.com/googleapis/java-spanner/issues/1744)) ([#2244](https://togithub.com/googleapis/java-spanner/issues/2244)) ([e7f4b40](https://togithub.com/googleapis/java-spanner/commit/e7f4b4016f8c4c7e4fac0b822f5af2cffd181134)) ### Dependencies * Update dependency com.google.cloud:google-cloud-monitoring to v3.11.0 ([#2262](https://togithub.com/googleapis/java-spanner/issues/2262)) ([d566613](https://togithub.com/googleapis/java-spanner/commit/d566613442217bdfc69caea7242464fba2647519)) * Update dependency com.google.cloud:google-cloud-shared-dependencies to v3.2.0 ([#2264](https://togithub.com/googleapis/java-spanner/issues/2264)) ([b5fdbc0](https://togithub.com/googleapis/java-spanner/commit/b5fdbc0accdaaf1f63c62c1837d72bb378dc8f43)) * Update dependency com.google.cloud:google-cloud-trace to v2.10.0 ([#2263](https://togithub.com/googleapis/java-spanner/issues/2263)) ([96f0c81](https://togithub.com/googleapis/java-spanner/commit/96f0c8181aeb8ca75647a783d8b163f371ad937e)) --- This PR was generated with [Release Please](https://togithub.com/googleapis/release-please). See [documentation](https://togithub.com/googleapis/release-please#release-please). --- CHANGELOG.md | 19 ++++++++++++++++++ google-cloud-spanner-bom/pom.xml | 20 +++++++++---------- google-cloud-spanner-executor/pom.xml | 4 ++-- google-cloud-spanner/pom.xml | 4 ++-- .../pom.xml | 4 ++-- .../pom.xml | 4 ++-- grpc-google-cloud-spanner-v1/pom.xml | 4 ++-- pom.xml | 16 +++++++-------- .../pom.xml | 4 ++-- .../pom.xml | 4 ++-- proto-google-cloud-spanner-v1/pom.xml | 4 ++-- samples/snapshot/pom.xml | 2 +- versions.txt | 16 +++++++-------- 13 files changed, 62 insertions(+), 43 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fcb51e5001f..175da69cefb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,24 @@ # Changelog +## [6.36.0](https://github.com/googleapis/java-spanner/compare/v6.35.2...v6.36.0) (2023-02-08) + + +### Features + +* Support UNRECOGNIZED types + decode BYTES columns lazily ([#2219](https://github.com/googleapis/java-spanner/issues/2219)) ([fc721c4](https://github.com/googleapis/java-spanner/commit/fc721c4d30de6ed9e5bc4fbbe0e1e7b79a5c7490)) + + +### Bug Fixes + +* **java:** Skip fixing poms for special modules ([#1744](https://github.com/googleapis/java-spanner/issues/1744)) ([#2244](https://github.com/googleapis/java-spanner/issues/2244)) ([e7f4b40](https://github.com/googleapis/java-spanner/commit/e7f4b4016f8c4c7e4fac0b822f5af2cffd181134)) + + +### Dependencies + +* Update dependency com.google.cloud:google-cloud-monitoring to v3.11.0 ([#2262](https://github.com/googleapis/java-spanner/issues/2262)) ([d566613](https://github.com/googleapis/java-spanner/commit/d566613442217bdfc69caea7242464fba2647519)) +* Update dependency com.google.cloud:google-cloud-shared-dependencies to v3.2.0 ([#2264](https://github.com/googleapis/java-spanner/issues/2264)) ([b5fdbc0](https://github.com/googleapis/java-spanner/commit/b5fdbc0accdaaf1f63c62c1837d72bb378dc8f43)) +* Update dependency com.google.cloud:google-cloud-trace to v2.10.0 ([#2263](https://github.com/googleapis/java-spanner/issues/2263)) ([96f0c81](https://github.com/googleapis/java-spanner/commit/96f0c8181aeb8ca75647a783d8b163f371ad937e)) + ## [6.35.2](https://github.com/googleapis/java-spanner/compare/v6.35.1...v6.35.2) (2023-01-24) diff --git a/google-cloud-spanner-bom/pom.xml b/google-cloud-spanner-bom/pom.xml index 5124d6d773c..8ec90443ed7 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 - 6.35.3-SNAPSHOT + 6.36.0 pom com.google.cloud @@ -53,48 +53,48 @@ com.google.cloud google-cloud-spanner - 6.35.3-SNAPSHOT + 6.36.0 com.google.cloud google-cloud-spanner-executor - 6.35.3-SNAPSHOT + 6.36.0 com.google.cloud google-cloud-spanner test-jar - 6.35.3-SNAPSHOT + 6.36.0 com.google.api.grpc grpc-google-cloud-spanner-v1 - 6.35.3-SNAPSHOT + 6.36.0 com.google.api.grpc grpc-google-cloud-spanner-admin-instance-v1 - 6.35.3-SNAPSHOT + 6.36.0 com.google.api.grpc grpc-google-cloud-spanner-admin-database-v1 - 6.35.3-SNAPSHOT + 6.36.0 com.google.api.grpc proto-google-cloud-spanner-admin-instance-v1 - 6.35.3-SNAPSHOT + 6.36.0 com.google.api.grpc proto-google-cloud-spanner-v1 - 6.35.3-SNAPSHOT + 6.36.0 com.google.api.grpc proto-google-cloud-spanner-admin-database-v1 - 6.35.3-SNAPSHOT + 6.36.0 diff --git a/google-cloud-spanner-executor/pom.xml b/google-cloud-spanner-executor/pom.xml index 053c865e490..4468a8f405c 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.35.3-SNAPSHOT + 6.36.0 jar Google Cloud Spanner Executor com.google.cloud google-cloud-spanner-parent - 6.35.3-SNAPSHOT + 6.36.0 diff --git a/google-cloud-spanner/pom.xml b/google-cloud-spanner/pom.xml index f54abea2d2d..f596fcc10af 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.35.3-SNAPSHOT + 6.36.0 jar Google Cloud Spanner https://github.com/googleapis/java-spanner @@ -11,7 +11,7 @@ com.google.cloud google-cloud-spanner-parent - 6.35.3-SNAPSHOT + 6.36.0 google-cloud-spanner diff --git a/grpc-google-cloud-spanner-admin-database-v1/pom.xml b/grpc-google-cloud-spanner-admin-database-v1/pom.xml index 5c678cacc65..3a0ce0140e0 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.35.3-SNAPSHOT + 6.36.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.35.3-SNAPSHOT + 6.36.0 diff --git a/grpc-google-cloud-spanner-admin-instance-v1/pom.xml b/grpc-google-cloud-spanner-admin-instance-v1/pom.xml index 16936b0fd00..9aec15a990d 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.35.3-SNAPSHOT + 6.36.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.35.3-SNAPSHOT + 6.36.0 diff --git a/grpc-google-cloud-spanner-v1/pom.xml b/grpc-google-cloud-spanner-v1/pom.xml index a176356829d..f693d3c23a5 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.35.3-SNAPSHOT + 6.36.0 grpc-google-cloud-spanner-v1 GRPC library for grpc-google-cloud-spanner-v1 com.google.cloud google-cloud-spanner-parent - 6.35.3-SNAPSHOT + 6.36.0 diff --git a/pom.xml b/pom.xml index db45c17d5fa..8d1ee45a4ff 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.google.cloud google-cloud-spanner-parent pom - 6.35.3-SNAPSHOT + 6.36.0 Google Cloud Spanner Parent https://github.com/googleapis/java-spanner @@ -62,37 +62,37 @@ com.google.api.grpc proto-google-cloud-spanner-admin-instance-v1 - 6.35.3-SNAPSHOT + 6.36.0 com.google.api.grpc proto-google-cloud-spanner-v1 - 6.35.3-SNAPSHOT + 6.36.0 com.google.api.grpc proto-google-cloud-spanner-admin-database-v1 - 6.35.3-SNAPSHOT + 6.36.0 com.google.api.grpc grpc-google-cloud-spanner-v1 - 6.35.3-SNAPSHOT + 6.36.0 com.google.api.grpc grpc-google-cloud-spanner-admin-instance-v1 - 6.35.3-SNAPSHOT + 6.36.0 com.google.api.grpc grpc-google-cloud-spanner-admin-database-v1 - 6.35.3-SNAPSHOT + 6.36.0 com.google.cloud google-cloud-spanner - 6.35.3-SNAPSHOT + 6.36.0 diff --git a/proto-google-cloud-spanner-admin-database-v1/pom.xml b/proto-google-cloud-spanner-admin-database-v1/pom.xml index 2ffc6a76586..430ef5e2cb4 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.35.3-SNAPSHOT + 6.36.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.35.3-SNAPSHOT + 6.36.0 diff --git a/proto-google-cloud-spanner-admin-instance-v1/pom.xml b/proto-google-cloud-spanner-admin-instance-v1/pom.xml index e761e4821c3..b3eb1283d4a 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.35.3-SNAPSHOT + 6.36.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.35.3-SNAPSHOT + 6.36.0 diff --git a/proto-google-cloud-spanner-v1/pom.xml b/proto-google-cloud-spanner-v1/pom.xml index c745d556ee3..c3382343b08 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.35.3-SNAPSHOT + 6.36.0 proto-google-cloud-spanner-v1 PROTO library for proto-google-cloud-spanner-v1 com.google.cloud google-cloud-spanner-parent - 6.35.3-SNAPSHOT + 6.36.0 diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml index e626cf71fc0..bb84381c335 100644 --- a/samples/snapshot/pom.xml +++ b/samples/snapshot/pom.xml @@ -31,7 +31,7 @@ com.google.cloud google-cloud-spanner - 6.35.3-SNAPSHOT + 6.36.0 diff --git a/versions.txt b/versions.txt index 788edb83139..b6965787230 100644 --- a/versions.txt +++ b/versions.txt @@ -1,11 +1,11 @@ # Format: # module:released-version:current-version -proto-google-cloud-spanner-admin-instance-v1:6.35.2:6.35.3-SNAPSHOT -proto-google-cloud-spanner-v1:6.35.2:6.35.3-SNAPSHOT -proto-google-cloud-spanner-admin-database-v1:6.35.2:6.35.3-SNAPSHOT -grpc-google-cloud-spanner-v1:6.35.2:6.35.3-SNAPSHOT -grpc-google-cloud-spanner-admin-instance-v1:6.35.2:6.35.3-SNAPSHOT -grpc-google-cloud-spanner-admin-database-v1:6.35.2:6.35.3-SNAPSHOT -google-cloud-spanner:6.35.2:6.35.3-SNAPSHOT -google-cloud-spanner-executor:6.35.2:6.35.3-SNAPSHOT +proto-google-cloud-spanner-admin-instance-v1:6.36.0:6.36.0 +proto-google-cloud-spanner-v1:6.36.0:6.36.0 +proto-google-cloud-spanner-admin-database-v1:6.36.0:6.36.0 +grpc-google-cloud-spanner-v1:6.36.0:6.36.0 +grpc-google-cloud-spanner-admin-instance-v1:6.36.0:6.36.0 +grpc-google-cloud-spanner-admin-database-v1:6.36.0:6.36.0 +google-cloud-spanner:6.36.0:6.36.0 +google-cloud-spanner-executor:6.36.0:6.36.0