diff --git a/google-cloud-spanner/clirr-ignored-differences.xml b/google-cloud-spanner/clirr-ignored-differences.xml
index 637ca06280a..8e4710354e3 100644
--- a/google-cloud-spanner/clirr-ignored-differences.xml
+++ b/google-cloud-spanner/clirr-ignored-differences.xml
@@ -192,6 +192,46 @@
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/StructReader
+ java.util.List getProtoEnumList(int, java.util.function.Function)
+
+
+ 7012
+ com/google/cloud/spanner/StructReader
+ java.util.List getProtoEnumList(java.lang.String, java.util.function.Function)
+
+
+ 7012
+ com/google/cloud/spanner/StructReader
+ java.util.List getProtoMessageList(int, com.google.protobuf.AbstractMessage)
+
+
+ 7012
+ com/google/cloud/spanner/StructReader
+ java.util.List 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 789e0945e17..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
@@ -32,12 +32,16 @@
import com.google.cloud.spanner.spi.v1.SpannerRpc;
import com.google.cloud.spanner.v1.stub.SpannerStubSettings;
import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
import com.google.common.collect.AbstractIterator;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.Uninterruptibles;
+import com.google.protobuf.AbstractMessage;
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;
@@ -65,6 +69,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 javax.annotation.Nullable;
@@ -391,6 +396,14 @@ private Object writeReplace() {
case JSON:
builder.set(fieldName).to(Value.json((String) value));
break;
+ case PROTO:
+ 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;
@@ -410,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:
@@ -431,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:
@@ -496,6 +511,7 @@ private static Object decodeValue(Type fieldType, com.google.protobuf.Value prot
checkType(fieldType, proto, KindCase.BOOL_VALUE);
return proto.getBoolValue();
case INT64:
+ case ENUM:
checkType(fieldType, proto, KindCase.STRING_VALUE);
return Long.parseLong(proto.getStringValue());
case FLOAT64:
@@ -510,6 +526,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:
@@ -547,7 +564,8 @@ private static Struct decodeStructValue(Type structType, ListValue structValue)
static Object decodeArrayValue(Type elementType, ListValue listValue) {
switch (elementType.getCode()) {
case INT64:
- // For int64/float64 types, use custom containers. These avoid wrapper object
+ case ENUM:
+ // For int64/float64/enum types, use custom containers. These avoid wrapper object
// creation for non-null arrays.
return new Int64Array(listValue);
case FLOAT64:
@@ -562,6 +580,7 @@ static Object decodeArrayValue(Type elementType, ListValue listValue) {
case TIMESTAMP:
case DATE:
case STRUCT:
+ case PROTO:
return Lists.transform(
listValue.getValuesList(), input -> decodeValue(elementType, input));
default:
@@ -597,6 +616,30 @@ public boolean isNull(int columnIndex) {
return rowData.get(columnIndex) == null;
}
+ @Override
+ protected T getProtoMessageInternal(int columnIndex, T message) {
+ Preconditions.checkNotNull(
+ message,
+ "Proto message may not be null. Use MyProtoClass.getDefaultInstance() as a parameter value.");
+ try {
+ return (T)
+ message
+ .toBuilder()
+ .mergeFrom(((ByteArray) rowData.get(columnIndex)).toByteArray())
+ .build();
+ } catch (InvalidProtocolBufferException e) {
+ throw SpannerExceptionFactory.asSpannerException(e);
+ }
+ }
+
+ @Override
+ protected T getProtoEnumInternal(
+ int columnIndex, Function method) {
+ Preconditions.checkNotNull(
+ method, "Method may not be null. Use 'MyProtoEnum::forNumber' as a parameter value.");
+ return (T) method.apply((int) getLongInternal(columnIndex));
+ }
+
@Override
protected boolean getBooleanInternal(int columnIndex) {
return (Boolean) rowData.get(columnIndex);
@@ -658,6 +701,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:
@@ -672,6 +717,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:
@@ -699,6 +746,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:
@@ -778,6 +831,52 @@ 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 message) {
+ Preconditions.checkNotNull(
+ 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<>(bytesArray.size());
+ for (ByteArray protoMessageBytes : bytesArray) {
+ if (protoMessageBytes == null) {
+ protoMessagesList.add(null);
+ } else {
+ protoMessagesList.add(
+ (T) message.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, "Method may not be null. Use 'MyProtoEnum::forNumber' as a parameter value.");
+
+ List enumIntArray = (List) rowData.get(columnIndex);
+ List protoEnumList = new ArrayList<>(enumIntArray.size());
+ 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) {
@@ -1310,6 +1409,17 @@ protected String getStringInternal(int columnIndex) {
return currRow().getStringInternal(columnIndex);
}
+ @Override
+ protected T getProtoMessageInternal(int columnIndex, T message) {
+ return currRow().getProtoMessageInternal(columnIndex, message);
+ }
+
+ @Override
+ protected T getProtoEnumInternal(
+ int columnIndex, Function method) {
+ return currRow().getProtoEnumInternal(columnIndex, method);
+ }
+
@Override
protected String getJsonInternal(int columnIndex) {
return currRow().getJsonInternal(columnIndex);
@@ -1395,6 +1505,18 @@ protected List getBytesListInternal(int columnIndex) {
return currRow().getBytesListInternal(columnIndex);
}
+ @Override
+ protected List getProtoMessageListInternal(
+ int columnIndex, T message) {
+ return currRow().getProtoMessageListInternal(columnIndex, message);
+ }
+
+ @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 1e897636245..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
@@ -21,9 +21,14 @@
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.ProtocolMessageEnum;
import java.math.BigDecimal;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
+import java.util.function.Function;
/**
* Base class for assisting {@link StructReader} implementations.
@@ -58,6 +63,25 @@ protected String getPgJsonbInternal(int columnIndex) {
protected abstract Date getDateInternal(int columnIndex);
+ protected T getProtoMessageInternal(int columnIndex, T message) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ protected T getProtoEnumInternal(
+ int columnIndex, Function method) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ protected List getProtoMessageListInternal(
+ int columnIndex, T message) {
+ 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");
}
@@ -129,14 +153,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);
}
@@ -212,14 +236,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);
}
@@ -249,6 +273,34 @@ public Date getDate(String columnName) {
return getDateInternal(columnIndex);
}
+ @Override
+ public T getProtoEnum(
+ int columnIndex, Function method) {
+ checkNonNullOfCodes(columnIndex, Arrays.asList(Code.ENUM, Code.INT64), columnIndex);
+ return getProtoEnumInternal(columnIndex, method);
+ }
+
+ @Override
+ public T getProtoEnum(
+ String columnName, Function method) {
+ int columnIndex = getColumnIndex(columnName);
+ checkNonNullOfCodes(columnIndex, Arrays.asList(Code.ENUM, Code.INT64), columnName);
+ return getProtoEnumInternal(columnIndex, method);
+ }
+
+ @Override
+ public T getProtoMessage(int columnIndex, T message) {
+ checkNonNullOfCodes(columnIndex, Arrays.asList(Code.PROTO, Code.BYTES), columnIndex);
+ return getProtoMessageInternal(columnIndex, message);
+ }
+
+ @Override
+ public T getProtoMessage(String columnName, T message) {
+ int columnIndex = getColumnIndex(columnName);
+ checkNonNullOfCodes(columnIndex, Arrays.asList(Code.PROTO, Code.BYTES), columnName);
+ return getProtoMessageInternal(columnIndex, message);
+ }
+
@Override
public Value getValue(int columnIndex) {
checkNonNull(columnIndex, columnIndex);
@@ -302,14 +354,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), columnName);
+ checkArrayElementType(columnIndex, Arrays.asList(Code.ENUM, Code.INT64), columnName);
return getLongListInternal(columnIndex);
}
@@ -401,17 +455,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), columnName);
+ checkArrayElementType(columnIndex, Arrays.asList(Code.PROTO, Code.BYTES), columnName);
return getBytesListInternal(columnIndex);
}
+ @Override
+ 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, message);
+ }
+
+ @Override
+ 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, message);
+ }
+
+ @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);
@@ -477,6 +565,30 @@ private void checkNonNullOfType(int columnIndex, Type expectedType, Object colum
checkNonNull(columnIndex, columnNameForError);
}
+ /** Checks if the value at {@code columnIndex} is one of {@code expectedCode} */
+ private void checkNonNullOfCodes(
+ int columnIndex, List expectedCodes, Object columnNameForError) {
+ Type actualType = getColumnType(columnIndex);
+ checkState(
+ expectedCodes.contains(actualType.getCode()),
+ "Column %s is not of correct type code: expected one of [%s] but was %s",
+ columnNameForError,
+ expectedCodes,
+ actualType);
+ checkNonNull(columnIndex, columnNameForError);
+ }
+
+ private void checkArrayElementType(
+ int columnIndex, List expectedCodes, Object columnNameForError) {
+ 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",
+ columnNameForError,
+ expectedCodes,
+ Type.array(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 2a85006fa95..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
@@ -22,8 +22,11 @@
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.ProtocolMessageEnum;
import java.math.BigDecimal;
import java.util.List;
+import java.util.function.Function;
/** Forwarding implements of StructReader */
public class ForwardingStructReader implements StructReader {
@@ -370,6 +373,32 @@ public List getDateList(String columnName) {
return delegate.get().getDateList(columnName);
}
+ @Override
+ public List getProtoMessageList(int columnIndex, T message) {
+ checkValidState();
+ return delegate.get().getProtoMessageList(columnIndex, message);
+ }
+
+ @Override
+ public List getProtoMessageList(String columnName, T message) {
+ checkValidState();
+ return delegate.get().getProtoMessageList(columnName, message);
+ }
+
+ @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();
@@ -382,6 +411,32 @@ public List getStructList(String columnName) {
return delegate.get().getStructList(columnName);
}
+ @Override
+ public T getProtoMessage(int columnIndex, T message) {
+ checkValidState();
+ return delegate.get().getProtoMessage(columnIndex, message);
+ }
+
+ @Override
+ public T getProtoMessage(String columnName, T message) {
+ checkValidState();
+ return delegate.get().getProtoMessage(columnName, message);
+ }
+
+ @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/Key.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Key.java
index 15d4e995bf0..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
@@ -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,10 @@ 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()));
} else {
throw new AssertionError("Illegal key part: " + part.getClass());
}
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 fa054ba0cda..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
@@ -29,10 +29,13 @@
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.ProtocolMessageEnum;
import com.google.spanner.v1.ResultSetMetadata;
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 {
@@ -300,6 +303,28 @@ public Date getDate(String columnName) {
return getCurrentRowAsStruct().getDate(columnName);
}
+ @Override
+ public T getProtoMessage(int columnIndex, T message) {
+ return getCurrentRowAsStruct().getProtoMessage(columnIndex, message);
+ }
+
+ @Override
+ public T getProtoMessage(String columnName, T message) {
+ return getCurrentRowAsStruct().getProtoMessage(columnName, message);
+ }
+
+ @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);
@@ -440,6 +465,28 @@ public List getDateList(String columnName) {
return getCurrentRowAsStruct().getDateList(columnName);
}
+ @Override
+ public List getProtoMessageList(int columnIndex, T message) {
+ return getCurrentRowAsStruct().getProtoMessageList(columnIndex, message);
+ }
+
+ @Override
+ public List getProtoMessageList(String columnName, T message) {
+ return getCurrentRowAsStruct().getProtoMessageList(columnName, message);
+ }
+
+ @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 48c989d145e..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
@@ -28,11 +28,14 @@
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.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;
/**
@@ -217,6 +220,17 @@ protected Date getDateInternal(int columnIndex) {
return values.get(columnIndex).getDate();
}
+ @Override
+ protected T getProtoMessageInternal(int columnIndex, T message) {
+ return values.get(columnIndex).getProtoMessage(message);
+ }
+
+ @Override
+ protected T getProtoEnumInternal(
+ int columnIndex, Function method) {
+ return values.get(columnIndex).getProtoEnum(method);
+ }
+
@Override
protected Value getValueInternal(int columnIndex) {
return values.get(columnIndex);
@@ -287,6 +301,18 @@ protected List getTimestampListInternal(int columnIndex) {
return values.get(columnIndex).getTimestampArray();
}
+ @Override
+ protected List getProtoMessageListInternal(
+ int columnIndex, T message) {
+ return values.get(columnIndex).getProtoMessageArray(message);
+ }
+
+ @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();
@@ -354,6 +380,7 @@ private Object getAsObject(int columnIndex) {
case BOOL:
return getBooleanInternal(columnIndex);
case INT64:
+ case ENUM:
return getLongInternal(columnIndex);
case FLOAT64:
return getDoubleInternal(columnIndex);
@@ -368,6 +395,7 @@ private Object getAsObject(int columnIndex) {
case PG_JSONB:
return getPgJsonbInternal(columnIndex);
case BYTES:
+ case PROTO:
return getBytesInternal(columnIndex);
case TIMESTAMP:
return getTimestampInternal(columnIndex);
@@ -380,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);
@@ -394,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 b767bd6d82c..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
@@ -19,8 +19,11 @@
import com.google.cloud.ByteArray;
import com.google.cloud.Date;
import com.google.cloud.Timestamp;
+import com.google.protobuf.AbstractMessage;
+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
@@ -51,6 +54,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
@@ -134,6 +138,60 @@ 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 message Proto message object. Message can't be null as it's internally used to find the
+ * type of proto. Use @code{MyProtoClass.getDefaultInstance()}. @see getDefaultInstance()
+ * @return The value of a non-{@code NULL} column with type {@link Type#proto(String)} ()}.
+ */
+ default T getProtoMessage(int columnIndex, T message) {
+ 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 message Proto message object. Message can't be null as it's internally used to find the
+ * type of proto. Use @code{MyProtoClass.getDefaultInstance()}. @see getDefaultInstance()
+ * @return The value of a non-{@code NULL} column with type {@link Type#proto(String)} ()}.
+ */
+ default T getProtoMessage(String columnName, T message) {
+ 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 A function that takes enum integer constant as argument and returns the enum. Use
+ * method @code{forNumber} from generated enum class (eg: MyProtoEnum::forNumber). @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 A function that takes enum integer constant as argument and returns the enum. Use
+ * method @code{forNumber} from generated enum class (eg: MyProtoEnum::forNumber). @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);
@@ -316,6 +374,64 @@ 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 message Proto message object. Message can't be null as it's internally used to find the
+ * type of proto. Use @code{MyProtoClass.getDefaultInstance()}. @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 message) {
+ 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 message Proto message object. Message can't be null as it's internally used to find the
+ * type of proto. Use @code{MyProtoClass.getDefaultInstance()}. @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 message) {
+ 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 A function that takes enum integer constant as argument and returns the enum. Use
+ * method @code{forNumber} from generated enum class (eg: MyProtoEnum::forNumber). @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 A function that takes enum integer constant as argument and returns the enum. Use
+ * method @code{forNumber} from generated enum class (eg: MyProtoEnum::forNumber). @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())}. The
* list returned by this method is lazily constructed. Create a copy of it if you intend to access
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..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
@@ -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;
@@ -122,6 +123,24 @@ 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, 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, protoTypeFqn);
+ }
+
/** Returns the descriptor for the {@code BYTES} type: a variable-length byte string. */
public static Type bytes() {
return TYPE_BYTES;
@@ -193,6 +212,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
@@ -209,6 +229,11 @@ private Type(
this.structFields = structFields;
}
+ private Type(Code code, @Nonnull String protoTypeFqn) {
+ this(code, null, null);
+ this.protoTypeFqn = protoTypeFqn;
+ }
+
/** Enumerates the categories of types. */
public enum Code {
BOOL(TypeCode.BOOL),
@@ -219,6 +244,8 @@ public enum Code {
STRING(TypeCode.STRING),
JSON(TypeCode.JSON),
PG_JSONB(TypeCode.JSON, TypeAnnotationCode.PG_JSONB),
+ PROTO(TypeCode.PROTO),
+ ENUM(TypeCode.ENUM),
BYTES(TypeCode.BYTES),
TIMESTAMP(TypeCode.TIMESTAMP),
DATE(TypeCode.DATE),
@@ -340,6 +367,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.PROTO or code() != Code.ENUM}
+ */
+ 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.
*
@@ -416,7 +454,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
@@ -435,7 +474,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();
}
@@ -464,6 +506,10 @@ static Type fromProto(com.google.spanner.v1.Type proto) {
return timestamp();
case DATE:
return date();
+ case PROTO:
+ 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 82f03e859e1..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
@@ -24,8 +24,13 @@
import com.google.common.base.Preconditions;
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;
+import com.google.protobuf.ProtocolMessageEnum;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.ArrayList;
@@ -35,6 +40,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;
@@ -218,7 +224,87 @@ public static Value pgJsonb(@Nullable String v) {
}
/**
- * Returns a {@code BYTES} value.
+ * Return a {@code PROTO} value for not null proto messages.
+ *
+ * @param v Not null Proto message.
+ */
+ public static Value protoMessage(AbstractMessage v) {
+ Preconditions.checkNotNull(
+ v, "Use protoMessage((ByteArray) null, MyProtoClass.getDescriptor()) 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 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 protoTypeFqn) {
+ return new ProtoMessageImpl(v == null, v, protoTypeFqn);
+ }
+
+ /**
+ * Return a {@code PROTO} value
+ *
+ * @param v Serialized Proto Array, which may be null.
+ * @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.");
+ return protoMessage(v, descriptor.getFullName());
+ }
+
+ /**
+ * Return a {@code ENUM} value for not null proto messages.
+ *
+ * @param v Proto Enum, which may be null.
+ */
+ public static Value protoEnum(ProtocolMessageEnum v) {
+ Preconditions.checkNotNull(
+ v, "Use protoEnum((Long) null, MyProtoEnum.getDescriptor()) for null values.");
+ return protoEnum(v.getNumber(), v.getDescriptorForType().getFullName());
+ }
+
+ /**
+ * Return a {@code ENUM} value.
+ *
+ * @param v Enum non-primitive Integer constant.
+ * @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 protoTypeFqn) {
+ return new ProtoEnumImpl(v == null, v, protoTypeFqn);
+ }
+
+ /**
+ * Return a {@code ENUM} value.
+ *
+ * @param v Enum non-primitive Integer constant.
+ * @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.");
+ return protoEnum(v, enumDescriptor.getFullName());
+ }
+
+ /**
+ * Return a {@code ENUM} value.
+ *
+ * @param v Enum integer primitive constant.
+ * @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 protoTypeFqn) {
+ return new ProtoEnumImpl(false, v, protoTypeFqn);
+ }
+
+ /**
+ * e Returns a {@code BYTES} value. Returns a {@code BYTES} value.
*
* @param v the value, which may be null
*/
@@ -421,6 +507,85 @@ 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) {
+ 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());
+ }
+
+ /**
+ * 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) {
+ 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());
+ }
+
+ /**
+ * 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);
+ }
+
/**
* Returns an {@code ARRAY} value.
*
@@ -541,6 +706,25 @@ 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");
+ }
+
/**
* Returns the value of a {@code BYTES}-typed instance.
*
@@ -633,6 +817,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.
@@ -1123,6 +1328,15 @@ public long getInt64() {
return value;
}
+ @Override
+ public T getProtoEnum(
+ Function method) {
+ Preconditions.checkNotNull(
+ method, "Method may not be null. Use 'MyProtoEnum::forNumber' as a parameter value.");
+ checkNotNull();
+ return (T) method.apply((int) value);
+ }
+
@Override
com.google.protobuf.Value valueToProto() {
return com.google.protobuf.Value.newBuilder().setStringValue(Long.toString(value)).build();
@@ -1310,6 +1524,19 @@ public ByteArray getBytes() {
return value;
}
+ @Override
+ public T getProtoMessage(T m) {
+ Preconditions.checkNotNull(
+ m,
+ "Proto message may not be null. Use MyProtoClass.getDefaultInstance() as a parameter value.");
+ 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();
@@ -1321,6 +1548,75 @@ void valueToString(StringBuilder b) {
}
}
+ private static class ProtoMessageImpl extends AbstractObjectValue {
+
+ private ProtoMessageImpl(boolean isNull, ByteArray serializedProtoArray, String protoTypeFqn) {
+ super(isNull, Type.proto(protoTypeFqn), serializedProtoArray);
+ }
+
+ @Override
+ public ByteArray getBytes() {
+ checkNotNull();
+ return value;
+ }
+
+ @Override
+ public T getProtoMessage(T m) {
+ Preconditions.checkNotNull(
+ m,
+ "Proto message may not be null. Use MyProtoClass.getDefaultInstance() as a parameter value.");
+ checkNotNull();
+ try {
+ return (T) m.toBuilder().mergeFrom(value.toByteArray()).build();
+ } catch (InvalidProtocolBufferException e) {
+ throw SpannerExceptionFactory.asSpannerException(e);
+ }
+ }
+
+ @Override
+ com.google.protobuf.Value valueToProto() {
+ String base64EncodedString = value.toBase64();
+ return com.google.protobuf.Value.newBuilder().setStringValue(base64EncodedString).build();
+ }
+
+ @Override
+ void valueToString(StringBuilder b) {
+ b.append(value.toString());
+ }
+ }
+
+ private static class ProtoEnumImpl extends AbstractObjectValue {
+
+ private ProtoEnumImpl(boolean isNull, Long enumValue, String protoTypeFqn) {
+ super(isNull, Type.protoEnum(protoTypeFqn), enumValue);
+ }
+
+ @Override
+ public long getInt64() {
+ checkNotNull();
+ return value;
+ }
+
+ @Override
+ public T getProtoEnum(
+ Function method) {
+ Preconditions.checkNotNull(
+ method, "Method may not be null. Use 'MyProtoEnum::forNumber' as a parameter value.");
+ checkNotNull();
+ return (T) method.apply(value.intValue());
+ }
+
+ @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)).build();
+ }
+ }
+
private static class TimestampImpl extends AbstractObjectValue {
private static final String COMMIT_TIMESTAMP_STRING = "spanner.commit_timestamp()";
@@ -1573,6 +1869,24 @@ public List getInt64Array() {
return getArray();
}
+ @Override
+ public List getProtoEnumArray(
+ Function method) {
+ Preconditions.checkNotNull(
+ method, "Method may not be null. Use 'MyProtoEnum::forNumber' as a parameter value.");
+ 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;
@@ -1760,6 +2074,28 @@ public List getBytesArray() {
return value;
}
+ @Override
+ public List getProtoMessageArray(T m) {
+ Preconditions.checkNotNull(
+ m,
+ "Proto message may not be null. Use MyProtoClass.getDefaultInstance() as a parameter value.");
+ checkNotNull();
+ try {
+ List protoMessagesList = new ArrayList<>(value.size());
+ 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();
@@ -1789,6 +2125,91 @@ 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 may not be null. Use MyProtoClass.getDefaultInstance() as a parameter value.");
+ checkNotNull();
+ try {
+ List protoMessagesList = new ArrayList<>(value.size());
+ 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, "Method may not be null. Use 'MyProtoEnum::forNumber' as a parameter value.");
+ 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) {
@@ -1946,6 +2367,10 @@ 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.getBytes(fieldIndex), fieldType.getProtoTypeFqn());
+ case ENUM:
+ return Value.protoEnum(value.getLong(fieldIndex), fieldType.getProtoTypeFqn());
case STRUCT:
return Value.struct(value.getStruct(fieldIndex));
case ARRAY:
@@ -1955,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));
@@ -1963,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/main/java/com/google/cloud/spanner/ValueBinder.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ValueBinder.java
index ec9e5a43d8f..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
@@ -19,6 +19,10 @@
import com.google.cloud.ByteArray;
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;
@@ -95,6 +99,41 @@ public R to(@Nullable String value) {
return handle(Value.string(value));
}
+ /** Binds to {@code Value.protoMessage(value)} */
+ 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.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));
+ }
+
+ /** 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, 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));
+ }
+
/** Binds to {@code Value.bytes(value)} */
public R to(@Nullable ByteArray value) {
return handle(Value.bytes(value));
@@ -203,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 bb2f2fb817a..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
@@ -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,6 +241,7 @@ public void funnel(Struct row, PrimitiveSink into) {
funnelValue(type, row.getString(i), into);
break;
case INT64:
+ case ENUM:
funnelValue(type, row.getLong(i), into);
break;
case STRING:
@@ -274,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);
@@ -304,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);
@@ -357,6 +361,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());
@@ -374,6 +379,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:
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 8690e154f4e..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
@@ -25,10 +25,13 @@
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.ProtocolMessageEnum;
import com.google.spanner.v1.ResultSetMetadata;
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
@@ -425,6 +428,32 @@ public List getDateList(String columnName) {
return delegate.getDateList(columnName);
}
+ @Override
+ public List getProtoMessageList(int columnIndex, T message) {
+ Preconditions.checkState(nextCalledByClient, MISSING_NEXT_CALL);
+ return delegate.getProtoMessageList(columnIndex, message);
+ }
+
+ @Override
+ public List getProtoMessageList(String columnName, T message) {
+ Preconditions.checkState(nextCalledByClient, MISSING_NEXT_CALL);
+ return delegate.getProtoMessageList(columnName, message);
+ }
+
+ @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);
@@ -437,6 +466,32 @@ public List getStructList(String columnName) {
return delegate.getStructList(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 message) {
+ Preconditions.checkState(nextCalledByClient, MISSING_NEXT_CALL);
+ return delegate.getProtoMessage(columnIndex, message);
+ }
+
+ @Override
+ public T getProtoMessage(String columnName, T message) {
+ Preconditions.checkState(nextCalledByClient, MISSING_NEXT_CALL);
+ return delegate.getProtoMessage(columnName, message);
+ }
+
@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 07e755b2b25..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
@@ -27,10 +27,13 @@
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.ProtocolMessageEnum;
import com.google.spanner.v1.ResultSetMetadata;
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
@@ -430,6 +433,32 @@ public List getDateList(String columnName) {
return delegate.getDateList(columnName);
}
+ @Override
+ public List getProtoMessageList(int columnIndex, T message) {
+ checkClosed();
+ return delegate.getProtoMessageList(columnIndex, message);
+ }
+
+ @Override
+ public List getProtoMessageList(String columnName, T message) {
+ checkClosed();
+ return delegate.getProtoMessageList(columnName, message);
+ }
+
+ @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();
@@ -441,4 +470,30 @@ public List getStructList(String columnName) {
checkClosed();
return delegate.getStructList(columnName);
}
+
+ @Override
+ public T getProtoMessage(int columnIndex, T message) {
+ checkClosed();
+ return delegate.getProtoMessage(columnIndex, message);
+ }
+
+ @Override
+ public T getProtoMessage(String columnName, T message) {
+ checkClosed();
+ return delegate.getProtoMessage(columnName, message);
+ }
+
+ @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 1b6280a6369..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
@@ -27,6 +27,8 @@
import com.google.cloud.Date;
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;
@@ -34,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;
@@ -95,6 +98,17 @@ protected Date getDateInternal(int columnIndex) {
return null;
}
+ @Override
+ protected T getProtoMessageInternal(int columnIndex, T message) {
+ return null;
+ }
+
+ @Override
+ protected T getProtoEnumInternal(
+ int columnIndex, Function method) {
+ return null;
+ }
+
@Override
protected Value getValueInternal(int columnIndex) {
return null;
@@ -160,6 +174,18 @@ protected List getTimestampListInternal(int columnIndex) {
return null;
}
+ @Override
+ protected List getProtoMessageListInternal(
+ int columnIndex, T message) {
+ 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 ff4e92a5215..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
@@ -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};
@@ -876,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 fe2b7aec94b..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
@@ -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")
@@ -603,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 87be602808c..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
@@ -28,9 +28,13 @@
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.AbstractMessage;
+import com.google.protobuf.ProtocolMessageEnum;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collections;
@@ -52,6 +56,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;
@@ -80,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(
@@ -94,6 +109,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())),
@@ -103,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")
@@ -128,6 +152,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")
@@ -148,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()
@@ -173,6 +209,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")
@@ -193,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));
@@ -259,6 +307,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);
@@ -305,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/SingerProto.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SingerProto.java
new file mode 100644
index 00000000000..56fb82c15d2
--- /dev/null
+++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SingerProto.java
@@ -0,0 +1,1199 @@
+/*
+ * 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 {
+ 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 spanner.examples.music.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 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:spanner.examples.music.Genre)
+ }
+
+ 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();
+
+ /**
+ * 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 .spanner.examples.music.Genre genre = 4;
+ *
+ * @return Whether the genre field is set.
+ */
+ boolean hasGenre();
+ /**
+ * optional .spanner.examples.music.Genre genre = 4;
+ *
+ * @return The genre.
+ */
+ com.google.cloud.spanner.SingerProto.Genre getGenre();
+ }
+ /** 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_ = "";
+ 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_spanner_examples_music_SingerInfo_descriptor;
+ }
+
+ @java.lang.Override
+ protected com.google.protobuf.GeneratedMessageV3.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);
+ }
+
+ 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 .spanner.examples.music.Genre genre = 4;
+ *
+ * @return Whether the genre field is set.
+ */
+ @java.lang.Override
+ public boolean hasGenre() {
+ return ((bitField0_ & 0x00000008) != 0);
+ }
+ /**
+ * 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_);
+ 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 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_spanner_examples_music_SingerInfo_descriptor;
+ }
+
+ @java.lang.Override
+ protected com.google.protobuf.GeneratedMessageV3.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);
+ }
+
+ // 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_spanner_examples_music_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_;
+ /**
+ *