Skip to content

Commit

Permalink
feat: add support for ARRAY<STRUCT> to CloudCilentExecutor (#3544)
Browse files Browse the repository at this point in the history
There are SPANNER_SYS tables that contain ARRAY<STRUCT> columns. Adding support for this in the CloudClientExecutor so that queries involving these tables do not throw an error.
  • Loading branch information
larkee authored Dec 18, 2024
1 parent dfc154e commit 6cbaf7e
Showing 1 changed file with 67 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2842,61 +2842,75 @@ private Status processResults(
/** Convert a result row to a row proto(value list) for sending back to the client. */
private com.google.spanner.executor.v1.ValueList buildRow(
StructReader result, OutcomeSender sender) throws SpannerException {
com.google.spanner.executor.v1.ValueList.Builder rowBuilder =
com.google.spanner.executor.v1.ValueList.newBuilder();
sender.setRowType(buildStructType(result));
return buildStruct(result);
}

/** Construct a StructType for a given struct. This is used to set the row type. */
private com.google.spanner.v1.StructType buildStructType(StructReader struct) {
com.google.spanner.v1.StructType.Builder rowTypeBuilder =
com.google.spanner.v1.StructType.newBuilder();
for (int i = 0; i < result.getColumnCount(); ++i) {
com.google.cloud.spanner.Type columnType = result.getColumnType(i);
for (int i = 0; i < struct.getColumnCount(); ++i) {
com.google.cloud.spanner.Type columnType = struct.getColumnType(i);
rowTypeBuilder.addFields(
com.google.spanner.v1.StructType.Field.newBuilder()
.setName(result.getType().getStructFields().get(i).getName())
.setName(struct.getType().getStructFields().get(i).getName())
.setType(cloudTypeToTypeProto(columnType))
.build());
}
return rowTypeBuilder.build();
}

/** Convert a struct to a proto(value list) for constructing result rows and struct values. */
private com.google.spanner.executor.v1.ValueList buildStruct(StructReader struct) {
com.google.spanner.executor.v1.ValueList.Builder structBuilder =
com.google.spanner.executor.v1.ValueList.newBuilder();
for (int i = 0; i < struct.getColumnCount(); ++i) {
com.google.cloud.spanner.Type columnType = struct.getColumnType(i);
com.google.spanner.executor.v1.Value.Builder value =
com.google.spanner.executor.v1.Value.newBuilder();
if (result.isNull(i)) {
if (struct.isNull(i)) {
value.setIsNull(true);
} else {
switch (columnType.getCode()) {
case BOOL:
value.setBoolValue(result.getBoolean(i));
value.setBoolValue(struct.getBoolean(i));
break;
case FLOAT32:
value.setDoubleValue((double) result.getFloat(i));
value.setDoubleValue((double) struct.getFloat(i));
break;
case FLOAT64:
value.setDoubleValue(result.getDouble(i));
value.setDoubleValue(struct.getDouble(i));
break;
case INT64:
value.setIntValue(result.getLong(i));
value.setIntValue(struct.getLong(i));
break;
case STRING:
value.setStringValue(result.getString(i));
value.setStringValue(struct.getString(i));
break;
case BYTES:
value.setBytesValue(toByteString(result.getBytes(i)));
value.setBytesValue(toByteString(struct.getBytes(i)));
break;
case TIMESTAMP:
value.setTimestampValue(timestampToProto(result.getTimestamp(i)));
value.setTimestampValue(timestampToProto(struct.getTimestamp(i)));
break;
case DATE:
value.setDateDaysValue(daysFromDate(result.getDate(i)));
value.setDateDaysValue(daysFromDate(struct.getDate(i)));
break;
case NUMERIC:
String ascii = result.getBigDecimal(i).toPlainString();
String ascii = struct.getBigDecimal(i).toPlainString();
value.setStringValue(ascii);
break;
case JSON:
value.setStringValue(result.getJson(i));
value.setStringValue(struct.getJson(i));
break;
case ARRAY:
switch (result.getColumnType(i).getArrayElementType().getCode()) {
switch (struct.getColumnType(i).getArrayElementType().getCode()) {
case BOOL:
{
com.google.spanner.executor.v1.ValueList.Builder builder =
com.google.spanner.executor.v1.ValueList.newBuilder();
List<Boolean> values = result.getBooleanList(i);
List<Boolean> values = struct.getBooleanList(i);
for (Boolean booleanValue : values) {
com.google.spanner.executor.v1.Value.Builder valueProto =
com.google.spanner.executor.v1.Value.newBuilder();
Expand All @@ -2915,7 +2929,7 @@ private com.google.spanner.executor.v1.ValueList buildRow(
{
com.google.spanner.executor.v1.ValueList.Builder builder =
com.google.spanner.executor.v1.ValueList.newBuilder();
List<Float> values = result.getFloatList(i);
List<Float> values = struct.getFloatList(i);
for (Float floatValue : values) {
com.google.spanner.executor.v1.Value.Builder valueProto =
com.google.spanner.executor.v1.Value.newBuilder();
Expand All @@ -2934,7 +2948,7 @@ private com.google.spanner.executor.v1.ValueList buildRow(
{
com.google.spanner.executor.v1.ValueList.Builder builder =
com.google.spanner.executor.v1.ValueList.newBuilder();
List<Double> values = result.getDoubleList(i);
List<Double> values = struct.getDoubleList(i);
for (Double doubleValue : values) {
com.google.spanner.executor.v1.Value.Builder valueProto =
com.google.spanner.executor.v1.Value.newBuilder();
Expand All @@ -2953,7 +2967,7 @@ private com.google.spanner.executor.v1.ValueList buildRow(
{
com.google.spanner.executor.v1.ValueList.Builder builder =
com.google.spanner.executor.v1.ValueList.newBuilder();
List<Long> values = result.getLongList(i);
List<Long> values = struct.getLongList(i);
for (Long longValue : values) {
com.google.spanner.executor.v1.Value.Builder valueProto =
com.google.spanner.executor.v1.Value.newBuilder();
Expand All @@ -2972,7 +2986,7 @@ private com.google.spanner.executor.v1.ValueList buildRow(
{
com.google.spanner.executor.v1.ValueList.Builder builder =
com.google.spanner.executor.v1.ValueList.newBuilder();
List<String> values = result.getStringList(i);
List<String> values = struct.getStringList(i);
for (String stringValue : values) {
com.google.spanner.executor.v1.Value.Builder valueProto =
com.google.spanner.executor.v1.Value.newBuilder();
Expand All @@ -2991,7 +3005,7 @@ private com.google.spanner.executor.v1.ValueList buildRow(
{
com.google.spanner.executor.v1.ValueList.Builder builder =
com.google.spanner.executor.v1.ValueList.newBuilder();
List<ByteArray> values = result.getBytesList(i);
List<ByteArray> values = struct.getBytesList(i);
for (ByteArray byteArrayValue : values) {
com.google.spanner.executor.v1.Value.Builder valueProto =
com.google.spanner.executor.v1.Value.newBuilder();
Expand All @@ -3013,7 +3027,7 @@ private com.google.spanner.executor.v1.ValueList buildRow(
{
com.google.spanner.executor.v1.ValueList.Builder builder =
com.google.spanner.executor.v1.ValueList.newBuilder();
List<Date> values = result.getDateList(i);
List<Date> values = struct.getDateList(i);
for (Date dateValue : values) {
com.google.spanner.executor.v1.Value.Builder valueProto =
com.google.spanner.executor.v1.Value.newBuilder();
Expand All @@ -3033,7 +3047,7 @@ private com.google.spanner.executor.v1.ValueList buildRow(
{
com.google.spanner.executor.v1.ValueList.Builder builder =
com.google.spanner.executor.v1.ValueList.newBuilder();
List<Timestamp> values = result.getTimestampList(i);
List<Timestamp> values = struct.getTimestampList(i);
for (Timestamp timestampValue : values) {
com.google.spanner.executor.v1.Value.Builder valueProto =
com.google.spanner.executor.v1.Value.newBuilder();
Expand All @@ -3053,7 +3067,7 @@ private com.google.spanner.executor.v1.ValueList buildRow(
{
com.google.spanner.executor.v1.ValueList.Builder builder =
com.google.spanner.executor.v1.ValueList.newBuilder();
List<BigDecimal> values = result.getBigDecimalList(i);
List<BigDecimal> values = struct.getBigDecimalList(i);
for (BigDecimal bigDec : values) {
com.google.spanner.executor.v1.Value.Builder valueProto =
com.google.spanner.executor.v1.Value.newBuilder();
Expand All @@ -3072,7 +3086,7 @@ private com.google.spanner.executor.v1.ValueList buildRow(
{
com.google.spanner.executor.v1.ValueList.Builder builder =
com.google.spanner.executor.v1.ValueList.newBuilder();
List<String> values = result.getJsonList(i);
List<String> values = struct.getJsonList(i);
for (String stringValue : values) {
com.google.spanner.executor.v1.Value.Builder valueProto =
com.google.spanner.executor.v1.Value.newBuilder();
Expand All @@ -3087,28 +3101,47 @@ private com.google.spanner.executor.v1.ValueList buildRow(
com.google.spanner.v1.Type.newBuilder().setCode(TypeCode.JSON).build());
}
break;
case STRUCT:
{
com.google.spanner.executor.v1.ValueList.Builder builder =
com.google.spanner.executor.v1.ValueList.newBuilder();
List<Struct> values = struct.getStructList(i);
for (StructReader structValue : values) {
com.google.spanner.executor.v1.Value.Builder valueProto =
com.google.spanner.executor.v1.Value.newBuilder();
if (structValue == null) {
builder.addValue(valueProto.setIsNull(true).build());
} else {
builder.addValue(valueProto.setStructValue(buildStruct(structValue))).build();
}
}
value.setArrayValue(builder.build());
value.setArrayType(
com.google.spanner.v1.Type.newBuilder().setCode(TypeCode.STRUCT).build());
}
break;
default:
throw SpannerExceptionFactory.newSpannerException(
ErrorCode.INVALID_ARGUMENT,
"Unsupported row array type: "
+ result.getColumnType(i)
+ struct.getColumnType(i)
+ " for result type "
+ result.getType().toString());
+ struct.getType().toString());
}
break;
default:
throw SpannerExceptionFactory.newSpannerException(
ErrorCode.INVALID_ARGUMENT,
"Unsupported row type: "
+ result.getColumnType(i)
+ struct.getColumnType(i)
+ " for result type "
+ result.getType().toString());
+ struct.getType().toString());
}
}
rowBuilder.addValue(value.build());
structBuilder.addValue(value.build());
}
sender.setRowType(rowTypeBuilder.build());
return rowBuilder.build();
;
return structBuilder.build();
}

/** Convert a ListValue proto to a list of cloud Value. */
Expand Down

0 comments on commit 6cbaf7e

Please sign in to comment.