Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -1137,7 +1137,6 @@ public static TableRow tableRowFromMessage(
boolean includeCdcColumns,
Predicate<String> includeField,
String namePrefix) {
// TODO: Would be more correct to generate TableRows using setF.
TableRow tableRow = new TableRow();
Comment thread
liferoad marked this conversation as resolved.
for (Map.Entry<FieldDescriptor, Object> field : message.getAllFields().entrySet()) {
StringBuilder fullName = new StringBuilder();
Expand All @@ -1147,10 +1146,24 @@ public static TableRow tableRowFromMessage(
Object fieldValue = field.getValue();
if ((includeCdcColumns || !StorageApiCDC.COLUMNS.contains(fullName.toString()))
&& includeField.test(fieldName)) {
tableRow.put(
fieldName,
Object convertedValue =
jsonValueFromMessageValue(
fieldDescriptor, fieldValue, true, includeField, fullName.append(".").toString()));
fieldDescriptor, fieldValue, true, includeField, fullName.append(".").toString());
Comment thread
liferoad marked this conversation as resolved.
Outdated

// Use setF when field name is "f" to avoid IllegalArgumentException with internal field
if ("f".equals(fieldName)) {
if (convertedValue instanceof List) {
@SuppressWarnings("unchecked")
List<TableCell> tableCells = (List<TableCell>) convertedValue;
tableRow.setF(tableCells);
Comment thread
liferoad marked this conversation as resolved.
Outdated
} else {
// For scalar values, wrap in a TableCell and create a single-element list
TableCell tableCell = new TableCell().setV(convertedValue);
tableRow.setF(ImmutableList.of(tableCell));
}
} else {
tableRow.put(fieldName, convertedValue);
}
}
}
return tableRow;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1860,4 +1860,39 @@ public void testCdcFields() throws Exception {
assertEquals(
Long.toHexString(42L), msg.getField(fieldDescriptors.get(StorageApiCDC.CHANGE_SQN_COLUMN)));
}

@Test
public void testTableRowFromMessageWithFieldNamedF() throws Exception {
TableSchema schema =
new TableSchema()
.setFields(
ImmutableList.of(
new TableFieldSchema().setType("STRING").setName("stringValue"),
new TableFieldSchema().setType("FLOAT64").setName("f")));

// Create a DynamicMessage directly to test the tableRowFromMessage fix
Descriptor descriptor =
TableRowToStorageApiProto.getDescriptorFromTableSchema(schema, false, false);
DynamicMessage.Builder builder = DynamicMessage.newBuilder(descriptor);

// Set field values in the message
FieldDescriptor stringField = descriptor.findFieldByName("stringvalue");
FieldDescriptor fField = descriptor.findFieldByName("f");

builder.setField(stringField, "test");
builder.setField(fField, 3.14);

DynamicMessage msg = builder.build();

// Convert DynamicMessage to TableRow - this should not throw IllegalArgumentException
TableRow result = TableRowToStorageApiProto.tableRowFromMessage(msg, false, field -> true);

// Verify the conversion worked correctly
assertEquals("test", result.get("stringvalue")); // Field name is lowercase in the result
// For field "f", the value should be wrapped in a TableCell within a List
@SuppressWarnings("unchecked")
List<TableCell> fValue = (List<TableCell>) result.get("f");
assertEquals(1, fValue.size());
assertEquals(3.14, fValue.get(0).getV());
}
}
Loading