This is an automated email from the ASF dual-hosted git repository.

yihua pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hudi.git


The following commit(s) were added to refs/heads/master by this push:
     new c82957df5f75 fix(gcp): skip "null" type fields when syncing the 
BigQuery schema (#20356)
c82957df5f75 is described below

commit c82957df5f757c2cc3faaf8b971fb554251100c1
Author: Prashant Wason <[email protected]>
AuthorDate: Thu Oct 15 22:49:27 2026 -0700

    fix(gcp): skip "null" type fields when syncing the BigQuery schema (#20356)
---
 .../hudi/gcp/bigquery/BigQuerySchemaResolver.java  | 14 ++++++---
 .../gcp/bigquery/TestBigQuerySchemaResolver.java   | 36 ++++++++++++++++++++++
 2 files changed, 45 insertions(+), 5 deletions(-)

diff --git 
a/hudi-gcp/src/main/java/org/apache/hudi/gcp/bigquery/BigQuerySchemaResolver.java
 
b/hudi-gcp/src/main/java/org/apache/hudi/gcp/bigquery/BigQuerySchemaResolver.java
index a26affe33ab2..24d7011e34c9 100644
--- 
a/hudi-gcp/src/main/java/org/apache/hudi/gcp/bigquery/BigQuerySchemaResolver.java
+++ 
b/hudi-gcp/src/main/java/org/apache/hudi/gcp/bigquery/BigQuerySchemaResolver.java
@@ -172,11 +172,15 @@ public class BigQuerySchemaResolver {
   }
 
   private List<Field> getFields(HoodieSchema schema) {
-    return schema.getFields().stream().map(field -> {
-      final HoodieSchema fieldSchema = field.getNonNullSchema();
-      final boolean nullable = field.isNullable();
-      return getField(fieldSchema, field.name(), nullable);
-    }).collect(Collectors.toList());
+    return schema.getFields().stream()
+        // Fields of the "null" type always hold null and are not written to 
the data files, so they are skipped
+        .filter(field -> !field.schema().isSchemaNull())
+        .map(field -> {
+          final HoodieSchema fieldSchema = field.getNonNullSchema();
+          final boolean nullable = field.isNullable();
+          return getField(fieldSchema, field.name(), nullable);
+        })
+        .collect(Collectors.toList());
   }
 
   private void validateUnion(List<HoodieSchema> subTypes) {
diff --git 
a/hudi-gcp/src/test/java/org/apache/hudi/gcp/bigquery/TestBigQuerySchemaResolver.java
 
b/hudi-gcp/src/test/java/org/apache/hudi/gcp/bigquery/TestBigQuerySchemaResolver.java
index c47d96c05869..2c9994f80313 100644
--- 
a/hudi-gcp/src/test/java/org/apache/hudi/gcp/bigquery/TestBigQuerySchemaResolver.java
+++ 
b/hudi-gcp/src/test/java/org/apache/hudi/gcp/bigquery/TestBigQuerySchemaResolver.java
@@ -243,6 +243,42 @@ public class TestBigQuerySchemaResolver {
     Assertions.assertEquals(expected, SCHEMA_RESOLVER.convertSchema(input));
   }
 
+  @Test
+  void convertSchema_nullTypeField() {
+    HoodieSchema input = HoodieSchema.parse("{\"type\": \"record\", \"name\": 
\"testRecord\", \"fields\": ["
+        + "{\"name\": \"name\", \"type\": \"string\"},"
+        + "{\"name\": \"clientpromotion\", \"type\": \"null\", \"default\": 
null},"
+        + "{\"name\": \"age\", \"type\": [\"null\", \"int\"], \"default\": 
null}]}");
+
+    Schema expected = Schema.of(
+        Field.newBuilder("name", 
StandardSQLTypeName.STRING).setMode(Field.Mode.REQUIRED).build(),
+        Field.newBuilder("age", 
StandardSQLTypeName.INT64).setMode(Field.Mode.NULLABLE).build());
+
+    Schema result = SCHEMA_RESOLVER.convertSchema(input);
+    Assertions.assertEquals(expected, result);
+    Assertions.assertEquals("`name` STRING NOT NULL, `age` INT64", 
schemaToSqlString(result));
+  }
+
+  @Test
+  void convertSchema_nestedNullTypeField() {
+    HoodieSchema input = HoodieSchema.parse("{\"type\": \"record\", \"name\": 
\"testRecord\", \"fields\": ["
+        + "{\"name\": \"name\", \"type\": \"string\"},"
+        + "{\"name\": \"address\", \"type\": {\"type\": \"record\", \"name\": 
\"address\", \"fields\": ["
+        + "{\"name\": \"city\", \"type\": \"string\"},"
+        + "{\"name\": \"nestedpromotion\", \"type\": \"null\", \"default\": 
null}]}}]}");
+
+    Schema expected = Schema.of(
+        Field.newBuilder("name", 
StandardSQLTypeName.STRING).setMode(Field.Mode.REQUIRED).build(),
+        Field.newBuilder("address", StandardSQLTypeName.STRUCT,
+                Field.newBuilder("city", 
StandardSQLTypeName.STRING).setMode(Field.Mode.REQUIRED).build())
+            .setMode(Field.Mode.REQUIRED).build());
+
+    Schema result = SCHEMA_RESOLVER.convertSchema(input);
+    Assertions.assertEquals(expected, result);
+    Assertions.assertEquals("`name` STRING NOT NULL, `address` STRUCT<`city` 
STRING NOT NULL> NOT NULL",
+        schemaToSqlString(result));
+  }
+
   @Test
   void getTableSchema_withPartitionFields() throws Exception {
     HoodieTableMetaClient mockMetaClient = mock(HoodieTableMetaClient.class);

Reply via email to