Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -572,10 +572,10 @@ static Field fromPb(TableFieldSchema fieldSchemaPb) {
if (fieldSchemaPb.getDefaultValueExpression() != null) {
fieldBuilder.setDefaultValueExpression(fieldSchemaPb.getDefaultValueExpression());
}
// Use FieldList.fromPb directly so nested subfields are eagerly materialized once rather
// than wrapped in a lazy Lists.transform view.
FieldList subFields =
fieldSchemaPb.getFields() != null
? FieldList.of(Lists.transform(fieldSchemaPb.getFields(), FROM_PB_FUNCTION))
: null;
fieldSchemaPb.getFields() != null ? FieldList.fromPb(fieldSchemaPb.getFields()) : null;
fieldBuilder.setType(LegacySQLTypeName.valueOf(fieldSchemaPb.getType()), subFields);
if (fieldSchemaPb.getCollation() != null) {
fieldBuilder.setCollation(fieldSchemaPb.getCollation());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ private FieldList(Iterable<Field> fields) {
this.fields = ImmutableList.copyOf(fields);
Map<String, Integer> caseInsensitiveMap = new CaseInsensitiveMap();
int index = 0;
for (Field field : fields) {
// Iterate this.fields (already materialized) instead of the input Iterable.
// Callers may pass a lazy view (e.g. Lists.transform), which would otherwise re-run
// and deserialize fields a second time.
for (Field field : this.fields) {
caseInsensitiveMap.put(field.getName(), index);
index++;
}
Expand Down Expand Up @@ -115,6 +118,11 @@ public static FieldList of(Field... fields) {
* @param fields the schema fields
*/
public static FieldList of(Iterable<Field> fields) {
// FieldList is immutable, so reuse it directly (e.g., when Schema.fromPb passes
// FieldList.fromPb(fields) into Schema.of) instead of rebuilding this.fields and nameIndex.
if (fields instanceof FieldList) {
return (FieldList) fields;
}
return new FieldList(fields);
}

Expand All @@ -123,6 +131,12 @@ List<TableFieldSchema> toPb() {
}

static FieldList fromPb(List<TableFieldSchema> fields) {
return FieldList.of(Lists.transform(fields, Field.FROM_PB_FUNCTION));
// Eagerly convert each field instead of wrapping in Lists.transform so every field and
// nested RECORD subtree is deserialized once with pre-sized storage.
ImmutableList.Builder<Field> builder = ImmutableList.builderWithExpectedSize(fields.size());
for (TableFieldSchema fieldPb : fields) {
builder.add(Field.fromPb(fieldPb));
}
return new FieldList(builder.build());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,12 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;

import com.google.common.collect.Lists;
import java.util.Collections;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.jupiter.api.Test;

class FieldListTest {
Expand Down Expand Up @@ -121,4 +125,22 @@ void testToAndFromPb() {

assertEquals(fieldsSchema, FieldList.fromPb(fieldsSchema.toPb()));
}

@Test
void testSingleEvaluationAndReuseInOf() {
assertSame(fieldsSchema, FieldList.of(fieldsSchema));
assertSame(fieldsSchema, Schema.of(fieldsSchema).getFields());

AtomicInteger transformCount = new AtomicInteger();
FieldList list =
FieldList.of(
Lists.transform(
Collections.singletonList(fieldSchema1),
field -> {
transformCount.incrementAndGet();
return field;
}));
assertEquals(1, list.size());
assertEquals(1, transformCount.get());
}
}
Loading