|
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(); |
|
} |
The logic within
RecordTypeTable::toDynamicMessageis currently missing handling forUUID-valued fields:fdb-record-layer/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/RecordTypeTable.java
Lines 188 to 301 in e2882db
(Note that the
switchin that function is missing aUUIDcase.) This means that, for example,executeInsertcommands using the direct access API fail if the schema template definition contains a UUID.