diff --git a/sdks/java/extensions/sql/zetasql/src/main/java/org/apache/beam/sdk/extensions/sql/zetasql/ZetaSqlCalciteTranslationUtils.java b/sdks/java/extensions/sql/zetasql/src/main/java/org/apache/beam/sdk/extensions/sql/zetasql/ZetaSqlCalciteTranslationUtils.java index 620342ee5ee5..6dc3c1e8d482 100644 --- a/sdks/java/extensions/sql/zetasql/src/main/java/org/apache/beam/sdk/extensions/sql/zetasql/ZetaSqlCalciteTranslationUtils.java +++ b/sdks/java/extensions/sql/zetasql/src/main/java/org/apache/beam/sdk/extensions/sql/zetasql/ZetaSqlCalciteTranslationUtils.java @@ -27,7 +27,9 @@ import java.math.BigDecimal; import java.time.LocalDateTime; import java.time.LocalTime; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.stream.Collectors; import org.apache.beam.sdk.annotations.Internal; import org.apache.beam.sdk.extensions.sql.meta.provider.bigquery.BeamBigQuerySqlDialect; @@ -180,12 +182,15 @@ private static RelDataType toCalciteStructType( private static List getFieldNameList(List fields) { ImmutableList.Builder b = ImmutableList.builder(); + Set usedName = new HashSet<>(); for (int i = 0; i < fields.size(); i++) { String name = fields.get(i).getName(); - if ("".equals(name)) { - name = "$col" + i; // avoid empty field names because Beam does not allow duplicate names + // Follow the same way that BigQuery handles unspecified or duplicate field name + if ("".equals(name) || name.startsWith("_field_") || usedName.contains(name)) { + name = "_field_" + (i + 1); // BigQuery uses 1-based default field name } b.add(name); + usedName.add(name); } return b.build(); }