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

Refactored client library code for Data Types for consolidating type transformation logic #2

Draft
wants to merge 16 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
1ff2513
Refactored client library code for Data Types for simplicity and code…
gunjan-juyal Sep 25, 2023
18a4e86
Added test cases for generic public API usage for reading from result…
gunjan-juyal Oct 12, 2023
8c1775c
Refactored client library code to consolidaet all Data Types transfor…
gunjan-juyal Oct 31, 2023
4d7e7a7
Replaced more internal data-type specific classes with composible con…
gunjan-juyal Nov 3, 2023
a1f0620
Bugfixes in GenericType implementation
gunjan-juyal Nov 14, 2023
3c138be
[Experiment - Do not merge] Deleted the NUMERIC type. Adding it back …
gunjan-juyal Nov 16, 2023
80885b1
Added code autogeneration using freemarker template and a custom pars…
gunjan-juyal Nov 27, 2023
a38de12
Added code autogeneration using freemarker template and a custom pars…
gunjan-juyal Nov 27, 2023
523e311
Added code replacement in source files for the code generated from te…
gunjan-juyal Dec 2, 2023
b1b47d0
Enhanced parsing of command line args for passing source-code directo…
gunjan-juyal Dec 4, 2023
52bb3ba
Bugfixes in types template and code-generation, adding auto-generatio…
gunjan-juyal Dec 4, 2023
32ae707
Added code-generation for if and switch blocks, including comments fo…
gunjan-juyal Dec 5, 2023
d6ce240
Minor improvements to log output, test improvements to parsing tags
gunjan-juyal Dec 5, 2023
7ccfeb2
Merged changes from main
gunjan-juyal Feb 4, 2024
3940756
Merge remote-tracking branch 'origin/refactor1' into refactor2
gunjan-juyal Feb 4, 2024
bed6ad5
Merged mainline changes chained through refactor1 branch, resolved co…
gunjan-juyal Feb 4, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion google-cloud-spanner/clirr-ignored-differences.xml
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@
<className>com/google/cloud/spanner/connection/Connection</className>
<method>void rollbackToSavepoint(java.lang.String)</method>
</difference>

<!-- Delay start transaction -->
<difference>
<differenceType>7012</differenceType>
Expand Down
10 changes: 10 additions & 0 deletions google-cloud-spanner/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,16 @@
<groupId>com.google.api</groupId>
<artifactId>gax-httpjson</artifactId>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.32</version>
</dependency>
<dependency>
<groupId>com.beust</groupId>
<artifactId>jcommander</artifactId>
<version>1.82</version>
</dependency>
<dependency>
<groupId>org.threeten</groupId>
<artifactId>threetenbp</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import com.google.cloud.Timestamp;
import com.google.cloud.spanner.Type.StructField;
import com.google.cloud.spanner.spi.v1.SpannerRpc;
import com.google.cloud.spanner.types.TypeConversionDelegate;
import com.google.cloud.spanner.v1.stub.SpannerStubSettings;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
Expand Down Expand Up @@ -85,6 +86,8 @@
import javax.annotation.Nullable;
import org.threeten.bp.Duration;

// Start-autogenerated-code #imports

/** Implementation of {@link ResultSet}. */
abstract class AbstractResultSet<R> extends AbstractStructReader implements ResultSet {
private static final Tracer tracer = Tracing.getTracer();
Expand Down Expand Up @@ -460,116 +463,70 @@ private Object writeReplace() {
String fieldName = field.getName();
Object value = rowData.get(i);
Type fieldType = field.getType();
switch (fieldType.getCode()) {
case BOOL:
builder.set(fieldName).to((Boolean) value);
break;
case INT64:
builder.set(fieldName).to((Long) value);
break;
case FLOAT64:
builder.set(fieldName).to((Double) value);
break;
case NUMERIC:
builder.set(fieldName).to((BigDecimal) value);
break;
case PG_NUMERIC:
builder.set(fieldName).to((String) value);
break;
case STRING:
builder.set(fieldName).to((String) value);
break;
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;
case BYTES:
builder
.set(fieldName)
.to(
Value.bytesFromBase64(
value == null ? null : ((LazyByteArray) value).getBase64String()));
break;
case TIMESTAMP:
builder.set(fieldName).to((Timestamp) value);
break;
case DATE:
builder.set(fieldName).to((Date) value);
break;
case ARRAY:
final Type elementType = fieldType.getArrayElementType();
switch (elementType.getCode()) {
case BOOL:
builder.set(fieldName).toBoolArray((Iterable<Boolean>) value);
break;
case INT64:
case ENUM:
builder.set(fieldName).toInt64Array((Iterable<Long>) value);
break;
case FLOAT64:
builder.set(fieldName).toFloat64Array((Iterable<Double>) value);
break;
case NUMERIC:
builder.set(fieldName).toNumericArray((Iterable<BigDecimal>) value);
break;
case PG_NUMERIC:
builder.set(fieldName).toPgNumericArray((Iterable<String>) value);
break;
case STRING:
builder.set(fieldName).toStringArray((Iterable<String>) value);
break;
case JSON:
builder.set(fieldName).toJsonArray((Iterable<String>) value);
break;
case PG_JSONB:
builder.set(fieldName).toPgJsonbArray((Iterable<String>) value);
break;
case BYTES:
case PROTO:
builder
.set(fieldName)
.toBytesArrayFromBase64(
value == null
? null
: ((List<LazyByteArray>) value)
.stream()
if (Type.isPrimitiveTypeCodeSupported(fieldType.getCode())) {
// Bytes needs specific decoding
if (fieldType.getCode() == Type.Code.BYTES) {
builder.set(fieldName)
.to(fieldType.getCode(), value == null ? null : ((LazyByteArray) value).getBase64String()
);
} else {
builder.set(fieldName).to(fieldType.getCode(), value);
}
} else {
switch (fieldType.getCode()) {
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 ARRAY:
final Type elementType = fieldType.getArrayElementType();
if (elementType.getCode() != Type.Code.BYTES && Type.isPrimitiveTypeCodeSupported(
elementType.getCode())) {
// Bytes needs specific decoding
// TODO(gunjj@): Consider using toPrimitiveArrayValue() for this also
builder.set(fieldName)
.to((Iterable<?>) value, elementType.getCode());
} else {
switch (elementType.getCode()) {
case ENUM:
builder.set(fieldName).toInt64Array((Iterable<Long>) value);
break;
case BYTES:
case PROTO:
builder
.set(fieldName)
.toBytesArrayFromBase64(
value == null
? null
: ((List<LazyByteArray>) value)
.stream()
.map(
element ->
element == null ? null : element.getBase64String())
.collect(Collectors.toList()));
break;
case TIMESTAMP:
builder.set(fieldName).toTimestampArray((Iterable<Timestamp>) value);
break;
case DATE:
builder.set(fieldName).toDateArray((Iterable<Date>) value);
break;
case STRUCT:
builder.set(fieldName).toStructArray(elementType, (Iterable<Struct>) value);
break;
default:
throw new AssertionError("Unhandled array type code: " + elementType);
}
break;
case STRUCT:
if (value == null) {
builder.set(fieldName).to(fieldType, null);
} else {
builder.set(fieldName).to((Struct) value);
}
break;
default:
throw new AssertionError("Unhandled type code: " + fieldType.getCode());
break;
case STRUCT:
builder.set(fieldName).toStructArray(elementType, (Iterable<Struct>) value);
break;
default:
throw new AssertionError("Unhandled array type code: " + elementType);
}
}
break;
case STRUCT:
if (value == null) {
builder.set(fieldName).to(fieldType, null);
} else {
builder.set(fieldName).to((Struct) value);
}
break;
default:
throw new AssertionError("Unhandled type code: " + fieldType.getCode());
}
}
}
return builder.build();
Expand Down Expand Up @@ -602,6 +559,8 @@ boolean consumeRow(Iterator<com.google.protobuf.Value> iterator) {
return true;
}

// TODO(gunjj@) This method contains the logic for transforming proto (from resultset) to
// java-language-specific type. Consider moving into Value or TypeHelper
private static Object decodeValue(Type fieldType, com.google.protobuf.Value proto) {
if (proto.getKindCase() == KindCase.NULL_VALUE) {
return null;
Expand All @@ -610,15 +569,12 @@ private static Object decodeValue(Type fieldType, com.google.protobuf.Value prot
case BOOL:
checkType(fieldType, proto, KindCase.BOOL_VALUE);
return proto.getBoolValue();
case INT64:
case INT64: // TODO(gunjj@) Insert the new implementation here : return TypeConversionDelegate.getInstance().parseType(fieldType, proto);
case ENUM:
checkType(fieldType, proto, KindCase.STRING_VALUE);
return Long.parseLong(proto.getStringValue());
case FLOAT64:
return valueProtoToFloat64(proto);
case NUMERIC:
checkType(fieldType, proto, KindCase.STRING_VALUE);
return new BigDecimal(proto.getStringValue());
case PG_NUMERIC:
case STRING:
case JSON:
Expand All @@ -645,6 +601,8 @@ private static Object decodeValue(Type fieldType, com.google.protobuf.Value prot
return decodeStructValue(fieldType, structValue);
case UNRECOGNIZED:
return proto;
// Start-autogenerated-code #switchBlockDecodeValue
// End-autogenerated-code #switchBlockDecodeValue
default:
throw new AssertionError("Unhandled type code: " + fieldType.getCode());
}
Expand Down Expand Up @@ -673,14 +631,15 @@ static Object decodeArrayValue(Type elementType, ListValue listValue) {
case FLOAT64:
return new Float64Array(listValue);
case BOOL:
case NUMERIC:
case PG_NUMERIC:
case STRING:
case JSON:
case PG_JSONB:
case BYTES:
case TIMESTAMP:
case DATE:
// Start-autogenerated-code #switchBlockDecodeArrayValue
// End-autogenerated-code #switchBlockDecodeArrayValue
case STRUCT:
case PROTO:
return Lists.transform(
Expand Down Expand Up @@ -757,16 +716,14 @@ protected long getLongInternal(int columnIndex) {
return (Long) rowData.get(columnIndex);
}

// TODO(gunjj@) This method is autoboxing {@code Double} to {@code double}. Consider the impact
// if we return Object {@code Double} instead, including when being used with ecosystem and
// previous Spanner versions. Benefit being seeked: Reusability of mthods
@Override
protected double getDoubleInternal(int columnIndex) {
return (Double) rowData.get(columnIndex);
}

@Override
protected BigDecimal getBigDecimalInternal(int columnIndex) {
return (BigDecimal) rowData.get(columnIndex);
}

@Override
protected String getStringInternal(int columnIndex) {
return (String) rowData.get(columnIndex);
Expand Down Expand Up @@ -805,21 +762,21 @@ protected com.google.protobuf.Value getProtoValueInternal(int columnIndex) {
return (com.google.protobuf.Value) rowData.get(columnIndex);
}

// TODO(gunjj@) Consider for refactoring
@Override
protected Value getValueInternal(int columnIndex) {
final List<Type.StructField> structFields = getType().getStructFields();
final StructField structField = structFields.get(columnIndex);
final Type columnType = structField.getType();
final boolean isNull = rowData.get(columnIndex) == null;
switch (columnType.getCode()) {
// TODO(gunjj@) Should we plug-in GenericType construction here instead of calling the "Internal" method?
case BOOL:
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:
return Value.pgNumeric(isNull ? null : getStringInternal(columnIndex));
case FLOAT64:
Expand Down Expand Up @@ -850,8 +807,6 @@ protected Value getValueInternal(int columnIndex) {
return Value.boolArray(isNull ? null : getBooleanListInternal(columnIndex));
case INT64:
return Value.int64Array(isNull ? null : getLongListInternal(columnIndex));
case NUMERIC:
return Value.numericArray(isNull ? null : getBigDecimalListInternal(columnIndex));
case PG_NUMERIC:
return Value.pgNumericArray(isNull ? null : getStringListInternal(columnIndex));
case FLOAT64:
Expand Down Expand Up @@ -891,6 +846,10 @@ protected Struct getStructInternal(int columnIndex) {
return (Struct) rowData.get(columnIndex);
}

// Start-autogenerated-code #GrpcStruct

// End-autogenerated-code #GrpcStruct

@Override
protected boolean[] getBooleanArrayInternal(int columnIndex) {
@SuppressWarnings("unchecked") // We know ARRAY<BOOL> produces a List<Boolean>.
Expand Down Expand Up @@ -931,12 +890,6 @@ protected Float64Array getDoubleListInternal(int columnIndex) {
return (Float64Array) rowData.get(columnIndex);
}

@Override
@SuppressWarnings("unchecked") // We know ARRAY<NUMERIC> produces a List<BigDecimal>.
protected List<BigDecimal> getBigDecimalListInternal(int columnIndex) {
return (List<BigDecimal>) rowData.get(columnIndex);
}

@Override
@SuppressWarnings("unchecked") // We know ARRAY<STRING> produces a List<String>.
protected List<String> getStringListInternal(int columnIndex) {
Expand Down Expand Up @@ -1592,11 +1545,6 @@ protected double getDoubleInternal(int columnIndex) {
return currRow().getDoubleInternal(columnIndex);
}

@Override
protected BigDecimal getBigDecimalInternal(int columnIndex) {
return currRow().getBigDecimalInternal(columnIndex);
}

@Override
protected String getStringInternal(int columnIndex) {
return currRow().getStringInternal(columnIndex);
Expand Down Expand Up @@ -1643,6 +1591,10 @@ protected Value getValueInternal(int columnIndex) {
return currRow().getValueInternal(columnIndex);
}

// Start-autogenerated-code #body

// End-autogenerated-code #body

@Override
protected boolean[] getBooleanArrayInternal(int columnIndex) {
return currRow().getBooleanArrayInternal(columnIndex);
Expand Down Expand Up @@ -1673,11 +1625,6 @@ protected List<Double> getDoubleListInternal(int columnIndex) {
return currRow().getDoubleListInternal(columnIndex);
}

@Override
protected List<BigDecimal> getBigDecimalListInternal(int columnIndex) {
return currRow().getBigDecimalListInternal(columnIndex);
}

@Override
protected List<String> getStringListInternal(int columnIndex) {
return currRow().getStringListInternal(columnIndex);
Expand Down
Loading
Loading