From b7be9944e479754bc13f252d4c56b6141f992fb2 Mon Sep 17 00:00:00 2001 From: gauravpurohit Date: Tue, 6 Sep 2022 12:44:30 +0000 Subject: [PATCH 01/21] feat: Importing Proto Changes Commit will be reverted, once PROTO changes are available publicly. --- .../main/java/com/google/spanner/v1/Type.java | 305 ++++++++++++++---- .../java/com/google/spanner/v1/TypeCode.java | 12 + .../com/google/spanner/v1/TypeOrBuilder.java | 13 + .../java/com/google/spanner/v1/TypeProto.java | 37 +-- .../main/proto/google/spanner/v1/type.proto | 12 + 5 files changed, 297 insertions(+), 82 deletions(-) diff --git a/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/Type.java b/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/Type.java index 7657be926d2..52ab5b34cc9 100644 --- a/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/Type.java +++ b/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/Type.java @@ -41,6 +41,7 @@ private Type(com.google.protobuf.GeneratedMessageV3.Builder builder) { private Type() { code_ = 0; typeAnnotation_ = 0; + protoTypeFqn_ = ""; } @java.lang.Override @@ -54,6 +55,96 @@ public final com.google.protobuf.UnknownFieldSet getUnknownFields() { return this.unknownFields; } + private Type( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 8: + { + int rawValue = input.readEnum(); + + code_ = rawValue; + break; + } + case 18: + { + com.google.spanner.v1.Type.Builder subBuilder = null; + if (arrayElementType_ != null) { + subBuilder = arrayElementType_.toBuilder(); + } + arrayElementType_ = + input.readMessage(com.google.spanner.v1.Type.parser(), extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(arrayElementType_); + arrayElementType_ = subBuilder.buildPartial(); + } + + break; + } + case 26: + { + com.google.spanner.v1.StructType.Builder subBuilder = null; + if (structType_ != null) { + subBuilder = structType_.toBuilder(); + } + structType_ = + input.readMessage(com.google.spanner.v1.StructType.parser(), extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(structType_); + structType_ = subBuilder.buildPartial(); + } + + break; + } + case 32: + { + int rawValue = input.readEnum(); + + typeAnnotation_ = rawValue; + break; + } + case 42: + { + java.lang.String s = input.readStringRequireUtf8(); + + protoTypeFqn_ = s; + break; + } + default: + { + if (!parseUnknownField(input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { return com.google.spanner.v1.TypeProto.internal_static_google_spanner_v1_Type_descriptor; } @@ -249,6 +340,43 @@ public com.google.spanner.v1.TypeAnnotationCode getTypeAnnotation() { return result == null ? com.google.spanner.v1.TypeAnnotationCode.UNRECOGNIZED : result; } + public static final int PROTO_TYPE_FQN_FIELD_NUMBER = 5; + private volatile java.lang.Object protoTypeFqn_; + /** + * string proto_type_fqn = 5; + * + * @return The protoTypeFqn. + */ + @java.lang.Override + public java.lang.String getProtoTypeFqn() { + java.lang.Object ref = protoTypeFqn_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + protoTypeFqn_ = s; + return s; + } + } + /** + * string proto_type_fqn = 5; + * + * @return The bytes for protoTypeFqn. + */ + @java.lang.Override + public com.google.protobuf.ByteString getProtoTypeFqnBytes() { + java.lang.Object ref = protoTypeFqn_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8((java.lang.String) ref); + protoTypeFqn_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + private byte memoizedIsInitialized = -1; @java.lang.Override @@ -276,7 +404,10 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io != com.google.spanner.v1.TypeAnnotationCode.TYPE_ANNOTATION_CODE_UNSPECIFIED.getNumber()) { output.writeEnum(4, typeAnnotation_); } - getUnknownFields().writeTo(output); + if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(protoTypeFqn_)) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 5, protoTypeFqn_); + } + unknownFields.writeTo(output); } @java.lang.Override @@ -298,7 +429,10 @@ public int getSerializedSize() { != com.google.spanner.v1.TypeAnnotationCode.TYPE_ANNOTATION_CODE_UNSPECIFIED.getNumber()) { size += com.google.protobuf.CodedOutputStream.computeEnumSize(4, typeAnnotation_); } - size += getUnknownFields().getSerializedSize(); + if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(protoTypeFqn_)) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(5, protoTypeFqn_); + } + size += unknownFields.getSerializedSize(); memoizedSize = size; return size; } @@ -323,7 +457,8 @@ public boolean equals(final java.lang.Object obj) { if (!getStructType().equals(other.getStructType())) return false; } if (typeAnnotation_ != other.typeAnnotation_) return false; - if (!getUnknownFields().equals(other.getUnknownFields())) return false; + if (!getProtoTypeFqn().equals(other.getProtoTypeFqn())) return false; + if (!unknownFields.equals(other.unknownFields)) return false; return true; } @@ -346,7 +481,9 @@ public int hashCode() { } hash = (37 * hash) + TYPE_ANNOTATION_FIELD_NUMBER; hash = (53 * hash) + typeAnnotation_; - hash = (29 * hash) + getUnknownFields().hashCode(); + hash = (37 * hash) + PROTO_TYPE_FQN_FIELD_NUMBER; + hash = (53 * hash) + getProtoTypeFqn().hashCode(); + hash = (29 * hash) + unknownFields.hashCode(); memoizedHashCode = hash; return hash; } @@ -473,10 +610,17 @@ public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { } // Construct using com.google.spanner.v1.Type.newBuilder() - private Builder() {} + private Builder() { + maybeForceBuilderInitialization(); + } private Builder(com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { super(parent); + maybeForceBuilderInitialization(); + } + + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders) {} } @java.lang.Override @@ -498,6 +642,8 @@ public Builder clear() { } typeAnnotation_ = 0; + protoTypeFqn_ = ""; + return this; } @@ -535,6 +681,7 @@ public com.google.spanner.v1.Type buildPartial() { result.structType_ = structTypeBuilder_.build(); } result.typeAnnotation_ = typeAnnotation_; + result.protoTypeFqn_ = protoTypeFqn_; onBuilt(); return result; } @@ -596,7 +743,11 @@ public Builder mergeFrom(com.google.spanner.v1.Type other) { if (other.typeAnnotation_ != 0) { setTypeAnnotationValue(other.getTypeAnnotationValue()); } - this.mergeUnknownFields(other.getUnknownFields()); + if (!other.getProtoTypeFqn().isEmpty()) { + protoTypeFqn_ = other.protoTypeFqn_; + onChanged(); + } + this.mergeUnknownFields(other.unknownFields); onChanged(); return this; } @@ -611,56 +762,17 @@ public Builder mergeFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { - if (extensionRegistry == null) { - throw new java.lang.NullPointerException(); - } + com.google.spanner.v1.Type parsedMessage = null; try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - case 8: - { - code_ = input.readEnum(); - - break; - } // case 8 - case 18: - { - input.readMessage( - getArrayElementTypeFieldBuilder().getBuilder(), extensionRegistry); - - break; - } // case 18 - case 26: - { - input.readMessage(getStructTypeFieldBuilder().getBuilder(), extensionRegistry); - - break; - } // case 26 - case 32: - { - typeAnnotation_ = input.readEnum(); - - break; - } // case 32 - default: - { - if (!super.parseUnknownField(input, extensionRegistry, tag)) { - done = true; // was an endgroup tag - } - break; - } // default: - } // switch (tag) - } // while (!done) + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (com.google.spanner.v1.Type) e.getUnfinishedMessage(); throw e.unwrapIOException(); } finally { - onChanged(); - } // finally + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } return this; } @@ -1258,6 +1370,82 @@ public Builder clearTypeAnnotation() { return this; } + private java.lang.Object protoTypeFqn_ = ""; + /** + * string proto_type_fqn = 5; + * + * @return The protoTypeFqn. + */ + public java.lang.String getProtoTypeFqn() { + java.lang.Object ref = protoTypeFqn_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + protoTypeFqn_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * string proto_type_fqn = 5; + * + * @return The bytes for protoTypeFqn. + */ + public com.google.protobuf.ByteString getProtoTypeFqnBytes() { + java.lang.Object ref = protoTypeFqn_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8((java.lang.String) ref); + protoTypeFqn_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * string proto_type_fqn = 5; + * + * @param value The protoTypeFqn to set. + * @return This builder for chaining. + */ + public Builder setProtoTypeFqn(java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + + protoTypeFqn_ = value; + onChanged(); + return this; + } + /** + * string proto_type_fqn = 5; + * + * @return This builder for chaining. + */ + public Builder clearProtoTypeFqn() { + + protoTypeFqn_ = getDefaultInstance().getProtoTypeFqn(); + onChanged(); + return this; + } + /** + * string proto_type_fqn = 5; + * + * @param value The bytes for protoTypeFqn to set. + * @return This builder for chaining. + */ + public Builder setProtoTypeFqnBytes(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + checkByteStringIsUtf8(value); + + protoTypeFqn_ = value; + onChanged(); + return this; + } + @java.lang.Override public final Builder setUnknownFields(final com.google.protobuf.UnknownFieldSet unknownFields) { return super.setUnknownFields(unknownFields); @@ -1290,18 +1478,7 @@ public Type parsePartialFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { - Builder builder = newBuilder(); - try { - builder.mergeFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(builder.buildPartial()); - } catch (com.google.protobuf.UninitializedMessageException e) { - throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException(e) - .setUnfinishedMessage(builder.buildPartial()); - } - return builder.buildPartial(); + return new Type(input, extensionRegistry); } }; diff --git a/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/TypeCode.java b/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/TypeCode.java index 4b6561e9def..7702f2257b4 100644 --- a/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/TypeCode.java +++ b/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/TypeCode.java @@ -177,6 +177,10 @@ public enum TypeCode implements com.google.protobuf.ProtocolMessageEnum { * JSON = 11; */ JSON(11), + /** PROTO = 13; */ + PROTO(13), + /** ENUM = 14; */ + ENUM(14), UNRECOGNIZED(-1), ; @@ -324,6 +328,10 @@ public enum TypeCode implements com.google.protobuf.ProtocolMessageEnum { * JSON = 11; */ public static final int JSON_VALUE = 11; + /** PROTO = 13; */ + public static final int PROTO_VALUE = 13; + /** ENUM = 14; */ + public static final int ENUM_VALUE = 14; public final int getNumber() { if (this == UNRECOGNIZED) { @@ -373,6 +381,10 @@ public static TypeCode forNumber(int value) { return NUMERIC; case 11: return JSON; + case 13: + return PROTO; + case 14: + return ENUM; default: return null; } diff --git a/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/TypeOrBuilder.java b/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/TypeOrBuilder.java index 4c78a470d6d..0c705016caf 100644 --- a/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/TypeOrBuilder.java +++ b/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/TypeOrBuilder.java @@ -158,4 +158,17 @@ public interface TypeOrBuilder * @return The typeAnnotation. */ com.google.spanner.v1.TypeAnnotationCode getTypeAnnotation(); + + /** + * string proto_type_fqn = 5; + * + * @return The protoTypeFqn. + */ + java.lang.String getProtoTypeFqn(); + /** + * string proto_type_fqn = 5; + * + * @return The bytes for protoTypeFqn. + */ + com.google.protobuf.ByteString getProtoTypeFqnBytes(); } diff --git a/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/TypeProto.java b/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/TypeProto.java index ebca4e75d38..63aa67d3fee 100644 --- a/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/TypeProto.java +++ b/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/TypeProto.java @@ -50,27 +50,28 @@ public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() { java.lang.String[] descriptorData = { "\n\034google/spanner/v1/type.proto\022\021google.s" + "panner.v1\032\037google/api/field_behavior.pro" - + "to\"\337\001\n\004Type\022.\n\004code\030\001 \001(\0162\033.google.spann" + + "to\"\367\001\n\004Type\022.\n\004code\030\001 \001(\0162\033.google.spann" + "er.v1.TypeCodeB\003\340A\002\0223\n\022array_element_typ" + "e\030\002 \001(\0132\027.google.spanner.v1.Type\0222\n\013stru" + "ct_type\030\003 \001(\0132\035.google.spanner.v1.Struct" + "Type\022>\n\017type_annotation\030\004 \001(\0162%.google.s" - + "panner.v1.TypeAnnotationCode\"\177\n\nStructTy" - + "pe\0223\n\006fields\030\001 \003(\0132#.google.spanner.v1.S" - + "tructType.Field\032<\n\005Field\022\014\n\004name\030\001 \001(\t\022%" - + "\n\004type\030\002 \001(\0132\027.google.spanner.v1.Type*\245\001" - + "\n\010TypeCode\022\031\n\025TYPE_CODE_UNSPECIFIED\020\000\022\010\n" - + "\004BOOL\020\001\022\t\n\005INT64\020\002\022\013\n\007FLOAT64\020\003\022\r\n\tTIMES" - + "TAMP\020\004\022\010\n\004DATE\020\005\022\n\n\006STRING\020\006\022\t\n\005BYTES\020\007\022" - + "\t\n\005ARRAY\020\010\022\n\n\006STRUCT\020\t\022\013\n\007NUMERIC\020\n\022\010\n\004J" - + "SON\020\013*X\n\022TypeAnnotationCode\022$\n TYPE_ANNO" - + "TATION_CODE_UNSPECIFIED\020\000\022\016\n\nPG_NUMERIC\020" - + "\002\022\014\n\010PG_JSONB\020\003B\257\001\n\025com.google.spanner.v" - + "1B\tTypeProtoP\001Z8google.golang.org/genpro" - + "to/googleapis/spanner/v1;spanner\252\002\027Googl" - + "e.Cloud.Spanner.V1\312\002\027Google\\Cloud\\Spanne" - + "r\\V1\352\002\032Google::Cloud::Spanner::V1b\006proto" - + "3" + + "panner.v1.TypeAnnotationCode\022\026\n\016proto_ty" + + "pe_fqn\030\005 \001(\t\"\177\n\nStructType\0223\n\006fields\030\001 \003" + + "(\0132#.google.spanner.v1.StructType.Field\032" + + "<\n\005Field\022\014\n\004name\030\001 \001(\t\022%\n\004type\030\002 \001(\0132\027.g" + + "oogle.spanner.v1.Type*\272\001\n\010TypeCode\022\031\n\025TY" + + "PE_CODE_UNSPECIFIED\020\000\022\010\n\004BOOL\020\001\022\t\n\005INT64" + + "\020\002\022\013\n\007FLOAT64\020\003\022\r\n\tTIMESTAMP\020\004\022\010\n\004DATE\020\005" + + "\022\n\n\006STRING\020\006\022\t\n\005BYTES\020\007\022\t\n\005ARRAY\020\010\022\n\n\006ST" + + "RUCT\020\t\022\013\n\007NUMERIC\020\n\022\010\n\004JSON\020\013\022\t\n\005PROTO\020\r" + + "\022\010\n\004ENUM\020\016*X\n\022TypeAnnotationCode\022$\n TYPE" + + "_ANNOTATION_CODE_UNSPECIFIED\020\000\022\016\n\nPG_NUM" + + "ERIC\020\002\022\014\n\010PG_JSONB\020\003B\257\001\n\025com.google.span" + + "ner.v1B\tTypeProtoP\001Z8google.golang.org/g" + + "enproto/googleapis/spanner/v1;spanner\252\002\027" + + "Google.Cloud.Spanner.V1\312\002\027Google\\Cloud\\S" + + "panner\\V1\352\002\032Google::Cloud::Spanner::V1b\006" + + "proto3" }; descriptor = com.google.protobuf.Descriptors.FileDescriptor.internalBuildGeneratedFileFrom( @@ -83,7 +84,7 @@ public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() { new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( internal_static_google_spanner_v1_Type_descriptor, new java.lang.String[] { - "Code", "ArrayElementType", "StructType", "TypeAnnotation", + "Code", "ArrayElementType", "StructType", "TypeAnnotation", "ProtoTypeFqn", }); internal_static_google_spanner_v1_StructType_descriptor = getDescriptor().getMessageTypes().get(1); diff --git a/proto-google-cloud-spanner-v1/src/main/proto/google/spanner/v1/type.proto b/proto-google-cloud-spanner-v1/src/main/proto/google/spanner/v1/type.proto index 712ec187fcc..0f2cd64ebad 100644 --- a/proto-google-cloud-spanner-v1/src/main/proto/google/spanner/v1/type.proto +++ b/proto-google-cloud-spanner-v1/src/main/proto/google/spanner/v1/type.proto @@ -47,6 +47,11 @@ message Type { // typically is not needed to process the content of a value (it doesn't // affect serialization) and clients can ignore it on the read path. TypeAnnotationCode type_annotation = 4; + + // If [code][] == [PROTO][TypeCode.PROTO] or [code][] == + // [ENUM][TypeCode.ENUM], then `proto_type_fqn` is the fully qualified name of + // the proto type representing the proto/enum definition. + string proto_type_fqn = 5; } // `StructType` defines the fields of a [STRUCT][google.spanner.v1.TypeCode.STRUCT] type. @@ -145,6 +150,13 @@ enum TypeCode { // preserved. // - JSON array elements will have their order preserved. JSON = 11; + + // Encoded as a base64-encoded `string`, as described in RFC 4648, + // section 4. + PROTO = 13; + + // Encoded as `string`, in decimal format. + ENUM = 14; } // `TypeAnnotationCode` is used as a part of [Type][google.spanner.v1.Type] to From a59bebf332ecbce50c53ca08de3e25e23e9e170c Mon Sep 17 00:00:00 2001 From: gauravpurohit Date: Tue, 13 Sep 2022 09:00:59 +0000 Subject: [PATCH 02/21] feat: Proto Message Implementation --- .../cloud/spanner/AbstractResultSet.java | 27 +++++++ .../cloud/spanner/AbstractStructReader.java | 35 +++++++++ .../cloud/spanner/ForwardingStructReader.java | 24 +++++++ .../cloud/spanner/ProtoMessageWrapper.java | 42 +++++++++++ .../com/google/cloud/spanner/ResultSets.java | 24 +++++++ .../java/com/google/cloud/spanner/Struct.java | 13 ++++ .../google/cloud/spanner/StructReader.java | 12 ++++ .../java/com/google/cloud/spanner/Type.java | 22 ++++++ .../java/com/google/cloud/spanner/Value.java | 72 ++++++++++++++++++- .../com/google/cloud/spanner/ValueBinder.java | 5 ++ .../spanner/connection/ChecksumResultSet.java | 3 + .../connection/DirectExecuteResultSet.java | 28 ++++++++ .../ReplaceableForwardingResultSet.java | 28 ++++++++ .../AbstractStructReaderTypesTest.java | 11 +++ 14 files changed, 345 insertions(+), 1 deletion(-) create mode 100644 google-cloud-spanner/src/main/java/com/google/cloud/spanner/ProtoMessageWrapper.java 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 6ccb28900f9..589fddb5cb0 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 @@ -36,7 +36,9 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.google.common.util.concurrent.Uninterruptibles; +import com.google.protobuf.AbstractMessage; import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.ListValue; import com.google.protobuf.Value.KindCase; import com.google.spanner.v1.PartialResultSet; @@ -385,6 +387,8 @@ private Object writeReplace() { case JSON: builder.set(fieldName).to(Value.json((String) value)); break; + case PROTO: + builder.set(fieldName).to(Value.protoMessage((byte[]) value)); case PG_JSONB: builder.set(fieldName).to(Value.pgJsonb((String) value)); break; @@ -504,6 +508,7 @@ private static Object decodeValue(Type fieldType, com.google.protobuf.Value prot checkType(fieldType, proto, KindCase.STRING_VALUE); return proto.getStringValue(); case BYTES: + case PROTO: checkType(fieldType, proto, KindCase.STRING_VALUE); return ByteArray.fromBase64(proto.getStringValue()); case TIMESTAMP: @@ -655,6 +660,17 @@ public boolean isNull(int columnIndex) { return rowData.get(columnIndex) == null; } + @Override + public byte[] getProtoMessageInternal(int columnIndex) { + return (byte[]) rowData.get(columnIndex); + } + + @Override + public T getProtoMessageInternal(int columnIndex, T m) + throws InvalidProtocolBufferException { + return (T) m.toBuilder().mergeFrom(getProtoMessage(columnIndex)).build(); + } + @Override protected boolean getBooleanInternal(int columnIndex) { return (Boolean) rowData.get(columnIndex); @@ -1368,6 +1384,17 @@ protected String getStringInternal(int columnIndex) { return currRow().getStringInternal(columnIndex); } + @Override + protected byte[] getProtoMessageInternal(int columnIndex) { + return currRow().getProtoMessageInternal(columnIndex); + } + + @Override + protected T getProtoMessageInternal(int columnIndex, T m) + throws InvalidProtocolBufferException { + return currRow().getProtoMessageInternal(columnIndex, m); + } + @Override protected String getJsonInternal(int columnIndex) { return currRow().getJsonInternal(columnIndex); 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..8d3db2feb91 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 @@ -21,6 +21,8 @@ import com.google.cloud.ByteArray; import com.google.cloud.Date; import com.google.cloud.Timestamp; +import com.google.protobuf.AbstractMessage; +import com.google.protobuf.InvalidProtocolBufferException; import java.math.BigDecimal; import java.util.Arrays; import java.util.List; @@ -58,6 +60,11 @@ protected String getPgJsonbInternal(int columnIndex) { protected abstract Date getDateInternal(int columnIndex); + protected abstract byte[] getProtoMessageInternal(int columnIndex); + + protected abstract T getProtoMessageInternal(int columnIndex, T m) + throws InvalidProtocolBufferException; + protected Value getValueInternal(int columnIndex) { throw new UnsupportedOperationException("method should be overwritten"); } @@ -249,6 +256,34 @@ public Date getDate(String columnName) { return getDateInternal(columnIndex); } + @Override + public byte[] getProtoMessage(int columnIndex) { + checkNonNullOfType(columnIndex, Type.proto(), columnIndex); + return getProtoMessageInternal(columnIndex); + } + + @Override + public byte[] getProtoMessage(String columnName) { + int columnIndex = getColumnIndex(columnName); + checkNonNullOfType(columnIndex, Type.proto(), columnName); + return getProtoMessageInternal(columnIndex); + } + + @Override + public T getProtoMessage(int columnIndex, T m) + throws InvalidProtocolBufferException { + checkNonNullOfType(columnIndex, Type.proto(), columnIndex); + return getProtoMessageInternal(columnIndex, m); + } + + @Override + public T getProtoMessage(String columnName, T m) + throws InvalidProtocolBufferException { + int columnIndex = getColumnIndex(columnName); + checkNonNullOfType(columnIndex, Type.proto(), columnName); + return getProtoMessageInternal(columnIndex, m); + } + @Override public Value getValue(int columnIndex) { checkNonNull(columnIndex, columnIndex); diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ForwardingStructReader.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ForwardingStructReader.java index 2a85006fa95..cfb674f5a1c 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ForwardingStructReader.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ForwardingStructReader.java @@ -22,6 +22,8 @@ import com.google.common.base.Preconditions; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; +import com.google.protobuf.AbstractMessage; +import com.google.protobuf.InvalidProtocolBufferException; import java.math.BigDecimal; import java.util.List; @@ -382,6 +384,28 @@ public List getStructList(String columnName) { return delegate.get().getStructList(columnName); } + @Override + public byte[] getProtoMessage(int columnIndex) { + return delegate.get().getProtoMessage(columnIndex); + } + + @Override + public byte[] getProtoMessage(String columnName) { + return delegate.get().getProtoMessage(columnName); + } + + @Override + public T getProtoMessage(int columnIndex, T m) + throws InvalidProtocolBufferException { + return delegate.get().getProtoMessage(columnIndex, m); + } + + @Override + public T getProtoMessage(String columnName, T m) + throws InvalidProtocolBufferException { + return delegate.get().getProtoMessage(columnName, m); + } + @Override public Value getValue(int columnIndex) { checkValidState(); diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ProtoMessageWrapper.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ProtoMessageWrapper.java new file mode 100644 index 00000000000..cb731d2a9a5 --- /dev/null +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ProtoMessageWrapper.java @@ -0,0 +1,42 @@ +/* + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.spanner; + +import com.google.protobuf.AbstractMessage; +import java.util.Arrays; + +public class ProtoMessageWrapper { + AbstractMessage message; + byte[] serializedMessage; + + ProtoMessageWrapper(AbstractMessage m) { + this.message = m; + this.serializedMessage = m.toByteArray(); + } + + ProtoMessageWrapper(byte[] serializedMessage) { + this.serializedMessage = serializedMessage; + } + + @Override + public String toString() { + if (message != null) { + return message.toString(); + } + return Arrays.toString(serializedMessage); + } +} diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ResultSets.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ResultSets.java index 6eacd3208e2..4ed392680a1 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ResultSets.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ResultSets.java @@ -29,6 +29,8 @@ import com.google.common.base.Supplier; import com.google.common.collect.Lists; import com.google.common.util.concurrent.ThreadFactoryBuilder; +import com.google.protobuf.AbstractMessage; +import com.google.protobuf.InvalidProtocolBufferException; import com.google.spanner.v1.ResultSetStats; import java.math.BigDecimal; import java.util.List; @@ -293,6 +295,28 @@ public Date getDate(String columnName) { return getCurrentRowAsStruct().getDate(columnName); } + @Override + public byte[] getProtoMessage(int columnIndex) { + return getCurrentRowAsStruct().getProtoMessage(columnIndex); + } + + @Override + public byte[] getProtoMessage(String columnName) { + return getCurrentRowAsStruct().getProtoMessage(columnName); + } + + @Override + public T getProtoMessage(int columnIndex, T m) + throws InvalidProtocolBufferException { + return getCurrentRowAsStruct().getProtoMessage(columnIndex, m); + } + + @Override + public T getProtoMessage(String columnName, T m) + throws InvalidProtocolBufferException { + return getCurrentRowAsStruct().getProtoMessage(columnName, m); + } + @Override public Value getValue(int columnIndex) { return getCurrentRowAsStruct().getValue(columnIndex); diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Struct.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Struct.java index 48c989d145e..f23cdc8de8d 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Struct.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Struct.java @@ -28,6 +28,8 @@ import com.google.common.primitives.Booleans; import com.google.common.primitives.Doubles; import com.google.common.primitives.Longs; +import com.google.protobuf.AbstractMessage; +import com.google.protobuf.InvalidProtocolBufferException; import java.io.Serializable; import java.math.BigDecimal; import java.util.ArrayList; @@ -217,6 +219,17 @@ protected Date getDateInternal(int columnIndex) { return values.get(columnIndex).getDate(); } + @Override + protected byte[] getProtoMessageInternal(int columnIndex) { + return values.get(columnIndex).getProtoMessage(); + } + + @Override + protected T getProtoMessageInternal(int columnIndex, T m) + throws InvalidProtocolBufferException { + return values.get(columnIndex).getProtoMessage(m); + } + @Override protected Value getValueInternal(int columnIndex) { return values.get(columnIndex); diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/StructReader.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/StructReader.java index a96c95cb953..f4141226c43 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/StructReader.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/StructReader.java @@ -19,6 +19,8 @@ import com.google.cloud.ByteArray; import com.google.cloud.Date; import com.google.cloud.Timestamp; +import com.google.protobuf.AbstractMessage; +import com.google.protobuf.InvalidProtocolBufferException; import java.math.BigDecimal; import java.util.List; @@ -294,6 +296,16 @@ default List getPgJsonbList(String columnName) { /** Returns the value of a non-{@code NULL} column with type {@code Type.array(Type.date())}. */ List getDateList(String columnName); + byte[] getProtoMessage(int columnIndex); + + byte[] getProtoMessage(String columnName); + + T getProtoMessage(int columnIndex, T m) + throws InvalidProtocolBufferException; + + T getProtoMessage(String columnName, T m) + throws InvalidProtocolBufferException; + /** * Returns the value of a non-{@code NULL} column with type {@code Type.array(Type.struct(...))}. */ 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..a0c02de4338 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 @@ -56,6 +56,8 @@ public final class Type implements Serializable { private static final Type TYPE_BYTES = new Type(Code.BYTES, null, null); private static final Type TYPE_TIMESTAMP = new Type(Code.TIMESTAMP, null, null); private static final Type TYPE_DATE = new Type(Code.DATE, null, null); + private static final Type TYPE_PROTO = new Type(Code.PROTO, null, null); + private static final Type TYPE_PROTO_ENUM = new Type(Code.PROTO_ENUM, null, null); private static final Type TYPE_ARRAY_BOOL = new Type(Code.ARRAY, TYPE_BOOL, null); private static final Type TYPE_ARRAY_INT64 = new Type(Code.ARRAY, TYPE_INT64, null); private static final Type TYPE_ARRAY_FLOAT64 = new Type(Code.ARRAY, TYPE_FLOAT64, null); @@ -64,6 +66,8 @@ public final class Type implements Serializable { private static final Type TYPE_ARRAY_STRING = new Type(Code.ARRAY, TYPE_STRING, null); private static final Type TYPE_ARRAY_JSON = new Type(Code.ARRAY, TYPE_JSON, null); private static final Type TYPE_ARRAY_PG_JSONB = new Type(Code.ARRAY, TYPE_PG_JSONB, null); + private static final Type TYPE_ARRAY_PROTO = new Type(Code.ARRAY, TYPE_PROTO, null); + private static final Type TYPE_ARRAY_PROTO_ENUM = new Type(Code.ARRAY, TYPE_PROTO_ENUM, null); private static final Type TYPE_ARRAY_BYTES = new Type(Code.ARRAY, TYPE_BYTES, null); private static final Type TYPE_ARRAY_TIMESTAMP = new Type(Code.ARRAY, TYPE_TIMESTAMP, null); private static final Type TYPE_ARRAY_DATE = new Type(Code.ARRAY, TYPE_DATE, null); @@ -122,6 +126,14 @@ public static Type pgJsonb() { return TYPE_PG_JSONB; } + public static Type proto() { + return TYPE_PROTO; + } + + public static Type protoEnum() { + return TYPE_PROTO_ENUM; + } + /** Returns the descriptor for the {@code BYTES} type: a variable-length byte string. */ public static Type bytes() { return TYPE_BYTES; @@ -169,6 +181,10 @@ public static Type array(Type elementType) { return TYPE_ARRAY_TIMESTAMP; case DATE: return TYPE_ARRAY_DATE; + case PROTO: + return TYPE_ARRAY_PROTO; + case PROTO_ENUM: + return TYPE_ARRAY_PROTO_ENUM; default: return new Type(Code.ARRAY, elementType, null); } @@ -219,6 +235,8 @@ public enum Code { STRING(TypeCode.STRING), JSON(TypeCode.JSON), PG_JSONB(TypeCode.JSON, TypeAnnotationCode.PG_JSONB), + PROTO(TypeCode.PROTO), + PROTO_ENUM(TypeCode.ENUM), BYTES(TypeCode.BYTES), TIMESTAMP(TypeCode.TIMESTAMP), DATE(TypeCode.DATE), @@ -464,6 +482,10 @@ static Type fromProto(com.google.spanner.v1.Type proto) { return timestamp(); case DATE: return date(); + case PROTO: + return proto(); + case PROTO_ENUM: + return protoEnum(); case ARRAY: checkArgument( proto.hasArrayElementType(), 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..c67bb620355 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 @@ -24,8 +24,12 @@ import com.google.common.base.Preconditions; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; +import com.google.common.io.BaseEncoding; +import com.google.protobuf.AbstractMessage; +import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.ListValue; import com.google.protobuf.NullValue; +import com.google.protobuf.ProtocolMessageEnum; import java.io.Serializable; import java.math.BigDecimal; import java.util.ArrayList; @@ -217,8 +221,16 @@ public static Value pgJsonb(@Nullable String v) { return new PgJsonbImpl(v == null, v); } + public static Value protoMessage(@Nullable AbstractMessage v) { + return new ProtoMessageImpl(v == null, v); + } + + public static Value protoMessage(@Nullable byte[] v) { + return new ProtoMessageImpl(v == null, v); + } + /** - * Returns a {@code BYTES} value. + * Returns a {@code BYTES} value. Returns a {@code BYTES} value. * * @param v the value, which may be null */ @@ -541,6 +553,19 @@ public String getPgJsonb() { throw new UnsupportedOperationException("Not implemented"); } + byte[] getProtoMessage() { + throw new UnsupportedOperationException("Not implemented"); + } + + public T getProtoMessage(T m) + throws InvalidProtocolBufferException { + throw new UnsupportedOperationException("Not implemented"); + } + + public ProtocolMessageEnum getProtoEnum() { + throw new UnsupportedOperationException("Not implemented"); + } + /** * Returns the value of a {@code BYTES}-typed instance. * @@ -851,6 +876,17 @@ public String getPgJsonb() { throw defaultGetter(Type.pgJsonb()); } + @Override + byte[] getProtoMessage() { + throw defaultGetter(Type.proto()); + } + + @Override + public T getProtoMessage(T m) + throws InvalidProtocolBufferException { + throw defaultGetter(Type.proto()); + } + @Override public ByteArray getBytes() { throw defaultGetter(Type.bytes()); @@ -1321,6 +1357,38 @@ void valueToString(StringBuilder b) { } } + private static class ProtoMessageImpl extends AbstractObjectValue { + private ProtoMessageImpl(boolean isNull, AbstractMessage value) { + super(isNull, Type.proto(), new ProtoMessageWrapper(value)); + } + + private ProtoMessageImpl(boolean isNull, byte[] serializedProtoArray) { + super(isNull, Type.proto(), new ProtoMessageWrapper(serializedProtoArray)); + } + + @Override + byte[] getProtoMessage() { + return value.serializedMessage; + } + + @Override + public T getProtoMessage(T m) + throws InvalidProtocolBufferException { + return (T) m.toBuilder().mergeFrom(value.serializedMessage); + } + + @Override + com.google.protobuf.Value valueToProto() { + String base64EncodedString = BaseEncoding.base16().encode(value.serializedMessage); + return com.google.protobuf.Value.newBuilder().setStringValue(base64EncodedString).build(); + } + + @Override + void valueToString(StringBuilder b) { + b.append(value.toString()); + } + } + private static class TimestampImpl extends AbstractObjectValue { private static final String COMMIT_TIMESTAMP_STRING = "spanner.commit_timestamp()"; @@ -1946,6 +2014,8 @@ private Value getValue(int fieldIndex) { return Value.date(value.getDate(fieldIndex)); case TIMESTAMP: return Value.timestamp(value.getTimestamp(fieldIndex)); + case PROTO: + return Value.protoMessage(value.getProtoMessage(fieldIndex)); case STRUCT: return Value.struct(value.getStruct(fieldIndex)); case ARRAY: 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..b4efbac5f4e 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 @@ -19,6 +19,7 @@ import com.google.cloud.ByteArray; import com.google.cloud.Date; import com.google.cloud.Timestamp; +import com.google.protobuf.GeneratedMessage; import java.math.BigDecimal; import javax.annotation.Nullable; @@ -95,6 +96,10 @@ public R to(@Nullable String value) { return handle(Value.string(value)); } + public R to(@Nullable GeneratedMessage m) { + return handle(Value.protoMessage(m)); + } + /** Binds to {@code Value.bytes(value)} */ public R to(@Nullable ByteArray value) { return handle(Value.bytes(value)); diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ChecksumResultSet.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ChecksumResultSet.java index bb2f2fb817a..18fa48f2798 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ChecksumResultSet.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ChecksumResultSet.java @@ -248,6 +248,9 @@ public void funnel(Struct row, PrimitiveSink into) { case JSON: funnelValue(type, row.getJson(i), into); break; + case PROTO: + funnelValue(type, row.getProtoMessage(i), into); + break; case PG_JSONB: funnelValue(type, row.getPgJsonb(i), into); break; diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DirectExecuteResultSet.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DirectExecuteResultSet.java index 8fb0bbe4409..138bfea3ea6 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DirectExecuteResultSet.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DirectExecuteResultSet.java @@ -25,6 +25,8 @@ import com.google.cloud.spanner.Type; import com.google.cloud.spanner.Value; import com.google.common.base.Preconditions; +import com.google.protobuf.AbstractMessage; +import com.google.protobuf.InvalidProtocolBufferException; import com.google.spanner.v1.ResultSetStats; import java.math.BigDecimal; import java.util.List; @@ -431,6 +433,32 @@ public List getStructList(String columnName) { return delegate.getStructList(columnName); } + @Override + public byte[] getProtoMessage(int columnIndex) { + Preconditions.checkState(nextCalledByClient, MISSING_NEXT_CALL); + return delegate.getProtoMessage(columnIndex); + } + + @Override + public byte[] getProtoMessage(String columnName) { + Preconditions.checkState(nextCalledByClient, MISSING_NEXT_CALL); + return delegate.getProtoMessage(columnName); + } + + @Override + public T getProtoMessage(int columnIndex, T m) + throws InvalidProtocolBufferException { + Preconditions.checkState(nextCalledByClient, MISSING_NEXT_CALL); + return delegate.getProtoMessage(columnIndex, m); + } + + @Override + public T getProtoMessage(String columnName, T m) + throws InvalidProtocolBufferException { + Preconditions.checkState(nextCalledByClient, MISSING_NEXT_CALL); + return delegate.getProtoMessage(columnName, m); + } + @Override public boolean equals(Object o) { if (!(o instanceof DirectExecuteResultSet)) { diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSet.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSet.java index cc9759a4870..49b1820fce5 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSet.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSet.java @@ -27,6 +27,8 @@ import com.google.cloud.spanner.Type; import com.google.cloud.spanner.Value; import com.google.common.base.Preconditions; +import com.google.protobuf.AbstractMessage; +import com.google.protobuf.InvalidProtocolBufferException; import com.google.spanner.v1.ResultSetStats; import java.math.BigDecimal; import java.util.List; @@ -435,4 +437,30 @@ public List getStructList(String columnName) { checkClosed(); return delegate.getStructList(columnName); } + + @Override + public byte[] getProtoMessage(int columnIndex) { + checkClosed(); + return delegate.getProtoMessage(columnIndex); + } + + @Override + public byte[] getProtoMessage(String columnName) { + checkClosed(); + return delegate.getProtoMessage(columnName); + } + + @Override + public T getProtoMessage(int columnIndex, T m) + throws InvalidProtocolBufferException { + checkClosed(); + return delegate.getProtoMessage(columnIndex, m); + } + + @Override + public T getProtoMessage(String columnName, T m) + throws InvalidProtocolBufferException { + checkClosed(); + return delegate.getProtoMessage(columnName, m); + } } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AbstractStructReaderTypesTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AbstractStructReaderTypesTest.java index 1b6280a6369..a1e8b6474c7 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AbstractStructReaderTypesTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AbstractStructReaderTypesTest.java @@ -27,6 +27,7 @@ import com.google.cloud.Date; import com.google.cloud.Timestamp; import com.google.common.base.Throwables; +import com.google.protobuf.AbstractMessage; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.math.BigDecimal; @@ -95,6 +96,16 @@ protected Date getDateInternal(int columnIndex) { return null; } + @Override + protected byte[] getProtoMessageInternal(int columnIndex) { + return null; + } + + @Override + protected T getProtoMessageInternal(int columnIndex, T m) { + return null; + } + @Override protected Value getValueInternal(int columnIndex) { return null; From ffec1120035a12d50d9c09aa354025db99f5c5d6 Mon Sep 17 00:00:00 2001 From: gauravpurohit Date: Mon, 19 Sep 2022 08:54:57 +0000 Subject: [PATCH 03/21] feat: Adding support for enum --- .../cloud/spanner/AbstractResultSet.java | 19 +++++- .../cloud/spanner/AbstractStructReader.java | 20 ++++++ .../cloud/spanner/ForwardingStructReader.java | 20 ++++++ .../cloud/spanner/ProtoEnumWrapper.java | 33 +++++++++ .../com/google/cloud/spanner/ResultSets.java | 14 ++++ .../java/com/google/cloud/spanner/Struct.java | 11 +++ .../google/cloud/spanner/StructReader.java | 9 +++ .../java/com/google/cloud/spanner/Value.java | 68 +++++++++++++++++-- .../com/google/cloud/spanner/ValueBinder.java | 5 ++ .../spanner/connection/ChecksumResultSet.java | 1 + .../connection/DirectExecuteResultSet.java | 16 +++++ .../ReplaceableForwardingResultSet.java | 16 +++++ .../AbstractStructReaderTypesTest.java | 8 +++ 13 files changed, 235 insertions(+), 5 deletions(-) create mode 100644 google-cloud-spanner/src/main/java/com/google/cloud/spanner/ProtoEnumWrapper.java 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 589fddb5cb0..f2e1a2dd2a4 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 @@ -40,6 +40,7 @@ import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.ListValue; +import com.google.protobuf.ProtocolMessageEnum; import com.google.protobuf.Value.KindCase; import com.google.spanner.v1.PartialResultSet; import com.google.spanner.v1.ResultSetMetadata; @@ -67,6 +68,7 @@ import java.util.concurrent.Executor; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; +import java.util.function.Function; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -389,6 +391,8 @@ private Object writeReplace() { break; case PROTO: builder.set(fieldName).to(Value.protoMessage((byte[]) value)); + case PROTO_ENUM: + builder.set(fieldName).to(Value.protoEnum((long) value)); case PG_JSONB: builder.set(fieldName).to(Value.pgJsonb((String) value)); break; @@ -494,6 +498,7 @@ private static Object decodeValue(Type fieldType, com.google.protobuf.Value prot checkType(fieldType, proto, KindCase.BOOL_VALUE); return proto.getBoolValue(); case INT64: + case PROTO_ENUM: checkType(fieldType, proto, KindCase.STRING_VALUE); return Long.parseLong(proto.getStringValue()); case FLOAT64: @@ -668,7 +673,13 @@ public byte[] getProtoMessageInternal(int columnIndex) { @Override public T getProtoMessageInternal(int columnIndex, T m) throws InvalidProtocolBufferException { - return (T) m.toBuilder().mergeFrom(getProtoMessage(columnIndex)).build(); + return (T) m.toBuilder().mergeFrom(getProtoMessageInternal(columnIndex)).build(); + } + + @Override + protected T getProtoEnumInternal( + int columnIndex, Function method) { + return (T) method.apply((int) getLongInternal(columnIndex)); } @Override @@ -1395,6 +1406,12 @@ protected T getProtoMessageInternal(int columnIndex, return currRow().getProtoMessageInternal(columnIndex, m); } + @Override + protected T getProtoEnumInternal( + int columnIndex, Function method) { + return currRow().getProtoEnumInternal(columnIndex, method); + } + @Override protected String getJsonInternal(int columnIndex) { return currRow().getJsonInternal(columnIndex); 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 8d3db2feb91..286edbc97c3 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 @@ -23,9 +23,11 @@ import com.google.cloud.Timestamp; import com.google.protobuf.AbstractMessage; import com.google.protobuf.InvalidProtocolBufferException; +import com.google.protobuf.ProtocolMessageEnum; import java.math.BigDecimal; import java.util.Arrays; import java.util.List; +import java.util.function.Function; /** * Base class for assisting {@link StructReader} implementations. @@ -65,6 +67,9 @@ protected String getPgJsonbInternal(int columnIndex) { protected abstract T getProtoMessageInternal(int columnIndex, T m) throws InvalidProtocolBufferException; + protected abstract T getProtoEnumInternal( + int columnIndex, Function method); + protected Value getValueInternal(int columnIndex) { throw new UnsupportedOperationException("method should be overwritten"); } @@ -269,6 +274,21 @@ public byte[] getProtoMessage(String columnName) { return getProtoMessageInternal(columnIndex); } + @Override + public T getProtoEnum( + int columnIndex, Function method) { + checkNonNullOfType(columnIndex, Type.protoEnum(), columnIndex); + return getProtoEnumInternal(columnIndex, method); + } + + @Override + public T getProtoEnum( + String columnName, Function method) { + int columnIndex = getColumnIndex(columnName); + checkNonNullOfType(columnIndex, Type.proto(), columnName); + return getProtoEnumInternal(columnIndex, method); + } + @Override public T getProtoMessage(int columnIndex, T m) throws InvalidProtocolBufferException { diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ForwardingStructReader.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ForwardingStructReader.java index cfb674f5a1c..fcc9112c48e 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ForwardingStructReader.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ForwardingStructReader.java @@ -24,8 +24,10 @@ import com.google.common.base.Suppliers; import com.google.protobuf.AbstractMessage; import com.google.protobuf.InvalidProtocolBufferException; +import com.google.protobuf.ProtocolMessageEnum; import java.math.BigDecimal; import java.util.List; +import java.util.function.Function; /** Forwarding implements of StructReader */ public class ForwardingStructReader implements StructReader { @@ -386,26 +388,44 @@ public List getStructList(String columnName) { @Override public byte[] getProtoMessage(int columnIndex) { + checkValidState(); return delegate.get().getProtoMessage(columnIndex); } @Override public byte[] getProtoMessage(String columnName) { + checkValidState(); return delegate.get().getProtoMessage(columnName); } @Override public T getProtoMessage(int columnIndex, T m) throws InvalidProtocolBufferException { + checkValidState(); return delegate.get().getProtoMessage(columnIndex, m); } @Override public T getProtoMessage(String columnName, T m) throws InvalidProtocolBufferException { + checkValidState(); return delegate.get().getProtoMessage(columnName, m); } + @Override + public T getProtoEnum( + int columnIndex, Function method) { + checkValidState(); + return delegate.get().getProtoEnum(columnIndex, method); + } + + @Override + public T getProtoEnum( + String columnName, Function method) { + checkValidState(); + return delegate.get().getProtoEnum(columnName, method); + } + @Override public Value getValue(int columnIndex) { checkValidState(); diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ProtoEnumWrapper.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ProtoEnumWrapper.java new file mode 100644 index 00000000000..2c965f75307 --- /dev/null +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ProtoEnumWrapper.java @@ -0,0 +1,33 @@ +/* + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.spanner; + +import com.google.protobuf.ProtocolMessageEnum; + +public class ProtoEnumWrapper { + ProtocolMessageEnum protocolMessageEnum; + long enumValue; + + ProtoEnumWrapper(ProtocolMessageEnum protoEnum) { + this.protocolMessageEnum = protoEnum; + this.enumValue = protoEnum.getNumber(); + } + + ProtoEnumWrapper(long val) { + this.enumValue = val; + } +} diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ResultSets.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ResultSets.java index 4ed392680a1..fe5626aa374 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ResultSets.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ResultSets.java @@ -31,9 +31,11 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.google.protobuf.AbstractMessage; import com.google.protobuf.InvalidProtocolBufferException; +import com.google.protobuf.ProtocolMessageEnum; import com.google.spanner.v1.ResultSetStats; import java.math.BigDecimal; import java.util.List; +import java.util.function.Function; /** Utility methods for working with {@link com.google.cloud.spanner.ResultSet}. */ public final class ResultSets { @@ -317,6 +319,18 @@ public T getProtoMessage(String columnName, T m) return getCurrentRowAsStruct().getProtoMessage(columnName, m); } + @Override + public T getProtoEnum( + int columnIndex, Function method) { + return getCurrentRowAsStruct().getProtoEnum(columnIndex, method); + } + + @Override + public T getProtoEnum( + String columnName, Function method) { + return getCurrentRowAsStruct().getProtoEnum(columnName, method); + } + @Override public Value getValue(int columnIndex) { return getCurrentRowAsStruct().getValue(columnIndex); diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Struct.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Struct.java index f23cdc8de8d..5544e735e62 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Struct.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Struct.java @@ -30,11 +30,13 @@ import com.google.common.primitives.Longs; import com.google.protobuf.AbstractMessage; import com.google.protobuf.InvalidProtocolBufferException; +import com.google.protobuf.ProtocolMessageEnum; import java.io.Serializable; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.function.Function; import javax.annotation.concurrent.Immutable; /** @@ -230,6 +232,12 @@ protected T getProtoMessageInternal(int columnIndex, return values.get(columnIndex).getProtoMessage(m); } + @Override + protected T getProtoEnumInternal( + int columnIndex, Function method) { + return (T) method.apply((int) values.get(columnIndex).getInt64()); + } + @Override protected Value getValueInternal(int columnIndex) { return values.get(columnIndex); @@ -367,6 +375,7 @@ private Object getAsObject(int columnIndex) { case BOOL: return getBooleanInternal(columnIndex); case INT64: + case PROTO_ENUM: return getLongInternal(columnIndex); case FLOAT64: return getDoubleInternal(columnIndex); @@ -378,6 +387,8 @@ private Object getAsObject(int columnIndex) { return getStringInternal(columnIndex); case JSON: return getJsonInternal(columnIndex); + case PROTO: + return getProtoMessageInternal(columnIndex); case PG_JSONB: return getPgJsonbInternal(columnIndex); case BYTES: diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/StructReader.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/StructReader.java index f4141226c43..76abbe6106f 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/StructReader.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/StructReader.java @@ -21,8 +21,10 @@ import com.google.cloud.Timestamp; import com.google.protobuf.AbstractMessage; import com.google.protobuf.InvalidProtocolBufferException; +import com.google.protobuf.ProtocolMessageEnum; import java.math.BigDecimal; import java.util.List; +import java.util.function.Function; /** * A base interface for reading the fields of a {@code STRUCT}. The Cloud Spanner yields {@code @@ -53,6 +55,7 @@ * Struct} is an immutable implementation of {@code StructReader}. */ public interface StructReader { + /** * Returns the type of the underlying data. This will always be a {@code STRUCT} type, with fields * corresponding to the data's columns. For the result of a read or query, this will always match @@ -306,6 +309,12 @@ T getProtoMessage(int columnIndex, T m) T getProtoMessage(String columnName, T m) throws InvalidProtocolBufferException; + T getProtoEnum( + int columnIndex, Function method); + + T getProtoEnum( + String columnName, Function method); + /** * Returns the value of a non-{@code NULL} column with type {@code Type.array(Type.struct(...))}. */ 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 c67bb620355..72bde36ff7a 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 @@ -31,6 +31,8 @@ import com.google.protobuf.NullValue; import com.google.protobuf.ProtocolMessageEnum; import java.io.Serializable; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; @@ -229,8 +231,16 @@ public static Value protoMessage(@Nullable byte[] v) { return new ProtoMessageImpl(v == null, v); } + public static Value protoEnum(@Nullable ProtocolMessageEnum v) { + return new ProtoEnumImpl(v == null, v); + } + + public static Value protoEnum(@Nullable long v) { + return new ProtoEnumImpl(false, v); + } + /** - * Returns a {@code BYTES} value. Returns a {@code BYTES} value. + * e Returns a {@code BYTES} value. Returns a {@code BYTES} value. * * @param v the value, which may be null */ @@ -557,12 +567,12 @@ byte[] getProtoMessage() { throw new UnsupportedOperationException("Not implemented"); } - public T getProtoMessage(T m) - throws InvalidProtocolBufferException { + public T getProtoMessage(T m) throws InvalidProtocolBufferException { throw new UnsupportedOperationException("Not implemented"); } - public ProtocolMessageEnum getProtoEnum() { + public T getProtoEnum(Class clazz) + throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { throw new UnsupportedOperationException("Not implemented"); } @@ -887,6 +897,12 @@ public T getProtoMessage(T m) throw defaultGetter(Type.proto()); } + @Override + public T getProtoEnum(Class clazz) + throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + throw defaultGetter(Type.protoEnum()); + } + @Override public ByteArray getBytes() { throw defaultGetter(Type.bytes()); @@ -1368,12 +1384,16 @@ private ProtoMessageImpl(boolean isNull, byte[] serializedProtoArray) { @Override byte[] getProtoMessage() { + checkType(Type.proto()); + checkNotNull(); return value.serializedMessage; } @Override public T getProtoMessage(T m) throws InvalidProtocolBufferException { + checkType(Type.proto()); + checkNotNull(); return (T) m.toBuilder().mergeFrom(value.serializedMessage); } @@ -1389,6 +1409,44 @@ void valueToString(StringBuilder b) { } } + private static class ProtoEnumImpl extends AbstractObjectValue { + private ProtoEnumImpl(boolean isNull, ProtocolMessageEnum protoEnum) { + super(isNull, Type.protoEnum(), new ProtoEnumWrapper(protoEnum)); + } + + private ProtoEnumImpl(boolean isNull, long enumValue) { + super(isNull, Type.protoEnum(), new ProtoEnumWrapper(enumValue)); + } + + @Override + public long getInt64() { + checkType(Type.protoEnum()); + checkNotNull(); + return value.enumValue; + } + + @Override + public T getProtoEnum(Class clazz) + throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + checkType(Type.protoEnum()); + checkNotNull(); + Method parseMethod = clazz.getMethod("forNumber", int.class); + return clazz.cast(parseMethod.invoke(null, value.enumValue)); + } + + @Override + void valueToString(StringBuilder b) { + b.append(value.toString()); + } + + @Override + com.google.protobuf.Value valueToProto() { + return com.google.protobuf.Value.newBuilder() + .setStringValue(Long.toString(value.enumValue)) + .build(); + } + } + private static class TimestampImpl extends AbstractObjectValue { private static final String COMMIT_TIMESTAMP_STRING = "spanner.commit_timestamp()"; @@ -2016,6 +2074,8 @@ private Value getValue(int fieldIndex) { return Value.timestamp(value.getTimestamp(fieldIndex)); case PROTO: return Value.protoMessage(value.getProtoMessage(fieldIndex)); + case PROTO_ENUM: + return Value.protoEnum(value.getLong(fieldIndex)); case STRUCT: return Value.struct(value.getStruct(fieldIndex)); case ARRAY: 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 b4efbac5f4e..b8d266b7756 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 @@ -20,6 +20,7 @@ import com.google.cloud.Date; import com.google.cloud.Timestamp; import com.google.protobuf.GeneratedMessage; +import com.google.protobuf.ProtocolMessageEnum; import java.math.BigDecimal; import javax.annotation.Nullable; @@ -100,6 +101,10 @@ public R to(@Nullable GeneratedMessage m) { return handle(Value.protoMessage(m)); } + public R to(@Nullable ProtocolMessageEnum en) { + return handle(Value.protoEnum(en)); + } + /** Binds to {@code Value.bytes(value)} */ public R to(@Nullable ByteArray value) { return handle(Value.bytes(value)); diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ChecksumResultSet.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ChecksumResultSet.java index 18fa48f2798..4f0808b9631 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ChecksumResultSet.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ChecksumResultSet.java @@ -240,6 +240,7 @@ public void funnel(Struct row, PrimitiveSink into) { funnelValue(type, row.getString(i), into); break; case INT64: + case PROTO_ENUM: funnelValue(type, row.getLong(i), into); break; case STRING: diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DirectExecuteResultSet.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DirectExecuteResultSet.java index 138bfea3ea6..e70706a11d8 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DirectExecuteResultSet.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DirectExecuteResultSet.java @@ -27,9 +27,11 @@ import com.google.common.base.Preconditions; import com.google.protobuf.AbstractMessage; import com.google.protobuf.InvalidProtocolBufferException; +import com.google.protobuf.ProtocolMessageEnum; import com.google.spanner.v1.ResultSetStats; import java.math.BigDecimal; import java.util.List; +import java.util.function.Function; /** * {@link ResultSet} implementation used by the Spanner connection API to ensure that the query for @@ -445,6 +447,20 @@ public byte[] getProtoMessage(String columnName) { return delegate.getProtoMessage(columnName); } + @Override + public T getProtoEnum( + int columnIndex, Function method) { + Preconditions.checkState(nextCalledByClient, MISSING_NEXT_CALL); + return delegate.getProtoEnum(columnIndex, method); + } + + @Override + public T getProtoEnum( + String columnName, Function method) { + Preconditions.checkState(nextCalledByClient, MISSING_NEXT_CALL); + return delegate.getProtoEnum(columnName, method); + } + @Override public T getProtoMessage(int columnIndex, T m) throws InvalidProtocolBufferException { diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSet.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSet.java index 49b1820fce5..9446502ffcf 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSet.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSet.java @@ -29,9 +29,11 @@ import com.google.common.base.Preconditions; import com.google.protobuf.AbstractMessage; import com.google.protobuf.InvalidProtocolBufferException; +import com.google.protobuf.ProtocolMessageEnum; import com.google.spanner.v1.ResultSetStats; import java.math.BigDecimal; import java.util.List; +import java.util.function.Function; /** * Forwarding implementation of {@link ResultSet} that forwards all calls to a delegate that can be @@ -463,4 +465,18 @@ public T getProtoMessage(String columnName, T m) checkClosed(); return delegate.getProtoMessage(columnName, m); } + + @Override + public T getProtoEnum( + int columnIndex, Function method) { + checkClosed(); + return delegate.getProtoEnum(columnIndex, method); + } + + @Override + public T getProtoEnum( + String columnName, Function method) { + checkClosed(); + return delegate.getProtoEnum(columnName, method); + } } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AbstractStructReaderTypesTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AbstractStructReaderTypesTest.java index a1e8b6474c7..75c10bcd194 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AbstractStructReaderTypesTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AbstractStructReaderTypesTest.java @@ -28,6 +28,7 @@ import com.google.cloud.Timestamp; import com.google.common.base.Throwables; import com.google.protobuf.AbstractMessage; +import com.google.protobuf.ProtocolMessageEnum; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.math.BigDecimal; @@ -35,6 +36,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.function.Function; import javax.annotation.Nullable; import org.junit.Before; import org.junit.Test; @@ -106,6 +108,12 @@ protected T getProtoMessageInternal(int columnIndex, return null; } + @Override + protected T getProtoEnumInternal( + int columnIndex, Function method) { + return null; + } + @Override protected Value getValueInternal(int columnIndex) { return null; From 7ade421cc48868151e8a1ad1d6611d7ec95fecb1 Mon Sep 17 00:00:00 2001 From: gauravpurohit Date: Thu, 29 Sep 2022 03:50:49 +0000 Subject: [PATCH 04/21] feat: Code refactoring Adding default implementation for newly added methods ByteArray compatability changes for Proto Messages --- .../cloud/spanner/AbstractResultSet.java | 35 +++--- .../cloud/spanner/AbstractStructReader.java | 57 +++++----- .../cloud/spanner/ForwardingStructReader.java | 19 +--- .../cloud/spanner/ProtoEnumWrapper.java | 33 ------ .../cloud/spanner/ProtoMessageWrapper.java | 42 ------- .../com/google/cloud/spanner/ResultSets.java | 17 +-- .../java/com/google/cloud/spanner/Struct.java | 16 +-- .../google/cloud/spanner/StructReader.java | 27 ++--- .../java/com/google/cloud/spanner/Type.java | 53 ++++++--- .../java/com/google/cloud/spanner/Value.java | 105 ++++++++---------- .../com/google/cloud/spanner/ValueBinder.java | 4 +- .../spanner/connection/ChecksumResultSet.java | 6 +- .../connection/DirectExecuteResultSet.java | 19 +--- .../ReplaceableForwardingResultSet.java | 19 +--- .../AbstractStructReaderTypesTest.java | 5 - 15 files changed, 155 insertions(+), 302 deletions(-) delete mode 100644 google-cloud-spanner/src/main/java/com/google/cloud/spanner/ProtoEnumWrapper.java delete mode 100644 google-cloud-spanner/src/main/java/com/google/cloud/spanner/ProtoMessageWrapper.java 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 f2e1a2dd2a4..e0a3eba4c1e 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 @@ -390,9 +390,11 @@ private Object writeReplace() { builder.set(fieldName).to(Value.json((String) value)); break; case PROTO: - builder.set(fieldName).to(Value.protoMessage((byte[]) value)); - case PROTO_ENUM: - builder.set(fieldName).to(Value.protoEnum((long) value)); + builder + .set(fieldName) + .to(Value.protoMessage((ByteArray) value, fieldType.getProtoTypeFqn())); + case ENUM: + builder.set(fieldName).to(Value.protoEnum((Long) value, fieldType.getProtoTypeFqn())); case PG_JSONB: builder.set(fieldName).to(Value.pgJsonb((String) value)); break; @@ -498,7 +500,7 @@ private static Object decodeValue(Type fieldType, com.google.protobuf.Value prot checkType(fieldType, proto, KindCase.BOOL_VALUE); return proto.getBoolValue(); case INT64: - case PROTO_ENUM: + case ENUM: checkType(fieldType, proto, KindCase.STRING_VALUE); return Long.parseLong(proto.getStringValue()); case FLOAT64: @@ -666,14 +668,13 @@ public boolean isNull(int columnIndex) { } @Override - public byte[] getProtoMessageInternal(int columnIndex) { - return (byte[]) rowData.get(columnIndex); - } - - @Override - public T getProtoMessageInternal(int columnIndex, T m) - throws InvalidProtocolBufferException { - return (T) m.toBuilder().mergeFrom(getProtoMessageInternal(columnIndex)).build(); + public T getProtoMessageInternal(int columnIndex, T m) { + try { + return (T) + m.toBuilder().mergeFrom(((ByteArray) rowData.get(columnIndex)).toByteArray()).build(); + } catch (InvalidProtocolBufferException e) { + throw SpannerExceptionFactory.asSpannerException(e); + } } @Override @@ -689,7 +690,7 @@ protected boolean getBooleanInternal(int columnIndex) { @Override protected long getLongInternal(int columnIndex) { - return (Long) rowData.get(columnIndex); + return (long) rowData.get(columnIndex); } @Override @@ -1396,13 +1397,7 @@ protected String getStringInternal(int columnIndex) { } @Override - protected byte[] getProtoMessageInternal(int columnIndex) { - return currRow().getProtoMessageInternal(columnIndex); - } - - @Override - protected T getProtoMessageInternal(int columnIndex, T m) - throws InvalidProtocolBufferException { + protected T getProtoMessageInternal(int columnIndex, T m) { return currRow().getProtoMessageInternal(columnIndex, m); } 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 286edbc97c3..f90bda9b4d0 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 @@ -21,8 +21,8 @@ import com.google.cloud.ByteArray; import com.google.cloud.Date; import com.google.cloud.Timestamp; +import com.google.cloud.spanner.Type.Code; import com.google.protobuf.AbstractMessage; -import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.ProtocolMessageEnum; import java.math.BigDecimal; import java.util.Arrays; @@ -62,13 +62,14 @@ protected String getPgJsonbInternal(int columnIndex) { protected abstract Date getDateInternal(int columnIndex); - protected abstract byte[] getProtoMessageInternal(int columnIndex); - - protected abstract T getProtoMessageInternal(int columnIndex, T m) - throws InvalidProtocolBufferException; + protected T getProtoMessageInternal(int columnIndex, T m) { + throw new UnsupportedOperationException("Not implemented"); + } - protected abstract T getProtoEnumInternal( - int columnIndex, Function method); + protected T getProtoEnumInternal( + int columnIndex, Function method) { + throw new UnsupportedOperationException("Not implemented"); + } protected Value getValueInternal(int columnIndex) { throw new UnsupportedOperationException("method should be overwritten"); @@ -261,23 +262,11 @@ public Date getDate(String columnName) { return getDateInternal(columnIndex); } - @Override - public byte[] getProtoMessage(int columnIndex) { - checkNonNullOfType(columnIndex, Type.proto(), columnIndex); - return getProtoMessageInternal(columnIndex); - } - - @Override - public byte[] getProtoMessage(String columnName) { - int columnIndex = getColumnIndex(columnName); - checkNonNullOfType(columnIndex, Type.proto(), columnName); - return getProtoMessageInternal(columnIndex); - } - @Override public T getProtoEnum( int columnIndex, Function method) { - checkNonNullOfType(columnIndex, Type.protoEnum(), columnIndex); + checkNonNullOfCode( + columnIndex, Code.ENUM, columnIndex); return getProtoEnumInternal(columnIndex, method); } @@ -285,22 +274,23 @@ public T getProtoEnum( public T getProtoEnum( String columnName, Function method) { int columnIndex = getColumnIndex(columnName); - checkNonNullOfType(columnIndex, Type.proto(), columnName); + checkNonNullOfCode( + columnIndex, Code.ENUM, columnName); return getProtoEnumInternal(columnIndex, method); } @Override - public T getProtoMessage(int columnIndex, T m) - throws InvalidProtocolBufferException { - checkNonNullOfType(columnIndex, Type.proto(), columnIndex); + public T getProtoMessage(int columnIndex, T m) { + checkNonNullOfCode( + columnIndex, Code.PROTO, columnIndex); return getProtoMessageInternal(columnIndex, m); } @Override - public T getProtoMessage(String columnName, T m) - throws InvalidProtocolBufferException { + public T getProtoMessage(String columnName, T m) { int columnIndex = getColumnIndex(columnName); - checkNonNullOfType(columnIndex, Type.proto(), columnName); + checkNonNullOfCode( + columnIndex, Code.PROTO, columnName); return getProtoMessageInternal(columnIndex, m); } @@ -532,6 +522,17 @@ private void checkNonNullOfType(int columnIndex, Type expectedType, Object colum checkNonNull(columnIndex, columnNameForError); } + private void checkNonNullOfCode(int columnIndex, Code expectedCode, Object columnNameForError) { + Code actualCode = getColumnType(columnIndex).getCode(); + checkState( + expectedCode.equals(actualCode), + "Column %s is not of correct type: expected %s but was %s", + columnNameForError, + expectedCode, + actualCode); + checkNonNull(columnIndex, columnNameForError); + } + private void checkNonNullOfTypes( int columnIndex, List expectedTypes, diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ForwardingStructReader.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ForwardingStructReader.java index fcc9112c48e..7c7957623c4 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ForwardingStructReader.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ForwardingStructReader.java @@ -23,7 +23,6 @@ import com.google.common.base.Supplier; import com.google.common.base.Suppliers; import com.google.protobuf.AbstractMessage; -import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.ProtocolMessageEnum; import java.math.BigDecimal; import java.util.List; @@ -387,27 +386,13 @@ public List getStructList(String columnName) { } @Override - public byte[] getProtoMessage(int columnIndex) { - checkValidState(); - return delegate.get().getProtoMessage(columnIndex); - } - - @Override - public byte[] getProtoMessage(String columnName) { - checkValidState(); - return delegate.get().getProtoMessage(columnName); - } - - @Override - public T getProtoMessage(int columnIndex, T m) - throws InvalidProtocolBufferException { + public T getProtoMessage(int columnIndex, T m) { checkValidState(); return delegate.get().getProtoMessage(columnIndex, m); } @Override - public T getProtoMessage(String columnName, T m) - throws InvalidProtocolBufferException { + public T getProtoMessage(String columnName, T m) { checkValidState(); return delegate.get().getProtoMessage(columnName, m); } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ProtoEnumWrapper.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ProtoEnumWrapper.java deleted file mode 100644 index 2c965f75307..00000000000 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ProtoEnumWrapper.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2022 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.cloud.spanner; - -import com.google.protobuf.ProtocolMessageEnum; - -public class ProtoEnumWrapper { - ProtocolMessageEnum protocolMessageEnum; - long enumValue; - - ProtoEnumWrapper(ProtocolMessageEnum protoEnum) { - this.protocolMessageEnum = protoEnum; - this.enumValue = protoEnum.getNumber(); - } - - ProtoEnumWrapper(long val) { - this.enumValue = val; - } -} diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ProtoMessageWrapper.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ProtoMessageWrapper.java deleted file mode 100644 index cb731d2a9a5..00000000000 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ProtoMessageWrapper.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2022 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.cloud.spanner; - -import com.google.protobuf.AbstractMessage; -import java.util.Arrays; - -public class ProtoMessageWrapper { - AbstractMessage message; - byte[] serializedMessage; - - ProtoMessageWrapper(AbstractMessage m) { - this.message = m; - this.serializedMessage = m.toByteArray(); - } - - ProtoMessageWrapper(byte[] serializedMessage) { - this.serializedMessage = serializedMessage; - } - - @Override - public String toString() { - if (message != null) { - return message.toString(); - } - return Arrays.toString(serializedMessage); - } -} diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ResultSets.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ResultSets.java index fe5626aa374..968b9643b52 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ResultSets.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ResultSets.java @@ -30,7 +30,6 @@ import com.google.common.collect.Lists; import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.google.protobuf.AbstractMessage; -import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.ProtocolMessageEnum; import com.google.spanner.v1.ResultSetStats; import java.math.BigDecimal; @@ -298,24 +297,12 @@ public Date getDate(String columnName) { } @Override - public byte[] getProtoMessage(int columnIndex) { - return getCurrentRowAsStruct().getProtoMessage(columnIndex); - } - - @Override - public byte[] getProtoMessage(String columnName) { - return getCurrentRowAsStruct().getProtoMessage(columnName); - } - - @Override - public T getProtoMessage(int columnIndex, T m) - throws InvalidProtocolBufferException { + public T getProtoMessage(int columnIndex, T m) { return getCurrentRowAsStruct().getProtoMessage(columnIndex, m); } @Override - public T getProtoMessage(String columnName, T m) - throws InvalidProtocolBufferException { + public T getProtoMessage(String columnName, T m) { return getCurrentRowAsStruct().getProtoMessage(columnName, m); } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Struct.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Struct.java index 5544e735e62..8b7b8e329f7 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Struct.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Struct.java @@ -29,7 +29,6 @@ import com.google.common.primitives.Doubles; import com.google.common.primitives.Longs; import com.google.protobuf.AbstractMessage; -import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.ProtocolMessageEnum; import java.io.Serializable; import java.math.BigDecimal; @@ -222,20 +221,14 @@ protected Date getDateInternal(int columnIndex) { } @Override - protected byte[] getProtoMessageInternal(int columnIndex) { - return values.get(columnIndex).getProtoMessage(); - } - - @Override - protected T getProtoMessageInternal(int columnIndex, T m) - throws InvalidProtocolBufferException { + protected T getProtoMessageInternal(int columnIndex, T m) { return values.get(columnIndex).getProtoMessage(m); } @Override protected T getProtoEnumInternal( int columnIndex, Function method) { - return (T) method.apply((int) values.get(columnIndex).getInt64()); + return values.get(columnIndex).getProtoEnum(method); } @Override @@ -375,7 +368,7 @@ private Object getAsObject(int columnIndex) { case BOOL: return getBooleanInternal(columnIndex); case INT64: - case PROTO_ENUM: + case ENUM: return getLongInternal(columnIndex); case FLOAT64: return getDoubleInternal(columnIndex); @@ -387,11 +380,10 @@ private Object getAsObject(int columnIndex) { return getStringInternal(columnIndex); case JSON: return getJsonInternal(columnIndex); - case PROTO: - return getProtoMessageInternal(columnIndex); case PG_JSONB: return getPgJsonbInternal(columnIndex); case BYTES: + case PROTO: return getBytesInternal(columnIndex); case TIMESTAMP: return getTimestampInternal(columnIndex); diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/StructReader.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/StructReader.java index 76abbe6106f..116686e7700 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/StructReader.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/StructReader.java @@ -20,7 +20,6 @@ import com.google.cloud.Date; import com.google.cloud.Timestamp; import com.google.protobuf.AbstractMessage; -import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.ProtocolMessageEnum; import java.math.BigDecimal; import java.util.List; @@ -299,21 +298,23 @@ default List getPgJsonbList(String columnName) { /** Returns the value of a non-{@code NULL} column with type {@code Type.array(Type.date())}. */ List getDateList(String columnName); - byte[] getProtoMessage(int columnIndex); - - byte[] getProtoMessage(String columnName); - - T getProtoMessage(int columnIndex, T m) - throws InvalidProtocolBufferException; + default T getProtoMessage(int columnIndex, T m) { + throw new UnsupportedOperationException("method should be overwritten"); + } - T getProtoMessage(String columnName, T m) - throws InvalidProtocolBufferException; + default T getProtoMessage(String columnName, T m) { + throw new UnsupportedOperationException("method should be overwritten"); + } - T getProtoEnum( - int columnIndex, Function method); + default T getProtoEnum( + int columnIndex, Function method) { + throw new UnsupportedOperationException("method should be overwritten"); + } - T getProtoEnum( - String columnName, Function method); + default T getProtoEnum( + String columnName, Function method) { + throw new UnsupportedOperationException("method should be overwritten"); + } /** * Returns the value of a non-{@code NULL} column with type {@code Type.array(Type.struct(...))}. 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 a0c02de4338..b4dea58fe8f 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 @@ -23,6 +23,7 @@ import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import com.google.protobuf.ProtocolMessageEnum; import com.google.spanner.v1.TypeAnnotationCode; import com.google.spanner.v1.TypeCode; import java.io.Serializable; @@ -33,6 +34,7 @@ import java.util.Map.Entry; import java.util.Objects; import java.util.TreeMap; +import java.util.function.Function; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; @@ -56,8 +58,6 @@ public final class Type implements Serializable { private static final Type TYPE_BYTES = new Type(Code.BYTES, null, null); private static final Type TYPE_TIMESTAMP = new Type(Code.TIMESTAMP, null, null); private static final Type TYPE_DATE = new Type(Code.DATE, null, null); - private static final Type TYPE_PROTO = new Type(Code.PROTO, null, null); - private static final Type TYPE_PROTO_ENUM = new Type(Code.PROTO_ENUM, null, null); private static final Type TYPE_ARRAY_BOOL = new Type(Code.ARRAY, TYPE_BOOL, null); private static final Type TYPE_ARRAY_INT64 = new Type(Code.ARRAY, TYPE_INT64, null); private static final Type TYPE_ARRAY_FLOAT64 = new Type(Code.ARRAY, TYPE_FLOAT64, null); @@ -66,8 +66,6 @@ public final class Type implements Serializable { private static final Type TYPE_ARRAY_STRING = new Type(Code.ARRAY, TYPE_STRING, null); private static final Type TYPE_ARRAY_JSON = new Type(Code.ARRAY, TYPE_JSON, null); private static final Type TYPE_ARRAY_PG_JSONB = new Type(Code.ARRAY, TYPE_PG_JSONB, null); - private static final Type TYPE_ARRAY_PROTO = new Type(Code.ARRAY, TYPE_PROTO, null); - private static final Type TYPE_ARRAY_PROTO_ENUM = new Type(Code.ARRAY, TYPE_PROTO_ENUM, null); private static final Type TYPE_ARRAY_BYTES = new Type(Code.ARRAY, TYPE_BYTES, null); private static final Type TYPE_ARRAY_TIMESTAMP = new Type(Code.ARRAY, TYPE_TIMESTAMP, null); private static final Type TYPE_ARRAY_DATE = new Type(Code.ARRAY, TYPE_DATE, null); @@ -126,12 +124,12 @@ public static Type pgJsonb() { return TYPE_PG_JSONB; } - public static Type proto() { - return TYPE_PROTO; + public static Type proto(String protoTypeFqn) { + return new Type(Code.PROTO, null, null, protoTypeFqn); } - public static Type protoEnum() { - return TYPE_PROTO_ENUM; + public static Type protoEnum(String protoTypeFqn) { + return new Type(Code.ENUM, null, null, protoTypeFqn); } /** Returns the descriptor for the {@code BYTES} type: a variable-length byte string. */ @@ -181,10 +179,6 @@ public static Type array(Type elementType) { return TYPE_ARRAY_TIMESTAMP; case DATE: return TYPE_ARRAY_DATE; - case PROTO: - return TYPE_ARRAY_PROTO; - case PROTO_ENUM: - return TYPE_ARRAY_PROTO_ENUM; default: return new Type(Code.ARRAY, elementType, null); } @@ -209,6 +203,7 @@ public static Type struct(StructField... fields) { private final Code code; private final Type arrayElementType; private final ImmutableList structFields; + private String protoTypeFqn; /** * Map of field name to field index. Ambiguous names are indexed to {@link #AMBIGUOUS_FIELD}. The @@ -225,6 +220,15 @@ private Type( this.structFields = structFields; } + private Type( + Code code, + @Nullable Type arrayElementType, + @Nullable ImmutableList structFields, + @Nullable String protoTypeFqn) { + this(code, arrayElementType, structFields); + this.protoTypeFqn = protoTypeFqn; + } + /** Enumerates the categories of types. */ public enum Code { BOOL(TypeCode.BOOL), @@ -236,7 +240,7 @@ public enum Code { JSON(TypeCode.JSON), PG_JSONB(TypeCode.JSON, TypeAnnotationCode.PG_JSONB), PROTO(TypeCode.PROTO), - PROTO_ENUM(TypeCode.ENUM), + ENUM(TypeCode.ENUM), BYTES(TypeCode.BYTES), TIMESTAMP(TypeCode.TIMESTAMP), DATE(TypeCode.DATE), @@ -358,6 +362,17 @@ public List getStructFields() { return structFields; } + /** + * Returns the full package name for elements of this {@code Proto or @code Enum} type. + * + * @throws IllegalStateException if {@code code() != Code.ARRAY} + */ + public String getProtoTypeFqn() { + Preconditions.checkState( + (code == Code.PROTO || code == Code.ENUM), "Illegal call for non-Proto type"); + return protoTypeFqn; + } + /** * Returns the index of the field named {@code fieldName} in this {@code STRUCT} type. * @@ -434,7 +449,8 @@ public boolean equals(Object o) { Type that = (Type) o; return code == that.code && Objects.equals(arrayElementType, that.arrayElementType) - && Objects.equals(structFields, that.structFields); + && Objects.equals(structFields, that.structFields) + && Objects.equals(protoTypeFqn, that.protoTypeFqn); } @Override @@ -453,7 +469,10 @@ com.google.spanner.v1.Type toProto() { for (StructField field : structFields) { fields.addFieldsBuilder().setName(field.getName()).setType(field.getType().toProto()); } + } else if (code == Code.PROTO || code == Code.ENUM) { + proto.setProtoTypeFqn(protoTypeFqn); } + return proto.build(); } @@ -483,9 +502,9 @@ static Type fromProto(com.google.spanner.v1.Type proto) { case DATE: return date(); case PROTO: - return proto(); - case PROTO_ENUM: - return protoEnum(); + return proto(proto.getProtoTypeFqn()); + case ENUM: + return protoEnum(proto.getProtoTypeFqn()); case ARRAY: checkArgument( proto.hasArrayElementType(), 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 72bde36ff7a..fbbd052873c 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 @@ -24,15 +24,12 @@ import com.google.common.base.Preconditions; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; -import com.google.common.io.BaseEncoding; import com.google.protobuf.AbstractMessage; import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.ListValue; import com.google.protobuf.NullValue; import com.google.protobuf.ProtocolMessageEnum; import java.io.Serializable; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; @@ -41,6 +38,7 @@ import java.util.Collections; import java.util.List; import java.util.Objects; +import java.util.function.Function; import java.util.stream.Collectors; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; @@ -224,19 +222,26 @@ public static Value pgJsonb(@Nullable String v) { } public static Value protoMessage(@Nullable AbstractMessage v) { - return new ProtoMessageImpl(v == null, v); + Preconditions.checkNotNull( + v, "Use proto(Descriptor, Message) or proto(Class, T) for null values."); + return protoMessage( + ByteArray.copyFrom(v.toByteArray()), v.getDescriptorForType().getFullName()); } - public static Value protoMessage(@Nullable byte[] v) { - return new ProtoMessageImpl(v == null, v); + public static Value protoMessage(@Nullable ByteArray v, String protoTypFqn) { + return new ProtoMessageImpl(v == null, v, protoTypFqn); } public static Value protoEnum(@Nullable ProtocolMessageEnum v) { - return new ProtoEnumImpl(v == null, v); + Preconditions.checkNotNull( + v, + "Use enumValue(EnumDescriptor, ProtocolMessageEnum) or " + + "enumValue(Class, ProtocolMessageEnum) for null values."); + return protoEnum(v.getNumber(), v.getDescriptorForType().getFullName()); } - public static Value protoEnum(@Nullable long v) { - return new ProtoEnumImpl(false, v); + public static Value protoEnum(@Nullable long v, String protoTypFqn) { + return new ProtoEnumImpl(false, v, protoTypFqn); } /** @@ -563,16 +568,12 @@ public String getPgJsonb() { throw new UnsupportedOperationException("Not implemented"); } - byte[] getProtoMessage() { + public T getProtoMessage(T m) { throw new UnsupportedOperationException("Not implemented"); } - public T getProtoMessage(T m) throws InvalidProtocolBufferException { - throw new UnsupportedOperationException("Not implemented"); - } - - public T getProtoEnum(Class clazz) - throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + public T getProtoEnum( + Function method) { throw new UnsupportedOperationException("Not implemented"); } @@ -887,20 +888,14 @@ public String getPgJsonb() { } @Override - byte[] getProtoMessage() { - throw defaultGetter(Type.proto()); - } - - @Override - public T getProtoMessage(T m) - throws InvalidProtocolBufferException { - throw defaultGetter(Type.proto()); + public T getProtoMessage(T m) { + throw defaultGetter(Type.proto("")); } @Override - public T getProtoEnum(Class clazz) - throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { - throw defaultGetter(Type.protoEnum()); + public T getProtoEnum( + Function method) { + throw defaultGetter(Type.protoEnum("")); } @Override @@ -1373,33 +1368,31 @@ void valueToString(StringBuilder b) { } } - private static class ProtoMessageImpl extends AbstractObjectValue { - private ProtoMessageImpl(boolean isNull, AbstractMessage value) { - super(isNull, Type.proto(), new ProtoMessageWrapper(value)); - } + private static class ProtoMessageImpl extends AbstractObjectValue { - private ProtoMessageImpl(boolean isNull, byte[] serializedProtoArray) { - super(isNull, Type.proto(), new ProtoMessageWrapper(serializedProtoArray)); + private ProtoMessageImpl(boolean isNull, ByteArray serializedProtoArray, String protoTypeFqn) { + super(isNull, Type.proto(protoTypeFqn), serializedProtoArray); } @Override - byte[] getProtoMessage() { - checkType(Type.proto()); + public ByteArray getBytes() { checkNotNull(); - return value.serializedMessage; + return value; } @Override - public T getProtoMessage(T m) - throws InvalidProtocolBufferException { - checkType(Type.proto()); + public T getProtoMessage(T m) { checkNotNull(); - return (T) m.toBuilder().mergeFrom(value.serializedMessage); + try { + return (T) m.toBuilder().mergeFrom(value.toByteArray()).build(); + } catch (InvalidProtocolBufferException e) { + throw SpannerExceptionFactory.asSpannerException(e); + } } @Override com.google.protobuf.Value valueToProto() { - String base64EncodedString = BaseEncoding.base16().encode(value.serializedMessage); + String base64EncodedString = value.toBase64(); return com.google.protobuf.Value.newBuilder().setStringValue(base64EncodedString).build(); } @@ -1409,29 +1402,23 @@ void valueToString(StringBuilder b) { } } - private static class ProtoEnumImpl extends AbstractObjectValue { - private ProtoEnumImpl(boolean isNull, ProtocolMessageEnum protoEnum) { - super(isNull, Type.protoEnum(), new ProtoEnumWrapper(protoEnum)); - } + private static class ProtoEnumImpl extends AbstractObjectValue { - private ProtoEnumImpl(boolean isNull, long enumValue) { - super(isNull, Type.protoEnum(), new ProtoEnumWrapper(enumValue)); + private ProtoEnumImpl(boolean isNull, long enumValue, String protoTypeFqn) { + super(isNull, Type.protoEnum(protoTypeFqn), enumValue); } @Override public long getInt64() { - checkType(Type.protoEnum()); checkNotNull(); - return value.enumValue; + return value; } @Override - public T getProtoEnum(Class clazz) - throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { - checkType(Type.protoEnum()); + public T getProtoEnum( + Function method) { checkNotNull(); - Method parseMethod = clazz.getMethod("forNumber", int.class); - return clazz.cast(parseMethod.invoke(null, value.enumValue)); + return (T) method.apply((int) value.intValue()); } @Override @@ -1441,9 +1428,7 @@ void valueToString(StringBuilder b) { @Override com.google.protobuf.Value valueToProto() { - return com.google.protobuf.Value.newBuilder() - .setStringValue(Long.toString(value.enumValue)) - .build(); + return com.google.protobuf.Value.newBuilder().setStringValue(Long.toString(value)).build(); } } @@ -2073,9 +2058,9 @@ private Value getValue(int fieldIndex) { case TIMESTAMP: return Value.timestamp(value.getTimestamp(fieldIndex)); case PROTO: - return Value.protoMessage(value.getProtoMessage(fieldIndex)); - case PROTO_ENUM: - return Value.protoEnum(value.getLong(fieldIndex)); + return Value.protoMessage(value.getBytes(fieldIndex), fieldType.getProtoTypeFqn()); + case ENUM: + return Value.protoEnum(value.getLong(fieldIndex), fieldType.getProtoTypeFqn()); case STRUCT: return Value.struct(value.getStruct(fieldIndex)); case ARRAY: 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 b8d266b7756..a0967ff2ffb 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 @@ -19,7 +19,7 @@ import com.google.cloud.ByteArray; import com.google.cloud.Date; import com.google.cloud.Timestamp; -import com.google.protobuf.GeneratedMessage; +import com.google.protobuf.AbstractMessage; import com.google.protobuf.ProtocolMessageEnum; import java.math.BigDecimal; import javax.annotation.Nullable; @@ -97,7 +97,7 @@ public R to(@Nullable String value) { return handle(Value.string(value)); } - public R to(@Nullable GeneratedMessage m) { + public R to(@Nullable AbstractMessage m) { return handle(Value.protoMessage(m)); } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ChecksumResultSet.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ChecksumResultSet.java index 4f0808b9631..7911ac43d3e 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ChecksumResultSet.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ChecksumResultSet.java @@ -225,6 +225,7 @@ public void funnel(Struct row, PrimitiveSink into) { funnelValue(type, row.getBoolean(i), into); break; case BYTES: + case PROTO: funnelValue(type, row.getBytes(i), into); break; case DATE: @@ -240,7 +241,7 @@ public void funnel(Struct row, PrimitiveSink into) { funnelValue(type, row.getString(i), into); break; case INT64: - case PROTO_ENUM: + case ENUM: funnelValue(type, row.getLong(i), into); break; case STRING: @@ -249,9 +250,6 @@ public void funnel(Struct row, PrimitiveSink into) { case JSON: funnelValue(type, row.getJson(i), into); break; - case PROTO: - funnelValue(type, row.getProtoMessage(i), into); - break; case PG_JSONB: funnelValue(type, row.getPgJsonb(i), into); break; diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DirectExecuteResultSet.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DirectExecuteResultSet.java index e70706a11d8..def095444d2 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DirectExecuteResultSet.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DirectExecuteResultSet.java @@ -26,7 +26,6 @@ import com.google.cloud.spanner.Value; import com.google.common.base.Preconditions; import com.google.protobuf.AbstractMessage; -import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.ProtocolMessageEnum; import com.google.spanner.v1.ResultSetStats; import java.math.BigDecimal; @@ -435,18 +434,6 @@ public List getStructList(String columnName) { return delegate.getStructList(columnName); } - @Override - public byte[] getProtoMessage(int columnIndex) { - Preconditions.checkState(nextCalledByClient, MISSING_NEXT_CALL); - return delegate.getProtoMessage(columnIndex); - } - - @Override - public byte[] getProtoMessage(String columnName) { - Preconditions.checkState(nextCalledByClient, MISSING_NEXT_CALL); - return delegate.getProtoMessage(columnName); - } - @Override public T getProtoEnum( int columnIndex, Function method) { @@ -462,15 +449,13 @@ public T getProtoEnum( } @Override - public T getProtoMessage(int columnIndex, T m) - throws InvalidProtocolBufferException { + public T getProtoMessage(int columnIndex, T m) { Preconditions.checkState(nextCalledByClient, MISSING_NEXT_CALL); return delegate.getProtoMessage(columnIndex, m); } @Override - public T getProtoMessage(String columnName, T m) - throws InvalidProtocolBufferException { + public T getProtoMessage(String columnName, T m) { Preconditions.checkState(nextCalledByClient, MISSING_NEXT_CALL); return delegate.getProtoMessage(columnName, m); } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSet.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSet.java index 9446502ffcf..d0646a5ffe5 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSet.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSet.java @@ -28,7 +28,6 @@ import com.google.cloud.spanner.Value; import com.google.common.base.Preconditions; import com.google.protobuf.AbstractMessage; -import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.ProtocolMessageEnum; import com.google.spanner.v1.ResultSetStats; import java.math.BigDecimal; @@ -441,27 +440,13 @@ public List getStructList(String columnName) { } @Override - public byte[] getProtoMessage(int columnIndex) { - checkClosed(); - return delegate.getProtoMessage(columnIndex); - } - - @Override - public byte[] getProtoMessage(String columnName) { - checkClosed(); - return delegate.getProtoMessage(columnName); - } - - @Override - public T getProtoMessage(int columnIndex, T m) - throws InvalidProtocolBufferException { + public T getProtoMessage(int columnIndex, T m) { checkClosed(); return delegate.getProtoMessage(columnIndex, m); } @Override - public T getProtoMessage(String columnName, T m) - throws InvalidProtocolBufferException { + public T getProtoMessage(String columnName, T m) { checkClosed(); return delegate.getProtoMessage(columnName, m); } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AbstractStructReaderTypesTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AbstractStructReaderTypesTest.java index 75c10bcd194..e8a61c233e0 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AbstractStructReaderTypesTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AbstractStructReaderTypesTest.java @@ -98,11 +98,6 @@ protected Date getDateInternal(int columnIndex) { return null; } - @Override - protected byte[] getProtoMessageInternal(int columnIndex) { - return null; - } - @Override protected T getProtoMessageInternal(int columnIndex, T m) { return null; From 570c95213f645f354a667ed4abd512fe8795ce72 Mon Sep 17 00:00:00 2001 From: gauravpurohit Date: Tue, 18 Oct 2022 12:33:01 +0000 Subject: [PATCH 05/21] docs: Adding Java docs for all the newly added methods. --- .../clirr-ignored-differences.xml | 20 ++++++ .../cloud/spanner/AbstractResultSet.java | 5 ++ .../cloud/spanner/AbstractStructReader.java | 13 ++-- .../google/cloud/spanner/StructReader.java | 68 ++++++++++++++----- .../java/com/google/cloud/spanner/Type.java | 14 +++- .../java/com/google/cloud/spanner/Value.java | 62 ++++++++++++++--- .../com/google/cloud/spanner/ValueBinder.java | 6 +- 7 files changed, 147 insertions(+), 41 deletions(-) diff --git a/google-cloud-spanner/clirr-ignored-differences.xml b/google-cloud-spanner/clirr-ignored-differences.xml index 039278a6436..26cc43b7d9c 100644 --- a/google-cloud-spanner/clirr-ignored-differences.xml +++ b/google-cloud-spanner/clirr-ignored-differences.xml @@ -192,6 +192,26 @@ com/google/cloud/spanner/StructReader java.util.List getPgJsonbList(java.lang.String) + + 7012 + com/google/cloud/spanner/StructReader + com.google.protobuf.ProtocolMessageEnum getProtoEnum(int, java.util.function.Function) + + + 7012 + com/google/cloud/spanner/StructReader + com.google.protobuf.ProtocolMessageEnum getProtoEnum(java.lang.String, java.util.function.Function) + + + 7012 + com/google/cloud/spanner/StructReader + com.google.protobuf.AbstractMessage getProtoMessage(int, com.google.protobuf.AbstractMessage) + + + 7012 + com/google/cloud/spanner/StructReader + com.google.protobuf.AbstractMessage getProtoMessage(java.lang.String, com.google.protobuf.AbstractMessage) + 7012 com/google/cloud/spanner/BatchClient 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 e0a3eba4c1e..a5daac26f68 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,6 +32,7 @@ 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; @@ -669,6 +670,8 @@ public boolean isNull(int columnIndex) { @Override public T getProtoMessageInternal(int columnIndex, T m) { + Preconditions.checkNotNull( + m, "Proto Message shouldn't be null, default instance from generated class can be used."); try { return (T) m.toBuilder().mergeFrom(((ByteArray) rowData.get(columnIndex)).toByteArray()).build(); @@ -680,6 +683,8 @@ public T getProtoMessageInternal(int columnIndex, T @Override protected T getProtoEnumInternal( int columnIndex, Function method) { + Preconditions.checkNotNull( + method, "Lambda method shouldn't be null, use forNumber from autogenerated enum class."); return (T) method.apply((int) getLongInternal(columnIndex)); } 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 f90bda9b4d0..051b47b67e7 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 @@ -265,8 +265,7 @@ public Date getDate(String columnName) { @Override public T getProtoEnum( int columnIndex, Function method) { - checkNonNullOfCode( - columnIndex, Code.ENUM, columnIndex); + checkNonNullOfCode(columnIndex, Code.ENUM, columnIndex); return getProtoEnumInternal(columnIndex, method); } @@ -274,23 +273,20 @@ public T getProtoEnum( public T getProtoEnum( String columnName, Function method) { int columnIndex = getColumnIndex(columnName); - checkNonNullOfCode( - columnIndex, Code.ENUM, columnName); + checkNonNullOfCode(columnIndex, Code.ENUM, columnName); return getProtoEnumInternal(columnIndex, method); } @Override public T getProtoMessage(int columnIndex, T m) { - checkNonNullOfCode( - columnIndex, Code.PROTO, columnIndex); + checkNonNullOfCode(columnIndex, Code.PROTO, columnIndex); return getProtoMessageInternal(columnIndex, m); } @Override public T getProtoMessage(String columnName, T m) { int columnIndex = getColumnIndex(columnName); - checkNonNullOfCode( - columnIndex, Code.PROTO, columnName); + checkNonNullOfCode(columnIndex, Code.PROTO, columnName); return getProtoMessageInternal(columnIndex, m); } @@ -522,6 +518,7 @@ private void checkNonNullOfType(int columnIndex, Type expectedType, Object colum checkNonNull(columnIndex, columnNameForError); } + /** Checks if the value at {@code columnIndex} is of {@code expectedCode} */ private void checkNonNullOfCode(int columnIndex, Code expectedCode, Object columnNameForError) { Code actualCode = getColumnType(columnIndex).getCode(); checkState( diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/StructReader.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/StructReader.java index 116686e7700..5be40106954 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/StructReader.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/StructReader.java @@ -138,6 +138,56 @@ default String getPgJsonb(String columnName) { throw new UnsupportedOperationException("method should be overwritten"); } + /** + * To get the proto message of generic type {@code T} from Struct. + * + * @param columnIndex Index of the column. + * @param m Proto message object. @see getDefaultInstance() + * @return The value of a non-{@code NULL} column with type {@link Type#proto(String)} ()}. + */ + default T getProtoMessage(int columnIndex, T m) { + throw new UnsupportedOperationException("method should be overwritten"); + } + + /** + * To get the proto message of type {@code T} from Struct. + * + * @param columnName Name of the column. + * @param m Proto message object. @see getDefaultInstance() + * @return The value of a non-{@code NULL} column with type {@link Type#proto(String)} ()}. + */ + default T getProtoMessage(String columnName, T m) { + throw new UnsupportedOperationException("method should be overwritten"); + } + + /** + * To get the proto enum of type {@code T} from Struct. + * + * @param columnIndex Index of the column. + * @param method Lambda method which takes a primitive int and return Enum. @see forNumber + * @return The value of a non-{@code NULL} column with type {@link Type#protoEnum(String)} ()}. + */ + default T getProtoEnum( + int columnIndex, Function method) { + throw new UnsupportedOperationException("method should be overwritten"); + } + + /** + * To get the proto enum of type {@code T} from Struct. + * + * @param columnName Name of the column. + * @param method Lambda method which takes a primitive int and return Enum. @see forNumber + * @return The value of a non-{@code NULL} column with type {@link Type#protoEnum(String)} ()}. + */ + default T getProtoEnum( + String columnName, Function method) { + throw new UnsupportedOperationException("method should be overwritten"); + } + /** Returns the value of a non-{@code NULL} column with type {@link Type#bytes()}. */ ByteArray getBytes(int columnIndex); @@ -298,24 +348,6 @@ default List getPgJsonbList(String columnName) { /** Returns the value of a non-{@code NULL} column with type {@code Type.array(Type.date())}. */ List getDateList(String columnName); - default T getProtoMessage(int columnIndex, T m) { - throw new UnsupportedOperationException("method should be overwritten"); - } - - default T getProtoMessage(String columnName, T m) { - throw new UnsupportedOperationException("method should be overwritten"); - } - - default T getProtoEnum( - int columnIndex, Function method) { - throw new UnsupportedOperationException("method should be overwritten"); - } - - default T getProtoEnum( - String columnName, Function method) { - throw new UnsupportedOperationException("method should be overwritten"); - } - /** * Returns the value of a non-{@code NULL} column with type {@code Type.array(Type.struct(...))}. */ 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 b4dea58fe8f..a6675633ed5 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 @@ -23,7 +23,6 @@ import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import com.google.protobuf.ProtocolMessageEnum; import com.google.spanner.v1.TypeAnnotationCode; import com.google.spanner.v1.TypeCode; import java.io.Serializable; @@ -34,7 +33,6 @@ import java.util.Map.Entry; import java.util.Objects; import java.util.TreeMap; -import java.util.function.Function; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; @@ -124,10 +122,20 @@ public static Type pgJsonb() { return TYPE_PG_JSONB; } + /** + * To get the descriptor for the {@code PROTO} type. + * + * @param protoTypeFqn Proto full name + */ public static Type proto(String protoTypeFqn) { return new Type(Code.PROTO, null, null, protoTypeFqn); } + /** + * To get the descriptor for the {@code ENUM} type. + * + * @param protoTypeFqn Proto ENUM full name + */ public static Type protoEnum(String protoTypeFqn) { return new Type(Code.ENUM, null, null, protoTypeFqn); } @@ -365,7 +373,7 @@ public List getStructFields() { /** * Returns the full package name for elements of this {@code Proto or @code Enum} type. * - * @throws IllegalStateException if {@code code() != Code.ARRAY} + * @throws IllegalStateException if {@code code() != Code.PROTO or code() != Code.ENUM} */ public String getProtoTypeFqn() { Preconditions.checkState( 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 fbbd052873c..c10e5095759 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 @@ -221,26 +221,54 @@ public static Value pgJsonb(@Nullable String v) { return new PgJsonbImpl(v == null, v); } - public static Value protoMessage(@Nullable AbstractMessage v) { - Preconditions.checkNotNull( - v, "Use proto(Descriptor, Message) or proto(Class, T) for null values."); + /** + * Return a {@code PROTO} value. + * + * @param v Not null Proto message. + */ + public static Value protoMessage(AbstractMessage v) { + Preconditions.checkNotNull(v, "Use protoMessage(ByteArray, String) for null values."); return protoMessage( ByteArray.copyFrom(v.toByteArray()), v.getDescriptorForType().getFullName()); } + /** + * Return a {@code PROTO} value + * + * @param v Serialized Proto Array, which may be null. + * @param protoTypFqn Fully qualified name of proto representing the proto definition. + */ public static Value protoMessage(@Nullable ByteArray v, String protoTypFqn) { return new ProtoMessageImpl(v == null, v, protoTypFqn); } - public static Value protoEnum(@Nullable ProtocolMessageEnum v) { - Preconditions.checkNotNull( - v, - "Use enumValue(EnumDescriptor, ProtocolMessageEnum) or " - + "enumValue(Class, ProtocolMessageEnum) for null values."); + /** + * Return a {@code ENUM} value. + * + * @param v Proto Enum, which may be null. + */ + public static Value protoEnum(ProtocolMessageEnum v) { + Preconditions.checkNotNull(v, "Use protoEnum(long, protoTypFqn) for null values."); return protoEnum(v.getNumber(), v.getDescriptorForType().getFullName()); } - public static Value protoEnum(@Nullable long v, String protoTypFqn) { + /** + * Return a {@code ENUM} value. + * + * @param v Enum non-primitive Integer constant. + * @param protoTypFqn Fully qualified name of proto representing the enum definition. + */ + public static Value protoEnum(Long v, String protoTypFqn) { + return new ProtoEnumImpl(v == null, v, protoTypFqn); + } + + /** + * Return a {@code ENUM} value. + * + * @param v Enum integer primitive constant. + * @param protoTypFqn Fully qualified name of proto representing the enum definition. + */ + public static Value protoEnum(long v, String protoTypFqn) { return new ProtoEnumImpl(false, v, protoTypFqn); } @@ -568,10 +596,20 @@ public String getPgJsonb() { throw new UnsupportedOperationException("Not implemented"); } + /** + * Returns the value of a {@code PROTO}-typed instance. + * + * @throws IllegalStateException if {@code isNull()} or the value is not of the expected type + */ public T getProtoMessage(T m) { throw new UnsupportedOperationException("Not implemented"); } + /** + * Returns the value of a {@code ENUM}-typed instance. + * + * @throws IllegalStateException if {@code isNull()} or the value is not of the expected type + */ public T getProtoEnum( Function method) { throw new UnsupportedOperationException("Not implemented"); @@ -1382,6 +1420,8 @@ public ByteArray getBytes() { @Override public T getProtoMessage(T m) { + Preconditions.checkNotNull( + m, "Proto Message shouldn't be null, default instance from generated class can be used."); checkNotNull(); try { return (T) m.toBuilder().mergeFrom(value.toByteArray()).build(); @@ -1404,7 +1444,7 @@ void valueToString(StringBuilder b) { private static class ProtoEnumImpl extends AbstractObjectValue { - private ProtoEnumImpl(boolean isNull, long enumValue, String protoTypeFqn) { + private ProtoEnumImpl(boolean isNull, Long enumValue, String protoTypeFqn) { super(isNull, Type.protoEnum(protoTypeFqn), enumValue); } @@ -1417,6 +1457,8 @@ public long getInt64() { @Override public T getProtoEnum( Function method) { + Preconditions.checkNotNull( + method, "Lambda method shouldn't be null, use forNumber from autogenerated enum class."); checkNotNull(); return (T) method.apply((int) value.intValue()); } 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 a0967ff2ffb..536713ad06b 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 @@ -97,12 +97,14 @@ public R to(@Nullable String value) { return handle(Value.string(value)); } + /** Binds to {@code Value.protoMessage(value)} */ public R to(@Nullable AbstractMessage m) { return handle(Value.protoMessage(m)); } - public R to(@Nullable ProtocolMessageEnum en) { - return handle(Value.protoEnum(en)); + /** Binds to {@code Value.protoEnum(value)} */ + public R to(@Nullable ProtocolMessageEnum value) { + return handle(Value.protoEnum(value)); } /** Binds to {@code Value.bytes(value)} */ From 0e804f50debcdb7a14e5eeb783fdd2a37b1072d3 Mon Sep 17 00:00:00 2001 From: gauravpurohit Date: Thu, 20 Oct 2022 13:21:49 +0000 Subject: [PATCH 06/21] test: Sample Proto & Generated classes for unit test --- .../com/google/cloud/spanner/SingerProto.java | 1163 +++++++++++++++++ .../com/google/cloud/spanner/singer.proto | 22 + 2 files changed, 1185 insertions(+) create mode 100644 google-cloud-spanner/src/test/java/com/google/cloud/spanner/SingerProto.java create mode 100644 google-cloud-spanner/src/test/resources/com/google/cloud/spanner/singer.proto diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SingerProto.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SingerProto.java new file mode 100644 index 00000000000..297f281045b --- /dev/null +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SingerProto.java @@ -0,0 +1,1163 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: src/test/resources/com/google/cloud/spanner/singer.proto + +package com.google.cloud.spanner; + +public final class SingerProto { + private SingerProto() {} + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistryLite registry) { + } + + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistry registry) { + registerAllExtensions( + (com.google.protobuf.ExtensionRegistryLite) registry); + } + /** + * Protobuf enum {@code com.singer.Genre} + */ + public enum Genre + implements com.google.protobuf.ProtocolMessageEnum { + /** + * POP = 0; + */ + POP(0), + /** + * JAZZ = 1; + */ + JAZZ(1), + /** + * FOLK = 2; + */ + FOLK(2), + /** + * ROCK = 3; + */ + ROCK(3), + ; + + /** + * POP = 0; + */ + public static final int POP_VALUE = 0; + /** + * JAZZ = 1; + */ + public static final int JAZZ_VALUE = 1; + /** + * FOLK = 2; + */ + public static final int FOLK_VALUE = 2; + /** + * ROCK = 3; + */ + public static final int ROCK_VALUE = 3; + + + public final int getNumber() { + return value; + } + + /** + * @param value The numeric wire value of the corresponding enum entry. + * @return The enum associated with the given numeric wire value. + * @deprecated Use {@link #forNumber(int)} instead. + */ + @java.lang.Deprecated + public static Genre valueOf(int value) { + return forNumber(value); + } + + /** + * @param value The numeric wire value of the corresponding enum entry. + * @return The enum associated with the given numeric wire value. + */ + public static Genre forNumber(int value) { + switch (value) { + case 0: return POP; + case 1: return JAZZ; + case 2: return FOLK; + case 3: return ROCK; + default: return null; + } + } + + public static com.google.protobuf.Internal.EnumLiteMap + internalGetValueMap() { + return internalValueMap; + } + private static final com.google.protobuf.Internal.EnumLiteMap< + Genre> internalValueMap = + new com.google.protobuf.Internal.EnumLiteMap() { + public Genre findValueByNumber(int number) { + return Genre.forNumber(number); + } + }; + + public final com.google.protobuf.Descriptors.EnumValueDescriptor + getValueDescriptor() { + return getDescriptor().getValues().get(ordinal()); + } + public final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptorForType() { + return getDescriptor(); + } + public static final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptor() { + return com.google.cloud.spanner.SingerProto.getDescriptor().getEnumTypes().get(0); + } + + private static final Genre[] VALUES = values(); + + public static Genre valueOf( + com.google.protobuf.Descriptors.EnumValueDescriptor desc) { + if (desc.getType() != getDescriptor()) { + throw new java.lang.IllegalArgumentException( + "EnumValueDescriptor is not for this type."); + } + return VALUES[desc.getIndex()]; + } + + private final int value; + + private Genre(int value) { + this.value = value; + } + + // @@protoc_insertion_point(enum_scope:com.singer.Genre) + } + + public interface SingerInfoOrBuilder extends + // @@protoc_insertion_point(interface_extends:com.singer.SingerInfo) + com.google.protobuf.MessageOrBuilder { + + /** + * optional int64 singer_id = 1; + * @return Whether the singerId field is set. + */ + boolean hasSingerId(); + /** + * optional int64 singer_id = 1; + * @return The singerId. + */ + long getSingerId(); + + /** + * optional string birth_date = 2; + * @return Whether the birthDate field is set. + */ + boolean hasBirthDate(); + /** + * optional string birth_date = 2; + * @return The birthDate. + */ + java.lang.String getBirthDate(); + /** + * optional string birth_date = 2; + * @return The bytes for birthDate. + */ + com.google.protobuf.ByteString + getBirthDateBytes(); + + /** + * optional string nationality = 3; + * @return Whether the nationality field is set. + */ + boolean hasNationality(); + /** + * optional string nationality = 3; + * @return The nationality. + */ + java.lang.String getNationality(); + /** + * optional string nationality = 3; + * @return The bytes for nationality. + */ + com.google.protobuf.ByteString + getNationalityBytes(); + + /** + * optional .com.singer.Genre genre = 4; + * @return Whether the genre field is set. + */ + boolean hasGenre(); + /** + * optional .com.singer.Genre genre = 4; + * @return The genre. + */ + com.google.cloud.spanner.SingerProto.Genre getGenre(); + } + /** + * Protobuf type {@code com.singer.SingerInfo} + */ + public static final class SingerInfo extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:com.singer.SingerInfo) + SingerInfoOrBuilder { + private static final long serialVersionUID = 0L; + // Use SingerInfo.newBuilder() to construct. + private SingerInfo(com.google.protobuf.GeneratedMessageV3.Builder builder) { + super(builder); + } + private SingerInfo() { + birthDate_ = ""; + nationality_ = ""; + genre_ = 0; + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new SingerInfo(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private SingerInfo( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 8: { + bitField0_ |= 0x00000001; + singerId_ = input.readInt64(); + break; + } + case 18: { + com.google.protobuf.ByteString bs = input.readBytes(); + bitField0_ |= 0x00000002; + birthDate_ = bs; + break; + } + case 26: { + com.google.protobuf.ByteString bs = input.readBytes(); + bitField0_ |= 0x00000004; + nationality_ = bs; + break; + } + case 32: { + int rawValue = input.readEnum(); + @SuppressWarnings("deprecation") + com.google.cloud.spanner.SingerProto.Genre value = com.google.cloud.spanner.SingerProto.Genre.valueOf(rawValue); + if (value == null) { + unknownFields.mergeVarintField(4, rawValue); + } else { + bitField0_ |= 0x00000008; + genre_ = rawValue; + } + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return com.google.cloud.spanner.SingerProto.internal_static_com_singer_SingerInfo_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return com.google.cloud.spanner.SingerProto.internal_static_com_singer_SingerInfo_fieldAccessorTable + .ensureFieldAccessorsInitialized( + com.google.cloud.spanner.SingerProto.SingerInfo.class, com.google.cloud.spanner.SingerProto.SingerInfo.Builder.class); + } + + private int bitField0_; + public static final int SINGER_ID_FIELD_NUMBER = 1; + private long singerId_; + /** + * optional int64 singer_id = 1; + * @return Whether the singerId field is set. + */ + @java.lang.Override + public boolean hasSingerId() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + * optional int64 singer_id = 1; + * @return The singerId. + */ + @java.lang.Override + public long getSingerId() { + return singerId_; + } + + public static final int BIRTH_DATE_FIELD_NUMBER = 2; + private volatile java.lang.Object birthDate_; + /** + * optional string birth_date = 2; + * @return Whether the birthDate field is set. + */ + @java.lang.Override + public boolean hasBirthDate() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + * optional string birth_date = 2; + * @return The birthDate. + */ + @java.lang.Override + public java.lang.String getBirthDate() { + java.lang.Object ref = birthDate_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + birthDate_ = s; + } + return s; + } + } + /** + * optional string birth_date = 2; + * @return The bytes for birthDate. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getBirthDateBytes() { + java.lang.Object ref = birthDate_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + birthDate_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int NATIONALITY_FIELD_NUMBER = 3; + private volatile java.lang.Object nationality_; + /** + * optional string nationality = 3; + * @return Whether the nationality field is set. + */ + @java.lang.Override + public boolean hasNationality() { + return ((bitField0_ & 0x00000004) != 0); + } + /** + * optional string nationality = 3; + * @return The nationality. + */ + @java.lang.Override + public java.lang.String getNationality() { + java.lang.Object ref = nationality_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + nationality_ = s; + } + return s; + } + } + /** + * optional string nationality = 3; + * @return The bytes for nationality. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getNationalityBytes() { + java.lang.Object ref = nationality_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + nationality_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int GENRE_FIELD_NUMBER = 4; + private int genre_; + /** + * optional .com.singer.Genre genre = 4; + * @return Whether the genre field is set. + */ + @java.lang.Override public boolean hasGenre() { + return ((bitField0_ & 0x00000008) != 0); + } + /** + * optional .com.singer.Genre genre = 4; + * @return The genre. + */ + @java.lang.Override public com.google.cloud.spanner.SingerProto.Genre getGenre() { + @SuppressWarnings("deprecation") + com.google.cloud.spanner.SingerProto.Genre result = com.google.cloud.spanner.SingerProto.Genre.valueOf(genre_); + return result == null ? com.google.cloud.spanner.SingerProto.Genre.POP : result; + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (((bitField0_ & 0x00000001) != 0)) { + output.writeInt64(1, singerId_); + } + if (((bitField0_ & 0x00000002) != 0)) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 2, birthDate_); + } + if (((bitField0_ & 0x00000004) != 0)) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 3, nationality_); + } + if (((bitField0_ & 0x00000008) != 0)) { + output.writeEnum(4, genre_); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeInt64Size(1, singerId_); + } + if (((bitField0_ & 0x00000002) != 0)) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, birthDate_); + } + if (((bitField0_ & 0x00000004) != 0)) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(3, nationality_); + } + if (((bitField0_ & 0x00000008) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeEnumSize(4, genre_); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof com.google.cloud.spanner.SingerProto.SingerInfo)) { + return super.equals(obj); + } + com.google.cloud.spanner.SingerProto.SingerInfo other = (com.google.cloud.spanner.SingerProto.SingerInfo) obj; + + if (hasSingerId() != other.hasSingerId()) return false; + if (hasSingerId()) { + if (getSingerId() + != other.getSingerId()) return false; + } + if (hasBirthDate() != other.hasBirthDate()) return false; + if (hasBirthDate()) { + if (!getBirthDate() + .equals(other.getBirthDate())) return false; + } + if (hasNationality() != other.hasNationality()) return false; + if (hasNationality()) { + if (!getNationality() + .equals(other.getNationality())) return false; + } + if (hasGenre() != other.hasGenre()) return false; + if (hasGenre()) { + if (genre_ != other.genre_) return false; + } + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + if (hasSingerId()) { + hash = (37 * hash) + SINGER_ID_FIELD_NUMBER; + hash = (53 * hash) + com.google.protobuf.Internal.hashLong( + getSingerId()); + } + if (hasBirthDate()) { + hash = (37 * hash) + BIRTH_DATE_FIELD_NUMBER; + hash = (53 * hash) + getBirthDate().hashCode(); + } + if (hasNationality()) { + hash = (37 * hash) + NATIONALITY_FIELD_NUMBER; + hash = (53 * hash) + getNationality().hashCode(); + } + if (hasGenre()) { + hash = (37 * hash) + GENRE_FIELD_NUMBER; + hash = (53 * hash) + genre_; + } + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static com.google.cloud.spanner.SingerProto.SingerInfo parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static com.google.cloud.spanner.SingerProto.SingerInfo parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static com.google.cloud.spanner.SingerProto.SingerInfo parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static com.google.cloud.spanner.SingerProto.SingerInfo parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static com.google.cloud.spanner.SingerProto.SingerInfo parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static com.google.cloud.spanner.SingerProto.SingerInfo parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static com.google.cloud.spanner.SingerProto.SingerInfo parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static com.google.cloud.spanner.SingerProto.SingerInfo parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static com.google.cloud.spanner.SingerProto.SingerInfo parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static com.google.cloud.spanner.SingerProto.SingerInfo parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static com.google.cloud.spanner.SingerProto.SingerInfo parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static com.google.cloud.spanner.SingerProto.SingerInfo parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(com.google.cloud.spanner.SingerProto.SingerInfo prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code com.singer.SingerInfo} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder implements + // @@protoc_insertion_point(builder_implements:com.singer.SingerInfo) + com.google.cloud.spanner.SingerProto.SingerInfoOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return com.google.cloud.spanner.SingerProto.internal_static_com_singer_SingerInfo_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return com.google.cloud.spanner.SingerProto.internal_static_com_singer_SingerInfo_fieldAccessorTable + .ensureFieldAccessorsInitialized( + com.google.cloud.spanner.SingerProto.SingerInfo.class, com.google.cloud.spanner.SingerProto.SingerInfo.Builder.class); + } + + // Construct using com.google.cloud.spanner.SingerProto.SingerInfo.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + singerId_ = 0L; + bitField0_ = (bitField0_ & ~0x00000001); + birthDate_ = ""; + bitField0_ = (bitField0_ & ~0x00000002); + nationality_ = ""; + bitField0_ = (bitField0_ & ~0x00000004); + genre_ = 0; + bitField0_ = (bitField0_ & ~0x00000008); + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return com.google.cloud.spanner.SingerProto.internal_static_com_singer_SingerInfo_descriptor; + } + + @java.lang.Override + public com.google.cloud.spanner.SingerProto.SingerInfo getDefaultInstanceForType() { + return com.google.cloud.spanner.SingerProto.SingerInfo.getDefaultInstance(); + } + + @java.lang.Override + public com.google.cloud.spanner.SingerProto.SingerInfo build() { + com.google.cloud.spanner.SingerProto.SingerInfo result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public com.google.cloud.spanner.SingerProto.SingerInfo buildPartial() { + com.google.cloud.spanner.SingerProto.SingerInfo result = new com.google.cloud.spanner.SingerProto.SingerInfo(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.singerId_ = singerId_; + to_bitField0_ |= 0x00000001; + } + if (((from_bitField0_ & 0x00000002) != 0)) { + to_bitField0_ |= 0x00000002; + } + result.birthDate_ = birthDate_; + if (((from_bitField0_ & 0x00000004) != 0)) { + to_bitField0_ |= 0x00000004; + } + result.nationality_ = nationality_; + if (((from_bitField0_ & 0x00000008) != 0)) { + to_bitField0_ |= 0x00000008; + } + result.genre_ = genre_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof com.google.cloud.spanner.SingerProto.SingerInfo) { + return mergeFrom((com.google.cloud.spanner.SingerProto.SingerInfo)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(com.google.cloud.spanner.SingerProto.SingerInfo other) { + if (other == com.google.cloud.spanner.SingerProto.SingerInfo.getDefaultInstance()) return this; + if (other.hasSingerId()) { + setSingerId(other.getSingerId()); + } + if (other.hasBirthDate()) { + bitField0_ |= 0x00000002; + birthDate_ = other.birthDate_; + onChanged(); + } + if (other.hasNationality()) { + bitField0_ |= 0x00000004; + nationality_ = other.nationality_; + onChanged(); + } + if (other.hasGenre()) { + setGenre(other.getGenre()); + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + com.google.cloud.spanner.SingerProto.SingerInfo parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (com.google.cloud.spanner.SingerProto.SingerInfo) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + private long singerId_ ; + /** + * optional int64 singer_id = 1; + * @return Whether the singerId field is set. + */ + @java.lang.Override + public boolean hasSingerId() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + * optional int64 singer_id = 1; + * @return The singerId. + */ + @java.lang.Override + public long getSingerId() { + return singerId_; + } + /** + * optional int64 singer_id = 1; + * @param value The singerId to set. + * @return This builder for chaining. + */ + public Builder setSingerId(long value) { + bitField0_ |= 0x00000001; + singerId_ = value; + onChanged(); + return this; + } + /** + * optional int64 singer_id = 1; + * @return This builder for chaining. + */ + public Builder clearSingerId() { + bitField0_ = (bitField0_ & ~0x00000001); + singerId_ = 0L; + onChanged(); + return this; + } + + private java.lang.Object birthDate_ = ""; + /** + * optional string birth_date = 2; + * @return Whether the birthDate field is set. + */ + public boolean hasBirthDate() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + * optional string birth_date = 2; + * @return The birthDate. + */ + public java.lang.String getBirthDate() { + java.lang.Object ref = birthDate_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + birthDate_ = s; + } + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * optional string birth_date = 2; + * @return The bytes for birthDate. + */ + public com.google.protobuf.ByteString + getBirthDateBytes() { + java.lang.Object ref = birthDate_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + birthDate_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string birth_date = 2; + * @param value The birthDate to set. + * @return This builder for chaining. + */ + public Builder setBirthDate( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + birthDate_ = value; + onChanged(); + return this; + } + /** + * optional string birth_date = 2; + * @return This builder for chaining. + */ + public Builder clearBirthDate() { + bitField0_ = (bitField0_ & ~0x00000002); + birthDate_ = getDefaultInstance().getBirthDate(); + onChanged(); + return this; + } + /** + * optional string birth_date = 2; + * @param value The bytes for birthDate to set. + * @return This builder for chaining. + */ + public Builder setBirthDateBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + birthDate_ = value; + onChanged(); + return this; + } + + private java.lang.Object nationality_ = ""; + /** + * optional string nationality = 3; + * @return Whether the nationality field is set. + */ + public boolean hasNationality() { + return ((bitField0_ & 0x00000004) != 0); + } + /** + * optional string nationality = 3; + * @return The nationality. + */ + public java.lang.String getNationality() { + java.lang.Object ref = nationality_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + nationality_ = s; + } + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * optional string nationality = 3; + * @return The bytes for nationality. + */ + public com.google.protobuf.ByteString + getNationalityBytes() { + java.lang.Object ref = nationality_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + nationality_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string nationality = 3; + * @param value The nationality to set. + * @return This builder for chaining. + */ + public Builder setNationality( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + nationality_ = value; + onChanged(); + return this; + } + /** + * optional string nationality = 3; + * @return This builder for chaining. + */ + public Builder clearNationality() { + bitField0_ = (bitField0_ & ~0x00000004); + nationality_ = getDefaultInstance().getNationality(); + onChanged(); + return this; + } + /** + * optional string nationality = 3; + * @param value The bytes for nationality to set. + * @return This builder for chaining. + */ + public Builder setNationalityBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + nationality_ = value; + onChanged(); + return this; + } + + private int genre_ = 0; + /** + * optional .com.singer.Genre genre = 4; + * @return Whether the genre field is set. + */ + @java.lang.Override public boolean hasGenre() { + return ((bitField0_ & 0x00000008) != 0); + } + /** + * optional .com.singer.Genre genre = 4; + * @return The genre. + */ + @java.lang.Override + public com.google.cloud.spanner.SingerProto.Genre getGenre() { + @SuppressWarnings("deprecation") + com.google.cloud.spanner.SingerProto.Genre result = com.google.cloud.spanner.SingerProto.Genre.valueOf(genre_); + return result == null ? com.google.cloud.spanner.SingerProto.Genre.POP : result; + } + /** + * optional .com.singer.Genre genre = 4; + * @param value The genre to set. + * @return This builder for chaining. + */ + public Builder setGenre(com.google.cloud.spanner.SingerProto.Genre value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000008; + genre_ = value.getNumber(); + onChanged(); + return this; + } + /** + * optional .com.singer.Genre genre = 4; + * @return This builder for chaining. + */ + public Builder clearGenre() { + bitField0_ = (bitField0_ & ~0x00000008); + genre_ = 0; + onChanged(); + return this; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:com.singer.SingerInfo) + } + + // @@protoc_insertion_point(class_scope:com.singer.SingerInfo) + private static final com.google.cloud.spanner.SingerProto.SingerInfo DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new com.google.cloud.spanner.SingerProto.SingerInfo(); + } + + public static com.google.cloud.spanner.SingerProto.SingerInfo getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + @java.lang.Deprecated public static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public SingerInfo parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new SingerInfo(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public com.google.cloud.spanner.SingerProto.SingerInfo getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + + } + + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_com_singer_SingerInfo_descriptor; + private static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_com_singer_SingerInfo_fieldAccessorTable; + + public static com.google.protobuf.Descriptors.FileDescriptor + getDescriptor() { + return descriptor; + } + private static com.google.protobuf.Descriptors.FileDescriptor + descriptor; + static { + java.lang.String[] descriptorData = { + "\n\014singer.proto\022\ncom.singer\"j\n\nSingerInfo" + + "\022\021\n\tsinger_id\030\001 \001(\003\022\022\n\nbirth_date\030\002 \001(\t\022" + + "\023\n\013nationality\030\003 \001(\t\022 \n\005genre\030\004 \001(\0162\021.co" + + "m.singer.Genre*.\n\005Genre\022\007\n\003POP\020\000\022\010\n\004JAZZ" + + "\020\001\022\010\n\004FOLK\020\002\022\010\n\004ROCK\020\003B4\n#com.example.sp" + + "anner.protobuf.singerB\013SingerProtoP\000" + }; + descriptor = com.google.protobuf.Descriptors.FileDescriptor + .internalBuildGeneratedFileFrom(descriptorData, + new com.google.protobuf.Descriptors.FileDescriptor[] { + }); + internal_static_com_singer_SingerInfo_descriptor = + getDescriptor().getMessageTypes().get(0); + internal_static_com_singer_SingerInfo_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_com_singer_SingerInfo_descriptor, + new java.lang.String[] { "SingerId", "BirthDate", "Nationality", "Genre", }); + } + + // @@protoc_insertion_point(outer_class_scope) +} diff --git a/google-cloud-spanner/src/test/resources/com/google/cloud/spanner/singer.proto b/google-cloud-spanner/src/test/resources/com/google/cloud/spanner/singer.proto new file mode 100644 index 00000000000..1e071e629f8 --- /dev/null +++ b/google-cloud-spanner/src/test/resources/com/google/cloud/spanner/singer.proto @@ -0,0 +1,22 @@ +syntax = "proto2"; + +package com.singer; + +option java_package = "com.google.cloud.spanner"; +option java_outer_classname = "SingerProto"; +option java_multiple_files = false; + +message SingerInfo { + optional int64 singer_id = 1; + optional string birth_date = 2; + optional string nationality = 3; + optional Genre genre = 4; +} + + +enum Genre { + POP = 0; + JAZZ = 1; + FOLK = 2; + ROCK = 3; +} \ No newline at end of file From 73df7367ec8bfea239d1792cc08e3b00781a76bf Mon Sep 17 00:00:00 2001 From: gauravpurohit Date: Wed, 26 Oct 2022 11:16:36 +0000 Subject: [PATCH 07/21] feat: Adding bytes/proto & int64/enum compatability Adding Additional check for ChecksumResultSet --- .../cloud/spanner/AbstractStructReader.java | 27 ++++++++++--------- .../spanner/connection/ChecksumResultSet.java | 2 ++ 2 files changed, 16 insertions(+), 13 deletions(-) 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 051b47b67e7..30d1cb5b16a 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 @@ -142,14 +142,14 @@ public boolean getBoolean(String columnName) { @Override public long getLong(int columnIndex) { - checkNonNullOfType(columnIndex, Type.int64(), columnIndex); + checkNonNullOfCodes(columnIndex, Arrays.asList(Code.ENUM, Code.INT64), columnIndex); return getLongInternal(columnIndex); } @Override public long getLong(String columnName) { int columnIndex = getColumnIndex(columnName); - checkNonNullOfType(columnIndex, Type.int64(), columnName); + checkNonNullOfCodes(columnIndex, Arrays.asList(Code.ENUM, Code.INT64), columnName); return getLongInternal(columnIndex); } @@ -225,14 +225,14 @@ public String getPgJsonb(String columnName) { @Override public ByteArray getBytes(int columnIndex) { - checkNonNullOfType(columnIndex, Type.bytes(), columnIndex); + checkNonNullOfCodes(columnIndex, Arrays.asList(Code.PROTO, Code.BYTES), columnIndex); return getBytesInternal(columnIndex); } @Override public ByteArray getBytes(String columnName) { int columnIndex = getColumnIndex(columnName); - checkNonNullOfType(columnIndex, Type.bytes(), columnName); + checkNonNullOfCodes(columnIndex, Arrays.asList(Code.PROTO, Code.BYTES), columnName); return getBytesInternal(columnIndex); } @@ -265,7 +265,7 @@ public Date getDate(String columnName) { @Override public T getProtoEnum( int columnIndex, Function method) { - checkNonNullOfCode(columnIndex, Code.ENUM, columnIndex); + checkNonNullOfCodes(columnIndex, Arrays.asList(Code.ENUM, Code.INT64), columnIndex); return getProtoEnumInternal(columnIndex, method); } @@ -273,20 +273,20 @@ public T getProtoEnum( public T getProtoEnum( String columnName, Function method) { int columnIndex = getColumnIndex(columnName); - checkNonNullOfCode(columnIndex, Code.ENUM, columnName); + checkNonNullOfCodes(columnIndex, Arrays.asList(Code.ENUM, Code.INT64), columnName); return getProtoEnumInternal(columnIndex, method); } @Override public T getProtoMessage(int columnIndex, T m) { - checkNonNullOfCode(columnIndex, Code.PROTO, columnIndex); + checkNonNullOfCodes(columnIndex, Arrays.asList(Code.PROTO, Code.BYTES), columnIndex); return getProtoMessageInternal(columnIndex, m); } @Override public T getProtoMessage(String columnName, T m) { int columnIndex = getColumnIndex(columnName); - checkNonNullOfCode(columnIndex, Code.PROTO, columnName); + checkNonNullOfCodes(columnIndex, Arrays.asList(Code.PROTO, Code.BYTES), columnName); return getProtoMessageInternal(columnIndex, m); } @@ -518,14 +518,15 @@ private void checkNonNullOfType(int columnIndex, Type expectedType, Object colum checkNonNull(columnIndex, columnNameForError); } - /** Checks if the value at {@code columnIndex} is of {@code expectedCode} */ - private void checkNonNullOfCode(int columnIndex, Code expectedCode, Object columnNameForError) { + /** Checks if the value at {@code columnIndex} is one of {@code expectedCode} */ + private void checkNonNullOfCodes( + int columnIndex, List expectedCodes, Object columnNameForError) { Code actualCode = getColumnType(columnIndex).getCode(); checkState( - expectedCode.equals(actualCode), - "Column %s is not of correct type: expected %s but was %s", + expectedCodes.contains(actualCode), + "Column %s is not of correct type code: expected one of [%s] but was %s", columnNameForError, - expectedCode, + expectedCodes, actualCode); checkNonNull(columnIndex, columnNameForError); } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ChecksumResultSet.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ChecksumResultSet.java index 7911ac43d3e..9af83df8ad4 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ChecksumResultSet.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ChecksumResultSet.java @@ -359,6 +359,7 @@ private void funnelValue(Code type, T value, PrimitiveSink into) { into.putBoolean((Boolean) value); break; case BYTES: + case PROTO: ByteArray byteArray = (ByteArray) value; into.putInt(byteArray.length()); into.putBytes(byteArray.toByteArray()); @@ -376,6 +377,7 @@ private void funnelValue(Code type, T value, PrimitiveSink into) { into.putUnencodedChars(stringRepresentation); break; case INT64: + case ENUM: into.putLong((Long) value); break; case PG_NUMERIC: From 82a54684a75724a5eb9944e0e25f73887217e60d Mon Sep 17 00:00:00 2001 From: gauravpurohit Date: Wed, 26 Oct 2022 11:45:52 +0000 Subject: [PATCH 08/21] test: Adding unit tests --- .../AbstractStructReaderTypesTest.java | 11 +++ .../cloud/spanner/GrpcResultSetTest.java | 64 ++++++++++++++++ .../google/cloud/spanner/MutationTest.java | 10 +++ .../google/cloud/spanner/ResultSetsTest.java | 34 +++++++++ .../com/google/cloud/spanner/TypeTest.java | 30 +++++++- .../com/google/cloud/spanner/ValueTest.java | 52 +++++++++++++ .../connection/ChecksumResultSetTest.java | 18 +++++ .../DirectExecuteResultSetTest.java | 43 ++++++++++- .../connection/RandomResultSetGenerator.java | 20 ++++- .../connection/ReadWriteTransactionTest.java | 74 ++++++++++++++++++- .../ReplaceableForwardingResultSetTest.java | 43 ++++++++++- 11 files changed, 388 insertions(+), 11 deletions(-) diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AbstractStructReaderTypesTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AbstractStructReaderTypesTest.java index e8a61c233e0..f6331ff7cab 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AbstractStructReaderTypesTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AbstractStructReaderTypesTest.java @@ -83,6 +83,17 @@ protected String getPgJsonbInternal(int columnIndex) { return null; } + @Override + public T getProtoEnum( + int columnIndex, Function method) { + return null; + } + + @Override + public T getProtoMessage(int columnIndex, T m) { + return null; + } + @Override protected ByteArray getBytesInternal(int columnIndex) { return null; 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..2f5ce489830 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/GrpcResultSetTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/GrpcResultSetTest.java @@ -25,6 +25,8 @@ import com.google.cloud.ByteArray; import com.google.cloud.Date; import com.google.cloud.Timestamp; +import com.google.cloud.spanner.SingerProto.Genre; +import com.google.cloud.spanner.SingerProto.SingerInfo; import com.google.cloud.spanner.spi.v1.SpannerRpc; import com.google.common.base.Function; import com.google.common.base.Strings; @@ -735,6 +737,68 @@ public void getPgJsonb() { assertEquals("[]", resultSet.getPgJsonb(0)); } + @Test + public void getProtoMessage() { + SingerInfo singerInfo1 = + SingerInfo.newBuilder() + .setSingerId(111) + .setNationality("COUNTRY1") + .setGenre(Genre.FOLK) + .build(); + SingerInfo singerInfo2 = SingerInfo.newBuilder().setSingerId(222).setGenre(Genre.JAZZ).build(); + String singerInfoFullName = SingerInfo.getDescriptor().getFullName(); + + consumer.onPartialResultSet( + PartialResultSet.newBuilder() + .setMetadata( + makeMetadata(Type.struct(Type.StructField.of("f", Type.proto(singerInfoFullName))))) + .addValues(Value.protoMessage(singerInfo1).toProto()) + .addValues( + Value.protoMessage( + ByteArray.copyFrom(singerInfo2.toByteArray()), singerInfoFullName) + .toProto()) + .addValues(Value.protoMessage(null, SingerInfo.getDescriptor().getFullName()).toProto()) + .build()); + consumer.onCompleted(); + + assertTrue(resultSet.next()); + assertEquals(singerInfo1, resultSet.getProtoMessage(0, SingerInfo.getDefaultInstance())); + assertTrue(resultSet.next()); + assertEquals(singerInfo2, resultSet.getProtoMessage(0, SingerInfo.getDefaultInstance())); + assertTrue(resultSet.next()); + assertThrows( + NullPointerException.class, + () -> { + resultSet.getProtoMessage(0, SingerInfo.getDefaultInstance()); + }); + } + + @Test + public void getProtoEnum() { + String genreFullyQualifiedName = Genre.getDescriptor().getFullName(); + consumer.onPartialResultSet( + PartialResultSet.newBuilder() + .setMetadata( + makeMetadata( + Type.struct(Type.StructField.of("f", Type.protoEnum(genreFullyQualifiedName))))) + .addValues(Value.protoEnum(Genre.FOLK).toProto()) + .addValues(Value.protoEnum(Genre.JAZZ.getNumber(), genreFullyQualifiedName).toProto()) + .addValues(Value.protoEnum(null, genreFullyQualifiedName).toProto()) + .build()); + consumer.onCompleted(); + + assertTrue(resultSet.next()); + assertEquals(Genre.FOLK, resultSet.getProtoEnum(0, Genre::forNumber)); + assertTrue(resultSet.next()); + assertEquals(Genre.JAZZ, resultSet.getProtoEnum(0, Genre::forNumber)); + assertTrue(resultSet.next()); + assertThrows( + NullPointerException.class, + () -> { + resultSet.getProtoEnum(0, Genre::forNumber); + }); + } + @Test public void getBooleanArray() { boolean[] boolArray = {true, true, false}; diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MutationTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MutationTest.java index fe2b7aec94b..afe01da6df2 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MutationTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MutationTest.java @@ -23,6 +23,8 @@ import com.google.cloud.ByteArray; import com.google.cloud.Date; import com.google.cloud.Timestamp; +import com.google.cloud.spanner.SingerProto.Genre; +import com.google.cloud.spanner.SingerProto.SingerInfo; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.testing.EqualsTester; @@ -545,6 +547,14 @@ private Mutation.WriteBuilder appendAllTypes(Mutation.WriteBuilder builder) { .to(Value.json("{\"key\": \"value\"}}")) .set("jsonNull") .to(Value.json(null)) + .set("protoMessage") + .to(SingerInfo.newBuilder().setSingerId(232).setGenre(Genre.POP).build()) + .set("protoMessageNull") + .to(Value.protoMessage(null, SingerInfo.getDescriptor().getFullName())) + .set("protoEnum") + .to(Genre.JAZZ) + .set("protoEnumNull") + .to(Value.protoEnum(null, SingerInfo.getDescriptor().getFullName())) .set("pgJsonb") .to(Value.pgJsonb("{\"key\": \"value\"}}")) .set("pgJsonbNull") diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ResultSetsTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ResultSetsTest.java index 87be602808c..13b7d9e2da3 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ResultSetsTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ResultSetsTest.java @@ -28,9 +28,12 @@ import com.google.cloud.Date; import com.google.cloud.Timestamp; import com.google.cloud.spanner.AsyncResultSet.CallbackResponse; +import com.google.cloud.spanner.SingerProto.Genre; +import com.google.cloud.spanner.SingerProto.SingerInfo; import com.google.common.primitives.Doubles; import com.google.common.primitives.Longs; import com.google.common.util.concurrent.MoreExecutors; +import com.google.protobuf.ProtocolMessageEnum; import java.math.BigDecimal; import java.util.Arrays; import java.util.Collections; @@ -52,6 +55,13 @@ public void resultSetIteration() { BigDecimal bigDecimalVal = BigDecimal.valueOf(123, 2); String stringVal = "stringVal"; String jsonVal = "{\"color\":\"red\",\"value\":\"#f00\"}"; + SingerInfo protoMessageVal = + SingerInfo.newBuilder() + .setSingerId(111) + .setNationality("COUNTRY1") + .setGenre(Genre.FOLK) + .build(); + ProtocolMessageEnum protoEnumVal = Genre.ROCK; String byteVal = "101"; long usecs = 32343; int year = 2018; @@ -94,6 +104,10 @@ public void resultSetIteration() { Type.StructField.of("byteVal", Type.bytes()), Type.StructField.of("timestamp", Type.timestamp()), Type.StructField.of("date", Type.date()), + Type.StructField.of( + "protoMessage", Type.proto(protoMessageVal.getDescriptorForType().getFullName())), + Type.StructField.of( + "protoEnum", Type.protoEnum(protoEnumVal.getDescriptorForType().getFullName())), Type.StructField.of("boolArray", Type.array(Type.bool())), Type.StructField.of("longArray", Type.array(Type.int64())), Type.StructField.of("doubleArray", Type.array(Type.float64())), @@ -128,6 +142,10 @@ public void resultSetIteration() { .to(Timestamp.ofTimeMicroseconds(usecs)) .set("date") .to(Date.fromYearMonthDay(year, month, day)) + .set("protoMessage") + .to(protoMessageVal) + .set("protoEnum") + .to(protoEnumVal) .set("boolArray") .to(Value.boolArray(boolArray)) .set("longArray") @@ -173,6 +191,10 @@ public void resultSetIteration() { .to(Timestamp.ofTimeMicroseconds(usecs)) .set("date") .to(Date.fromYearMonthDay(year, month, day)) + .set("protoMessage") + .to(protoMessageVal) + .set("protoEnum") + .to(protoEnumVal) .set("boolArray") .to(Value.boolArray(boolArray)) .set("longArray") @@ -259,6 +281,18 @@ public void resultSetIteration() { .isEqualTo(Value.date(Date.fromYearMonthDay(year, month, day))); assertThat(rs.getDate("date")).isEqualTo(Date.fromYearMonthDay(year, month, day)); assertThat(rs.getValue("date")).isEqualTo(Value.date(Date.fromYearMonthDay(year, month, day))); + + assertEquals(protoMessageVal, rs.getProtoMessage(columnIndex, SingerInfo.getDefaultInstance())); + assertEquals(Value.protoMessage(protoMessageVal), rs.getValue(columnIndex++)); + assertEquals( + protoMessageVal, rs.getProtoMessage("protoMessage", SingerInfo.getDefaultInstance())); + assertEquals(Value.protoMessage(protoMessageVal), rs.getValue("protoMessage")); + + assertEquals(protoEnumVal, rs.getProtoEnum(columnIndex, Genre::forNumber)); + assertEquals(Value.protoEnum(protoEnumVal), rs.getValue(columnIndex++)); + assertEquals(protoEnumVal, rs.getProtoEnum("protoEnum", Genre::forNumber)); + assertEquals(Value.protoEnum(protoEnumVal), rs.getValue("protoEnum")); + assertThat(rs.getBooleanArray(columnIndex)).isEqualTo(boolArray); assertThat(rs.getValue(columnIndex++)).isEqualTo(Value.boolArray(boolArray)); assertThat(rs.getBooleanArray("boolArray")).isEqualTo(boolArray); 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..308b45c2400 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 @@ -38,6 +38,7 @@ private abstract static class ScalarTypeTester { private final Type.Code expectedCode; private final TypeCode expectedTypeCode; private final TypeAnnotationCode expectedTypeAnnotationCode; + private String protoTypeFqn = ""; ScalarTypeTester(Type.Code expectedCode, TypeCode expectedTypeCode) { this(expectedCode, expectedTypeCode, TypeAnnotationCode.TYPE_ANNOTATION_CODE_UNSPECIFIED); @@ -52,12 +53,17 @@ private abstract static class ScalarTypeTester { this.expectedTypeAnnotationCode = expectedTypeAnnotationCode; } + ScalarTypeTester(Type.Code expectedCode, TypeCode expectedTypeCode, String protoTypeFqn) { + this(expectedCode, expectedTypeCode); + this.protoTypeFqn = protoTypeFqn; + } + abstract Type newType(); void test() { Type t = newType(); assertThat(t.getCode()).isEqualTo(expectedCode); - assertThat(newType()).isSameInstanceAs(t); // Interned. + assertThat(newType()).isEqualTo(t); // Interned. // String form is deliberately the same as the corresponding type enum in the public API. if (expectedTypeAnnotationCode != TypeAnnotationCode.TYPE_ANNOTATION_CODE_UNSPECIFIED) { assertThat(t.toString()) @@ -70,13 +76,13 @@ void test() { com.google.spanner.v1.Type proto = t.toProto(); assertThat(proto.getCode()).isEqualTo(expectedTypeCode); assertThat(proto.getTypeAnnotation()).isEqualTo(expectedTypeAnnotationCode); + assertThat(proto.getProtoTypeFqn()).isEqualTo(protoTypeFqn); assertThat(proto.hasArrayElementType()).isFalse(); assertThat(proto.hasStructType()).isFalse(); // Round trip. Type fromProto = Type.fromProto(proto); assertThat(fromProto).isEqualTo(t); - assertThat(fromProto).isSameInstanceAs(t); reserializeAndAssert(t); } @@ -172,6 +178,26 @@ Type newType() { }.test(); } + @Test + public void proto() { + new ScalarTypeTester(Type.Code.PROTO, TypeCode.PROTO, "com.google.temp") { + @Override + Type newType() { + return Type.proto("com.google.temp"); + } + }.test(); + } + + @Test + public void protoEnum() { + new ScalarTypeTester(Type.Code.ENUM, TypeCode.ENUM, "com.google.temp.enum") { + @Override + Type newType() { + return Type.protoEnum("com.google.temp.enum"); + } + }.test(); + } + @Test public void timestamp() { new ScalarTypeTester(Type.Code.TIMESTAMP, TypeCode.TIMESTAMP) { 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..446dd515751 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,8 @@ import com.google.cloud.ByteArray; import com.google.cloud.Date; import com.google.cloud.Timestamp; +import com.google.cloud.spanner.SingerProto.Genre; +import com.google.cloud.spanner.SingerProto.SingerInfo; import com.google.cloud.spanner.Type.StructField; import com.google.common.base.Strings; import com.google.common.collect.ForwardingList; @@ -629,6 +631,56 @@ public void dateNull() { assertThat(e.getMessage()).contains("null value"); } + @Test + public void protoMessage() { + SingerInfo singerInfo = SingerInfo.newBuilder().setSingerId(111).setGenre(Genre.FOLK).build(); + Value v = Value.protoMessage(singerInfo); + assertThat(v.getType()).isEqualTo(Type.proto(SingerInfo.getDescriptor().getFullName())); + assertThat(v.isNull()).isFalse(); + assertThat(v.getProtoMessage(SingerInfo.getDefaultInstance())).isEqualTo(singerInfo); + assertThat(v.getBytes().toByteArray()).isEqualTo(singerInfo.toByteArray()); + } + + @Test + public void protoMessageNull() { + Value v = Value.protoMessage(null, SingerInfo.getDescriptor().getFullName()); + assertThat(v.getType()).isEqualTo(Type.proto(SingerInfo.getDescriptor().getFullName())); + assertThat(v.isNull()).isTrue(); + assertThat(v.toString()).isEqualTo(NULL_STRING); + IllegalStateException e = + assertThrows( + IllegalStateException.class, + () -> { + v.getProtoMessage(SingerInfo.getDefaultInstance()); + }); + assertThat(e.getMessage()).contains("null value"); + } + + @Test + public void protoEnum() { + Genre genre = Genre.FOLK; + Value v = Value.protoEnum(genre); + assertThat(v.getType()).isEqualTo(Type.protoEnum(Genre.getDescriptor().getFullName())); + assertThat(v.isNull()).isFalse(); + assertThat(v.getInt64()).isEqualTo(genre.getNumber()); + assertEquals(genre, v.getProtoEnum(Genre::forNumber)); + } + + @Test + public void protoEnumNull() { + Value v = Value.protoEnum(null, Genre.getDescriptor().getFullName()); + assertThat(v.getType()).isEqualTo(Type.protoEnum(Genre.getDescriptor().getFullName())); + assertThat(v.isNull()).isTrue(); + assertThat(v.toString()).isEqualTo(NULL_STRING); + IllegalStateException e = + assertThrows( + IllegalStateException.class, + () -> { + v.getProtoEnum(Genre::forNumber); + }); + assertThat(e.getMessage()).contains("null value"); + } + @Test public void boolArray() { Value v = Value.boolArray(new boolean[] {true, false}); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ChecksumResultSetTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ChecksumResultSetTest.java index 1f3f59e96ab..0f3ee1cbf5d 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ChecksumResultSetTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ChecksumResultSetTest.java @@ -29,6 +29,8 @@ import com.google.cloud.spanner.AbortedException; import com.google.cloud.spanner.ResultSet; import com.google.cloud.spanner.ResultSets; +import com.google.cloud.spanner.SingerProto.Genre; +import com.google.cloud.spanner.SingerProto.SingerInfo; import com.google.cloud.spanner.Statement; import com.google.cloud.spanner.Struct; import com.google.cloud.spanner.Struct.Builder; @@ -65,6 +67,10 @@ public class ChecksumResultSetTest { .to(Value.json("{\"color\":\"red\",\"value\":\"#ff0\"}")) .set("pgJsonbVal") .to(Value.pgJsonb("{\"color\":\"red\",\"value\":\"#00f\"}")) + .set("protoMessageVal") + .to(SingerInfo.newBuilder().setSingerId(23).build()) + .set("protoEnumVal") + .to(Genre.JAZZ) .set("byteVal") .to(Value.bytes(ByteArray.copyFrom("bytes".getBytes(StandardCharsets.UTF_8)))) .set("timestamp") @@ -118,6 +124,10 @@ public void testRetry() { Type.StructField.of("stringVal", Type.string()), Type.StructField.of("jsonVal", Type.json()), Type.StructField.of("pgJsonbVal", Type.pgJsonb()), + Type.StructField.of( + "protoMessageVal", Type.proto(SingerInfo.getDescriptor().getFullName())), + Type.StructField.of( + "protoEnumVal", Type.protoEnum(Genre.getDescriptor().getFullName())), Type.StructField.of("byteVal", Type.bytes()), Type.StructField.of("timestamp", Type.timestamp()), Type.StructField.of("date", Type.date()), @@ -150,6 +160,10 @@ public void testRetry() { .to(Value.json("{\"color\":\"red\",\"value\":\"#f00\"}")) .set("pgJsonbVal") .to(Value.pgJsonb("{\"color\":\"red\",\"value\":\"#f00\"}")) + .set("protoMessageVal") + .to(SingerInfo.newBuilder().setSingerId(98).setNationality("C1").build()) + .set("protoEnumVal") + .to(Genre.POP) .set("byteVal") .to(Value.bytes(ByteArray.copyFrom("test".getBytes(StandardCharsets.UTF_8)))) .set("timestamp") @@ -211,6 +225,10 @@ public void testRetry() { .to(Value.json(null)) .set("pgJsonbVal") .to(Value.pgJsonb(null)) + .set("protoMessageVal") + .to(Value.protoMessage(null, SingerInfo.getDescriptor().getFullName())) + .set("protoEnumVal") + .to(Value.protoEnum(null, Genre.getDescriptor().getFullName())) .set("byteVal") .to((ByteArray) null) .set("timestamp") diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/DirectExecuteResultSetTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/DirectExecuteResultSetTest.java index 094503cfbcc..85d1d5d26e8 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/DirectExecuteResultSetTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/DirectExecuteResultSetTest.java @@ -25,15 +25,20 @@ import com.google.cloud.spanner.ResultSet; import com.google.cloud.spanner.ResultSets; +import com.google.cloud.spanner.SingerProto.Genre; +import com.google.cloud.spanner.SingerProto.SingerInfo; import com.google.cloud.spanner.Struct; import com.google.cloud.spanner.Type; import com.google.cloud.spanner.Type.StructField; +import com.google.protobuf.AbstractMessage; +import com.google.protobuf.ProtocolMessageEnum; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.function.Function; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -120,7 +125,7 @@ private void callMethods( boolean exception = false; int numberOfParameters = method.getParameterTypes().length; Class firstParameterType = null; - if (numberOfParameters == 1) { + if (numberOfParameters >= 1) { firstParameterType = method.getParameterTypes()[0]; } try { @@ -137,6 +142,31 @@ private void callMethods( fail("unknown parameter type"); } break; + case 2: + Class secondParameterType = method.getParameterTypes()[1]; + Object firstArgument = null, secondArgument = null; + + if (firstParameterType == String.class) { + firstArgument = "test"; + } + else if (firstParameterType == int.class) { + firstArgument = 0; + } + + if (secondParameterType == Function.class) { + Function lambdaFunction = (val) -> Genre.forNumber( + val.intValue()); + secondArgument = lambdaFunction; + } else if (secondParameterType == AbstractMessage.class) { + secondArgument = SingerInfo.getDefaultInstance(); + } + + if (firstArgument != null && secondArgument!=null) { + method.invoke(subject, firstArgument, secondArgument); + } else { + fail("unknown parameter type"); + } + break; default: fail("method with more than 1 parameter is unknown"); } @@ -258,6 +288,17 @@ public void testValidMethodCall() throws IllegalArgumentException { subject.getPgJsonbList("test2"); verify(delegate).getPgJsonbList("test2"); + subject.getProtoMessage(0, SingerInfo.getDefaultInstance()); + verify(delegate).getProtoMessage(0, SingerInfo.getDefaultInstance()); + subject.getProtoMessage("test0", SingerInfo.getDefaultInstance()); + verify(delegate).getProtoMessage("test0", SingerInfo.getDefaultInstance()); + + Function lambdaFunction = Genre::forNumber; + subject.getProtoEnum(0, lambdaFunction); + verify(delegate).getProtoEnum(0, lambdaFunction); + subject.getProtoEnum("test0", lambdaFunction); + verify(delegate).getProtoEnum("test0", lambdaFunction); + subject.getStructList(0); subject.getStructList("test0"); 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..712bfc888ac 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 @@ -32,6 +32,9 @@ import com.google.spanner.v1.TypeAnnotationCode; import com.google.spanner.v1.TypeCode; import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import java.util.Random; /** @@ -40,7 +43,7 @@ */ public class RandomResultSetGenerator { private static Type[] generateTypes(Dialect dialect) { - return new Type[] { + List types = new ArrayList(Arrays.asList( Type.newBuilder().setCode(TypeCode.BOOL).build(), Type.newBuilder().setCode(TypeCode.INT64).build(), Type.newBuilder().setCode(TypeCode.FLOAT64).build(), @@ -105,8 +108,17 @@ private static Type[] generateTypes(Dialect dialect) { Type.newBuilder() .setCode(TypeCode.ARRAY) .setArrayElementType(Type.newBuilder().setCode(TypeCode.TIMESTAMP)) - .build(), - }; + .build() + )); + + if (dialect == Dialect.GOOGLE_STANDARD_SQL) { + types.add(Type.newBuilder().setCode(TypeCode.PROTO).setProtoTypeFqn("testProto").build()); + types.add(Type.newBuilder().setCode(TypeCode.ENUM).setProtoTypeFqn("testEnum").build()); + } + + Type[] typeArray = new Type[types.size()]; + typeArray = types.toArray(typeArray); + return typeArray; } private static ResultSetMetadata generateMetadata(Type[] types) { @@ -171,6 +183,7 @@ private void setRandomValue(Value.Builder builder, Type type) { break; case STRING: case BYTES: + case PROTO: byte[] bytes = new byte[random.nextInt(200)]; random.nextBytes(bytes); builder.setStringValue(BaseEncoding.base64().encode(bytes)); @@ -199,6 +212,7 @@ private void setRandomValue(Value.Builder builder, Type type) { } break; case INT64: + case ENUM: builder.setStringValue(String.valueOf(random.nextLong())); break; case TIMESTAMP: diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ReadWriteTransactionTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ReadWriteTransactionTest.java index aae85bcec62..c21635cca4c 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ReadWriteTransactionTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ReadWriteTransactionTest.java @@ -40,6 +40,8 @@ import com.google.cloud.spanner.ReadContext.QueryAnalyzeMode; import com.google.cloud.spanner.ResultSet; import com.google.cloud.spanner.ResultSets; +import com.google.cloud.spanner.SingerProto.Genre; +import com.google.cloud.spanner.SingerProto.SingerInfo; import com.google.cloud.spanner.SpannerException; import com.google.cloud.spanner.SpannerExceptionFactory; import com.google.cloud.spanner.Statement; @@ -52,6 +54,7 @@ import com.google.cloud.spanner.Value; import com.google.cloud.spanner.connection.AbstractStatementParser.ParsedStatement; import com.google.cloud.spanner.connection.AbstractStatementParser.StatementType; +import com.google.protobuf.ProtocolMessageEnum; import com.google.rpc.RetryInfo; import com.google.spanner.v1.ResultSetStats; import io.grpc.Metadata; @@ -499,13 +502,25 @@ public void testChecksumResultSet() { "[{\"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\"}]"; String emptyArrayJson = "[]"; String simpleJson = "{\"color\":\"red\",\"value\":\"#f00\"}"; + SingerInfo protoMessageVal = + SingerInfo.newBuilder() + .setSingerId(111) + .setNationality("COUNTRY1") + .setGenre(Genre.FOLK) + .build(); + ProtocolMessageEnum protoEnumVal = Genre.ROCK; ResultSet delegate1 = ResultSets.forRows( Type.struct( StructField.of("ID", Type.int64()), StructField.of("NAME", Type.string()), StructField.of("AMOUNT", Type.numeric()), - StructField.of("JSON", Type.json())), + StructField.of("JSON", Type.json()), + StructField.of( + "PROTO", Type.proto(protoMessageVal.getDescriptorForType().getFullName())), + StructField.of( + "PROTOENUM", + Type.protoEnum(protoEnumVal.getDescriptorForType().getFullName()))), Arrays.asList( Struct.newBuilder() .set("ID") @@ -516,6 +531,10 @@ public void testChecksumResultSet() { .to(BigDecimal.valueOf(550, 2)) .set("JSON") .to(Value.json(simpleJson)) + .set("PROTO") + .to(protoMessageVal) + .set("PROTOENUM") + .to(protoEnumVal) .build(), Struct.newBuilder() .set("ID") @@ -526,6 +545,10 @@ public void testChecksumResultSet() { .to(BigDecimal.valueOf(750, 2)) .set("JSON") .to(Value.json(arrayJson)) + .set("PROTO") + .to(protoMessageVal) + .set("PROTOENUM") + .to(Genre.JAZZ) .build())); ChecksumResultSet rs1 = transaction.createChecksumResultSet(delegate1, parsedStatement, AnalyzeMode.NONE); @@ -535,7 +558,12 @@ public void testChecksumResultSet() { StructField.of("ID", Type.int64()), StructField.of("NAME", Type.string()), StructField.of("AMOUNT", Type.numeric()), - StructField.of("JSON", Type.json())), + StructField.of("JSON", Type.json()), + StructField.of( + "PROTO", Type.proto(protoMessageVal.getDescriptorForType().getFullName())), + StructField.of( + "PROTOENUM", + Type.protoEnum(protoEnumVal.getDescriptorForType().getFullName()))), Arrays.asList( Struct.newBuilder() .set("ID") @@ -546,6 +574,10 @@ public void testChecksumResultSet() { .to(new BigDecimal("5.50")) .set("JSON") .to(Value.json(simpleJson)) + .set("PROTO") + .to(protoMessageVal) + .set("PROTOENUM") + .to(protoEnumVal) .build(), Struct.newBuilder() .set("ID") @@ -556,6 +588,10 @@ public void testChecksumResultSet() { .to(new BigDecimal("7.50")) .set("JSON") .to(Value.json(arrayJson)) + .set("PROTO") + .to(protoMessageVal) + .set("PROTOENUM") + .to(Genre.JAZZ) .build())); ChecksumResultSet rs2 = transaction.createChecksumResultSet(delegate2, parsedStatement, AnalyzeMode.NONE); @@ -566,7 +602,12 @@ public void testChecksumResultSet() { StructField.of("ID", Type.int64()), StructField.of("NAME", Type.string()), StructField.of("AMOUNT", Type.numeric()), - StructField.of("JSON", Type.json())), + StructField.of("JSON", Type.json()), + StructField.of( + "PROTO", Type.proto(protoMessageVal.getDescriptorForType().getFullName())), + StructField.of( + "PROTOENUM", + Type.protoEnum(protoEnumVal.getDescriptorForType().getFullName()))), Arrays.asList( Struct.newBuilder() .set("ID") @@ -577,6 +618,10 @@ public void testChecksumResultSet() { .to(new BigDecimal("7.50")) .set("JSON") .to(Value.json(arrayJson)) + .set("PROTO") + .to(protoMessageVal) + .set("PROTOENUM") + .to(Genre.JAZZ) .build(), Struct.newBuilder() .set("ID") @@ -587,6 +632,10 @@ public void testChecksumResultSet() { .to(new BigDecimal("5.50")) .set("JSON") .to(Value.json(simpleJson)) + .set("PROTO") + .to(protoMessageVal) + .set("PROTOENUM") + .to(protoEnumVal) .build())); ChecksumResultSet rs3 = transaction.createChecksumResultSet(delegate3, parsedStatement, AnalyzeMode.NONE); @@ -598,7 +647,12 @@ public void testChecksumResultSet() { StructField.of("ID", Type.int64()), StructField.of("NAME", Type.string()), StructField.of("AMOUNT", Type.numeric()), - StructField.of("JSON", Type.json())), + StructField.of("JSON", Type.json()), + StructField.of( + "PROTO", Type.proto(protoMessageVal.getDescriptorForType().getFullName())), + StructField.of( + "PROTOENUM", + Type.protoEnum(protoEnumVal.getDescriptorForType().getFullName()))), Arrays.asList( Struct.newBuilder() .set("ID") @@ -609,6 +663,10 @@ public void testChecksumResultSet() { .to(new BigDecimal("5.50")) .set("JSON") .to(Value.json(simpleJson)) + .set("PROTO") + .to(protoMessageVal) + .set("PROTOENUM") + .to(protoEnumVal) .build(), Struct.newBuilder() .set("ID") @@ -619,6 +677,10 @@ public void testChecksumResultSet() { .to(new BigDecimal("7.50")) .set("JSON") .to(Value.json(arrayJson)) + .set("PROTO") + .to(protoMessageVal) + .set("PROTOENUM") + .to(Genre.JAZZ) .build(), Struct.newBuilder() .set("ID") @@ -629,6 +691,10 @@ public void testChecksumResultSet() { .to(new BigDecimal("9.99")) .set("JSON") .to(Value.json(emptyArrayJson)) + .set("PROTO") + .to(protoMessageVal) + .set("PROTOENUM") + .to(Genre.POP) .build())); ChecksumResultSet rs4 = transaction.createChecksumResultSet(delegate4, parsedStatement, AnalyzeMode.NONE); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSetTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSetTest.java index 4d09f0c8401..09b6f160941 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSetTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSetTest.java @@ -26,16 +26,21 @@ import com.google.cloud.spanner.ResultSet; import com.google.cloud.spanner.ResultSets; +import com.google.cloud.spanner.SingerProto.Genre; +import com.google.cloud.spanner.SingerProto.SingerInfo; import com.google.cloud.spanner.SpannerException; import com.google.cloud.spanner.Struct; import com.google.cloud.spanner.Type; import com.google.cloud.spanner.Type.StructField; +import com.google.protobuf.AbstractMessage; +import com.google.protobuf.ProtocolMessageEnum; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.function.Function; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -161,7 +166,7 @@ private void callMethods( boolean exception = false; int numberOfParameters = method.getParameterTypes().length; Class firstParameterType = null; - if (numberOfParameters == 1) { + if (numberOfParameters >= 1) { firstParameterType = method.getParameterTypes()[0]; } try { @@ -178,6 +183,31 @@ private void callMethods( fail("unknown parameter type"); } break; + case 2: + Class secondParameterType = method.getParameterTypes()[1]; + Object firstArgument = null, secondArgument = null; + + if (firstParameterType == String.class) { + firstArgument = "test"; + } + else if (firstParameterType == int.class) { + firstArgument = 0; + } + + if (secondParameterType == Function.class) { + Function lambdaFunction = (val) -> Genre.forNumber( + val.intValue()); + secondArgument = lambdaFunction; + } else if (secondParameterType == AbstractMessage.class) { + secondArgument = SingerInfo.getDefaultInstance(); + } + + if (firstArgument != null && secondArgument!=null) { + method.invoke(subject, firstArgument, secondArgument); + } else { + fail("unknown parameter type"); + } + break; default: fail("method with more than 1 parameter is unknown"); } @@ -293,6 +323,17 @@ public void testValidMethodCall() throws IllegalArgumentException { subject.getJsonList("test2"); verify(delegate).getJsonList("test2"); + subject.getProtoMessage(0, SingerInfo.getDefaultInstance()); + verify(delegate).getProtoMessage(0, SingerInfo.getDefaultInstance()); + subject.getProtoMessage("test0", SingerInfo.getDefaultInstance()); + verify(delegate).getProtoMessage("test0", SingerInfo.getDefaultInstance()); + + Function lambdaFunction = Genre::forNumber; + subject.getProtoEnum(0, lambdaFunction); + verify(delegate).getProtoEnum(0, lambdaFunction); + subject.getProtoEnum("test0", lambdaFunction); + verify(delegate).getProtoEnum("test0", lambdaFunction); + subject.getStructList(0); subject.getStructList("test0"); From b125a1f75214d314416f0e2c4262b5868820326f Mon Sep 17 00:00:00 2001 From: gauravpurohit Date: Thu, 27 Oct 2022 06:43:46 +0000 Subject: [PATCH 09/21] test: Adding unit tests for ValueBinder.java --- .../com/google/cloud/spanner/ValueBinder.java | 19 ++++++++-- .../google/cloud/spanner/ValueBinderTest.java | 36 +++++++++++++++++-- 2 files changed, 50 insertions(+), 5 deletions(-) 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 536713ad06b..e06b0a81637 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 @@ -98,15 +98,30 @@ public R to(@Nullable String value) { } /** Binds to {@code Value.protoMessage(value)} */ - public R to(@Nullable AbstractMessage m) { + public R to(AbstractMessage m) { return handle(Value.protoMessage(m)); } + /** Binds to {@code Value.protoMessage(value, protoType)} */ + public R to(@Nullable ByteArray v, String protoTypFqn) { + return handle(Value.protoMessage(v, protoTypFqn)); + } + /** Binds to {@code Value.protoEnum(value)} */ - public R to(@Nullable ProtocolMessageEnum value) { + public R to(ProtocolMessageEnum value) { return handle(Value.protoEnum(value)); } + /** Binds to {@code Value.protoEnum(value, protoType)} */ + public R to(@Nullable Long v, String protoTypFqn) { + return handle(Value.protoEnum(v, protoTypFqn)); + } + + /** Binds to {@code Value.protoEnum(value, protoType)} */ + public R to(long v, String protoTypFqn) { + return handle(Value.protoEnum(v, protoTypFqn)); + } + /** Binds to {@code Value.bytes(value)} */ public R to(@Nullable ByteArray value) { return handle(Value.bytes(value)); 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..d0a6764a80b 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 @@ -24,6 +24,10 @@ import com.google.cloud.ByteArray; import com.google.cloud.Date; import com.google.cloud.Timestamp; +import com.google.cloud.spanner.SingerProto.Genre; +import com.google.cloud.spanner.SingerProto.SingerInfo; +import com.google.protobuf.AbstractMessage; +import com.google.protobuf.ProtocolMessageEnum; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -40,8 +44,11 @@ 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 PROTO_MESSAGE_METHOD_NAME = "protoMessage"; + private static final String PROTO_ENUM_METHOD_NAME = "protoEnum"; public static final String DEFAULT_PG_NUMERIC = "1.23"; + private Value lastValue; private int lastReturnValue; private ValueBinder binder = new BinderImpl(); @@ -121,7 +128,9 @@ public void reflection() } } else if (binderMethod.getParameterTypes().length == 1) { // Test unary null. - if (!binderMethod.getParameterTypes()[0].isPrimitive()) { + if (!binderMethod.getParameterTypes()[0].isPrimitive() + && (!method.getName().equalsIgnoreCase(PROTO_MESSAGE_METHOD_NAME) + && !method.getName().equalsIgnoreCase(PROTO_ENUM_METHOD_NAME))) { if (method.getName().equalsIgnoreCase(JSON_METHOD_NAME)) { // Special case for json to change the method from ValueBinder.to(String) to // ValueBinder.to(Value) @@ -139,7 +148,6 @@ public void reflection() } Value expected = (Value) method.invoke(Value.class, (Object) null); assertThat(lastValue).isEqualTo(expected); - assertThat(binder.to(expected)).isEqualTo(lastReturnValue); assertThat(lastValue).isEqualTo(expected); } @@ -169,7 +177,21 @@ public void reflection() assertThat(binder.to(expected)).isEqualTo(lastReturnValue); assertThat(lastValue).isEqualTo(expected); - } else { + } else if (binderMethod.getParameterTypes().length == 2 + && (method.getName().equalsIgnoreCase(PROTO_MESSAGE_METHOD_NAME) + || method.getName().equalsIgnoreCase(PROTO_ENUM_METHOD_NAME))) { + // Test unary null. + Object firstArgument = null; + if (binderMethod.getParameterTypes()[0].isPrimitive()) { + firstArgument = 0; + } + assertThat(binderMethod.invoke(binder, firstArgument, "com.proto.example")).isEqualTo(lastReturnValue); + Value expected = (Value) method.invoke(Value.class, firstArgument, "com.proto.example"); + assertThat(lastValue).isEqualTo(expected); + assertThat(binder.to(expected)).isEqualTo(lastReturnValue); + assertThat(lastValue).isEqualTo(expected); + } + else { // Array slice method: depends on DefaultValues returning arrays of length 2. assertThat(binderMethod.getParameterTypes().length).isEqualTo(3); assertThat(binderMethod.getParameterTypes()[1]).isEqualTo(int.class); @@ -234,6 +256,14 @@ public static BigDecimal defaultBigDecimal() { return BigDecimal.valueOf(123, 2); } + public static AbstractMessage defaultAbstractMessage() { + return SingerInfo.newBuilder().setSingerId(323).build(); + } + + public static ProtocolMessageEnum defaultProtocolMessageEnum() { + return Genre.FOLK; + } + public static String defaultString() { return "x"; } From 5c48accbc4727cc89a02089e18d5c1a77b175e01 Mon Sep 17 00:00:00 2001 From: gauravpurohit Date: Thu, 27 Oct 2022 09:13:51 +0000 Subject: [PATCH 10/21] feat: refactoring to add support for getValue & other minor changes --- .../google/cloud/spanner/AbstractResultSet.java | 4 ++++ .../cloud/spanner/AbstractStructReader.java | 6 +++--- .../com/google/cloud/spanner/SingerProto.java | 17 ++++++++++++++++- 3 files changed, 23 insertions(+), 4 deletions(-) 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 a5daac26f68..84e751cf5a3 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 @@ -749,6 +749,8 @@ protected Value getValueInternal(int columnIndex) { return Value.bool(isNull ? null : getBooleanInternal(columnIndex)); case INT64: return Value.int64(isNull ? null : getLongInternal(columnIndex)); + case ENUM: + return Value.protoEnum(getLongInternal(columnIndex), columnType.getProtoTypeFqn()); case NUMERIC: return Value.numeric(isNull ? null : getBigDecimalInternal(columnIndex)); case PG_NUMERIC: @@ -763,6 +765,8 @@ protected Value getValueInternal(int columnIndex) { return Value.pgJsonb(isNull ? null : getPgJsonbInternal(columnIndex)); case BYTES: return Value.bytes(isNull ? null : getBytesInternal(columnIndex)); + case PROTO: + return Value.protoMessage(getBytesInternal(columnIndex), columnType.getProtoTypeFqn()); case TIMESTAMP: return Value.timestamp(isNull ? null : getTimestampInternal(columnIndex)); case DATE: 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 30d1cb5b16a..3a28cae831c 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 @@ -521,13 +521,13 @@ private void checkNonNullOfType(int columnIndex, Type expectedType, Object colum /** Checks if the value at {@code columnIndex} is one of {@code expectedCode} */ private void checkNonNullOfCodes( int columnIndex, List expectedCodes, Object columnNameForError) { - Code actualCode = getColumnType(columnIndex).getCode(); + Type actualType = getColumnType(columnIndex); checkState( - expectedCodes.contains(actualCode), + expectedCodes.contains(actualType.getCode()), "Column %s is not of correct type code: expected one of [%s] but was %s", columnNameForError, expectedCodes, - actualCode); + actualType); checkNonNull(columnIndex, columnNameForError); } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SingerProto.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SingerProto.java index 297f281045b..d061013dbc8 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SingerProto.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SingerProto.java @@ -1,6 +1,21 @@ +/* + * Copyright 2022 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. + */ + // Generated by the protocol buffer compiler. DO NOT EDIT! // source: src/test/resources/com/google/cloud/spanner/singer.proto - package com.google.cloud.spanner; public final class SingerProto { From 6c1637752a58391fd3a343080f72c821385077c6 Mon Sep 17 00:00:00 2001 From: gauravpurohit Date: Fri, 28 Oct 2022 17:36:37 +0000 Subject: [PATCH 11/21] feat: Minor refactoring 1. Adding docs and formatting the code. 2. Adding additional methods for enum and message which accepts descriptors. --- .../java/com/google/cloud/spanner/Value.java | 28 +- .../com/google/cloud/spanner/ValueBinder.java | 12 + .../com/google/cloud/spanner/SingerProto.java | 575 +++++++++--------- .../google/cloud/spanner/ValueBinderTest.java | 22 +- .../DirectExecuteResultSetTest.java | 9 +- .../connection/RandomResultSetGenerator.java | 135 ++-- .../ReplaceableForwardingResultSetTest.java | 9 +- .../com/google/cloud/spanner/singer.proto | 2 +- 8 files changed, 429 insertions(+), 363 deletions(-) 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 c10e5095759..db5c6dc8e4b 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 @@ -25,6 +25,8 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.protobuf.AbstractMessage; +import com.google.protobuf.Descriptors.Descriptor; +import com.google.protobuf.Descriptors.EnumDescriptor; import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.ListValue; import com.google.protobuf.NullValue; @@ -242,6 +244,18 @@ public static Value protoMessage(@Nullable ByteArray v, String protoTypFqn) { return new ProtoMessageImpl(v == null, v, protoTypFqn); } + /** + * Return a {@code PROTO} value + * + * @param v Serialized Proto Array, which may be null. + * @param descriptor Proto Type Descriptor, use static method {@code + * MESSAGE.getDescriptorForType()}. + */ + public static Value protoMessage(@Nullable ByteArray v, Descriptor descriptor) { + Preconditions.checkNotNull(descriptor, "descriptor can't be null."); + return new ProtoMessageImpl(v == null, v, descriptor.getFullName()); + } + /** * Return a {@code ENUM} value. * @@ -258,10 +272,22 @@ public static Value protoEnum(ProtocolMessageEnum v) { * @param v Enum non-primitive Integer constant. * @param protoTypFqn Fully qualified name of proto representing the enum definition. */ - public static Value protoEnum(Long v, String protoTypFqn) { + public static Value protoEnum(@Nullable Long v, String protoTypFqn) { return new ProtoEnumImpl(v == null, v, protoTypFqn); } + /** + * Return a {@code ENUM} value. + * + * @param v Enum non-primitive Integer constant. + * @param enumDescriptor Enum Type Descriptor, use static method {@code + * ENUM.getDescriptorForType()}. + */ + public static Value protoEnum(@Nullable Long v, EnumDescriptor enumDescriptor) { + Preconditions.checkNotNull(enumDescriptor, "descriptor can't be null."); + return new ProtoEnumImpl(v == null, v, enumDescriptor.getFullName()); + } + /** * Return a {@code ENUM} value. * 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 e06b0a81637..1d9e81e0bc5 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 @@ -20,6 +20,8 @@ import com.google.cloud.Date; import com.google.cloud.Timestamp; import com.google.protobuf.AbstractMessage; +import com.google.protobuf.Descriptors.Descriptor; +import com.google.protobuf.Descriptors.EnumDescriptor; import com.google.protobuf.ProtocolMessageEnum; import java.math.BigDecimal; import javax.annotation.Nullable; @@ -107,6 +109,11 @@ public R to(@Nullable ByteArray v, String protoTypFqn) { return handle(Value.protoMessage(v, protoTypFqn)); } + /** Binds to {@code Value.protoMessage(value, descriptor)} */ + public R to(@Nullable ByteArray v, Descriptor descriptor) { + return handle(Value.protoMessage(v, descriptor)); + } + /** Binds to {@code Value.protoEnum(value)} */ public R to(ProtocolMessageEnum value) { return handle(Value.protoEnum(value)); @@ -117,6 +124,11 @@ public R to(@Nullable Long v, String protoTypFqn) { return handle(Value.protoEnum(v, protoTypFqn)); } + /** Binds to {@code Value.protoEnum(value, enumDescriptor)} */ + public R to(@Nullable Long v, EnumDescriptor enumDescriptor) { + return handle(Value.protoEnum(v, enumDescriptor)); + } + /** Binds to {@code Value.protoEnum(value, protoType)} */ public R to(long v, String protoTypFqn) { return handle(Value.protoEnum(v, protoTypFqn)); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SingerProto.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SingerProto.java index d061013dbc8..56fb82c15d2 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SingerProto.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SingerProto.java @@ -20,56 +20,33 @@ public final class SingerProto { private SingerProto() {} - public static void registerAllExtensions( - com.google.protobuf.ExtensionRegistryLite registry) { - } - public static void registerAllExtensions( - com.google.protobuf.ExtensionRegistry registry) { - registerAllExtensions( - (com.google.protobuf.ExtensionRegistryLite) registry); + public static void registerAllExtensions(com.google.protobuf.ExtensionRegistryLite registry) {} + + public static void registerAllExtensions(com.google.protobuf.ExtensionRegistry registry) { + registerAllExtensions((com.google.protobuf.ExtensionRegistryLite) registry); } - /** - * Protobuf enum {@code com.singer.Genre} - */ - public enum Genre - implements com.google.protobuf.ProtocolMessageEnum { - /** - * POP = 0; - */ + /** Protobuf enum {@code spanner.examples.music.Genre} */ + public enum Genre implements com.google.protobuf.ProtocolMessageEnum { + /** POP = 0; */ POP(0), - /** - * JAZZ = 1; - */ + /** JAZZ = 1; */ JAZZ(1), - /** - * FOLK = 2; - */ + /** FOLK = 2; */ FOLK(2), - /** - * ROCK = 3; - */ + /** ROCK = 3; */ ROCK(3), ; - /** - * POP = 0; - */ + /** POP = 0; */ public static final int POP_VALUE = 0; - /** - * JAZZ = 1; - */ + /** JAZZ = 1; */ public static final int JAZZ_VALUE = 1; - /** - * FOLK = 2; - */ + /** FOLK = 2; */ public static final int FOLK_VALUE = 2; - /** - * ROCK = 3; - */ + /** ROCK = 3; */ public static final int ROCK_VALUE = 3; - public final int getNumber() { return value; } @@ -90,46 +67,47 @@ public static Genre valueOf(int value) { */ public static Genre forNumber(int value) { switch (value) { - case 0: return POP; - case 1: return JAZZ; - case 2: return FOLK; - case 3: return ROCK; - default: return null; + case 0: + return POP; + case 1: + return JAZZ; + case 2: + return FOLK; + case 3: + return ROCK; + default: + return null; } } - public static com.google.protobuf.Internal.EnumLiteMap - internalGetValueMap() { + public static com.google.protobuf.Internal.EnumLiteMap internalGetValueMap() { return internalValueMap; } - private static final com.google.protobuf.Internal.EnumLiteMap< - Genre> internalValueMap = + + private static final com.google.protobuf.Internal.EnumLiteMap internalValueMap = new com.google.protobuf.Internal.EnumLiteMap() { public Genre findValueByNumber(int number) { return Genre.forNumber(number); } }; - public final com.google.protobuf.Descriptors.EnumValueDescriptor - getValueDescriptor() { + public final com.google.protobuf.Descriptors.EnumValueDescriptor getValueDescriptor() { return getDescriptor().getValues().get(ordinal()); } - public final com.google.protobuf.Descriptors.EnumDescriptor - getDescriptorForType() { + + public final com.google.protobuf.Descriptors.EnumDescriptor getDescriptorForType() { return getDescriptor(); } - public static final com.google.protobuf.Descriptors.EnumDescriptor - getDescriptor() { + + public static final com.google.protobuf.Descriptors.EnumDescriptor getDescriptor() { return com.google.cloud.spanner.SingerProto.getDescriptor().getEnumTypes().get(0); } private static final Genre[] VALUES = values(); - public static Genre valueOf( - com.google.protobuf.Descriptors.EnumValueDescriptor desc) { + public static Genre valueOf(com.google.protobuf.Descriptors.EnumValueDescriptor desc) { if (desc.getType() != getDescriptor()) { - throw new java.lang.IllegalArgumentException( - "EnumValueDescriptor is not for this type."); + throw new java.lang.IllegalArgumentException("EnumValueDescriptor is not for this type."); } return VALUES[desc.getIndex()]; } @@ -140,81 +118,89 @@ private Genre(int value) { this.value = value; } - // @@protoc_insertion_point(enum_scope:com.singer.Genre) + // @@protoc_insertion_point(enum_scope:spanner.examples.music.Genre) } - public interface SingerInfoOrBuilder extends - // @@protoc_insertion_point(interface_extends:com.singer.SingerInfo) + public interface SingerInfoOrBuilder + extends + // @@protoc_insertion_point(interface_extends:spanner.examples.music.SingerInfo) com.google.protobuf.MessageOrBuilder { /** * optional int64 singer_id = 1; + * * @return Whether the singerId field is set. */ boolean hasSingerId(); /** * optional int64 singer_id = 1; + * * @return The singerId. */ long getSingerId(); /** * optional string birth_date = 2; + * * @return Whether the birthDate field is set. */ boolean hasBirthDate(); /** * optional string birth_date = 2; + * * @return The birthDate. */ java.lang.String getBirthDate(); /** * optional string birth_date = 2; + * * @return The bytes for birthDate. */ - com.google.protobuf.ByteString - getBirthDateBytes(); + com.google.protobuf.ByteString getBirthDateBytes(); /** * optional string nationality = 3; + * * @return Whether the nationality field is set. */ boolean hasNationality(); /** * optional string nationality = 3; + * * @return The nationality. */ java.lang.String getNationality(); /** * optional string nationality = 3; + * * @return The bytes for nationality. */ - com.google.protobuf.ByteString - getNationalityBytes(); + com.google.protobuf.ByteString getNationalityBytes(); /** - * optional .com.singer.Genre genre = 4; + * optional .spanner.examples.music.Genre genre = 4; + * * @return Whether the genre field is set. */ boolean hasGenre(); /** - * optional .com.singer.Genre genre = 4; + * optional .spanner.examples.music.Genre genre = 4; + * * @return The genre. */ com.google.cloud.spanner.SingerProto.Genre getGenre(); } - /** - * Protobuf type {@code com.singer.SingerInfo} - */ - public static final class SingerInfo extends - com.google.protobuf.GeneratedMessageV3 implements - // @@protoc_insertion_point(message_implements:com.singer.SingerInfo) + /** Protobuf type {@code spanner.examples.music.SingerInfo} */ + public static final class SingerInfo extends com.google.protobuf.GeneratedMessageV3 + implements + // @@protoc_insertion_point(message_implements:spanner.examples.music.SingerInfo) SingerInfoOrBuilder { private static final long serialVersionUID = 0L; // Use SingerInfo.newBuilder() to construct. private SingerInfo(com.google.protobuf.GeneratedMessageV3.Builder builder) { super(builder); } + private SingerInfo() { birthDate_ = ""; nationality_ = ""; @@ -223,16 +209,15 @@ private SingerInfo() { @java.lang.Override @SuppressWarnings({"unused"}) - protected java.lang.Object newInstance( - UnusedPrivateParameter unused) { + protected java.lang.Object newInstance(UnusedPrivateParameter unused) { return new SingerInfo(); } @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { + public final com.google.protobuf.UnknownFieldSet getUnknownFields() { return this.unknownFields; } + private SingerInfo( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) @@ -252,65 +237,72 @@ private SingerInfo( case 0: done = true; break; - case 8: { - bitField0_ |= 0x00000001; - singerId_ = input.readInt64(); - break; - } - case 18: { - com.google.protobuf.ByteString bs = input.readBytes(); - bitField0_ |= 0x00000002; - birthDate_ = bs; - break; - } - case 26: { - com.google.protobuf.ByteString bs = input.readBytes(); - bitField0_ |= 0x00000004; - nationality_ = bs; - break; - } - case 32: { - int rawValue = input.readEnum(); - @SuppressWarnings("deprecation") - com.google.cloud.spanner.SingerProto.Genre value = com.google.cloud.spanner.SingerProto.Genre.valueOf(rawValue); - if (value == null) { - unknownFields.mergeVarintField(4, rawValue); - } else { - bitField0_ |= 0x00000008; - genre_ = rawValue; + case 8: + { + bitField0_ |= 0x00000001; + singerId_ = input.readInt64(); + break; } - break; - } - default: { - if (!parseUnknownField( - input, unknownFields, extensionRegistry, tag)) { - done = true; + case 18: + { + com.google.protobuf.ByteString bs = input.readBytes(); + bitField0_ |= 0x00000002; + birthDate_ = bs; + break; + } + case 26: + { + com.google.protobuf.ByteString bs = input.readBytes(); + bitField0_ |= 0x00000004; + nationality_ = bs; + break; + } + case 32: + { + int rawValue = input.readEnum(); + @SuppressWarnings("deprecation") + com.google.cloud.spanner.SingerProto.Genre value = + com.google.cloud.spanner.SingerProto.Genre.valueOf(rawValue); + if (value == null) { + unknownFields.mergeVarintField(4, rawValue); + } else { + bitField0_ |= 0x00000008; + genre_ = rawValue; + } + break; + } + default: + { + if (!parseUnknownField(input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; } - break; - } } } } catch (com.google.protobuf.InvalidProtocolBufferException e) { throw e.setUnfinishedMessage(this); } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e).setUnfinishedMessage(this); + throw new com.google.protobuf.InvalidProtocolBufferException(e).setUnfinishedMessage(this); } finally { this.unknownFields = unknownFields.build(); makeExtensionsImmutable(); } } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.google.cloud.spanner.SingerProto.internal_static_com_singer_SingerInfo_descriptor; + + public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { + return com.google.cloud.spanner.SingerProto + .internal_static_spanner_examples_music_SingerInfo_descriptor; } @java.lang.Override protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.google.cloud.spanner.SingerProto.internal_static_com_singer_SingerInfo_fieldAccessorTable + internalGetFieldAccessorTable() { + return com.google.cloud.spanner.SingerProto + .internal_static_spanner_examples_music_SingerInfo_fieldAccessorTable .ensureFieldAccessorsInitialized( - com.google.cloud.spanner.SingerProto.SingerInfo.class, com.google.cloud.spanner.SingerProto.SingerInfo.Builder.class); + com.google.cloud.spanner.SingerProto.SingerInfo.class, + com.google.cloud.spanner.SingerProto.SingerInfo.Builder.class); } private int bitField0_; @@ -318,6 +310,7 @@ private SingerInfo( private long singerId_; /** * optional int64 singer_id = 1; + * * @return Whether the singerId field is set. */ @java.lang.Override @@ -326,6 +319,7 @@ public boolean hasSingerId() { } /** * optional int64 singer_id = 1; + * * @return The singerId. */ @java.lang.Override @@ -337,6 +331,7 @@ public long getSingerId() { private volatile java.lang.Object birthDate_; /** * optional string birth_date = 2; + * * @return Whether the birthDate field is set. */ @java.lang.Override @@ -345,6 +340,7 @@ public boolean hasBirthDate() { } /** * optional string birth_date = 2; + * * @return The birthDate. */ @java.lang.Override @@ -353,8 +349,7 @@ public java.lang.String getBirthDate() { if (ref instanceof java.lang.String) { return (java.lang.String) ref; } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); if (bs.isValidUtf8()) { birthDate_ = s; @@ -364,16 +359,15 @@ public java.lang.String getBirthDate() { } /** * optional string birth_date = 2; + * * @return The bytes for birthDate. */ @java.lang.Override - public com.google.protobuf.ByteString - getBirthDateBytes() { + public com.google.protobuf.ByteString getBirthDateBytes() { java.lang.Object ref = birthDate_; if (ref instanceof java.lang.String) { com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); + com.google.protobuf.ByteString.copyFromUtf8((java.lang.String) ref); birthDate_ = b; return b; } else { @@ -385,6 +379,7 @@ public java.lang.String getBirthDate() { private volatile java.lang.Object nationality_; /** * optional string nationality = 3; + * * @return Whether the nationality field is set. */ @java.lang.Override @@ -393,6 +388,7 @@ public boolean hasNationality() { } /** * optional string nationality = 3; + * * @return The nationality. */ @java.lang.Override @@ -401,8 +397,7 @@ public java.lang.String getNationality() { if (ref instanceof java.lang.String) { return (java.lang.String) ref; } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); if (bs.isValidUtf8()) { nationality_ = s; @@ -412,16 +407,15 @@ public java.lang.String getNationality() { } /** * optional string nationality = 3; + * * @return The bytes for nationality. */ @java.lang.Override - public com.google.protobuf.ByteString - getNationalityBytes() { + public com.google.protobuf.ByteString getNationalityBytes() { java.lang.Object ref = nationality_; if (ref instanceof java.lang.String) { com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); + com.google.protobuf.ByteString.copyFromUtf8((java.lang.String) ref); nationality_ = b; return b; } else { @@ -432,23 +426,29 @@ public java.lang.String getNationality() { public static final int GENRE_FIELD_NUMBER = 4; private int genre_; /** - * optional .com.singer.Genre genre = 4; + * optional .spanner.examples.music.Genre genre = 4; + * * @return Whether the genre field is set. */ - @java.lang.Override public boolean hasGenre() { + @java.lang.Override + public boolean hasGenre() { return ((bitField0_ & 0x00000008) != 0); } /** - * optional .com.singer.Genre genre = 4; + * optional .spanner.examples.music.Genre genre = 4; + * * @return The genre. */ - @java.lang.Override public com.google.cloud.spanner.SingerProto.Genre getGenre() { + @java.lang.Override + public com.google.cloud.spanner.SingerProto.Genre getGenre() { @SuppressWarnings("deprecation") - com.google.cloud.spanner.SingerProto.Genre result = com.google.cloud.spanner.SingerProto.Genre.valueOf(genre_); + com.google.cloud.spanner.SingerProto.Genre result = + com.google.cloud.spanner.SingerProto.Genre.valueOf(genre_); return result == null ? com.google.cloud.spanner.SingerProto.Genre.POP : result; } private byte memoizedIsInitialized = -1; + @java.lang.Override public final boolean isInitialized() { byte isInitialized = memoizedIsInitialized; @@ -460,8 +460,7 @@ public final boolean isInitialized() { } @java.lang.Override - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { + public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException { if (((bitField0_ & 0x00000001) != 0)) { output.writeInt64(1, singerId_); } @@ -484,8 +483,7 @@ public int getSerializedSize() { size = 0; if (((bitField0_ & 0x00000001) != 0)) { - size += com.google.protobuf.CodedOutputStream - .computeInt64Size(1, singerId_); + size += com.google.protobuf.CodedOutputStream.computeInt64Size(1, singerId_); } if (((bitField0_ & 0x00000002) != 0)) { size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, birthDate_); @@ -494,8 +492,7 @@ public int getSerializedSize() { size += com.google.protobuf.GeneratedMessageV3.computeStringSize(3, nationality_); } if (((bitField0_ & 0x00000008) != 0)) { - size += com.google.protobuf.CodedOutputStream - .computeEnumSize(4, genre_); + size += com.google.protobuf.CodedOutputStream.computeEnumSize(4, genre_); } size += unknownFields.getSerializedSize(); memoizedSize = size; @@ -510,22 +507,20 @@ public boolean equals(final java.lang.Object obj) { if (!(obj instanceof com.google.cloud.spanner.SingerProto.SingerInfo)) { return super.equals(obj); } - com.google.cloud.spanner.SingerProto.SingerInfo other = (com.google.cloud.spanner.SingerProto.SingerInfo) obj; + com.google.cloud.spanner.SingerProto.SingerInfo other = + (com.google.cloud.spanner.SingerProto.SingerInfo) obj; if (hasSingerId() != other.hasSingerId()) return false; if (hasSingerId()) { - if (getSingerId() - != other.getSingerId()) return false; + if (getSingerId() != other.getSingerId()) return false; } if (hasBirthDate() != other.hasBirthDate()) return false; if (hasBirthDate()) { - if (!getBirthDate() - .equals(other.getBirthDate())) return false; + if (!getBirthDate().equals(other.getBirthDate())) return false; } if (hasNationality() != other.hasNationality()) return false; if (hasNationality()) { - if (!getNationality() - .equals(other.getNationality())) return false; + if (!getNationality().equals(other.getNationality())) return false; } if (hasGenre() != other.hasGenre()) return false; if (hasGenre()) { @@ -544,8 +539,7 @@ public int hashCode() { hash = (19 * hash) + getDescriptor().hashCode(); if (hasSingerId()) { hash = (37 * hash) + SINGER_ID_FIELD_NUMBER; - hash = (53 * hash) + com.google.protobuf.Internal.hashLong( - getSingerId()); + hash = (53 * hash) + com.google.protobuf.Internal.hashLong(getSingerId()); } if (hasBirthDate()) { hash = (37 * hash) + BIRTH_DATE_FIELD_NUMBER; @@ -565,87 +559,93 @@ public int hashCode() { } public static com.google.cloud.spanner.SingerProto.SingerInfo parseFrom( - java.nio.ByteBuffer data) - throws com.google.protobuf.InvalidProtocolBufferException { + java.nio.ByteBuffer data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } + public static com.google.cloud.spanner.SingerProto.SingerInfo parseFrom( - java.nio.ByteBuffer data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) + java.nio.ByteBuffer data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } + public static com.google.cloud.spanner.SingerProto.SingerInfo parseFrom( com.google.protobuf.ByteString data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } + public static com.google.cloud.spanner.SingerProto.SingerInfo parseFrom( com.google.protobuf.ByteString data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } + public static com.google.cloud.spanner.SingerProto.SingerInfo parseFrom(byte[] data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } + public static com.google.cloud.spanner.SingerProto.SingerInfo parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) + byte[] data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } - public static com.google.cloud.spanner.SingerProto.SingerInfo parseFrom(java.io.InputStream input) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseWithIOException(PARSER, input); + + public static com.google.cloud.spanner.SingerProto.SingerInfo parseFrom( + java.io.InputStream input) throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input); } + public static com.google.cloud.spanner.SingerProto.SingerInfo parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) + java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseWithIOException(PARSER, input, extensionRegistry); + return com.google.protobuf.GeneratedMessageV3.parseWithIOException( + PARSER, input, extensionRegistry); } - public static com.google.cloud.spanner.SingerProto.SingerInfo parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseDelimitedWithIOException(PARSER, input); + + public static com.google.cloud.spanner.SingerProto.SingerInfo parseDelimitedFrom( + java.io.InputStream input) throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException(PARSER, input); } + public static com.google.cloud.spanner.SingerProto.SingerInfo parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) + java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException( + PARSER, input, extensionRegistry); } + public static com.google.cloud.spanner.SingerProto.SingerInfo parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseWithIOException(PARSER, input); + com.google.protobuf.CodedInputStream input) throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input); } + public static com.google.cloud.spanner.SingerProto.SingerInfo parseFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseWithIOException(PARSER, input, extensionRegistry); + return com.google.protobuf.GeneratedMessageV3.parseWithIOException( + PARSER, input, extensionRegistry); } @java.lang.Override - public Builder newBuilderForType() { return newBuilder(); } + public Builder newBuilderForType() { + return newBuilder(); + } + public static Builder newBuilder() { return DEFAULT_INSTANCE.toBuilder(); } + public static Builder newBuilder(com.google.cloud.spanner.SingerProto.SingerInfo prototype) { return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); } + @java.lang.Override public Builder toBuilder() { - return this == DEFAULT_INSTANCE - ? new Builder() : new Builder().mergeFrom(this); + return this == DEFAULT_INSTANCE ? new Builder() : new Builder().mergeFrom(this); } @java.lang.Override @@ -654,24 +654,25 @@ protected Builder newBuilderForType( Builder builder = new Builder(parent); return builder; } - /** - * Protobuf type {@code com.singer.SingerInfo} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessageV3.Builder implements - // @@protoc_insertion_point(builder_implements:com.singer.SingerInfo) + /** Protobuf type {@code spanner.examples.music.SingerInfo} */ + public static final class Builder + extends com.google.protobuf.GeneratedMessageV3.Builder + implements + // @@protoc_insertion_point(builder_implements:spanner.examples.music.SingerInfo) com.google.cloud.spanner.SingerProto.SingerInfoOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.google.cloud.spanner.SingerProto.internal_static_com_singer_SingerInfo_descriptor; + public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { + return com.google.cloud.spanner.SingerProto + .internal_static_spanner_examples_music_SingerInfo_descriptor; } @java.lang.Override protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.google.cloud.spanner.SingerProto.internal_static_com_singer_SingerInfo_fieldAccessorTable + internalGetFieldAccessorTable() { + return com.google.cloud.spanner.SingerProto + .internal_static_spanner_examples_music_SingerInfo_fieldAccessorTable .ensureFieldAccessorsInitialized( - com.google.cloud.spanner.SingerProto.SingerInfo.class, com.google.cloud.spanner.SingerProto.SingerInfo.Builder.class); + com.google.cloud.spanner.SingerProto.SingerInfo.class, + com.google.cloud.spanner.SingerProto.SingerInfo.Builder.class); } // Construct using com.google.cloud.spanner.SingerProto.SingerInfo.newBuilder() @@ -679,16 +680,15 @@ private Builder() { maybeForceBuilderInitialization(); } - private Builder( - com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + private Builder(com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { super(parent); maybeForceBuilderInitialization(); } + private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessageV3 - .alwaysUseFieldBuilders) { - } + if (com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders) {} } + @java.lang.Override public Builder clear() { super.clear(); @@ -704,9 +704,9 @@ public Builder clear() { } @java.lang.Override - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return com.google.cloud.spanner.SingerProto.internal_static_com_singer_SingerInfo_descriptor; + public com.google.protobuf.Descriptors.Descriptor getDescriptorForType() { + return com.google.cloud.spanner.SingerProto + .internal_static_spanner_examples_music_SingerInfo_descriptor; } @java.lang.Override @@ -725,7 +725,8 @@ public com.google.cloud.spanner.SingerProto.SingerInfo build() { @java.lang.Override public com.google.cloud.spanner.SingerProto.SingerInfo buildPartial() { - com.google.cloud.spanner.SingerProto.SingerInfo result = new com.google.cloud.spanner.SingerProto.SingerInfo(this); + com.google.cloud.spanner.SingerProto.SingerInfo result = + new com.google.cloud.spanner.SingerProto.SingerInfo(this); int from_bitField0_ = bitField0_; int to_bitField0_ = 0; if (((from_bitField0_ & 0x00000001) != 0)) { @@ -753,38 +754,41 @@ public com.google.cloud.spanner.SingerProto.SingerInfo buildPartial() { public Builder clone() { return super.clone(); } + @java.lang.Override public Builder setField( - com.google.protobuf.Descriptors.FieldDescriptor field, - java.lang.Object value) { + com.google.protobuf.Descriptors.FieldDescriptor field, java.lang.Object value) { return super.setField(field, value); } + @java.lang.Override - public Builder clearField( - com.google.protobuf.Descriptors.FieldDescriptor field) { + public Builder clearField(com.google.protobuf.Descriptors.FieldDescriptor field) { return super.clearField(field); } + @java.lang.Override - public Builder clearOneof( - com.google.protobuf.Descriptors.OneofDescriptor oneof) { + public Builder clearOneof(com.google.protobuf.Descriptors.OneofDescriptor oneof) { return super.clearOneof(oneof); } + @java.lang.Override public Builder setRepeatedField( com.google.protobuf.Descriptors.FieldDescriptor field, - int index, java.lang.Object value) { + int index, + java.lang.Object value) { return super.setRepeatedField(field, index, value); } + @java.lang.Override public Builder addRepeatedField( - com.google.protobuf.Descriptors.FieldDescriptor field, - java.lang.Object value) { + com.google.protobuf.Descriptors.FieldDescriptor field, java.lang.Object value) { return super.addRepeatedField(field, value); } + @java.lang.Override public Builder mergeFrom(com.google.protobuf.Message other) { if (other instanceof com.google.cloud.spanner.SingerProto.SingerInfo) { - return mergeFrom((com.google.cloud.spanner.SingerProto.SingerInfo)other); + return mergeFrom((com.google.cloud.spanner.SingerProto.SingerInfo) other); } else { super.mergeFrom(other); return this; @@ -792,7 +796,8 @@ public Builder mergeFrom(com.google.protobuf.Message other) { } public Builder mergeFrom(com.google.cloud.spanner.SingerProto.SingerInfo other) { - if (other == com.google.cloud.spanner.SingerProto.SingerInfo.getDefaultInstance()) return this; + if (other == com.google.cloud.spanner.SingerProto.SingerInfo.getDefaultInstance()) + return this; if (other.hasSingerId()) { setSingerId(other.getSingerId()); } @@ -828,7 +833,8 @@ public Builder mergeFrom( try { parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (com.google.cloud.spanner.SingerProto.SingerInfo) e.getUnfinishedMessage(); + parsedMessage = + (com.google.cloud.spanner.SingerProto.SingerInfo) e.getUnfinishedMessage(); throw e.unwrapIOException(); } finally { if (parsedMessage != null) { @@ -837,11 +843,13 @@ public Builder mergeFrom( } return this; } + private int bitField0_; - private long singerId_ ; + private long singerId_; /** * optional int64 singer_id = 1; + * * @return Whether the singerId field is set. */ @java.lang.Override @@ -850,6 +858,7 @@ public boolean hasSingerId() { } /** * optional int64 singer_id = 1; + * * @return The singerId. */ @java.lang.Override @@ -858,6 +867,7 @@ public long getSingerId() { } /** * optional int64 singer_id = 1; + * * @param value The singerId to set. * @return This builder for chaining. */ @@ -869,6 +879,7 @@ public Builder setSingerId(long value) { } /** * optional int64 singer_id = 1; + * * @return This builder for chaining. */ public Builder clearSingerId() { @@ -881,6 +892,7 @@ public Builder clearSingerId() { private java.lang.Object birthDate_ = ""; /** * optional string birth_date = 2; + * * @return Whether the birthDate field is set. */ public boolean hasBirthDate() { @@ -888,13 +900,13 @@ public boolean hasBirthDate() { } /** * optional string birth_date = 2; + * * @return The birthDate. */ public java.lang.String getBirthDate() { java.lang.Object ref = birthDate_; if (!(ref instanceof java.lang.String)) { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); if (bs.isValidUtf8()) { birthDate_ = s; @@ -906,15 +918,14 @@ public java.lang.String getBirthDate() { } /** * optional string birth_date = 2; + * * @return The bytes for birthDate. */ - public com.google.protobuf.ByteString - getBirthDateBytes() { + public com.google.protobuf.ByteString getBirthDateBytes() { java.lang.Object ref = birthDate_; if (ref instanceof String) { com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); + com.google.protobuf.ByteString.copyFromUtf8((java.lang.String) ref); birthDate_ = b; return b; } else { @@ -923,11 +934,11 @@ public java.lang.String getBirthDate() { } /** * optional string birth_date = 2; + * * @param value The birthDate to set. * @return This builder for chaining. */ - public Builder setBirthDate( - java.lang.String value) { + public Builder setBirthDate(java.lang.String value) { if (value == null) { throw new NullPointerException(); } @@ -938,6 +949,7 @@ public Builder setBirthDate( } /** * optional string birth_date = 2; + * * @return This builder for chaining. */ public Builder clearBirthDate() { @@ -948,11 +960,11 @@ public Builder clearBirthDate() { } /** * optional string birth_date = 2; + * * @param value The bytes for birthDate to set. * @return This builder for chaining. */ - public Builder setBirthDateBytes( - com.google.protobuf.ByteString value) { + public Builder setBirthDateBytes(com.google.protobuf.ByteString value) { if (value == null) { throw new NullPointerException(); } @@ -965,6 +977,7 @@ public Builder setBirthDateBytes( private java.lang.Object nationality_ = ""; /** * optional string nationality = 3; + * * @return Whether the nationality field is set. */ public boolean hasNationality() { @@ -972,13 +985,13 @@ public boolean hasNationality() { } /** * optional string nationality = 3; + * * @return The nationality. */ public java.lang.String getNationality() { java.lang.Object ref = nationality_; if (!(ref instanceof java.lang.String)) { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); if (bs.isValidUtf8()) { nationality_ = s; @@ -990,15 +1003,14 @@ public java.lang.String getNationality() { } /** * optional string nationality = 3; + * * @return The bytes for nationality. */ - public com.google.protobuf.ByteString - getNationalityBytes() { + public com.google.protobuf.ByteString getNationalityBytes() { java.lang.Object ref = nationality_; if (ref instanceof String) { com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); + com.google.protobuf.ByteString.copyFromUtf8((java.lang.String) ref); nationality_ = b; return b; } else { @@ -1007,11 +1019,11 @@ public java.lang.String getNationality() { } /** * optional string nationality = 3; + * * @param value The nationality to set. * @return This builder for chaining. */ - public Builder setNationality( - java.lang.String value) { + public Builder setNationality(java.lang.String value) { if (value == null) { throw new NullPointerException(); } @@ -1022,6 +1034,7 @@ public Builder setNationality( } /** * optional string nationality = 3; + * * @return This builder for chaining. */ public Builder clearNationality() { @@ -1032,11 +1045,11 @@ public Builder clearNationality() { } /** * optional string nationality = 3; + * * @param value The bytes for nationality to set. * @return This builder for chaining. */ - public Builder setNationalityBytes( - com.google.protobuf.ByteString value) { + public Builder setNationalityBytes(com.google.protobuf.ByteString value) { if (value == null) { throw new NullPointerException(); } @@ -1048,24 +1061,29 @@ public Builder setNationalityBytes( private int genre_ = 0; /** - * optional .com.singer.Genre genre = 4; + * optional .spanner.examples.music.Genre genre = 4; + * * @return Whether the genre field is set. */ - @java.lang.Override public boolean hasGenre() { + @java.lang.Override + public boolean hasGenre() { return ((bitField0_ & 0x00000008) != 0); } /** - * optional .com.singer.Genre genre = 4; + * optional .spanner.examples.music.Genre genre = 4; + * * @return The genre. */ @java.lang.Override public com.google.cloud.spanner.SingerProto.Genre getGenre() { @SuppressWarnings("deprecation") - com.google.cloud.spanner.SingerProto.Genre result = com.google.cloud.spanner.SingerProto.Genre.valueOf(genre_); + com.google.cloud.spanner.SingerProto.Genre result = + com.google.cloud.spanner.SingerProto.Genre.valueOf(genre_); return result == null ? com.google.cloud.spanner.SingerProto.Genre.POP : result; } /** - * optional .com.singer.Genre genre = 4; + * optional .spanner.examples.music.Genre genre = 4; + * * @param value The genre to set. * @return This builder for chaining. */ @@ -1079,7 +1097,8 @@ public Builder setGenre(com.google.cloud.spanner.SingerProto.Genre value) { return this; } /** - * optional .com.singer.Genre genre = 4; + * optional .spanner.examples.music.Genre genre = 4; + * * @return This builder for chaining. */ public Builder clearGenre() { @@ -1088,6 +1107,7 @@ public Builder clearGenre() { onChanged(); return this; } + @java.lang.Override public final Builder setUnknownFields( final com.google.protobuf.UnknownFieldSet unknownFields) { @@ -1100,12 +1120,12 @@ public final Builder mergeUnknownFields( return super.mergeUnknownFields(unknownFields); } - - // @@protoc_insertion_point(builder_scope:com.singer.SingerInfo) + // @@protoc_insertion_point(builder_scope:spanner.examples.music.SingerInfo) } - // @@protoc_insertion_point(class_scope:com.singer.SingerInfo) + // @@protoc_insertion_point(class_scope:spanner.examples.music.SingerInfo) private static final com.google.cloud.spanner.SingerProto.SingerInfo DEFAULT_INSTANCE; + static { DEFAULT_INSTANCE = new com.google.cloud.spanner.SingerProto.SingerInfo(); } @@ -1114,16 +1134,17 @@ public static com.google.cloud.spanner.SingerProto.SingerInfo getDefaultInstance return DEFAULT_INSTANCE; } - @java.lang.Deprecated public static final com.google.protobuf.Parser - PARSER = new com.google.protobuf.AbstractParser() { - @java.lang.Override - public SingerInfo parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new SingerInfo(input, extensionRegistry); - } - }; + @java.lang.Deprecated + public static final com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + @java.lang.Override + public SingerInfo parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new SingerInfo(input, extensionRegistry); + } + }; public static com.google.protobuf.Parser parser() { return PARSER; @@ -1138,40 +1159,40 @@ public com.google.protobuf.Parser getParserForType() { public com.google.cloud.spanner.SingerProto.SingerInfo getDefaultInstanceForType() { return DEFAULT_INSTANCE; } - } private static final com.google.protobuf.Descriptors.Descriptor - internal_static_com_singer_SingerInfo_descriptor; - private static final - com.google.protobuf.GeneratedMessageV3.FieldAccessorTable - internal_static_com_singer_SingerInfo_fieldAccessorTable; + internal_static_spanner_examples_music_SingerInfo_descriptor; + private static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_spanner_examples_music_SingerInfo_fieldAccessorTable; - public static com.google.protobuf.Descriptors.FileDescriptor - getDescriptor() { + public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() { return descriptor; } - private static com.google.protobuf.Descriptors.FileDescriptor - descriptor; + + private static com.google.protobuf.Descriptors.FileDescriptor descriptor; + static { java.lang.String[] descriptorData = { - "\n\014singer.proto\022\ncom.singer\"j\n\nSingerInfo" + - "\022\021\n\tsinger_id\030\001 \001(\003\022\022\n\nbirth_date\030\002 \001(\t\022" + - "\023\n\013nationality\030\003 \001(\t\022 \n\005genre\030\004 \001(\0162\021.co" + - "m.singer.Genre*.\n\005Genre\022\007\n\003POP\020\000\022\010\n\004JAZZ" + - "\020\001\022\010\n\004FOLK\020\002\022\010\n\004ROCK\020\003B4\n#com.example.sp" + - "anner.protobuf.singerB\013SingerProtoP\000" + "\n\014singer.proto\022\026spanner.examples.music\"v" + + "\n\nSingerInfo\022\021\n\tsinger_id\030\001 \001(\003\022\022\n\nbirth" + + "_date\030\002 \001(\t\022\023\n\013nationality\030\003 \001(\t\022,\n\005genr" + + "e\030\004 \001(\0162\035.spanner.examples.music.Genre*." + + "\n\005Genre\022\007\n\003POP\020\000\022\010\n\004JAZZ\020\001\022\010\n\004FOLK\020\002\022\010\n\004" + + "ROCK\020\003B)\n\030com.google.cloud.spannerB\013Sing" + + "erProtoP\000" }; - descriptor = com.google.protobuf.Descriptors.FileDescriptor - .internalBuildGeneratedFileFrom(descriptorData, - new com.google.protobuf.Descriptors.FileDescriptor[] { - }); - internal_static_com_singer_SingerInfo_descriptor = + descriptor = + com.google.protobuf.Descriptors.FileDescriptor.internalBuildGeneratedFileFrom( + descriptorData, new com.google.protobuf.Descriptors.FileDescriptor[] {}); + internal_static_spanner_examples_music_SingerInfo_descriptor = getDescriptor().getMessageTypes().get(0); - internal_static_com_singer_SingerInfo_fieldAccessorTable = new - com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( - internal_static_com_singer_SingerInfo_descriptor, - new java.lang.String[] { "SingerId", "BirthDate", "Nationality", "Genre", }); + internal_static_spanner_examples_music_SingerInfo_fieldAccessorTable = + new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_spanner_examples_music_SingerInfo_descriptor, + new java.lang.String[] { + "SingerId", "BirthDate", "Nationality", "Genre", + }); } // @@protoc_insertion_point(outer_class_scope) 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 d0a6764a80b..88c6f8cf02d 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 @@ -27,6 +27,8 @@ import com.google.cloud.spanner.SingerProto.Genre; import com.google.cloud.spanner.SingerProto.SingerInfo; import com.google.protobuf.AbstractMessage; +import com.google.protobuf.Descriptors.Descriptor; +import com.google.protobuf.Descriptors.EnumDescriptor; import com.google.protobuf.ProtocolMessageEnum; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -48,7 +50,6 @@ public class ValueBinderTest { private static final String PROTO_ENUM_METHOD_NAME = "protoEnum"; public static final String DEFAULT_PG_NUMERIC = "1.23"; - private Value lastValue; private int lastReturnValue; private ValueBinder binder = new BinderImpl(); @@ -130,7 +131,7 @@ public void reflection() // Test unary null. if (!binderMethod.getParameterTypes()[0].isPrimitive() && (!method.getName().equalsIgnoreCase(PROTO_MESSAGE_METHOD_NAME) - && !method.getName().equalsIgnoreCase(PROTO_ENUM_METHOD_NAME))) { + && !method.getName().equalsIgnoreCase(PROTO_ENUM_METHOD_NAME))) { if (method.getName().equalsIgnoreCase(JSON_METHOD_NAME)) { // Special case for json to change the method from ValueBinder.to(String) to // ValueBinder.to(Value) @@ -179,19 +180,26 @@ public void reflection() assertThat(lastValue).isEqualTo(expected); } else if (binderMethod.getParameterTypes().length == 2 && (method.getName().equalsIgnoreCase(PROTO_MESSAGE_METHOD_NAME) - || method.getName().equalsIgnoreCase(PROTO_ENUM_METHOD_NAME))) { + || method.getName().equalsIgnoreCase(PROTO_ENUM_METHOD_NAME))) { // Test unary null. Object firstArgument = null; if (binderMethod.getParameterTypes()[0].isPrimitive()) { firstArgument = 0; } - assertThat(binderMethod.invoke(binder, firstArgument, "com.proto.example")).isEqualTo(lastReturnValue); - Value expected = (Value) method.invoke(Value.class, firstArgument, "com.proto.example"); + + Object secondArgument = "com.proto.example"; + if (binderMethod.getParameterTypes()[1] == Descriptor.class) { + secondArgument = SingerInfo.getDescriptor(); + } else if (binderMethod.getParameterTypes()[1] == EnumDescriptor.class) { + secondArgument = Genre.getDescriptor(); + } + assertThat(binderMethod.invoke(binder, firstArgument, secondArgument)) + .isEqualTo(lastReturnValue); + Value expected = (Value) method.invoke(Value.class, firstArgument, secondArgument); assertThat(lastValue).isEqualTo(expected); assertThat(binder.to(expected)).isEqualTo(lastReturnValue); assertThat(lastValue).isEqualTo(expected); - } - else { + } else { // Array slice method: depends on DefaultValues returning arrays of length 2. assertThat(binderMethod.getParameterTypes().length).isEqualTo(3); assertThat(binderMethod.getParameterTypes()[1]).isEqualTo(int.class); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/DirectExecuteResultSetTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/DirectExecuteResultSetTest.java index 85d1d5d26e8..7b25a497137 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/DirectExecuteResultSetTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/DirectExecuteResultSetTest.java @@ -148,20 +148,19 @@ private void callMethods( if (firstParameterType == String.class) { firstArgument = "test"; - } - else if (firstParameterType == int.class) { + } else if (firstParameterType == int.class) { firstArgument = 0; } if (secondParameterType == Function.class) { - Function lambdaFunction = (val) -> Genre.forNumber( - val.intValue()); + Function lambdaFunction = + (val) -> Genre.forNumber(val.intValue()); secondArgument = lambdaFunction; } else if (secondParameterType == AbstractMessage.class) { secondArgument = SingerInfo.getDefaultInstance(); } - if (firstArgument != null && secondArgument!=null) { + if (firstArgument != null && secondArgument != null) { method.invoke(subject, firstArgument, secondArgument); } else { fail("unknown parameter type"); 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 712bfc888ac..a6d322071b7 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 @@ -43,73 +43,74 @@ */ public class RandomResultSetGenerator { private static Type[] generateTypes(Dialect dialect) { - List types = new ArrayList(Arrays.asList( - Type.newBuilder().setCode(TypeCode.BOOL).build(), - Type.newBuilder().setCode(TypeCode.INT64).build(), - Type.newBuilder().setCode(TypeCode.FLOAT64).build(), - dialect == Dialect.POSTGRESQL - ? Type.newBuilder() - .setCode(TypeCode.NUMERIC) - .setTypeAnnotation(TypeAnnotationCode.PG_NUMERIC) - .build() - : Type.newBuilder().setCode(TypeCode.NUMERIC).build(), - Type.newBuilder().setCode(TypeCode.STRING).build(), - dialect == Dialect.POSTGRESQL - ? Type.newBuilder() - .setCode(TypeCode.JSON) - .setTypeAnnotation(TypeAnnotationCode.PG_JSONB) - .build() - : Type.newBuilder().setCode(TypeCode.JSON).build(), - Type.newBuilder().setCode(TypeCode.BYTES).build(), - Type.newBuilder().setCode(TypeCode.DATE).build(), - Type.newBuilder().setCode(TypeCode.TIMESTAMP).build(), - Type.newBuilder() - .setCode(TypeCode.ARRAY) - .setArrayElementType(Type.newBuilder().setCode(TypeCode.BOOL)) - .build(), - Type.newBuilder() - .setCode(TypeCode.ARRAY) - .setArrayElementType(Type.newBuilder().setCode(TypeCode.INT64)) - .build(), - Type.newBuilder() - .setCode(TypeCode.ARRAY) - .setArrayElementType(Type.newBuilder().setCode(TypeCode.FLOAT64)) - .build(), - Type.newBuilder() - .setCode(TypeCode.ARRAY) - .setArrayElementType( - dialect == Dialect.POSTGRESQL - ? Type.newBuilder() - .setCode(TypeCode.NUMERIC) - .setTypeAnnotation(TypeAnnotationCode.PG_NUMERIC) - : Type.newBuilder().setCode(TypeCode.NUMERIC)) - .build(), - Type.newBuilder() - .setCode(TypeCode.ARRAY) - .setArrayElementType(Type.newBuilder().setCode(TypeCode.STRING)) - .build(), - Type.newBuilder() - .setCode(TypeCode.ARRAY) - .setArrayElementType( - dialect == Dialect.POSTGRESQL - ? Type.newBuilder() - .setCode(TypeCode.JSON) - .setTypeAnnotation(TypeAnnotationCode.PG_JSONB) - : Type.newBuilder().setCode(TypeCode.JSON)) - .build(), - Type.newBuilder() - .setCode(TypeCode.ARRAY) - .setArrayElementType(Type.newBuilder().setCode(TypeCode.BYTES)) - .build(), - Type.newBuilder() - .setCode(TypeCode.ARRAY) - .setArrayElementType(Type.newBuilder().setCode(TypeCode.DATE)) - .build(), - Type.newBuilder() - .setCode(TypeCode.ARRAY) - .setArrayElementType(Type.newBuilder().setCode(TypeCode.TIMESTAMP)) - .build() - )); + List types = + new ArrayList( + Arrays.asList( + Type.newBuilder().setCode(TypeCode.BOOL).build(), + Type.newBuilder().setCode(TypeCode.INT64).build(), + Type.newBuilder().setCode(TypeCode.FLOAT64).build(), + dialect == Dialect.POSTGRESQL + ? Type.newBuilder() + .setCode(TypeCode.NUMERIC) + .setTypeAnnotation(TypeAnnotationCode.PG_NUMERIC) + .build() + : Type.newBuilder().setCode(TypeCode.NUMERIC).build(), + Type.newBuilder().setCode(TypeCode.STRING).build(), + dialect == Dialect.POSTGRESQL + ? Type.newBuilder() + .setCode(TypeCode.JSON) + .setTypeAnnotation(TypeAnnotationCode.PG_JSONB) + .build() + : Type.newBuilder().setCode(TypeCode.JSON).build(), + Type.newBuilder().setCode(TypeCode.BYTES).build(), + Type.newBuilder().setCode(TypeCode.DATE).build(), + Type.newBuilder().setCode(TypeCode.TIMESTAMP).build(), + Type.newBuilder() + .setCode(TypeCode.ARRAY) + .setArrayElementType(Type.newBuilder().setCode(TypeCode.BOOL)) + .build(), + Type.newBuilder() + .setCode(TypeCode.ARRAY) + .setArrayElementType(Type.newBuilder().setCode(TypeCode.INT64)) + .build(), + Type.newBuilder() + .setCode(TypeCode.ARRAY) + .setArrayElementType(Type.newBuilder().setCode(TypeCode.FLOAT64)) + .build(), + Type.newBuilder() + .setCode(TypeCode.ARRAY) + .setArrayElementType( + dialect == Dialect.POSTGRESQL + ? Type.newBuilder() + .setCode(TypeCode.NUMERIC) + .setTypeAnnotation(TypeAnnotationCode.PG_NUMERIC) + : Type.newBuilder().setCode(TypeCode.NUMERIC)) + .build(), + Type.newBuilder() + .setCode(TypeCode.ARRAY) + .setArrayElementType(Type.newBuilder().setCode(TypeCode.STRING)) + .build(), + Type.newBuilder() + .setCode(TypeCode.ARRAY) + .setArrayElementType( + dialect == Dialect.POSTGRESQL + ? Type.newBuilder() + .setCode(TypeCode.JSON) + .setTypeAnnotation(TypeAnnotationCode.PG_JSONB) + : Type.newBuilder().setCode(TypeCode.JSON)) + .build(), + Type.newBuilder() + .setCode(TypeCode.ARRAY) + .setArrayElementType(Type.newBuilder().setCode(TypeCode.BYTES)) + .build(), + Type.newBuilder() + .setCode(TypeCode.ARRAY) + .setArrayElementType(Type.newBuilder().setCode(TypeCode.DATE)) + .build(), + Type.newBuilder() + .setCode(TypeCode.ARRAY) + .setArrayElementType(Type.newBuilder().setCode(TypeCode.TIMESTAMP)) + .build())); if (dialect == Dialect.GOOGLE_STANDARD_SQL) { types.add(Type.newBuilder().setCode(TypeCode.PROTO).setProtoTypeFqn("testProto").build()); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSetTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSetTest.java index 09b6f160941..3d4d5e87ad2 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSetTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSetTest.java @@ -189,20 +189,19 @@ private void callMethods( if (firstParameterType == String.class) { firstArgument = "test"; - } - else if (firstParameterType == int.class) { + } else if (firstParameterType == int.class) { firstArgument = 0; } if (secondParameterType == Function.class) { - Function lambdaFunction = (val) -> Genre.forNumber( - val.intValue()); + Function lambdaFunction = + (val) -> Genre.forNumber(val.intValue()); secondArgument = lambdaFunction; } else if (secondParameterType == AbstractMessage.class) { secondArgument = SingerInfo.getDefaultInstance(); } - if (firstArgument != null && secondArgument!=null) { + if (firstArgument != null && secondArgument != null) { method.invoke(subject, firstArgument, secondArgument); } else { fail("unknown parameter type"); diff --git a/google-cloud-spanner/src/test/resources/com/google/cloud/spanner/singer.proto b/google-cloud-spanner/src/test/resources/com/google/cloud/spanner/singer.proto index 1e071e629f8..72e5c67b2cc 100644 --- a/google-cloud-spanner/src/test/resources/com/google/cloud/spanner/singer.proto +++ b/google-cloud-spanner/src/test/resources/com/google/cloud/spanner/singer.proto @@ -1,6 +1,6 @@ syntax = "proto2"; -package com.singer; +package spanner.examples.music; option java_package = "com.google.cloud.spanner"; option java_outer_classname = "SingerProto"; From be26f871925037509698503590ab4f3e519bee2d Mon Sep 17 00:00:00 2001 From: gauravpurohit Date: Wed, 2 Nov 2022 07:37:58 +0000 Subject: [PATCH 12/21] feat: Adding bytes/message & int64/enum compatability in Value --- .../java/com/google/cloud/spanner/Value.java | 36 ++++++++++++------- 1 file changed, 23 insertions(+), 13 deletions(-) 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 db5c6dc8e4b..a991bfada8d 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 @@ -951,17 +951,6 @@ public String getPgJsonb() { throw defaultGetter(Type.pgJsonb()); } - @Override - public T getProtoMessage(T m) { - throw defaultGetter(Type.proto("")); - } - - @Override - public T getProtoEnum( - Function method) { - throw defaultGetter(Type.protoEnum("")); - } - @Override public ByteArray getBytes() { throw defaultGetter(Type.bytes()); @@ -1234,6 +1223,15 @@ public long getInt64() { return value; } + @Override + public T getProtoEnum( + Function method) { + Preconditions.checkNotNull( + method, "Lambda method shouldn't be null, use forNumber from generated enum class."); + checkNotNull(); + return (T) method.apply((int) value); + } + @Override com.google.protobuf.Value valueToProto() { return com.google.protobuf.Value.newBuilder().setStringValue(Long.toString(value)).build(); @@ -1421,6 +1419,18 @@ public ByteArray getBytes() { return value; } + @Override + public T getProtoMessage(T m) { + Preconditions.checkNotNull( + m, "Proto Message shouldn't be null, default instance from generated class can be used."); + checkNotNull(); + try { + return (T) m.toBuilder().mergeFrom(value.toByteArray()).build(); + } catch (InvalidProtocolBufferException e) { + throw SpannerExceptionFactory.asSpannerException(e); + } + } + @Override com.google.protobuf.Value valueToProto() { return com.google.protobuf.Value.newBuilder().setStringValue(value.toBase64()).build(); @@ -1484,9 +1494,9 @@ public long getInt64() { public T getProtoEnum( Function method) { Preconditions.checkNotNull( - method, "Lambda method shouldn't be null, use forNumber from autogenerated enum class."); + method, "Lambda method shouldn't be null, use forNumber from generated enum class."); checkNotNull(); - return (T) method.apply((int) value.intValue()); + return (T) method.apply(value.intValue()); } @Override From e4eb26c22ca757c35b3f4c6ddaac2eb99ed4e8ca Mon Sep 17 00:00:00 2001 From: gauravpurohit Date: Wed, 9 Nov 2022 07:16:27 +0000 Subject: [PATCH 13/21] refactor: Minor refactoring --- .../main/java/com/google/cloud/spanner/AbstractResultSet.java | 2 +- .../src/main/java/com/google/cloud/spanner/Value.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) 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 84e751cf5a3..114c42624f4 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 @@ -695,7 +695,7 @@ protected boolean getBooleanInternal(int columnIndex) { @Override protected long getLongInternal(int columnIndex) { - return (long) rowData.get(columnIndex); + return (Long) rowData.get(columnIndex); } @Override diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Value.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Value.java index a991bfada8d..7decf151b9b 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 @@ -229,7 +229,7 @@ public static Value pgJsonb(@Nullable String v) { * @param v Not null Proto message. */ public static Value protoMessage(AbstractMessage v) { - Preconditions.checkNotNull(v, "Use protoMessage(ByteArray, String) for null values."); + Preconditions.checkNotNull(v, "Use protoMessage(ByteArray, Descriptor) for null values."); return protoMessage( ByteArray.copyFrom(v.toByteArray()), v.getDescriptorForType().getFullName()); } @@ -262,7 +262,7 @@ public static Value protoMessage(@Nullable ByteArray v, Descriptor descriptor) { * @param v Proto Enum, which may be null. */ public static Value protoEnum(ProtocolMessageEnum v) { - Preconditions.checkNotNull(v, "Use protoEnum(long, protoTypFqn) for null values."); + Preconditions.checkNotNull(v, "Use protoEnum(Long, Descriptor) for null values."); return protoEnum(v.getNumber(), v.getDescriptorForType().getFullName()); } From f5af48ea8254ecde7e275e68bd0ea7f05b0fa776 Mon Sep 17 00:00:00 2001 From: gauravpurohit Date: Tue, 8 Nov 2022 10:56:03 +0000 Subject: [PATCH 14/21] feat: Adding Proto Array Implementation --- .../clirr-ignored-differences.xml | 20 ++ .../cloud/spanner/AbstractResultSet.java | 71 ++++++- .../cloud/spanner/AbstractStructReader.java | 65 +++++- .../cloud/spanner/ForwardingStructReader.java | 26 +++ .../com/google/cloud/spanner/ResultSets.java | 22 ++ .../java/com/google/cloud/spanner/Struct.java | 14 ++ .../google/cloud/spanner/StructReader.java | 54 +++++ .../java/com/google/cloud/spanner/Value.java | 191 ++++++++++++++++++ .../com/google/cloud/spanner/ValueBinder.java | 21 ++ .../spanner/connection/ChecksumResultSet.java | 2 + .../connection/DirectExecuteResultSet.java | 26 +++ .../ReplaceableForwardingResultSet.java | 26 +++ 12 files changed, 533 insertions(+), 5 deletions(-) diff --git a/google-cloud-spanner/clirr-ignored-differences.xml b/google-cloud-spanner/clirr-ignored-differences.xml index 26cc43b7d9c..bd780b75532 100644 --- a/google-cloud-spanner/clirr-ignored-differences.xml +++ b/google-cloud-spanner/clirr-ignored-differences.xml @@ -212,6 +212,26 @@ com/google/cloud/spanner/StructReader com.google.protobuf.AbstractMessage getProtoMessage(java.lang.String, com.google.protobuf.AbstractMessage) + + 7012 + com/google/cloud/spanner/StructReader + com.google.protobuf.ProtocolMessageEnum getProtoEnumList(int, java.util.function.Function) + + + 7012 + com/google/cloud/spanner/StructReader + com.google.protobuf.ProtocolMessageEnum getProtoEnumList(java.lang.String, java.util.function.Function) + + + 7012 + com/google/cloud/spanner/StructReader + com.google.protobuf.AbstractMessage getProtoMessageList(int, com.google.protobuf.AbstractMessage) + + + 7012 + com/google/cloud/spanner/StructReader + com.google.protobuf.AbstractMessage getProtoMessageList(java.lang.String, com.google.protobuf.AbstractMessage) + 7012 com/google/cloud/spanner/BatchClient 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 114c42624f4..61a6c51241a 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 @@ -559,6 +559,7 @@ static Object decodeArrayValue(Type elementType, ListValue listValue) { listValue.getValuesList(), input -> input.getKindCase() == KindCase.NULL_VALUE ? null : input.getBoolValue()); case INT64: + case ENUM: // For int64/float64 types, use custom containers. These avoid wrapper object // creation for non-null arrays. return new Int64Array(listValue); @@ -586,6 +587,7 @@ static Object decodeArrayValue(Type elementType, ListValue listValue) { input.getKindCase() == KindCase.NULL_VALUE ? null : input.getStringValue()) .collect(Collectors.toList()); case BYTES: + case PROTO: { // Materialize list: element conversion is expensive and should happen only once. ArrayList list = new ArrayList<>(listValue.getValuesCount()); @@ -794,6 +796,12 @@ protected Value getValueInternal(int columnIndex) { return Value.pgJsonbArray(isNull ? null : getPgJsonbListInternal(columnIndex)); case BYTES: return Value.bytesArray(isNull ? null : getBytesListInternal(columnIndex)); + case PROTO: + return Value.protoMessageArray( + isNull ? null : getBytesListInternal(columnIndex), elementType.getProtoTypeFqn()); + case ENUM: + return Value.protoEnumArray( + isNull ? null : getLongListInternal(columnIndex), elementType.getProtoTypeFqn()); case TIMESTAMP: return Value.timestampArray(isNull ? null : getTimestampListInternal(columnIndex)); case DATE: @@ -842,7 +850,12 @@ protected long[] getLongArrayInternal(int columnIndex) { @Override protected Int64Array getLongListInternal(int columnIndex) { - return (Int64Array) rowData.get(columnIndex); + try { + return (Int64Array) rowData.get(columnIndex); + } catch (Exception e) { + System.out.println(e); + } + return null; } @Override @@ -873,6 +886,51 @@ protected List getJsonListInternal(int columnIndex) { return Collections.unmodifiableList((List) rowData.get(columnIndex)); } + @Override + @SuppressWarnings("unchecked") // We know ARRAY produces a List. + protected List getProtoMessageListInternal( + int columnIndex, T m) { + Preconditions.checkNotNull( + m, "Proto Message shouldn't be null, default instance from generated class can be used."); + + List bytesArray = (List) rowData.get(columnIndex); + + try { + List protoMessagesList = new ArrayList<>(); + for (ByteArray protoMessageBytes : bytesArray) { + if (protoMessageBytes == null) { + protoMessagesList.add(null); + } else { + protoMessagesList.add( + (T) m.toBuilder().mergeFrom(protoMessageBytes.toByteArray()).build()); + } + } + return protoMessagesList; + } catch (InvalidProtocolBufferException e) { + throw SpannerExceptionFactory.asSpannerException(e); + } + } + + @Override + @SuppressWarnings("unchecked") // We know ARRAY produces a List. + protected List getProtoEnumListInternal( + int columnIndex, Function method) { + Preconditions.checkNotNull( + method, "Lambda method shouldn't be null, use forNumber from autogenerated enum class."); + + List enumIntArray = (List) rowData.get(columnIndex); + List protoEnumList = new ArrayList<>(); + for (Long enumIntValue : enumIntArray) { + if (enumIntValue == null) { + protoEnumList.add(null); + } else { + protoEnumList.add((T) method.apply(enumIntValue.intValue())); + } + } + + return protoEnumList; + } + @Override @SuppressWarnings("unchecked") // We know ARRAY produces a List. protected List getPgJsonbListInternal(int columnIndex) { @@ -1501,6 +1559,17 @@ protected List getBytesListInternal(int columnIndex) { return currRow().getBytesListInternal(columnIndex); } + @Override + protected List getProtoMessageListInternal(int columnIndex, T m) { + return currRow().getProtoMessageListInternal(columnIndex, m); + } + + @Override + protected List getProtoEnumListInternal( + int columnIndex, Function method) { + return currRow().getProtoEnumListInternal(columnIndex, method); + } + @Override protected List getTimestampListInternal(int columnIndex) { return currRow().getTimestampListInternal(columnIndex); 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 3a28cae831c..86c09447abd 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 @@ -26,6 +26,7 @@ import com.google.protobuf.ProtocolMessageEnum; import java.math.BigDecimal; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.function.Function; @@ -71,6 +72,15 @@ protected T getProtoEnumInternal( throw new UnsupportedOperationException("Not implemented"); } + protected List getProtoMessageListInternal(int columnIndex, T m) { + throw new UnsupportedOperationException("Not implemented"); + } + + protected List getProtoEnumListInternal( + int columnIndex, Function method) { + throw new UnsupportedOperationException("Not implemented"); + } + protected Value getValueInternal(int columnIndex) { throw new UnsupportedOperationException("method should be overwritten"); } @@ -343,14 +353,16 @@ public long[] getLongArray(String columnName) { @Override public List getLongList(int columnIndex) { - checkNonNullOfType(columnIndex, Type.array(Type.int64()), columnIndex); + checkNonNullOfCodes(columnIndex, Collections.singletonList(Code.ARRAY), columnIndex); + checkArrayElementType(columnIndex, Arrays.asList(Code.ENUM, Code.INT64), columnIndex); return getLongListInternal(columnIndex); } @Override public List getLongList(String columnName) { int columnIndex = getColumnIndex(columnName); - checkNonNullOfType(columnIndex, Type.array(Type.int64()), columnName); + checkNonNullOfCodes(columnIndex, Collections.singletonList(Code.ARRAY), columnIndex); + checkArrayElementType(columnIndex, Arrays.asList(Code.ENUM, Code.INT64), columnIndex); return getLongListInternal(columnIndex); } @@ -442,17 +454,51 @@ public List getPgJsonbList(String columnName) { @Override public List getBytesList(int columnIndex) { - checkNonNullOfType(columnIndex, Type.array(Type.bytes()), columnIndex); + checkNonNullOfCodes(columnIndex, Collections.singletonList(Code.ARRAY), columnIndex); + checkArrayElementType(columnIndex, Arrays.asList(Code.PROTO, Code.BYTES), columnIndex); return getBytesListInternal(columnIndex); } @Override public List getBytesList(String columnName) { int columnIndex = getColumnIndex(columnName); - checkNonNullOfType(columnIndex, Type.array(Type.bytes()), columnName); + checkNonNullOfCodes(columnIndex, Collections.singletonList(Code.ARRAY), columnIndex); + checkArrayElementType(columnIndex, Arrays.asList(Code.PROTO, Code.BYTES), columnIndex); return getBytesListInternal(columnIndex); } + @Override + public List getProtoMessageList(int columnIndex, T m) { + checkNonNullOfCodes(columnIndex, Collections.singletonList(Code.ARRAY), columnIndex); + checkArrayElementType(columnIndex, Arrays.asList(Code.PROTO, Code.BYTES), columnIndex); + return getProtoMessageListInternal(columnIndex, m); + } + + @Override + public List getProtoMessageList(String columnName, T m) { + int columnIndex = getColumnIndex(columnName); + checkNonNullOfCodes(columnIndex, Collections.singletonList(Code.ARRAY), columnName); + checkArrayElementType(columnIndex, Arrays.asList(Code.PROTO, Code.BYTES), columnName); + return getProtoMessageListInternal(columnIndex, m); + } + + @Override + public List getProtoEnumList( + int columnIndex, Function method) { + checkNonNullOfCodes(columnIndex, Collections.singletonList(Code.ARRAY), columnIndex); + checkArrayElementType(columnIndex, Arrays.asList(Code.ENUM, Code.INT64), columnIndex); + return getProtoEnumListInternal(columnIndex, method); + } + + @Override + public List getProtoEnumList( + String columnName, Function method) { + int columnIndex = getColumnIndex(columnName); + checkNonNullOfCodes(columnIndex, Collections.singletonList(Code.ARRAY), columnName); + checkArrayElementType(columnIndex, Arrays.asList(Code.ENUM, Code.INT64), columnName); + return getProtoEnumListInternal(columnIndex, method); + } + @Override public List getTimestampList(int columnIndex) { checkNonNullOfType(columnIndex, Type.array(Type.timestamp()), columnIndex); @@ -531,6 +577,17 @@ private void checkNonNullOfCodes( checkNonNull(columnIndex, columnNameForError); } + private void checkArrayElementType( + int columnIndex, List expectedCodes, Object columnNameForError) { + Type arrayElementType = getColumnType(columnIndex).getArrayElementType(); + checkState( + expectedCodes.contains(arrayElementType.getCode()), + "Array element %s is not of correct type code: expected one of [%s] but was %s", + columnNameForError, + expectedCodes, + arrayElementType); + } + private void checkNonNullOfTypes( int columnIndex, List expectedTypes, diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ForwardingStructReader.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ForwardingStructReader.java index 7c7957623c4..7b06a8551bb 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ForwardingStructReader.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ForwardingStructReader.java @@ -373,6 +373,32 @@ public List getDateList(String columnName) { return delegate.get().getDateList(columnName); } + @Override + public List getProtoMessageList(int columnIndex, T m) { + checkValidState(); + return delegate.get().getProtoMessageList(columnIndex, m); + } + + @Override + public List getProtoMessageList(String columnName, T m) { + checkValidState(); + return delegate.get().getProtoMessageList(columnName, m); + } + + @Override + public List getProtoEnumList( + int columnIndex, Function method) { + checkValidState(); + return delegate.get().getProtoEnumList(columnIndex, method); + } + + @Override + public List getProtoEnumList( + String columnName, Function method) { + checkValidState(); + return delegate.get().getProtoEnumList(columnName, method); + } + @Override public List getStructList(int columnIndex) { checkValidState(); diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ResultSets.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ResultSets.java index 968b9643b52..a4015c62f0e 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ResultSets.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ResultSets.java @@ -458,6 +458,28 @@ public List getDateList(String columnName) { return getCurrentRowAsStruct().getDateList(columnName); } + @Override + public List getProtoMessageList(int columnIndex, T m) { + return getCurrentRowAsStruct().getProtoMessageList(columnIndex, m); + } + + @Override + public List getProtoMessageList(String columnName, T m) { + return getCurrentRowAsStruct().getProtoMessageList(columnName, m); + } + + @Override + public List getProtoEnumList( + int columnIndex, Function method) { + return getCurrentRowAsStruct().getProtoEnumList(columnIndex, method); + } + + @Override + public List getProtoEnumList( + String columnName, Function method) { + return getCurrentRowAsStruct().getProtoEnumList(columnName, method); + } + @Override public List getStructList(int columnIndex) { return getCurrentRowAsStruct().getStructList(columnIndex); diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Struct.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Struct.java index 8b7b8e329f7..240718cebb5 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Struct.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Struct.java @@ -301,6 +301,18 @@ protected List getTimestampListInternal(int columnIndex) { return values.get(columnIndex).getTimestampArray(); } + @Override + protected List getProtoMessageListInternal( + int columnIndex, T m) { + return values.get(columnIndex).getProtoMessageArray(m); + } + + @Override + protected List getProtoEnumListInternal( + int columnIndex, Function method) { + return values.get(columnIndex).getProtoEnumArray(method); + } + @Override protected List getDateListInternal(int columnIndex) { return values.get(columnIndex).getDateArray(); @@ -396,6 +408,7 @@ private Object getAsObject(int columnIndex) { case BOOL: return getBooleanListInternal(columnIndex); case INT64: + case ENUM: return getLongListInternal(columnIndex); case FLOAT64: return getDoubleListInternal(columnIndex); @@ -410,6 +423,7 @@ private Object getAsObject(int columnIndex) { case PG_JSONB: return getPgJsonbListInternal(columnIndex); case BYTES: + case PROTO: return getBytesListInternal(columnIndex); case TIMESTAMP: return getTimestampListInternal(columnIndex); diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/StructReader.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/StructReader.java index 5be40106954..b8f7c69bf88 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/StructReader.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/StructReader.java @@ -326,6 +326,60 @@ default List getPgJsonbList(String columnName) { throw new UnsupportedOperationException("method should be overwritten"); }; + /** + * To get the proto message of generic type {@code T} from Struct. + * + * @param columnIndex Index of the column. + * @param m Proto message object. @see getDefaultInstance() + * @return The value of a non-{@code NULL} column with type {@code + * Type.array(Type.proto(String))}. + */ + default List getProtoMessageList(int columnIndex, T m) { + throw new UnsupportedOperationException("method should be overwritten"); + } + + /** + * To get the proto message of type {@code T} from Struct. + * + * @param columnName Name of the column. + * @param m Proto message object. @see getDefaultInstance() + * @return The value of a non-{@code NULL} column with type {@code + * Type.array(Type.proto(String))}. + */ + default List getProtoMessageList(String columnName, T m) { + throw new UnsupportedOperationException("method should be overwritten"); + } + + /** + * To get the proto enum of type {@code T} from Struct. + * + * @param columnIndex Index of the column. + * @param method Lambda method which takes a primitive int and return Enum. @see forNumber + * @return The value of a non-{@code NULL} column with type {@code + * Type.array(Type.protoEnum(String))}. + */ + default List getProtoEnumList( + int columnIndex, Function method) { + throw new UnsupportedOperationException("method should be overwritten"); + } + + /** + * To get the proto enum list of type {@code T} from Struct. + * + * @param columnName Name of the column. + * @param method Lambda method which takes a primitive int and return Enum. @see forNumber + * @return The value of a non-{@code NULL} column with type {@code + * Type.array(Type.protoEnum(String))}. + */ + default List getProtoEnumList( + String columnName, Function method) { + throw new UnsupportedOperationException("method should be overwritten"); + } + /** Returns the value of a non-{@code NULL} column with type {@code Type.array(Type.bytes())}. */ List getBytesList(int columnIndex); 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 7decf151b9b..0241aebdd9b 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 @@ -502,6 +502,53 @@ public static Value pgJsonbArray(@Nullable Iterable v) { return new PgJsonbArrayImpl(v == null, v == null ? null : immutableCopyOf(v)); } + public static Value protoMessageArray( + @Nullable Iterable v, Descriptor descriptor) { + if (v == null) { + return new ProtoMessageArrayImpl(true, null, descriptor.getFullName()); + } + + List serializedArray = new ArrayList<>(); + v.forEach( + (message) -> { + if (message != null) { + serializedArray.add(ByteArray.copyFrom(message.toByteArray())); + } else { + serializedArray.add(null); + } + }); + + return new ProtoMessageArrayImpl(false, serializedArray, descriptor.getFullName()); + } + + public static Value protoMessageArray(@Nullable Iterable v, String protoTypeFqn) { + return new ProtoMessageArrayImpl( + v == null, v != null ? immutableCopyOf(v) : null, protoTypeFqn); + } + + public static Value protoEnumArray( + @Nullable Iterable v, EnumDescriptor descriptor) { + if (v == null) { + return new ProtoEnumArrayImpl(true, null, descriptor.getFullName()); + } + + List enumConstValues = new ArrayList<>(); + v.forEach( + (protoEnum) -> { + if (protoEnum != null) { + enumConstValues.add((long) protoEnum.getNumber()); + } else { + enumConstValues.add(null); + } + }); + + return new ProtoEnumArrayImpl(false, enumConstValues, descriptor.getFullName()); + } + + public static Value protoEnumArray(@Nullable Iterable v, String protoTypeFqn) { + return new ProtoEnumArrayImpl(v == null, v != null ? immutableCopyOf(v) : null, protoTypeFqn); + } + /** * Returns an {@code ARRAY} value. * @@ -733,6 +780,27 @@ public List getPgJsonbArray() { throw new UnsupportedOperationException("Not implemented"); } + /** + * Returns the value of an {@code ARRAY}-typed instance. While the returned list itself + * will never be {@code null}, elements of that list may be null. + * + * @throws IllegalStateException if {@code isNull()} or the value is not of the expected type + */ + public List getProtoMessageArray(T m) { + throw new UnsupportedOperationException("Not implemented"); + } + + /** + * Returns the value of an {@code ARRAY}-typed instance. While the returned list itself will + * never be {@code null}, elements of that list may be null. + * + * @throws IllegalStateException if {@code isNull()} or the value is not of the expected type + */ + public List getProtoEnumArray( + Function method) { + throw new UnsupportedOperationException("Not implemented"); + } + /** * Returns the value of an {@code ARRAY}-typed instance. While the returned list itself * will never be {@code null}, elements of that list may be null. @@ -1762,6 +1830,24 @@ public List getInt64Array() { return getArray(); } + @Override + public List getProtoEnumArray( + Function method) { + Preconditions.checkNotNull( + method, "Lambda method shouldn't be null, use forNumber from generated enum class."); + checkNotNull(); + + List protoEnumList = new ArrayList<>(); + for (Long enumIntValue : values) { + if (enumIntValue == null) { + protoEnumList.add(null); + } else { + protoEnumList.add((T) method.apply(enumIntValue.intValue())); + } + } + return protoEnumList; + } + @Override boolean valueEquals(Value v) { Int64ArrayImpl that = (Int64ArrayImpl) v; @@ -1949,6 +2035,27 @@ public List getBytesArray() { return value; } + @Override + public List getProtoMessageArray(T m) { + Preconditions.checkNotNull( + m, "Proto Message shouldn't be null, default instance from generated class can be used."); + checkNotNull(); + try { + List protoMessagesList = new ArrayList<>(); + for (ByteArray protoMessageBytes : value) { + if (protoMessageBytes == null) { + protoMessagesList.add(null); + } else { + protoMessagesList.add( + (T) m.toBuilder().mergeFrom(protoMessageBytes.toByteArray()).build()); + } + } + return protoMessagesList; + } catch (InvalidProtocolBufferException e) { + throw SpannerExceptionFactory.asSpannerException(e); + } + } + @Override String elementToString(ByteArray element) { return element.toBase64(); @@ -1978,6 +2085,90 @@ void appendElement(StringBuilder b, Timestamp element) { } } + private static class ProtoMessageArrayImpl extends AbstractArrayValue { + + private ProtoMessageArrayImpl( + boolean isNull, @Nullable List values, String protoTypeFqn) { + super(isNull, Type.proto(protoTypeFqn), values); + } + + @Override + public List getBytesArray() { + return value; + } + + @Override + public List getProtoMessageArray(T m) { + Preconditions.checkNotNull( + m, "Proto Message shouldn't be null, default instance from generated class can be used."); + checkNotNull(); + try { + List protoMessagesList = new ArrayList<>(); + for (ByteArray protoMessageBytes : value) { + if (protoMessageBytes == null) { + protoMessagesList.add(null); + } else { + protoMessagesList.add( + (T) m.toBuilder().mergeFrom(protoMessageBytes.toByteArray()).build()); + } + } + return protoMessagesList; + } catch (InvalidProtocolBufferException e) { + throw SpannerExceptionFactory.asSpannerException(e); + } + } + + @Override + String elementToString(ByteArray element) { + return element.toBase64(); + } + + @Override + void appendElement(StringBuilder b, ByteArray element) { + b.append(element.toString()); + } + } + + private static class ProtoEnumArrayImpl extends AbstractArrayValue { + + private ProtoEnumArrayImpl(boolean isNull, @Nullable List values, String protoTypeFqn) { + super(isNull, Type.protoEnum(protoTypeFqn), values); + } + + @Override + public List getInt64Array() { + return value; + } + + @Override + public List getProtoEnumArray( + Function method) { + Preconditions.checkNotNull( + method, "Lambda method shouldn't be null, use forNumber from generated enum class."); + checkNotNull(); + + List protoEnumList = new ArrayList<>(); + for (Long enumIntValue : value) { + if (enumIntValue == null) { + protoEnumList.add(null); + } else { + protoEnumList.add((T) method.apply(enumIntValue.intValue())); + } + } + return protoEnumList; + } + + @Override + String elementToString(Long element) { + return Long.toString(element); + } + + @Override + void appendElement(StringBuilder b, Long element) { + b.append(element); + } + } + private static class DateArrayImpl extends AbstractArrayValue { private DateArrayImpl(boolean isNull, @Nullable List values) { 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 1d9e81e0bc5..cdb1fd10b51 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 @@ -242,6 +242,27 @@ public R toTimestampArray(@Nullable Iterable values) { return handle(Value.timestampArray(values)); } + /** Binds to {@code Value.protoMessageArray(values, descriptor)} */ + public R toProtoMessageArray(@Nullable Iterable values, Descriptor descriptor) { + return handle(Value.protoMessageArray(values, descriptor)); + } + + /** Binds to {@code Value.protoMessageArray(values, protoTypeFq)} */ + public R toProtoMessageArray(@Nullable Iterable values, String protoTypeFq) { + return handle(Value.protoMessageArray(values, protoTypeFq)); + } + + /** Binds to {@code Value.protoEnumArray(values, descriptor)} */ + public R toProtoEnumArray( + @Nullable Iterable values, EnumDescriptor descriptor) { + return handle(Value.protoEnumArray(values, descriptor)); + } + + /** Binds to {@code Value.protoEnumArray(values, protoTypeFq)} */ + public R toProtoEnumArray(@Nullable Iterable values, String protoTypeFq) { + return handle(Value.protoEnumArray(values, protoTypeFq)); + } + /** Binds to {@code Value.dateArray(values)} */ public R toDateArray(@Nullable Iterable values) { return handle(Value.dateArray(values)); diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ChecksumResultSet.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ChecksumResultSet.java index 9af83df8ad4..24389546669 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ChecksumResultSet.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ChecksumResultSet.java @@ -276,6 +276,7 @@ private void funnelArray( } break; case BYTES: + case PROTO: into.putInt(row.getBytesList(columnIndex).size()); for (ByteArray value : row.getBytesList(columnIndex)) { funnelValue(Code.BYTES, value, into); @@ -306,6 +307,7 @@ private void funnelArray( } break; case INT64: + case ENUM: into.putInt(row.getLongList(columnIndex).size()); for (Long value : row.getLongList(columnIndex)) { funnelValue(Code.INT64, value, into); diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DirectExecuteResultSet.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DirectExecuteResultSet.java index def095444d2..ad06bcb07fd 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DirectExecuteResultSet.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DirectExecuteResultSet.java @@ -422,6 +422,32 @@ public List getDateList(String columnName) { return delegate.getDateList(columnName); } + @Override + public List getProtoMessageList(int columnIndex, T m) { + Preconditions.checkState(nextCalledByClient, MISSING_NEXT_CALL); + return delegate.getProtoMessageList(columnIndex, m); + } + + @Override + public List getProtoMessageList(String columnName, T m) { + Preconditions.checkState(nextCalledByClient, MISSING_NEXT_CALL); + return delegate.getProtoMessageList(columnName, m); + } + + @Override + public List getProtoEnumList( + int columnIndex, Function method) { + Preconditions.checkState(nextCalledByClient, MISSING_NEXT_CALL); + return delegate.getProtoEnumList(columnIndex, method); + } + + @Override + public List getProtoEnumList( + String columnName, Function method) { + Preconditions.checkState(nextCalledByClient, MISSING_NEXT_CALL); + return delegate.getProtoEnumList(columnName, method); + } + @Override public List getStructList(int columnIndex) { Preconditions.checkState(nextCalledByClient, MISSING_NEXT_CALL); diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSet.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSet.java index d0646a5ffe5..0c00a18b2f8 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSet.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSet.java @@ -427,6 +427,32 @@ public List getDateList(String columnName) { return delegate.getDateList(columnName); } + @Override + public List getProtoMessageList(int columnIndex, T m) { + checkClosed(); + return delegate.getProtoMessageList(columnIndex, m); + } + + @Override + public List getProtoMessageList(String columnName, T m) { + checkClosed(); + return delegate.getProtoMessageList(columnName, m); + } + + @Override + public List getProtoEnumList( + int columnIndex, Function method) { + checkClosed(); + return delegate.getProtoEnumList(columnIndex, method); + } + + @Override + public List getProtoEnumList( + String columnName, Function method) { + checkClosed(); + return delegate.getProtoEnumList(columnName, method); + } + @Override public List getStructList(int columnIndex) { checkClosed(); From 65424da60dc7f59ff577b8942b29bbf37bb09c6d Mon Sep 17 00:00:00 2001 From: gauravpurohit Date: Thu, 10 Nov 2022 09:51:28 +0000 Subject: [PATCH 15/21] test: Implementing unit tests for array of protos and enums --- .../cloud/spanner/AbstractStructReader.java | 12 ++-- .../AbstractStructReaderTypesTest.java | 23 +++--- .../cloud/spanner/GrpcResultSetTest.java | 71 +++++++++++++++++++ .../google/cloud/spanner/MutationTest.java | 18 +++++ .../google/cloud/spanner/ResultSetsTest.java | 50 ++++++++++++- .../com/google/cloud/spanner/TypeTest.java | 34 +++++++++ .../google/cloud/spanner/ValueBinderTest.java | 4 +- .../com/google/cloud/spanner/ValueTest.java | 64 +++++++++++++++++ .../connection/ChecksumResultSetTest.java | 29 +++++++- .../DirectExecuteResultSetTest.java | 8 +++ .../connection/RandomResultSetGenerator.java | 18 +++++ .../ReplaceableForwardingResultSetTest.java | 8 +++ 12 files changed, 317 insertions(+), 22 deletions(-) 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 86c09447abd..5e40080e60a 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 @@ -361,8 +361,8 @@ public List getLongList(int columnIndex) { @Override public List getLongList(String columnName) { int columnIndex = getColumnIndex(columnName); - checkNonNullOfCodes(columnIndex, Collections.singletonList(Code.ARRAY), columnIndex); - checkArrayElementType(columnIndex, Arrays.asList(Code.ENUM, Code.INT64), columnIndex); + checkNonNullOfCodes(columnIndex, Collections.singletonList(Code.ARRAY), columnName); + checkArrayElementType(columnIndex, Arrays.asList(Code.ENUM, Code.INT64), columnName); return getLongListInternal(columnIndex); } @@ -462,8 +462,8 @@ public List getBytesList(int columnIndex) { @Override public List getBytesList(String columnName) { int columnIndex = getColumnIndex(columnName); - checkNonNullOfCodes(columnIndex, Collections.singletonList(Code.ARRAY), columnIndex); - checkArrayElementType(columnIndex, Arrays.asList(Code.PROTO, Code.BYTES), columnIndex); + checkNonNullOfCodes(columnIndex, Collections.singletonList(Code.ARRAY), columnName); + checkArrayElementType(columnIndex, Arrays.asList(Code.PROTO, Code.BYTES), columnName); return getBytesListInternal(columnIndex); } @@ -582,10 +582,10 @@ private void checkArrayElementType( Type arrayElementType = getColumnType(columnIndex).getArrayElementType(); checkState( expectedCodes.contains(arrayElementType.getCode()), - "Array element %s is not of correct type code: expected one of [%s] but was %s", + "Array element for Column %s is not of correct type code: expected one of [%s] but was %s", columnNameForError, expectedCodes, - arrayElementType); + Type.array(arrayElementType)); } private void checkNonNullOfTypes( diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AbstractStructReaderTypesTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AbstractStructReaderTypesTest.java index f6331ff7cab..7a749321605 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AbstractStructReaderTypesTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AbstractStructReaderTypesTest.java @@ -83,17 +83,6 @@ protected String getPgJsonbInternal(int columnIndex) { return null; } - @Override - public T getProtoEnum( - int columnIndex, Function method) { - return null; - } - - @Override - public T getProtoMessage(int columnIndex, T m) { - return null; - } - @Override protected ByteArray getBytesInternal(int columnIndex) { return null; @@ -185,6 +174,18 @@ protected List getTimestampListInternal(int columnIndex) { return null; } + @Override + protected List getProtoMessageListInternal( + int columnIndex, T m) { + return null; + } + + @Override + protected List getProtoEnumListInternal( + int columnIndex, Function method) { + return null; + } + @Override protected List getDateListInternal(int columnIndex) { return null; 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 2f5ce489830..dba2d2d1136 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 @@ -940,4 +940,75 @@ public void getPgJsonbList() { assertTrue(resultSet.next()); assertEquals(jsonList, resultSet.getPgJsonbList(0)); } + + @Test + public void getProtoMessageList() { + SingerInfo singerInfo1 = + SingerInfo.newBuilder() + .setSingerId(111) + .setNationality("COUNTRY1") + .setGenre(Genre.FOLK) + .build(); + SingerInfo singerInfo2 = SingerInfo.newBuilder().setSingerId(222).setGenre(Genre.JAZZ).build(); + String singerInfoFullName = SingerInfo.getDescriptor().getFullName(); + + consumer.onPartialResultSet( + PartialResultSet.newBuilder() + .setMetadata( + makeMetadata( + Type.struct( + Type.StructField.of("f", Type.array(Type.proto(singerInfoFullName)))))) + .addValues( + Value.protoMessageArray( + Arrays.asList(singerInfo1, singerInfo2), SingerInfo.getDescriptor()) + .toProto()) + .addValues( + Value.protoMessageArray( + Arrays.asList(singerInfo2, null, singerInfo1), SingerInfo.getDescriptor()) + .toProto()) + .addValues(Value.protoMessageArray(null, SingerInfo.getDescriptor()).toProto()) + .build()); + consumer.onCompleted(); + + assertTrue(resultSet.next()); + assertEquals( + Arrays.asList(singerInfo1, singerInfo2), + resultSet.getProtoMessageList(0, SingerInfo.getDefaultInstance())); + assertTrue(resultSet.next()); + assertEquals( + Arrays.asList(singerInfo2, null, singerInfo1), + resultSet.getProtoMessageList(0, SingerInfo.getDefaultInstance())); + assertTrue(resultSet.next()); + assertThrows( + NullPointerException.class, + () -> { + resultSet.getProtoMessageList(0, SingerInfo.getDefaultInstance()); + }); + } + + @Test + public void getProtoEnumList() { + String genreFullyQualifiedName = Genre.getDescriptor().getFullName(); + consumer.onPartialResultSet( + PartialResultSet.newBuilder() + .setMetadata( + makeMetadata( + Type.struct(Type.StructField.of("f", Type.protoEnum(genreFullyQualifiedName))))) + .addValues(Value.protoEnum(Genre.FOLK).toProto()) + .addValues(Value.protoEnum(Genre.JAZZ.getNumber(), genreFullyQualifiedName).toProto()) + .addValues(Value.protoEnum(null, genreFullyQualifiedName).toProto()) + .build()); + consumer.onCompleted(); + + assertTrue(resultSet.next()); + assertEquals(Genre.FOLK, resultSet.getProtoEnum(0, Genre::forNumber)); + assertTrue(resultSet.next()); + assertEquals(Genre.JAZZ, resultSet.getProtoEnum(0, Genre::forNumber)); + assertTrue(resultSet.next()); + assertThrows( + NullPointerException.class, + () -> { + resultSet.getProtoEnum(0, Genre::forNumber); + }); + } } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MutationTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MutationTest.java index afe01da6df2..f38b5e47b8d 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MutationTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MutationTest.java @@ -613,6 +613,24 @@ private Mutation.WriteBuilder appendAllTypes(Mutation.WriteBuilder builder) { .toJsonArray(null) .set("jsonArrValue") .to(Value.jsonArray(ImmutableList.of("{\"key\": \"value1\"}}", "{\"key\": \"value2\"}"))) + .set("protoMessageArr") + .toProtoMessageArray( + ImmutableList.of(SingerInfo.newBuilder().setSingerId(232).setGenre(Genre.POP).build()), + SingerInfo.getDescriptor()) + .set("protoMessageArrNull") + .toProtoMessageArray(null, SingerInfo.getDescriptor()) + .set("protoMessageArrValue") + .to( + Value.protoMessageArray( + ImmutableList.of( + SingerInfo.newBuilder().setSingerId(232).setGenre(Genre.POP).build()), + SingerInfo.getDescriptor())) + .set("protoEnumArr") + .toProtoEnumArray(ImmutableList.of(Genre.JAZZ), Genre.getDescriptor()) + .set("protoEnumArrNull") + .toProtoEnumArray(null, Genre.getDescriptor()) + .set("protoEnumArrValue") + .to(Value.protoEnumArray(ImmutableList.of(Genre.JAZZ), Genre.getDescriptor())) .set("pgJsonbArr") .toPgJsonbArray(ImmutableList.of("{\"key\": \"value1\"}}", "{\"key\": \"value2\"}")) .set("pgJsonbArrNull") diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ResultSetsTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ResultSetsTest.java index 13b7d9e2da3..8e1f257594b 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ResultSetsTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ResultSetsTest.java @@ -33,6 +33,7 @@ import com.google.common.primitives.Doubles; import com.google.common.primitives.Longs; import com.google.common.util.concurrent.MoreExecutors; +import com.google.protobuf.AbstractMessage; import com.google.protobuf.ProtocolMessageEnum; import java.math.BigDecimal; import java.util.Arrays; @@ -90,6 +91,10 @@ public void resultSetIteration() { }; String[] stringArray = {"abc", "def", "ghi"}; String[] jsonArray = {"{}", "{\"color\":\"red\",\"value\":\"#f00\"}", "[]"}; + AbstractMessage[] protoMessageArray = { + protoMessageVal, SingerInfo.newBuilder().setSingerId(1).build() + }; + ProtocolMessageEnum[] protoEnumArray = {protoEnumVal, Genre.JAZZ}; Type type = Type.struct( @@ -117,7 +122,12 @@ public void resultSetIteration() { Type.StructField.of("dateArray", Type.array(Type.date())), Type.StructField.of("stringArray", Type.array(Type.string())), Type.StructField.of("jsonArray", Type.array(Type.json())), - Type.StructField.of("pgJsonbArray", Type.array(Type.pgJsonb()))); + Type.StructField.of("pgJsonbArray", Type.array(Type.pgJsonb())), + Type.StructField.of( + "protoMessageArray", + Type.array(Type.proto(SingerInfo.getDescriptor().getFullName()))), + Type.StructField.of( + "protoEnumArray", Type.array(Type.protoEnum(Genre.getDescriptor().getFullName())))); Struct struct1 = Struct.newBuilder() .set("f1") @@ -166,6 +176,14 @@ public void resultSetIteration() { .to(Value.jsonArray(Arrays.asList(jsonArray))) .set("pgJsonbArray") .to(Value.pgJsonbArray(Arrays.asList(jsonArray))) + .set("protoMessageArray") + .to( + Value.protoMessageArray( + Arrays.asList(protoMessageArray), protoMessageVal.getDescriptorForType())) + .set("protoEnumArray") + .to( + Value.protoEnumArray( + Arrays.asList(protoEnumArray), protoEnumVal.getDescriptorForType())) .build(); Struct struct2 = Struct.newBuilder() @@ -215,6 +233,14 @@ public void resultSetIteration() { .to(Value.jsonArray(Arrays.asList(jsonArray))) .set("pgJsonbArray") .to(Value.pgJsonbArray(Arrays.asList(jsonArray))) + .set("protoMessageArray") + .to( + Value.protoMessageArray( + Arrays.asList(protoMessageArray), protoMessageVal.getDescriptorForType())) + .set("protoEnumArray") + .to( + Value.protoEnumArray( + Arrays.asList(protoEnumArray), protoEnumVal.getDescriptorForType())) .build(); ResultSet rs = ResultSets.forRows(type, Arrays.asList(struct1, struct2)); @@ -339,9 +365,29 @@ public void resultSetIteration() { assertThat(rs.getJsonList(columnIndex++)).isEqualTo(Arrays.asList(jsonArray)); assertThat(rs.getJsonList("jsonArray")).isEqualTo(Arrays.asList(jsonArray)); - assertEquals(Arrays.asList(jsonArray), rs.getPgJsonbList(columnIndex)); + assertEquals(Arrays.asList(jsonArray), rs.getPgJsonbList(columnIndex++)); assertEquals(Arrays.asList(jsonArray), rs.getPgJsonbList("pgJsonbArray")); + assertThat(rs.getProtoMessageList(columnIndex, SingerInfo.getDefaultInstance())) + .isEqualTo(Arrays.asList(protoMessageArray)); + assertThat(rs.getValue(columnIndex++)) + .isEqualTo( + Value.protoMessageArray(Arrays.asList(protoMessageArray), SingerInfo.getDescriptor())); + assertThat(rs.getProtoMessageList("protoMessageArray", SingerInfo.getDefaultInstance())) + .isEqualTo(Arrays.asList(protoMessageArray)); + assertThat(rs.getValue("protoMessageArray")) + .isEqualTo( + Value.protoMessageArray(Arrays.asList(protoMessageArray), SingerInfo.getDescriptor())); + + assertThat(rs.getProtoEnumList(columnIndex, Genre::forNumber)) + .isEqualTo(Arrays.asList(protoEnumArray)); + assertThat(rs.getValue(columnIndex)) + .isEqualTo(Value.protoEnumArray(Arrays.asList(protoEnumArray), Genre.getDescriptor())); + assertThat(rs.getProtoEnumList("protoEnumArray", Genre::forNumber)) + .isEqualTo(Arrays.asList(protoEnumArray)); + assertThat(rs.getValue("protoEnumArray")) + .isEqualTo(Value.protoEnumArray(Arrays.asList(protoEnumArray), Genre.getDescriptor())); + assertThat(rs.next()).isTrue(); assertThat(rs.getCurrentRowAsStruct()).isEqualTo(struct2); assertThat(rs.getString(0)).isEqualTo("y"); 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 308b45c2400..9b525f08e70 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 @@ -223,6 +223,7 @@ abstract static class ArrayTypeTester { private final TypeCode expectedElementTypeCode; private final TypeAnnotationCode expectedTypeAnnotationCode; private final boolean expectInterned; + private String protoTypeFqn = ""; ArrayTypeTester( Type.Code expectedElementCode, TypeCode expectedElementTypeCode, boolean expectInterned) { @@ -233,6 +234,19 @@ abstract static class ArrayTypeTester { expectInterned); } + ArrayTypeTester( + Type.Code expectedElementCode, + TypeCode expectedElementTypeCode, + String protoTypeFqn, + boolean expectInterned) { + this( + expectedElementCode, + expectedElementTypeCode, + TypeAnnotationCode.TYPE_ANNOTATION_CODE_UNSPECIFIED, + expectInterned); + this.protoTypeFqn = protoTypeFqn; + } + ArrayTypeTester( Type.Code expectedElementCode, TypeCode expectedElementTypeCode, @@ -382,6 +396,26 @@ Type newElementType() { }.test(); } + @Test + public void protoArray() { + new ArrayTypeTester(Type.Code.PROTO, TypeCode.PROTO, "com.google.temp", false) { + @Override + Type newElementType() { + return Type.proto("com.google.temp"); + } + }.test(); + } + + @Test + public void protoEnumArray() { + new ArrayTypeTester(Type.Code.ENUM, TypeCode.ENUM, "com.google.temp.enum", false) { + @Override + Type newElementType() { + return Type.protoEnum("com.google.temp.enum"); + } + }.test(); + } + @Test public void arrayOfArray() { new ArrayTypeTester(Type.Code.ARRAY, TypeCode.ARRAY, false /* not interned */) { 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 88c6f8cf02d..8962c0accfc 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 @@ -179,8 +179,8 @@ public void reflection() assertThat(binder.to(expected)).isEqualTo(lastReturnValue); assertThat(lastValue).isEqualTo(expected); } else if (binderMethod.getParameterTypes().length == 2 - && (method.getName().equalsIgnoreCase(PROTO_MESSAGE_METHOD_NAME) - || method.getName().equalsIgnoreCase(PROTO_ENUM_METHOD_NAME))) { + && (method.getName().contains(PROTO_MESSAGE_METHOD_NAME) + || method.getName().contains(PROTO_ENUM_METHOD_NAME))) { // Test unary null. Object firstArgument = null; if (binderMethod.getParameterTypes()[0].isPrimitive()) { 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 446dd515751..7c96d95b514 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 @@ -1102,6 +1102,70 @@ public void dateArrayNull() { assertThat(e.getMessage()).contains("null value"); } + @Test + public void protoMessageArray() { + SingerInfo singerInfo1 = SingerInfo.newBuilder().setSingerId(111).setGenre(Genre.FOLK).build(); + SingerInfo singerInfo2 = SingerInfo.newBuilder().setSingerId(222).build(); + Value v = + Value.protoMessageArray( + Arrays.asList(singerInfo1, null, singerInfo2), SingerInfo.getDescriptor()); + assertThat(v.getType()) + .isEqualTo(Type.array(Type.proto(SingerInfo.getDescriptor().getFullName()))); + assertThat(v.isNull()).isFalse(); + assertThat(v.getProtoMessageArray(SingerInfo.getDefaultInstance())) + .containsExactly(singerInfo1, null, singerInfo2); + assertThat(v.getBytesArray()) + .containsExactly( + ByteArray.copyFrom(singerInfo1.toByteArray()), + null, + ByteArray.copyFrom(singerInfo2.toByteArray())); + } + + @Test + public void protoMessageNullArray() { + Value v = Value.protoMessageArray(null, SingerInfo.getDescriptor()); + assertThat(v.getType()) + .isEqualTo(Type.array(Type.proto(SingerInfo.getDescriptor().getFullName()))); + assertThat(v.isNull()).isTrue(); + assertThat(v.toString()).isEqualTo(NULL_STRING); + IllegalStateException e = + assertThrows( + IllegalStateException.class, + () -> { + v.getProtoMessageArray(SingerInfo.getDefaultInstance()); + }); + assertThat(e.getMessage()).contains("null value"); + } + + @Test + public void protoEnumArray() { + Genre genre1 = Genre.ROCK; + Genre genre2 = Genre.JAZZ; + Value v = Value.protoEnumArray(Arrays.asList(genre1, null, genre2), Genre.getDescriptor()); + assertThat(v.getType()) + .isEqualTo(Type.array(Type.protoEnum(Genre.getDescriptor().getFullName()))); + assertThat(v.isNull()).isFalse(); + assertThat(v.getProtoEnumArray(Genre::forNumber)).containsExactly(genre1, null, genre2); + assertThat(v.getInt64Array()) + .containsExactly((long) genre1.getNumber(), null, (long) genre2.getNumber()); + } + + @Test + public void protoEnumNullArray() { + Value v = Value.protoEnumArray(null, Genre.getDescriptor()); + assertThat(v.getType()) + .isEqualTo(Type.array(Type.protoEnum(Genre.getDescriptor().getFullName()))); + assertThat(v.isNull()).isTrue(); + assertThat(v.toString()).isEqualTo(NULL_STRING); + IllegalStateException e = + assertThrows( + IllegalStateException.class, + () -> { + v.getProtoEnumArray(Genre::forNumber); + }); + assertThat(e.getMessage()).contains("null value"); + } + @Test public void struct() { Struct struct = Struct.newBuilder().set("f1").to("v1").set("f2").to(30).build(); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ChecksumResultSetTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ChecksumResultSetTest.java index 0f3ee1cbf5d..4e8fb0cfcbb 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ChecksumResultSetTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ChecksumResultSetTest.java @@ -110,6 +110,15 @@ public class ChecksumResultSetTest { .to( Value.pgJsonbArray( Arrays.asList("{\"color\":\"red\",\"value\":\"#f00\"}", null, "[]"))) + .set("protoMessageArray") + .to( + Value.protoMessageArray( + Arrays.asList( + SingerInfo.newBuilder().setSingerId(23).build(), + SingerInfo.getDefaultInstance()), + SingerInfo.getDescriptor())) + .set("protoEnumArray") + .to(Value.protoEnumArray(Arrays.asList(Genre.JAZZ, Genre.ROCK), Genre.getDescriptor())) .build(); @Test @@ -141,7 +150,12 @@ public void testRetry() { Type.StructField.of("dateArray", Type.array(Type.date())), Type.StructField.of("stringArray", Type.array(Type.string())), Type.StructField.of("jsonArray", Type.array(Type.json())), - Type.StructField.of("pgJsonbArray", Type.array(Type.pgJsonb()))); + Type.StructField.of("pgJsonbArray", Type.array(Type.pgJsonb())), + Type.StructField.of( + "protoMessageArray", + Type.array(Type.proto(SingerInfo.getDescriptor().getFullName()))), + Type.StructField.of( + "protoEnumArray", Type.array(Type.protoEnum(Genre.getDescriptor().getFullName())))); Struct rowNonNullValues = Struct.newBuilder() .set("boolVal") @@ -206,6 +220,15 @@ public void testRetry() { .to( Value.pgJsonbArray( Arrays.asList("{\"color\":\"red\",\"value\":\"#f00\"}", null, "{}"))) + .set("protoMessageArray") + .to( + Value.protoMessageArray( + Arrays.asList( + SingerInfo.newBuilder().setSingerId(11).setNationality("C1").build(), + SingerInfo.getDefaultInstance()), + SingerInfo.getDescriptor())) + .set("protoEnumArray") + .to(Value.protoEnumArray(Arrays.asList(Genre.POP, Genre.ROCK), Genre.getDescriptor())) .build(); Struct rowNullValues = Struct.newBuilder() @@ -257,6 +280,10 @@ public void testRetry() { .toJsonArray(null) .set("pgJsonbArray") .toPgJsonbArray(null) + .set("protoMessageArray") + .to(Value.protoMessageArray(null, SingerInfo.getDescriptor())) + .set("protoEnumArray") + .to(Value.protoEnumArray(null, Genre.getDescriptor())) .build(); ParsedStatement parsedStatement = mock(ParsedStatement.class); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/DirectExecuteResultSetTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/DirectExecuteResultSetTest.java index 7b25a497137..8c275912b02 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/DirectExecuteResultSetTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/DirectExecuteResultSetTest.java @@ -291,12 +291,20 @@ public void testValidMethodCall() throws IllegalArgumentException { verify(delegate).getProtoMessage(0, SingerInfo.getDefaultInstance()); subject.getProtoMessage("test0", SingerInfo.getDefaultInstance()); verify(delegate).getProtoMessage("test0", SingerInfo.getDefaultInstance()); + subject.getProtoMessageList(0, SingerInfo.getDefaultInstance()); + verify(delegate).getProtoMessageList(0, SingerInfo.getDefaultInstance()); + subject.getProtoMessageList("test0", SingerInfo.getDefaultInstance()); + verify(delegate).getProtoMessageList("test0", SingerInfo.getDefaultInstance()); Function lambdaFunction = Genre::forNumber; subject.getProtoEnum(0, lambdaFunction); verify(delegate).getProtoEnum(0, lambdaFunction); subject.getProtoEnum("test0", lambdaFunction); verify(delegate).getProtoEnum("test0", lambdaFunction); + subject.getProtoEnumList(0, lambdaFunction); + verify(delegate).getProtoEnumList(0, lambdaFunction); + subject.getProtoEnumList("test0", lambdaFunction); + verify(delegate).getProtoEnumList("test0", lambdaFunction); subject.getStructList(0); subject.getStructList("test0"); 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 a6d322071b7..8e556d127c9 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 @@ -19,6 +19,8 @@ import com.google.cloud.Date; import com.google.cloud.Timestamp; import com.google.cloud.spanner.Dialect; +import com.google.cloud.spanner.SingerProto.Genre; +import com.google.cloud.spanner.SingerProto.SingerInfo; import com.google.common.io.BaseEncoding; import com.google.protobuf.ListValue; import com.google.protobuf.NullValue; @@ -115,6 +117,22 @@ private static Type[] generateTypes(Dialect dialect) { if (dialect == Dialect.GOOGLE_STANDARD_SQL) { types.add(Type.newBuilder().setCode(TypeCode.PROTO).setProtoTypeFqn("testProto").build()); types.add(Type.newBuilder().setCode(TypeCode.ENUM).setProtoTypeFqn("testEnum").build()); + types.add( + Type.newBuilder() + .setCode(TypeCode.ARRAY) + .setArrayElementType( + Type.newBuilder() + .setCode(TypeCode.PROTO) + .setProtoTypeFqn(SingerInfo.getDescriptor().getFullName())) + .build()); + types.add( + Type.newBuilder() + .setCode(TypeCode.ARRAY) + .setArrayElementType( + Type.newBuilder() + .setCode(TypeCode.ENUM) + .setProtoTypeFqn(Genre.getDescriptor().getFullName())) + .build()); } Type[] typeArray = new Type[types.size()]; diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSetTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSetTest.java index 3d4d5e87ad2..df915a16818 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSetTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSetTest.java @@ -326,12 +326,20 @@ public void testValidMethodCall() throws IllegalArgumentException { verify(delegate).getProtoMessage(0, SingerInfo.getDefaultInstance()); subject.getProtoMessage("test0", SingerInfo.getDefaultInstance()); verify(delegate).getProtoMessage("test0", SingerInfo.getDefaultInstance()); + subject.getProtoMessageList(0, SingerInfo.getDefaultInstance()); + verify(delegate).getProtoMessageList(0, SingerInfo.getDefaultInstance()); + subject.getProtoMessageList("test0", SingerInfo.getDefaultInstance()); + verify(delegate).getProtoMessageList("test0", SingerInfo.getDefaultInstance()); Function lambdaFunction = Genre::forNumber; subject.getProtoEnum(0, lambdaFunction); verify(delegate).getProtoEnum(0, lambdaFunction); subject.getProtoEnum("test0", lambdaFunction); verify(delegate).getProtoEnum("test0", lambdaFunction); + subject.getProtoEnumList(0, lambdaFunction); + verify(delegate).getProtoEnumList(0, lambdaFunction); + subject.getProtoEnumList("test0", lambdaFunction); + verify(delegate).getProtoEnumList("test0", lambdaFunction); subject.getStructList(0); subject.getStructList("test0"); From 64b7bda6e89b6c9b7df0adb2c9b1fa5ccfac4388 Mon Sep 17 00:00:00 2001 From: gauravpurohit Date: Mon, 28 Nov 2022 06:56:14 +0000 Subject: [PATCH 16/21] refactor: adding clirr ignores --- google-cloud-spanner/clirr-ignored-differences.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/google-cloud-spanner/clirr-ignored-differences.xml b/google-cloud-spanner/clirr-ignored-differences.xml index bd780b75532..88f02b8f13b 100644 --- a/google-cloud-spanner/clirr-ignored-differences.xml +++ b/google-cloud-spanner/clirr-ignored-differences.xml @@ -215,22 +215,22 @@ 7012 com/google/cloud/spanner/StructReader - com.google.protobuf.ProtocolMessageEnum getProtoEnumList(int, java.util.function.Function) + java.util.List getProtoEnumList(int, java.util.function.Function) 7012 com/google/cloud/spanner/StructReader - com.google.protobuf.ProtocolMessageEnum getProtoEnumList(java.lang.String, java.util.function.Function) + java.util.List getProtoEnumList(java.lang.String, java.util.function.Function) 7012 com/google/cloud/spanner/StructReader - com.google.protobuf.AbstractMessage getProtoMessageList(int, com.google.protobuf.AbstractMessage) + java.util.List getProtoMessageList(int, com.google.protobuf.AbstractMessage) 7012 com/google/cloud/spanner/StructReader - com.google.protobuf.AbstractMessage getProtoMessageList(java.lang.String, com.google.protobuf.AbstractMessage) + java.util.List getProtoMessageList(java.lang.String, com.google.protobuf.AbstractMessage) 7012 From 426fc312546c3c1e2e75031cfecaa763d8a747fd Mon Sep 17 00:00:00 2001 From: gauravpurohit Date: Thu, 1 Dec 2022 14:05:14 +0000 Subject: [PATCH 17/21] feat: Adding support for enum as Primary Key --- .../src/main/java/com/google/cloud/spanner/Key.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Key.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Key.java index 15d4e995bf0..2302f2345ac 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Key.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Key.java @@ -24,6 +24,7 @@ import com.google.common.base.Joiner; import com.google.protobuf.ListValue; import com.google.protobuf.NullValue; +import com.google.protobuf.ProtocolMessageEnum; import com.google.protobuf.Value; import java.io.Serializable; import java.math.BigDecimal; @@ -141,6 +142,11 @@ public Builder append(@Nullable BigDecimal value) { buffer.add(value); return this; } + /** Appends a {@code ENUM} value to the key. */ + public Builder append(@Nullable ProtocolMessageEnum value) { + buffer.add(value); + return this; + } /** Appends a {@code STRING} value to the key. */ public Builder append(@Nullable String value) { buffer.add(value); @@ -192,6 +198,8 @@ public Builder appendObject(@Nullable Object value) { append((Timestamp) value); } else if (value instanceof Date) { append((Date) value); + } else if (value instanceof ProtocolMessageEnum) { + append((ProtocolMessageEnum) value); } else { throw new IllegalArgumentException( "Unsupported type [" @@ -300,6 +308,11 @@ ListValue toProto() { builder.addValuesBuilder().setStringValue(part.toString()); } else if (part instanceof Date) { builder.addValuesBuilder().setStringValue(part.toString()); + } else if (part instanceof ProtocolMessageEnum) { + builder + .addValuesBuilder() + .setStringValue(Long.toString(((ProtocolMessageEnum) part).getNumber())) + .build(); } else { throw new AssertionError("Illegal key part: " + part.getClass()); } From 3be24f02f1eb3d54a48185845345dce1c078e76d Mon Sep 17 00:00:00 2001 From: gauravpurohit Date: Thu, 8 Dec 2022 09:46:12 +0000 Subject: [PATCH 18/21] feat: Code Review Changes, minor refactoring and adding docs --- .../cloud/spanner/AbstractResultSet.java | 43 ++++++++--------- .../cloud/spanner/AbstractStructReader.java | 21 +++++---- .../cloud/spanner/ForwardingStructReader.java | 16 +++---- .../com/google/cloud/spanner/ResultSets.java | 16 +++---- .../java/com/google/cloud/spanner/Struct.java | 8 ++-- .../google/cloud/spanner/StructReader.java | 32 ++++++++----- .../java/com/google/cloud/spanner/Type.java | 3 +- .../java/com/google/cloud/spanner/Value.java | 47 +++++++++++-------- .../connection/DirectExecuteResultSet.java | 16 +++---- .../ReplaceableForwardingResultSet.java | 16 +++---- .../AbstractStructReaderTypesTest.java | 4 +- .../DirectExecuteResultSetTest.java | 2 +- .../connection/RandomResultSetGenerator.java | 12 +++-- .../connection/ReadWriteTransactionTest.java | 2 +- .../ReplaceableForwardingResultSetTest.java | 2 +- 15 files changed, 132 insertions(+), 108 deletions(-) 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 2383331a03e..d54d824174d 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 @@ -561,7 +561,7 @@ static Object decodeArrayValue(Type elementType, ListValue listValue) { switch (elementType.getCode()) { case INT64: case ENUM: - // For int64/float64 types, use custom containers. These avoid wrapper object + // For int64/float64/eum types, use custom containers. These avoid wrapper object // creation for non-null arrays. return new Int64Array(listValue); case FLOAT64: @@ -613,12 +613,16 @@ public boolean isNull(int columnIndex) { } @Override - public T getProtoMessageInternal(int columnIndex, T m) { + public T getProtoMessageInternal(int columnIndex, T message) { Preconditions.checkNotNull( - m, "Proto Message shouldn't be null, default instance from generated class can be used."); + message, + "Proto message may not be null. Use MyProtoClass.getDefaultInstance() as a parameter value."); try { return (T) - m.toBuilder().mergeFrom(((ByteArray) rowData.get(columnIndex)).toByteArray()).build(); + message + .toBuilder() + .mergeFrom(((ByteArray) rowData.get(columnIndex)).toByteArray()) + .build(); } catch (InvalidProtocolBufferException e) { throw SpannerExceptionFactory.asSpannerException(e); } @@ -628,7 +632,7 @@ public T getProtoMessageInternal(int columnIndex, T protected T getProtoEnumInternal( int columnIndex, Function method) { Preconditions.checkNotNull( - method, "Lambda method shouldn't be null, use forNumber from autogenerated enum class."); + method, "Method may not be null. Use 'MyProtoEnum::forNumber' as a parameter value."); return (T) method.apply((int) getLongInternal(columnIndex)); } @@ -792,12 +796,7 @@ protected long[] getLongArrayInternal(int columnIndex) { @Override protected Int64Array getLongListInternal(int columnIndex) { - try { - return (Int64Array) rowData.get(columnIndex); - } catch (Exception e) { - System.out.println(e); - } - return null; + return (Int64Array) rowData.get(columnIndex); } @Override @@ -831,20 +830,21 @@ protected List getJsonListInternal(int columnIndex) { @Override @SuppressWarnings("unchecked") // We know ARRAY produces a List. protected List getProtoMessageListInternal( - int columnIndex, T m) { + int columnIndex, T message) { Preconditions.checkNotNull( - m, "Proto Message shouldn't be null, default instance from generated class can be used."); + message, + "Proto message may not be null. Use MyProtoClass.getDefaultInstance() as a parameter value."); List bytesArray = (List) rowData.get(columnIndex); try { - List protoMessagesList = new ArrayList<>(); + List protoMessagesList = new ArrayList<>(bytesArray.size()); for (ByteArray protoMessageBytes : bytesArray) { if (protoMessageBytes == null) { protoMessagesList.add(null); } else { protoMessagesList.add( - (T) m.toBuilder().mergeFrom(protoMessageBytes.toByteArray()).build()); + (T) message.toBuilder().mergeFrom(protoMessageBytes.toByteArray()).build()); } } return protoMessagesList; @@ -858,10 +858,10 @@ protected List getProtoMessageListInternal( protected List getProtoEnumListInternal( int columnIndex, Function method) { Preconditions.checkNotNull( - method, "Lambda method shouldn't be null, use forNumber from autogenerated enum class."); + method, "Method may not be null. Use 'MyProtoEnum::forNumber' as a parameter value."); List enumIntArray = (List) rowData.get(columnIndex); - List protoEnumList = new ArrayList<>(); + List protoEnumList = new ArrayList<>(enumIntArray.size()); for (Long enumIntValue : enumIntArray) { if (enumIntValue == null) { protoEnumList.add(null); @@ -1406,8 +1406,8 @@ protected String getStringInternal(int columnIndex) { } @Override - protected T getProtoMessageInternal(int columnIndex, T m) { - return currRow().getProtoMessageInternal(columnIndex, m); + protected T getProtoMessageInternal(int columnIndex, T message) { + return currRow().getProtoMessageInternal(columnIndex, message); } @Override @@ -1502,8 +1502,9 @@ protected List getBytesListInternal(int columnIndex) { } @Override - protected List getProtoMessageListInternal(int columnIndex, T m) { - return currRow().getProtoMessageListInternal(columnIndex, m); + protected List getProtoMessageListInternal( + int columnIndex, T message) { + return currRow().getProtoMessageListInternal(columnIndex, message); } @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 5e40080e60a..5c205b967eb 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 @@ -63,7 +63,7 @@ protected String getPgJsonbInternal(int columnIndex) { protected abstract Date getDateInternal(int columnIndex); - protected T getProtoMessageInternal(int columnIndex, T m) { + protected T getProtoMessageInternal(int columnIndex, T message) { throw new UnsupportedOperationException("Not implemented"); } @@ -72,7 +72,8 @@ protected T getProtoEnumInternal( throw new UnsupportedOperationException("Not implemented"); } - protected List getProtoMessageListInternal(int columnIndex, T m) { + protected List getProtoMessageListInternal( + int columnIndex, T message) { throw new UnsupportedOperationException("Not implemented"); } @@ -288,16 +289,16 @@ public T getProtoEnum( } @Override - public T getProtoMessage(int columnIndex, T m) { + public T getProtoMessage(int columnIndex, T message) { checkNonNullOfCodes(columnIndex, Arrays.asList(Code.PROTO, Code.BYTES), columnIndex); - return getProtoMessageInternal(columnIndex, m); + return getProtoMessageInternal(columnIndex, message); } @Override - public T getProtoMessage(String columnName, T m) { + public T getProtoMessage(String columnName, T message) { int columnIndex = getColumnIndex(columnName); checkNonNullOfCodes(columnIndex, Arrays.asList(Code.PROTO, Code.BYTES), columnName); - return getProtoMessageInternal(columnIndex, m); + return getProtoMessageInternal(columnIndex, message); } @Override @@ -468,18 +469,18 @@ public List getBytesList(String columnName) { } @Override - public List getProtoMessageList(int columnIndex, T m) { + public List getProtoMessageList(int columnIndex, T message) { checkNonNullOfCodes(columnIndex, Collections.singletonList(Code.ARRAY), columnIndex); checkArrayElementType(columnIndex, Arrays.asList(Code.PROTO, Code.BYTES), columnIndex); - return getProtoMessageListInternal(columnIndex, m); + return getProtoMessageListInternal(columnIndex, message); } @Override - public List getProtoMessageList(String columnName, T m) { + public List getProtoMessageList(String columnName, T message) { int columnIndex = getColumnIndex(columnName); checkNonNullOfCodes(columnIndex, Collections.singletonList(Code.ARRAY), columnName); checkArrayElementType(columnIndex, Arrays.asList(Code.PROTO, Code.BYTES), columnName); - return getProtoMessageListInternal(columnIndex, m); + return getProtoMessageListInternal(columnIndex, message); } @Override diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ForwardingStructReader.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ForwardingStructReader.java index 7b06a8551bb..97c39c00a8d 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ForwardingStructReader.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ForwardingStructReader.java @@ -374,15 +374,15 @@ public List getDateList(String columnName) { } @Override - public List getProtoMessageList(int columnIndex, T m) { + public List getProtoMessageList(int columnIndex, T message) { checkValidState(); - return delegate.get().getProtoMessageList(columnIndex, m); + return delegate.get().getProtoMessageList(columnIndex, message); } @Override - public List getProtoMessageList(String columnName, T m) { + public List getProtoMessageList(String columnName, T message) { checkValidState(); - return delegate.get().getProtoMessageList(columnName, m); + return delegate.get().getProtoMessageList(columnName, message); } @Override @@ -412,15 +412,15 @@ public List getStructList(String columnName) { } @Override - public T getProtoMessage(int columnIndex, T m) { + public T getProtoMessage(int columnIndex, T message) { checkValidState(); - return delegate.get().getProtoMessage(columnIndex, m); + return delegate.get().getProtoMessage(columnIndex, message); } @Override - public T getProtoMessage(String columnName, T m) { + public T getProtoMessage(String columnName, T message) { checkValidState(); - return delegate.get().getProtoMessage(columnName, m); + return delegate.get().getProtoMessage(columnName, message); } @Override diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ResultSets.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ResultSets.java index 4d1c4915b83..d55d4091b9f 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ResultSets.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ResultSets.java @@ -304,13 +304,13 @@ public Date getDate(String columnName) { } @Override - public T getProtoMessage(int columnIndex, T m) { - return getCurrentRowAsStruct().getProtoMessage(columnIndex, m); + public T getProtoMessage(int columnIndex, T message) { + return getCurrentRowAsStruct().getProtoMessage(columnIndex, message); } @Override - public T getProtoMessage(String columnName, T m) { - return getCurrentRowAsStruct().getProtoMessage(columnName, m); + public T getProtoMessage(String columnName, T message) { + return getCurrentRowAsStruct().getProtoMessage(columnName, message); } @Override @@ -466,13 +466,13 @@ public List getDateList(String columnName) { } @Override - public List getProtoMessageList(int columnIndex, T m) { - return getCurrentRowAsStruct().getProtoMessageList(columnIndex, m); + public List getProtoMessageList(int columnIndex, T message) { + return getCurrentRowAsStruct().getProtoMessageList(columnIndex, message); } @Override - public List getProtoMessageList(String columnName, T m) { - return getCurrentRowAsStruct().getProtoMessageList(columnName, m); + public List getProtoMessageList(String columnName, T message) { + return getCurrentRowAsStruct().getProtoMessageList(columnName, message); } @Override diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Struct.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Struct.java index 240718cebb5..40c30148d0e 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Struct.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Struct.java @@ -221,8 +221,8 @@ protected Date getDateInternal(int columnIndex) { } @Override - protected T getProtoMessageInternal(int columnIndex, T m) { - return values.get(columnIndex).getProtoMessage(m); + protected T getProtoMessageInternal(int columnIndex, T message) { + return values.get(columnIndex).getProtoMessage(message); } @Override @@ -303,8 +303,8 @@ protected List getTimestampListInternal(int columnIndex) { @Override protected List getProtoMessageListInternal( - int columnIndex, T m) { - return values.get(columnIndex).getProtoMessageArray(m); + int columnIndex, T message) { + return values.get(columnIndex).getProtoMessageArray(message); } @Override diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/StructReader.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/StructReader.java index 861a817eb1e..7108e49ebc2 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/StructReader.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/StructReader.java @@ -142,11 +142,12 @@ default String getPgJsonb(String columnName) { * To get the proto message of generic type {@code T} from Struct. * * @param columnIndex Index of the column. - * @param m Proto message object. @see getDefaultInstance() * @return The value of a non-{@code NULL} column with type {@link Type#proto(String)} ()}. */ - default T getProtoMessage(int columnIndex, T m) { + default T getProtoMessage(int columnIndex, T message) { throw new UnsupportedOperationException("method should be overwritten"); } @@ -154,11 +155,12 @@ default T getProtoMessage(int columnIndex, T m) { * To get the proto message of type {@code T} from Struct. * * @param columnName Name of the column. - * @param m Proto message object. @see getDefaultInstance() * @return The value of a non-{@code NULL} column with type {@link Type#proto(String)} ()}. */ - default T getProtoMessage(String columnName, T m) { + default T getProtoMessage(String columnName, T message) { throw new UnsupportedOperationException("method should be overwritten"); } @@ -166,7 +168,8 @@ default T getProtoMessage(String columnName, T m) { * To get the proto enum of type {@code T} from Struct. * * @param columnIndex Index of the column. - * @param method Lambda method which takes a primitive int and return Enum. @see forNumber * @return The value of a non-{@code NULL} column with type {@link Type#protoEnum(String)} ()}. */ @@ -179,7 +182,8 @@ default T getProtoEnum( * To get the proto enum of type {@code T} from Struct. * * @param columnName Name of the column. - * @param method Lambda method which takes a primitive int and return Enum. @see forNumber * @return The value of a non-{@code NULL} column with type {@link Type#protoEnum(String)} ()}. */ @@ -374,12 +378,13 @@ default List getPgJsonbList(String columnName) { * To get the proto message of generic type {@code T} from Struct. * * @param columnIndex Index of the column. - * @param m Proto message object. @see getDefaultInstance() * @return The value of a non-{@code NULL} column with type {@code * Type.array(Type.proto(String))}. */ - default List getProtoMessageList(int columnIndex, T m) { + default List getProtoMessageList(int columnIndex, T message) { throw new UnsupportedOperationException("method should be overwritten"); } @@ -387,12 +392,13 @@ default List getProtoMessageList(int columnIndex, * To get the proto message of type {@code T} from Struct. * * @param columnName Name of the column. - * @param m Proto message object. @see getDefaultInstance() * @return The value of a non-{@code NULL} column with type {@code * Type.array(Type.proto(String))}. */ - default List getProtoMessageList(String columnName, T m) { + default List getProtoMessageList(String columnName, T message) { throw new UnsupportedOperationException("method should be overwritten"); } @@ -400,7 +406,8 @@ default List getProtoMessageList(String columnNam * To get the proto enum of type {@code T} from Struct. * * @param columnIndex Index of the column. - * @param method Lambda method which takes a primitive int and return Enum. @see forNumber * @return The value of a non-{@code NULL} column with type {@code * Type.array(Type.protoEnum(String))}. @@ -414,7 +421,8 @@ default List getProtoEnumList( * To get the proto enum list of type {@code T} from Struct. * * @param columnName Name of the column. - * @param method Lambda method which takes a primitive int and return Enum. @see forNumber * @return The value of a non-{@code NULL} column with type {@code * Type.array(Type.protoEnum(String))}. 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 a6675633ed5..83a1abef1af 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; @@ -232,7 +233,7 @@ private Type( Code code, @Nullable Type arrayElementType, @Nullable ImmutableList structFields, - @Nullable String protoTypeFqn) { + @Nonnull String protoTypeFqn) { this(code, arrayElementType, structFields); this.protoTypeFqn = protoTypeFqn; } 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 0241aebdd9b..fff170423d9 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 @@ -229,7 +229,8 @@ public static Value pgJsonb(@Nullable String v) { * @param v Not null Proto message. */ public static Value protoMessage(AbstractMessage v) { - Preconditions.checkNotNull(v, "Use protoMessage(ByteArray, Descriptor) for null values."); + Preconditions.checkNotNull( + v, "Use protoMessage((ByteArray) null, MyProtoClass.getDescriptor()) for null values."); return protoMessage( ByteArray.copyFrom(v.toByteArray()), v.getDescriptorForType().getFullName()); } @@ -238,7 +239,8 @@ public static Value protoMessage(AbstractMessage v) { * Return a {@code PROTO} value * * @param v Serialized Proto Array, which may be null. - * @param protoTypFqn Fully qualified name of proto representing the proto definition. + * @param protoTypFqn Fully qualified name of proto representing the proto definition. Use static + * method from proto class {@code MyProtoClass.getDescriptor().getFullName()} */ public static Value protoMessage(@Nullable ByteArray v, String protoTypFqn) { return new ProtoMessageImpl(v == null, v, protoTypFqn); @@ -248,8 +250,8 @@ public static Value protoMessage(@Nullable ByteArray v, String protoTypFqn) { * Return a {@code PROTO} value * * @param v Serialized Proto Array, which may be null. - * @param descriptor Proto Type Descriptor, use static method {@code - * MESSAGE.getDescriptorForType()}. + * @param descriptor Proto Type Descriptor, use static method from proto class {@code + * MyProtoClass.getDescriptor()}. */ public static Value protoMessage(@Nullable ByteArray v, Descriptor descriptor) { Preconditions.checkNotNull(descriptor, "descriptor can't be null."); @@ -262,7 +264,8 @@ public static Value protoMessage(@Nullable ByteArray v, Descriptor descriptor) { * @param v Proto Enum, which may be null. */ public static Value protoEnum(ProtocolMessageEnum v) { - Preconditions.checkNotNull(v, "Use protoEnum(Long, Descriptor) for null values."); + Preconditions.checkNotNull( + v, "Use protoEnum((Long) null, MyProtoEnum.getDescriptor()) for null values."); return protoEnum(v.getNumber(), v.getDescriptorForType().getFullName()); } @@ -270,7 +273,8 @@ public static Value protoEnum(ProtocolMessageEnum v) { * Return a {@code ENUM} value. * * @param v Enum non-primitive Integer constant. - * @param protoTypFqn Fully qualified name of proto representing the enum definition. + * @param protoTypFqn Fully qualified name of proto representing the enum definition. Use static + * method from proto class {@code MyProtoEnum.getDescriptor().getFullName()} */ public static Value protoEnum(@Nullable Long v, String protoTypFqn) { return new ProtoEnumImpl(v == null, v, protoTypFqn); @@ -280,8 +284,8 @@ public static Value protoEnum(@Nullable Long v, String protoTypFqn) { * Return a {@code ENUM} value. * * @param v Enum non-primitive Integer constant. - * @param enumDescriptor Enum Type Descriptor, use static method {@code - * ENUM.getDescriptorForType()}. + * @param enumDescriptor Enum Type Descriptor. Use static method from proto class {@code * + * MyProtoEnum.getDescriptor()}. */ public static Value protoEnum(@Nullable Long v, EnumDescriptor enumDescriptor) { Preconditions.checkNotNull(enumDescriptor, "descriptor can't be null."); @@ -292,7 +296,8 @@ public static Value protoEnum(@Nullable Long v, EnumDescriptor enumDescriptor) { * Return a {@code ENUM} value. * * @param v Enum integer primitive constant. - * @param protoTypFqn Fully qualified name of proto representing the enum definition. + * @param protoTypFqn Fully qualified name of proto representing the enum definition. Use static + * method from proto class {@code MyProtoEnum.getDescriptor().getFullName()} */ public static Value protoEnum(long v, String protoTypFqn) { return new ProtoEnumImpl(false, v, protoTypFqn); @@ -1295,7 +1300,7 @@ public long getInt64() { public T getProtoEnum( Function method) { Preconditions.checkNotNull( - method, "Lambda method shouldn't be null, use forNumber from generated enum class."); + method, "Method may not be null. Use 'MyProtoEnum::forNumber' as a parameter value."); checkNotNull(); return (T) method.apply((int) value); } @@ -1490,7 +1495,8 @@ public ByteArray getBytes() { @Override public T getProtoMessage(T m) { Preconditions.checkNotNull( - m, "Proto Message shouldn't be null, default instance from generated class can be used."); + m, + "Proto message may not be null. Use MyProtoClass.getDefaultInstance() as a parameter value."); checkNotNull(); try { return (T) m.toBuilder().mergeFrom(value.toByteArray()).build(); @@ -1525,7 +1531,8 @@ public ByteArray getBytes() { @Override public T getProtoMessage(T m) { Preconditions.checkNotNull( - m, "Proto Message shouldn't be null, default instance from generated class can be used."); + m, + "Proto message may not be null. Use MyProtoClass.getDefaultInstance() as a parameter value."); checkNotNull(); try { return (T) m.toBuilder().mergeFrom(value.toByteArray()).build(); @@ -1562,7 +1569,7 @@ public long getInt64() { public T getProtoEnum( Function method) { Preconditions.checkNotNull( - method, "Lambda method shouldn't be null, use forNumber from generated enum class."); + method, "Method may not be null. Use 'MyProtoEnum::forNumber' as a parameter value."); checkNotNull(); return (T) method.apply(value.intValue()); } @@ -1834,7 +1841,7 @@ public List getInt64Array() { public List getProtoEnumArray( Function method) { Preconditions.checkNotNull( - method, "Lambda method shouldn't be null, use forNumber from generated enum class."); + method, "Method may not be null. Use 'MyProtoEnum::forNumber' as a parameter value."); checkNotNull(); List protoEnumList = new ArrayList<>(); @@ -2038,10 +2045,11 @@ public List getBytesArray() { @Override public List getProtoMessageArray(T m) { Preconditions.checkNotNull( - m, "Proto Message shouldn't be null, default instance from generated class can be used."); + m, + "Proto message may not be null. Use MyProtoClass.getDefaultInstance() as a parameter value."); checkNotNull(); try { - List protoMessagesList = new ArrayList<>(); + List protoMessagesList = new ArrayList<>(value.size()); for (ByteArray protoMessageBytes : value) { if (protoMessageBytes == null) { protoMessagesList.add(null); @@ -2100,10 +2108,11 @@ public List getBytesArray() { @Override public List getProtoMessageArray(T m) { Preconditions.checkNotNull( - m, "Proto Message shouldn't be null, default instance from generated class can be used."); + m, + "Proto message may not be null. Use MyProtoClass.getDefaultInstance() as a parameter value."); checkNotNull(); try { - List protoMessagesList = new ArrayList<>(); + List protoMessagesList = new ArrayList<>(value.size()); for (ByteArray protoMessageBytes : value) { if (protoMessageBytes == null) { protoMessagesList.add(null); @@ -2144,7 +2153,7 @@ public List getInt64Array() { public List getProtoEnumArray( Function method) { Preconditions.checkNotNull( - method, "Lambda method shouldn't be null, use forNumber from generated enum class."); + method, "Method may not be null. Use 'MyProtoEnum::forNumber' as a parameter value."); checkNotNull(); List protoEnumList = new ArrayList<>(); diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DirectExecuteResultSet.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DirectExecuteResultSet.java index 0588c898c48..dff915e2cce 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DirectExecuteResultSet.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DirectExecuteResultSet.java @@ -429,15 +429,15 @@ public List getDateList(String columnName) { } @Override - public List getProtoMessageList(int columnIndex, T m) { + public List getProtoMessageList(int columnIndex, T message) { Preconditions.checkState(nextCalledByClient, MISSING_NEXT_CALL); - return delegate.getProtoMessageList(columnIndex, m); + return delegate.getProtoMessageList(columnIndex, message); } @Override - public List getProtoMessageList(String columnName, T m) { + public List getProtoMessageList(String columnName, T message) { Preconditions.checkState(nextCalledByClient, MISSING_NEXT_CALL); - return delegate.getProtoMessageList(columnName, m); + return delegate.getProtoMessageList(columnName, message); } @Override @@ -481,15 +481,15 @@ public T getProtoEnum( } @Override - public T getProtoMessage(int columnIndex, T m) { + public T getProtoMessage(int columnIndex, T message) { Preconditions.checkState(nextCalledByClient, MISSING_NEXT_CALL); - return delegate.getProtoMessage(columnIndex, m); + return delegate.getProtoMessage(columnIndex, message); } @Override - public T getProtoMessage(String columnName, T m) { + public T getProtoMessage(String columnName, T message) { Preconditions.checkState(nextCalledByClient, MISSING_NEXT_CALL); - return delegate.getProtoMessage(columnName, m); + return delegate.getProtoMessage(columnName, message); } @Override diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSet.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSet.java index 2e19c2f5126..7370551a46f 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSet.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSet.java @@ -434,15 +434,15 @@ public List getDateList(String columnName) { } @Override - public List getProtoMessageList(int columnIndex, T m) { + public List getProtoMessageList(int columnIndex, T message) { checkClosed(); - return delegate.getProtoMessageList(columnIndex, m); + return delegate.getProtoMessageList(columnIndex, message); } @Override - public List getProtoMessageList(String columnName, T m) { + public List getProtoMessageList(String columnName, T message) { checkClosed(); - return delegate.getProtoMessageList(columnName, m); + return delegate.getProtoMessageList(columnName, message); } @Override @@ -472,15 +472,15 @@ public List getStructList(String columnName) { } @Override - public T getProtoMessage(int columnIndex, T m) { + public T getProtoMessage(int columnIndex, T message) { checkClosed(); - return delegate.getProtoMessage(columnIndex, m); + return delegate.getProtoMessage(columnIndex, message); } @Override - public T getProtoMessage(String columnName, T m) { + public T getProtoMessage(String columnName, T message) { checkClosed(); - return delegate.getProtoMessage(columnName, m); + return delegate.getProtoMessage(columnName, message); } @Override diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AbstractStructReaderTypesTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AbstractStructReaderTypesTest.java index 7a749321605..4fc3c67ceba 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AbstractStructReaderTypesTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AbstractStructReaderTypesTest.java @@ -99,7 +99,7 @@ protected Date getDateInternal(int columnIndex) { } @Override - protected T getProtoMessageInternal(int columnIndex, T m) { + protected T getProtoMessageInternal(int columnIndex, T message) { return null; } @@ -176,7 +176,7 @@ protected List getTimestampListInternal(int columnIndex) { @Override protected List getProtoMessageListInternal( - int columnIndex, T m) { + int columnIndex, T message) { return null; } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/DirectExecuteResultSetTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/DirectExecuteResultSetTest.java index cae9c82d475..069e107c522 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/DirectExecuteResultSetTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/DirectExecuteResultSetTest.java @@ -170,7 +170,7 @@ private void callMethods( } break; default: - fail("method with more than 1 parameter is unknown"); + fail("method with more than 2 parameter is unknown"); } } catch (InvocationTargetException e) { if (e.getCause().getClass().equals(expectedException)) { 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 8e556d127c9..e783cff25bf 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 @@ -114,6 +114,14 @@ private static Type[] generateTypes(Dialect dialect) { .setArrayElementType(Type.newBuilder().setCode(TypeCode.TIMESTAMP)) .build())); + appendProtoTypes(types, dialect); + Type[] typeArray = new Type[types.size()]; + typeArray = types.toArray(typeArray); + return typeArray; + } + + /** To append Proto & Enum types * */ + private static void appendProtoTypes(List types, Dialect dialect) { if (dialect == Dialect.GOOGLE_STANDARD_SQL) { types.add(Type.newBuilder().setCode(TypeCode.PROTO).setProtoTypeFqn("testProto").build()); types.add(Type.newBuilder().setCode(TypeCode.ENUM).setProtoTypeFqn("testEnum").build()); @@ -134,10 +142,6 @@ private static Type[] generateTypes(Dialect dialect) { .setProtoTypeFqn(Genre.getDescriptor().getFullName())) .build()); } - - Type[] typeArray = new Type[types.size()]; - typeArray = types.toArray(typeArray); - return typeArray; } private static ResultSetMetadata generateMetadata(Type[] types) { diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ReadWriteTransactionTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ReadWriteTransactionTest.java index c21635cca4c..1fc8d1cb790 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ReadWriteTransactionTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ReadWriteTransactionTest.java @@ -692,7 +692,7 @@ public void testChecksumResultSet() { .set("JSON") .to(Value.json(emptyArrayJson)) .set("PROTO") - .to(protoMessageVal) + .to(null, SingerInfo.getDescriptor()) .set("PROTOENUM") .to(Genre.POP) .build())); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSetTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSetTest.java index 09b70a5cb3e..dddd63b64e4 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSetTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSetTest.java @@ -211,7 +211,7 @@ private void callMethods( } break; default: - fail("method with more than 1 parameter is unknown"); + fail("method with more than 2 parameter is unknown"); } } catch (InvocationTargetException e) { if (e.getCause().getClass().equals(expectedException)) { From dfc38fbb97a5f0df1d1701d382fbd3b6ab180d5d Mon Sep 17 00:00:00 2001 From: gauravpurohit Date: Tue, 13 Dec 2022 06:53:00 +0000 Subject: [PATCH 19/21] feat: Addressing review comments -Modified Docs/Comments -Minor Refactoring --- .../com/google/cloud/spanner/AbstractResultSet.java | 2 +- .../google/cloud/spanner/AbstractStructReader.java | 2 +- .../src/main/java/com/google/cloud/spanner/Type.java | 12 ++++-------- .../main/java/com/google/cloud/spanner/Value.java | 4 ++-- 4 files changed, 8 insertions(+), 12 deletions(-) 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 d54d824174d..9c8aec76cf0 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 @@ -613,7 +613,7 @@ public boolean isNull(int columnIndex) { } @Override - public T getProtoMessageInternal(int columnIndex, T message) { + protected T getProtoMessageInternal(int columnIndex, T message) { Preconditions.checkNotNull( message, "Proto message may not be null. Use MyProtoClass.getDefaultInstance() as a parameter value."); 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 5c205b967eb..986360efa9f 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 @@ -583,7 +583,7 @@ private void checkArrayElementType( Type arrayElementType = getColumnType(columnIndex).getArrayElementType(); checkState( expectedCodes.contains(arrayElementType.getCode()), - "Array element for Column %s is not of correct type code: expected one of [%s] but was %s", + "Array element for column %s is not of correct type code: expected one of [%s] but was %s", columnNameForError, expectedCodes, Type.array(arrayElementType)); 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 83a1abef1af..e81f0c874ea 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 @@ -129,7 +129,7 @@ public static Type pgJsonb() { * @param protoTypeFqn Proto full name */ public static Type proto(String protoTypeFqn) { - return new Type(Code.PROTO, null, null, protoTypeFqn); + return new Type(Code.PROTO, protoTypeFqn); } /** @@ -138,7 +138,7 @@ public static Type proto(String protoTypeFqn) { * @param protoTypeFqn Proto ENUM full name */ public static Type protoEnum(String protoTypeFqn) { - return new Type(Code.ENUM, null, null, protoTypeFqn); + return new Type(Code.ENUM, protoTypeFqn); } /** Returns the descriptor for the {@code BYTES} type: a variable-length byte string. */ @@ -229,12 +229,8 @@ private Type( this.structFields = structFields; } - private Type( - Code code, - @Nullable Type arrayElementType, - @Nullable ImmutableList structFields, - @Nonnull String protoTypeFqn) { - this(code, arrayElementType, structFields); + private Type(Code code, @Nonnull String protoTypeFqn) { + this(code, null, null); this.protoTypeFqn = protoTypeFqn; } 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 fff170423d9..12773ffc1b3 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 @@ -224,7 +224,7 @@ public static Value pgJsonb(@Nullable String v) { } /** - * Return a {@code PROTO} value. + * Return a {@code PROTO} value for not null proto messages. * * @param v Not null Proto message. */ @@ -259,7 +259,7 @@ public static Value protoMessage(@Nullable ByteArray v, Descriptor descriptor) { } /** - * Return a {@code ENUM} value. + * Return a {@code ENUM} value for not null proto messages. * * @param v Proto Enum, which may be null. */ From 4c6cc398ff741c082b8c5b86a3580f6682494552 Mon Sep 17 00:00:00 2001 From: gauravpurohit Date: Tue, 13 Dec 2022 07:13:44 +0000 Subject: [PATCH 20/21] refactor: Using Column instead of column to avoid test failures --- .../java/com/google/cloud/spanner/AbstractStructReader.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 986360efa9f..5c205b967eb 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 @@ -583,7 +583,7 @@ private void checkArrayElementType( Type arrayElementType = getColumnType(columnIndex).getArrayElementType(); checkState( expectedCodes.contains(arrayElementType.getCode()), - "Array element for column %s is not of correct type code: expected one of [%s] but was %s", + "Array element for Column %s is not of correct type code: expected one of [%s] but was %s", columnNameForError, expectedCodes, Type.array(arrayElementType)); From b205121086a71ee5e7725ea85d03787c8313e035 Mon Sep 17 00:00:00 2001 From: gauravpurohit Date: Tue, 27 Dec 2022 11:42:54 +0000 Subject: [PATCH 21/21] feat: Minor refactoring -code review comments -adding function docs --- .../cloud/spanner/AbstractResultSet.java | 6 +- .../java/com/google/cloud/spanner/Key.java | 3 +- .../java/com/google/cloud/spanner/Value.java | 56 +++++++++++++++---- .../DirectExecuteResultSetTest.java | 2 +- .../ReplaceableForwardingResultSetTest.java | 2 +- .../com/google/cloud/spanner/singer.proto | 3 +- 6 files changed, 54 insertions(+), 18 deletions(-) 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 9c8aec76cf0..938668c74c0 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 @@ -400,8 +400,10 @@ private Object writeReplace() { builder .set(fieldName) .to(Value.protoMessage((ByteArray) value, fieldType.getProtoTypeFqn())); + break; case ENUM: builder.set(fieldName).to(Value.protoEnum((Long) value, fieldType.getProtoTypeFqn())); + break; case PG_JSONB: builder.set(fieldName).to(Value.pgJsonb((String) value)); break; @@ -421,6 +423,7 @@ private Object writeReplace() { builder.set(fieldName).toBoolArray((Iterable) value); break; case INT64: + case ENUM: builder.set(fieldName).toInt64Array((Iterable) value); break; case FLOAT64: @@ -442,6 +445,7 @@ private Object writeReplace() { builder.set(fieldName).toPgJsonbArray((Iterable) value); break; case BYTES: + case PROTO: builder.set(fieldName).toBytesArray((Iterable) value); break; case TIMESTAMP: @@ -561,7 +565,7 @@ static Object decodeArrayValue(Type elementType, ListValue listValue) { switch (elementType.getCode()) { case INT64: case ENUM: - // For int64/float64/eum types, use custom containers. These avoid wrapper object + // For int64/float64/enum types, use custom containers. These avoid wrapper object // creation for non-null arrays. return new Int64Array(listValue); case FLOAT64: diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Key.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Key.java index 2302f2345ac..3467052605a 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Key.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Key.java @@ -311,8 +311,7 @@ ListValue toProto() { } else if (part instanceof ProtocolMessageEnum) { builder .addValuesBuilder() - .setStringValue(Long.toString(((ProtocolMessageEnum) part).getNumber())) - .build(); + .setStringValue(Long.toString(((ProtocolMessageEnum) part).getNumber())); } else { throw new AssertionError("Illegal key part: " + part.getClass()); } 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 12773ffc1b3..23ca8f335e2 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 @@ -239,11 +239,11 @@ public static Value protoMessage(AbstractMessage v) { * Return a {@code PROTO} value * * @param v Serialized Proto Array, which may be null. - * @param protoTypFqn Fully qualified name of proto representing the proto definition. Use static + * @param protoTypeFqn Fully qualified name of proto representing the proto definition. Use static * method from proto class {@code MyProtoClass.getDescriptor().getFullName()} */ - public static Value protoMessage(@Nullable ByteArray v, String protoTypFqn) { - return new ProtoMessageImpl(v == null, v, protoTypFqn); + public static Value protoMessage(@Nullable ByteArray v, String protoTypeFqn) { + return new ProtoMessageImpl(v == null, v, protoTypeFqn); } /** @@ -255,7 +255,7 @@ public static Value protoMessage(@Nullable ByteArray v, String protoTypFqn) { */ public static Value protoMessage(@Nullable ByteArray v, Descriptor descriptor) { Preconditions.checkNotNull(descriptor, "descriptor can't be null."); - return new ProtoMessageImpl(v == null, v, descriptor.getFullName()); + return protoMessage(v, descriptor.getFullName()); } /** @@ -273,11 +273,11 @@ public static Value protoEnum(ProtocolMessageEnum v) { * Return a {@code ENUM} value. * * @param v Enum non-primitive Integer constant. - * @param protoTypFqn Fully qualified name of proto representing the enum definition. Use static + * @param protoTypeFqn Fully qualified name of proto representing the enum definition. Use static * method from proto class {@code MyProtoEnum.getDescriptor().getFullName()} */ - public static Value protoEnum(@Nullable Long v, String protoTypFqn) { - return new ProtoEnumImpl(v == null, v, protoTypFqn); + public static Value protoEnum(@Nullable Long v, String protoTypeFqn) { + return new ProtoEnumImpl(v == null, v, protoTypeFqn); } /** @@ -289,18 +289,18 @@ public static Value protoEnum(@Nullable Long v, String protoTypFqn) { */ public static Value protoEnum(@Nullable Long v, EnumDescriptor enumDescriptor) { Preconditions.checkNotNull(enumDescriptor, "descriptor can't be null."); - return new ProtoEnumImpl(v == null, v, enumDescriptor.getFullName()); + return protoEnum(v, enumDescriptor.getFullName()); } /** * Return a {@code ENUM} value. * * @param v Enum integer primitive constant. - * @param protoTypFqn Fully qualified name of proto representing the enum definition. Use static + * @param protoTypeFqn Fully qualified name of proto representing the enum definition. Use static * method from proto class {@code MyProtoEnum.getDescriptor().getFullName()} */ - public static Value protoEnum(long v, String protoTypFqn) { - return new ProtoEnumImpl(false, v, protoTypFqn); + public static Value protoEnum(long v, String protoTypeFqn) { + return new ProtoEnumImpl(false, v, protoTypeFqn); } /** @@ -507,6 +507,14 @@ public static Value pgJsonbArray(@Nullable Iterable v) { return new PgJsonbArrayImpl(v == null, v == null ? null : immutableCopyOf(v)); } + /** + * Returns an {@code ARRAY} value. + * + * @param v 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}. + * @param descriptor Proto Type Descriptor, use static method from proto class {@code + * MyProtoClass.getDescriptor()}. + */ public static Value protoMessageArray( @Nullable Iterable v, Descriptor descriptor) { if (v == null) { @@ -526,11 +534,27 @@ public static Value protoMessageArray( return new ProtoMessageArrayImpl(false, serializedArray, descriptor.getFullName()); } + /** + * Returns an {@code ARRAY} value. + * + * @param v 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}. + * @param protoTypeFqn Fully qualified name of proto representing the proto definition. Use static + * method from proto class {@code MyProtoClass.getDescriptor().getFullName()} + */ public static Value protoMessageArray(@Nullable Iterable v, String protoTypeFqn) { return new ProtoMessageArrayImpl( v == null, v != null ? immutableCopyOf(v) : null, protoTypeFqn); } + /** + * Returns an {@code ARRAY} value. + * + * @param v 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}. + * @param descriptor Proto Type Descriptor, use static method from proto class {@code + * MyProtoClass.getDescriptor()}. + */ public static Value protoEnumArray( @Nullable Iterable v, EnumDescriptor descriptor) { if (v == null) { @@ -550,6 +574,14 @@ public static Value protoEnumArray( return new ProtoEnumArrayImpl(false, enumConstValues, descriptor.getFullName()); } + /** + * Returns an {@code ARRAY} value. + * + * @param v 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}. + * @param protoTypeFqn Fully qualified name of proto representing the enum definition. Use static + * method from proto class {@code MyProtoEnum.getDescriptor().getFullName()} + */ public static Value protoEnumArray(@Nullable Iterable v, String protoTypeFqn) { return new ProtoEnumArrayImpl(v == null, v != null ? immutableCopyOf(v) : null, protoTypeFqn); } @@ -2348,6 +2380,7 @@ private Value getValue(int fieldIndex) { case BOOL: return Value.boolArray(value.getBooleanList(fieldIndex)); case INT64: + case ENUM: return Value.int64Array(value.getLongList(fieldIndex)); case STRING: return Value.stringArray(value.getStringList(fieldIndex)); @@ -2356,6 +2389,7 @@ private Value getValue(int fieldIndex) { case PG_JSONB: return Value.pgJsonbArray(value.getPgJsonbList(fieldIndex)); case BYTES: + case PROTO: return Value.bytesArray(value.getBytesList(fieldIndex)); case FLOAT64: return Value.float64Array(value.getDoubleList(fieldIndex)); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/DirectExecuteResultSetTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/DirectExecuteResultSetTest.java index 069e107c522..1e4f96d1568 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/DirectExecuteResultSetTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/DirectExecuteResultSetTest.java @@ -170,7 +170,7 @@ private void callMethods( } break; default: - fail("method with more than 2 parameter is unknown"); + fail("method with more than 2 parameters is unknown"); } } catch (InvocationTargetException e) { if (e.getCause().getClass().equals(expectedException)) { diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSetTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSetTest.java index dddd63b64e4..bbb34675147 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSetTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSetTest.java @@ -211,7 +211,7 @@ private void callMethods( } break; default: - fail("method with more than 2 parameter is unknown"); + fail("method with more than 2 parameters is unknown"); } } catch (InvocationTargetException e) { if (e.getCause().getClass().equals(expectedException)) { diff --git a/google-cloud-spanner/src/test/resources/com/google/cloud/spanner/singer.proto b/google-cloud-spanner/src/test/resources/com/google/cloud/spanner/singer.proto index 72e5c67b2cc..b62b48ed67f 100644 --- a/google-cloud-spanner/src/test/resources/com/google/cloud/spanner/singer.proto +++ b/google-cloud-spanner/src/test/resources/com/google/cloud/spanner/singer.proto @@ -13,10 +13,9 @@ message SingerInfo { optional Genre genre = 4; } - enum Genre { POP = 0; JAZZ = 1; FOLK = 2; ROCK = 3; -} \ No newline at end of file +}