Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for Proto Columns #2779

Merged
merged 32 commits into from Jan 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
cbabd6c
feat: Support for Proto Messages & Enums (#2155)
gauravpurohit06 Dec 28, 2022
1fb6e94
samples: Adding samples for updating & querying Proto messages & enum…
gauravpurohit06 Jan 5, 2023
6c97b99
test: Proto Column Integration tests (#2212)
gauravpurohit06 Jan 5, 2023
dd358c2
Merge remote-tracking branch 'upstream/main' into proto-column-enhanc…
gauravpurohit06 Jan 5, 2023
53397e6
Configured jitpack.yml to use OpenJDK 11 (#2218)
gauravpurohit06 Jan 5, 2023
2f8eb42
feat: add support for Proto Columns DDL (#2277)
harshachinta Mar 15, 2023
a70f259
Merge branch 'main' into proto-column-enhancement-alpha
harshachinta Jun 8, 2023
9513753
teat: update pom file to run tests on cloud-devel region temporarily …
harshachinta Jun 8, 2023
6701f52
🦉 Updates from OwlBot post-processor
gcf-owl-bot[bot] Jun 8, 2023
f63da18
fix: revert host changes in pom.xml file
harshachinta Jun 9, 2023
5fbef25
Merge branch 'main' into proto-column-enhancement-alpha
harshachinta Jan 10, 2024
7f5bd98
🦉 Updates from OwlBot post-processor
gcf-owl-bot[bot] Jan 10, 2024
058b75c
feat(spanner): revert autogenerated code
harshachinta Jan 24, 2024
e01d0c7
🦉 Updates from OwlBot post-processor
gcf-owl-bot[bot] Jan 24, 2024
fb482c4
Merge branch 'main' into proto-column-enhancement-alpha
harshachinta Jan 24, 2024
45ad85e
feat(spanner): remove samples
harshachinta Jan 24, 2024
ab35680
🦉 Updates from OwlBot post-processor
gcf-owl-bot[bot] Jan 24, 2024
d7fbc09
feat(spanner): remove clirr
harshachinta Jan 24, 2024
2de342f
feat(spanner): skip emulator test
harshachinta Jan 24, 2024
d18c28e
feat(spanner): clirr
harshachinta Jan 24, 2024
3e46cd9
feat(spanner): fix javadoc
harshachinta Jan 24, 2024
ee333a3
🦉 Updates from OwlBot post-processor
gcf-owl-bot[bot] Jan 24, 2024
e105101
feat(spanner): fix javadoc
harshachinta Jan 24, 2024
7dfe81e
feat(spanner): fix javadoc
harshachinta Jan 24, 2024
5491ca3
feat(spanner): fix javadoc
harshachinta Jan 24, 2024
5e972fe
🦉 Updates from OwlBot post-processor
gcf-owl-bot[bot] Jan 24, 2024
74c1e86
Merge branch 'proto-column-enhancement-alpha' of https://github.com/g…
gcf-owl-bot[bot] Jan 24, 2024
be99f94
feat(spanner): fix emulator skip
harshachinta Jan 25, 2024
b8915b1
build: ignore all changes in v1 package
olavloite Jan 25, 2024
2f75cb0
feat(spanner): add optimizations to deserialize proto messages
harshachinta Jan 26, 2024
63c720d
feat(spanner): remove TODO
harshachinta Jan 26, 2024
42fafc0
feat(spanner): remove TODO
harshachinta Jan 26, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
79 changes: 79 additions & 0 deletions google-cloud-spanner/clirr-ignored-differences.xml
Expand Up @@ -192,6 +192,46 @@
<className>com/google/cloud/spanner/StructReader</className>
<method>java.util.List getPgJsonbList(java.lang.String)</method>
</difference>
<difference>
<differenceType>7012</differenceType>
<className>com/google/cloud/spanner/StructReader</className>
<method>com.google.protobuf.ProtocolMessageEnum getProtoEnum(int, java.util.function.Function)</method>
</difference>
<difference>
<differenceType>7012</differenceType>
<className>com/google/cloud/spanner/StructReader</className>
<method>com.google.protobuf.ProtocolMessageEnum getProtoEnum(java.lang.String, java.util.function.Function)</method>
</difference>
<difference>
<differenceType>7012</differenceType>
<className>com/google/cloud/spanner/StructReader</className>
<method>com.google.protobuf.AbstractMessage getProtoMessage(int, com.google.protobuf.AbstractMessage)</method>
</difference>
<difference>
<differenceType>7012</differenceType>
<className>com/google/cloud/spanner/StructReader</className>
<method>com.google.protobuf.AbstractMessage getProtoMessage(java.lang.String, com.google.protobuf.AbstractMessage)</method>
</difference>
<difference>
<differenceType>7012</differenceType>
<className>com/google/cloud/spanner/StructReader</className>
<method>java.util.List getProtoEnumList(int, java.util.function.Function)</method>
</difference>
<difference>
<differenceType>7012</differenceType>
<className>com/google/cloud/spanner/StructReader</className>
<method>java.util.List getProtoEnumList(java.lang.String, java.util.function.Function)</method>
</difference>
<difference>
<differenceType>7012</differenceType>
<className>com/google/cloud/spanner/StructReader</className>
<method>java.util.List getProtoMessageList(int, com.google.protobuf.AbstractMessage)</method>
</difference>
<difference>
<differenceType>7012</differenceType>
<className>com/google/cloud/spanner/StructReader</className>
<method>java.util.List getProtoMessageList(java.lang.String, com.google.protobuf.AbstractMessage)</method>
</difference>
<difference>
<differenceType>7012</differenceType>
<className>com/google/cloud/spanner/BatchClient</className>
Expand Down Expand Up @@ -222,6 +262,38 @@
<className>com/google/cloud/spanner/connection/Connection</className>
<method>com.google.cloud.spanner.ResultSet analyzeUpdateStatement(com.google.cloud.spanner.Statement, com.google.cloud.spanner.ReadContext$QueryAnalyzeMode, com.google.cloud.spanner.Options$UpdateOption[])</method>
</difference>
<difference>
<differenceType>7012</differenceType>
<className>com/google/cloud/spanner/DatabaseAdminClient</className>
<method>com.google.spanner.admin.database.v1.GetDatabaseDdlResponse getDatabaseDdlResponse(java.lang.String, java.lang.String)</method>
</difference>
<difference>
<differenceType>7012</differenceType>
<className>com/google/cloud/spanner/DatabaseAdminClient</className>
<method>com.google.api.gax.longrunning.OperationFuture updateDatabaseDdl(com.google.cloud.spanner.Database, java.lang.Iterable, java.lang.String)</method>
</difference>
<difference>
<differenceType>7013</differenceType>
<className>com/google/cloud/spanner/DatabaseInfo$Builder</className>
<method>com.google.cloud.spanner.DatabaseInfo$Builder setProtoDescriptors(byte[])</method>
</difference>
<difference>
<differenceType>7013</differenceType>
<className>com/google/cloud/spanner/DatabaseInfo$Builder</className>
<method>com.google.cloud.spanner.DatabaseInfo$Builder setProtoDescriptors(java.io.InputStream)</method>
</difference>
<difference>
<differenceType>7013</differenceType>
<className>com/google/cloud/spanner/DatabaseInfo$Builder</className>
<method>com.google.cloud.spanner.DatabaseInfo$Builder setProtoDescriptors(java.lang.String)</method>
</difference>
<difference>
<differenceType>7006</differenceType>
<className>com/google/cloud/spanner/spi/v1/GapicSpannerRpc</className>
<method>java.util.List getDatabaseDdl(java.lang.String)</method>
<from>java.util.List</from>
<to>com.google.spanner.admin.database.v1.GetDatabaseDdlResponse</to>
</difference>
<difference>
<differenceType>7004</differenceType>
<className>com/google/cloud/spanner/spi/v1/GapicSpannerRpc</className>
Expand Down Expand Up @@ -277,6 +349,13 @@
<method>com.google.spanner.v1.ResultSet executeQuery(com.google.spanner.v1.ExecuteSqlRequest, java.util.Map)</method>
<to>com.google.cloud.spanner.spi.v1.SpannerRpc$StreamingCall</to>
</difference>
<difference>
<differenceType>7006</differenceType>
<className>com/google/cloud/spanner/spi/v1/SpannerRpc</className>
<method>java.util.List getDatabaseDdl(java.lang.String)</method>
<from>java.util.List</from>
<to>com.google.spanner.admin.database.v1.GetDatabaseDdlResponse</to>
</difference>
<difference>
<differenceType>7004</differenceType>
<className>com/google/cloud/spanner/spi/v1/SpannerRpc</className>
Expand Down
9 changes: 9 additions & 0 deletions google-cloud-spanner/pom.xml
Expand Up @@ -134,6 +134,15 @@
<groupId>org.codehaus.mojo</groupId>
<artifactId>flatten-maven-plugin</artifactId>
</plugin>

<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>clirr-maven-plugin</artifactId>
<configuration>
<!-- Exclude (public) classes in this internal package -->
<excludes>com/google/cloud/spanner/spi/v1/**</excludes>
</configuration>
</plugin>
</plugins>
<pluginManagement>
<plugins>
Expand Down
Expand Up @@ -39,10 +39,13 @@
import com.google.common.collect.AbstractIterator;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.io.CharSource;
import com.google.common.util.concurrent.Uninterruptibles;
import com.google.protobuf.AbstractMessage;
import com.google.protobuf.ByteString;
import com.google.protobuf.ListValue;
import com.google.protobuf.NullValue;
import com.google.protobuf.ProtocolMessageEnum;
import com.google.protobuf.Value.KindCase;
import com.google.spanner.v1.PartialResultSet;
import com.google.spanner.v1.ResultSetMetadata;
Expand All @@ -58,6 +61,7 @@
import java.io.IOException;
import java.io.Serializable;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Base64;
Expand All @@ -73,6 +77,7 @@
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -477,6 +482,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;
Expand All @@ -500,6 +513,7 @@ private Object writeReplace() {
builder.set(fieldName).toBoolArray((Iterable<Boolean>) value);
break;
case INT64:
case ENUM:
builder.set(fieldName).toInt64Array((Iterable<Long>) value);
break;
case FLOAT64:
Expand All @@ -521,6 +535,7 @@ private Object writeReplace() {
builder.set(fieldName).toPgJsonbArray((Iterable<String>) value);
break;
case BYTES:
case PROTO:
builder
.set(fieldName)
.toBytesArrayFromBase64(
Expand Down Expand Up @@ -596,6 +611,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:
Expand All @@ -610,6 +626,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 new LazyByteArray(proto.getStringValue());
case TIMESTAMP:
Expand Down Expand Up @@ -649,7 +666,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:
Expand All @@ -664,6 +682,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:
Expand Down Expand Up @@ -699,6 +718,35 @@ public boolean isNull(int columnIndex) {
return rowData.get(columnIndex) == null;
}

@Override
protected <T extends AbstractMessage> 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(
Base64.getDecoder()
.wrap(
CharSource.wrap(((LazyByteArray) rowData.get(columnIndex)).base64String)
.asByteSource(StandardCharsets.UTF_8)
.openStream()))
.build();
} catch (IOException ioException) {
throw SpannerExceptionFactory.asSpannerException(ioException);
}
}

@Override
protected <T extends ProtocolMessageEnum> T getProtoEnumInternal(
int columnIndex, Function<Integer, ProtocolMessageEnum> 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);
Expand Down Expand Up @@ -768,6 +816,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:
Expand All @@ -782,6 +832,8 @@ protected Value getValueInternal(int columnIndex) {
return Value.pgJsonb(isNull ? null : getPgJsonbInternal(columnIndex));
case BYTES:
return Value.internalBytes(isNull ? null : getLazyBytesInternal(columnIndex));
case PROTO:
return Value.protoMessage(getBytesInternal(columnIndex), columnType.getProtoTypeFqn());
case TIMESTAMP:
return Value.timestamp(isNull ? null : getTimestampInternal(columnIndex));
case DATE:
Expand Down Expand Up @@ -812,6 +864,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:
Expand Down Expand Up @@ -891,6 +949,61 @@ protected List<String> getJsonListInternal(int columnIndex) {
return Collections.unmodifiableList((List<String>) rowData.get(columnIndex));
}

@Override
@SuppressWarnings("unchecked") // We know ARRAY<PROTO> produces a List<ByteArray>.
protected <T extends AbstractMessage> List<T> getProtoMessageListInternal(
int columnIndex, T message) {
Preconditions.checkNotNull(
message,
"Proto message may not be null. Use MyProtoClass.getDefaultInstance() as a parameter value.");

List<LazyByteArray> bytesArray = (List<LazyByteArray>) rowData.get(columnIndex);

try {
List<T> protoMessagesList = new ArrayList<>(bytesArray.size());
for (LazyByteArray protoMessageBytes : bytesArray) {
if (protoMessageBytes == null) {
protoMessagesList.add(null);
} else {
protoMessagesList.add(
(T)
message
.toBuilder()
.mergeFrom(
Base64.getDecoder()
.wrap(
CharSource.wrap(protoMessageBytes.base64String)
.asByteSource(StandardCharsets.UTF_8)
.openStream()))
.build());
}
}
return protoMessagesList;
} catch (IOException ioException) {
throw SpannerExceptionFactory.asSpannerException(ioException);
}
}

@Override
@SuppressWarnings("unchecked") // We know ARRAY<ENUM> produces a List<Long>.
protected <T extends ProtocolMessageEnum> List<T> getProtoEnumListInternal(
int columnIndex, Function<Integer, ProtocolMessageEnum> method) {
Preconditions.checkNotNull(
method, "Method may not be null. Use 'MyProtoEnum::forNumber' as a parameter value.");

List<Long> enumIntArray = (List<Long>) rowData.get(columnIndex);
List<T> 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<JSONB> produces a List<String>.
protected List<String> getPgJsonbListInternal(int columnIndex) {
Expand Down Expand Up @@ -1489,6 +1602,17 @@ protected String getStringInternal(int columnIndex) {
return currRow().getStringInternal(columnIndex);
}

@Override
protected <T extends AbstractMessage> T getProtoMessageInternal(int columnIndex, T message) {
return currRow().getProtoMessageInternal(columnIndex, message);
}

@Override
protected <T extends ProtocolMessageEnum> T getProtoEnumInternal(
int columnIndex, Function<Integer, ProtocolMessageEnum> method) {
return currRow().getProtoEnumInternal(columnIndex, method);
}

@Override
protected String getJsonInternal(int columnIndex) {
return currRow().getJsonInternal(columnIndex);
Expand Down Expand Up @@ -1574,6 +1698,18 @@ protected List<ByteArray> getBytesListInternal(int columnIndex) {
return currRow().getBytesListInternal(columnIndex);
}

@Override
protected <T extends AbstractMessage> List<T> getProtoMessageListInternal(
int columnIndex, T message) {
return currRow().getProtoMessageListInternal(columnIndex, message);
}

@Override
protected <T extends ProtocolMessageEnum> List<T> getProtoEnumListInternal(
int columnIndex, Function<Integer, ProtocolMessageEnum> method) {
return currRow().getProtoEnumListInternal(columnIndex, method);
}

@Override
protected List<Timestamp> getTimestampListInternal(int columnIndex) {
return currRow().getTimestampListInternal(columnIndex);
Expand Down