Skip to content

Translation logic for translating RelationalStructs into DynamicMessages missing handling for UUID-valued fields #4240

Description

@alecgrieser

The logic within RecordTypeTable::toDynamicMessage is currently missing handling for UUID-valued fields:

public static Message toDynamicMessage(RelationalStruct struct, Descriptors.Descriptor descriptor) throws RelationalException {
DynamicMessage.Builder builder = DynamicMessage.newBuilder(descriptor);
try {
final var type = struct.getMetaData().getRelationalDataType();
final var fields = type.getFields();
for (int i = 0; i < fields.size(); i++) {
final var field = fields.get(i);
final var columnName = fields.get(i).getName();
Descriptors.FieldDescriptor fd = builder.getDescriptorForType().findFieldByName(columnName);
Assert.thatUnchecked(fd != null, ErrorCode.INVALID_PARAMETER, "Cannot find column name: " + columnName);
// TODO: Add type checking, nudging, and coercion at this point! Per bfines, good place to do it!
// TODO (Add type checking, nudging, and coercion)
if (fd.getType() == Descriptors.FieldDescriptor.Type.ENUM) {
final var maybeEnumValue = struct.getString(i + 1);
if (maybeEnumValue != null) {
final var valueDescriptor = fd.getEnumType().findValueByName(maybeEnumValue);
Assert.that(valueDescriptor != null, ErrorCode.CANNOT_CONVERT_TYPE, "Invalid enum value: %s", maybeEnumValue);
builder.setField(fd, valueDescriptor);
}
continue;
}
switch (field.getType().getCode()) {
case BOOLEAN:
case DOUBLE:
case FLOAT:
case INTEGER:
case LONG:
case STRING:
final var obj = struct.getObject(i + 1);
if (obj != null) {
try {
builder.setField(fd, obj);
} catch (IllegalArgumentException ex) {
throw new RelationalException("Unexpected Column type " + struct.getMetaData().getColumnTypeName(i) + " for column " + columnName, ErrorCode.CANNOT_CONVERT_TYPE, ex);
}
}
break;
case BYTES:
final var bytes = struct.getBytes(i + 1);
if (bytes != null) {
builder.setField(fd, ByteString.copyFrom(bytes));
}
break;
case STRUCT:
var subStruct = struct.getStruct(i + 1);
if (subStruct != null) {
builder.setField(fd, toDynamicMessage(subStruct, fd.getMessageType()));
}
break;
case ARRAY:
var array = struct.getArray(i + 1);
// if the fieldDescriptor is repeatable, then it's an unwrapped array
if (fd.isRepeated()) {
final var arrayItems = (Object[]) (array == null ? new Object[]{} : array.getArray());
for (Object arrayItem : arrayItems) {
if (arrayItem instanceof RelationalStruct) {
builder.addRepeatedField(fd, toDynamicMessage((RelationalStruct) arrayItem, fd.getMessageType()));
} else {
builder.addRepeatedField(fd, arrayItem);
}
}
} else {
Assert.that(fd.getType() == Descriptors.FieldDescriptor.Type.MESSAGE, ErrorCode.CANNOT_CONVERT_TYPE,
"Field Type expected to be of Type ARRAY but is actually " + fd.getType());
Assert.that(NullableArrayUtils.isWrappedArrayDescriptor(fd.getMessageType()));
// wrap array in a struct and call toDynamicMessage again
final var wrapper = new ImmutableRowStruct(new ArrayRow(array), RelationalStructMetaData.of(
DataType.StructType.from("STRUCT", List.of(
DataType.StructType.Field.from(NullableArrayUtils.REPEATED_FIELD_NAME, array.getMetaData().asRelationalType(), 0)
), true)));
builder.setField(fd, toDynamicMessage(wrapper, fd.getMessageType()));
}
break;
case VECTOR:
final var vector = struct.getObject(i + 1);
if (vector != null) {
Assert.that(vector instanceof AbstractRealVector, ErrorCode.CANNOT_CONVERT_TYPE,
"Field Type expected to be of Type VECTOR but is actually " + fd.getType());
final var fieldOptionMaybe = Optional.ofNullable(fd.getOptions()).map(f -> f.getExtension(RecordMetaDataOptionsProto.field));
Assert.that(fieldOptionMaybe.isPresent() && fieldOptionMaybe.get().hasVectorOptions(), ErrorCode.CANNOT_CONVERT_TYPE, "Cannot insert non vector type into vector column");
final var vectorOptions = fieldOptionMaybe.get().getVectorOptions();
Assert.that(vectorOptions.getDimensions() == ((AbstractRealVector)vector).getNumDimensions(), ErrorCode.CANNOT_CONVERT_TYPE, "Wrong number of dimension for vector");
final int precision = vectorOptions.getPrecision();
switch (precision) {
case 16:
Assert.that(vector instanceof HalfRealVector, ErrorCode.CANNOT_CONVERT_TYPE, "Wrong precision for vector");
break;
case 32:
Assert.that(vector instanceof FloatRealVector, ErrorCode.CANNOT_CONVERT_TYPE, "Wrong precision for vector");
break;
case 64:
Assert.that(vector instanceof DoubleRealVector, ErrorCode.CANNOT_CONVERT_TYPE, "Wrong precision for vector");
break;
default:
Assert.fail(ErrorCode.INTERNAL_ERROR, "Unknown precision for vector");
break;
}
builder.setField(fd, ((AbstractRealVector) vector).getRawData());
}
break;
case NULL:
break;
default:
Assert.fail(ErrorCode.INTERNAL_ERROR, (String.format(Locale.ROOT, "Unexpected Column type <%s> for column <%s>",
struct.getMetaData().getColumnTypeName(i), columnName)));
break;
}
}
} catch (SQLException sqle) {
throw new RelationalException(sqle);
}
return builder.build();
}

(Note that the switch in that function is missing a UUID case.) This means that, for example, executeInsert commands using the direct access API fail if the schema template definition contains a UUID.

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions