From 6cbaf7ec6502d04fc0a0c09720e2054bd10bead9 Mon Sep 17 00:00:00 2001 From: larkee <31196561+larkee@users.noreply.github.com> Date: Thu, 19 Dec 2024 09:18:20 +1100 Subject: [PATCH] feat: add support for ARRAY to CloudCilentExecutor (#3544) There are SPANNER_SYS tables that contain ARRAY columns. Adding support for this in the CloudClientExecutor so that queries involving these tables do not throw an error. --- .../executor/spanner/CloudClientExecutor.java | 101 ++++++++++++------ 1 file changed, 67 insertions(+), 34 deletions(-) diff --git a/google-cloud-spanner-executor/src/main/java/com/google/cloud/executor/spanner/CloudClientExecutor.java b/google-cloud-spanner-executor/src/main/java/com/google/cloud/executor/spanner/CloudClientExecutor.java index d0490be44a..64a29ed3e6 100644 --- a/google-cloud-spanner-executor/src/main/java/com/google/cloud/executor/spanner/CloudClientExecutor.java +++ b/google-cloud-spanner-executor/src/main/java/com/google/cloud/executor/spanner/CloudClientExecutor.java @@ -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 values = result.getBooleanList(i); + List values = struct.getBooleanList(i); for (Boolean booleanValue : values) { com.google.spanner.executor.v1.Value.Builder valueProto = com.google.spanner.executor.v1.Value.newBuilder(); @@ -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 values = result.getFloatList(i); + List values = struct.getFloatList(i); for (Float floatValue : values) { com.google.spanner.executor.v1.Value.Builder valueProto = com.google.spanner.executor.v1.Value.newBuilder(); @@ -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 values = result.getDoubleList(i); + List values = struct.getDoubleList(i); for (Double doubleValue : values) { com.google.spanner.executor.v1.Value.Builder valueProto = com.google.spanner.executor.v1.Value.newBuilder(); @@ -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 values = result.getLongList(i); + List values = struct.getLongList(i); for (Long longValue : values) { com.google.spanner.executor.v1.Value.Builder valueProto = com.google.spanner.executor.v1.Value.newBuilder(); @@ -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 values = result.getStringList(i); + List values = struct.getStringList(i); for (String stringValue : values) { com.google.spanner.executor.v1.Value.Builder valueProto = com.google.spanner.executor.v1.Value.newBuilder(); @@ -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 values = result.getBytesList(i); + List values = struct.getBytesList(i); for (ByteArray byteArrayValue : values) { com.google.spanner.executor.v1.Value.Builder valueProto = com.google.spanner.executor.v1.Value.newBuilder(); @@ -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 values = result.getDateList(i); + List values = struct.getDateList(i); for (Date dateValue : values) { com.google.spanner.executor.v1.Value.Builder valueProto = com.google.spanner.executor.v1.Value.newBuilder(); @@ -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 values = result.getTimestampList(i); + List values = struct.getTimestampList(i); for (Timestamp timestampValue : values) { com.google.spanner.executor.v1.Value.Builder valueProto = com.google.spanner.executor.v1.Value.newBuilder(); @@ -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 values = result.getBigDecimalList(i); + List values = struct.getBigDecimalList(i); for (BigDecimal bigDec : values) { com.google.spanner.executor.v1.Value.Builder valueProto = com.google.spanner.executor.v1.Value.newBuilder(); @@ -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 values = result.getJsonList(i); + List values = struct.getJsonList(i); for (String stringValue : values) { com.google.spanner.executor.v1.Value.Builder valueProto = com.google.spanner.executor.v1.Value.newBuilder(); @@ -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 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. */