pvary commented on code in PR #17513:
URL: https://github.com/apache/iceberg/pull/17513#discussion_r3715530260


##########
api/src/test/java/org/apache/iceberg/types/TestTypeUtil.java:
##########
@@ -972,6 +972,151 @@ public void ancestorFieldsInNestedSchema() {
     assertThat(TypeUtil.ancestorFields(schema, 
17)).containsExactly(pointsElement, points);
   }
 
+  @Test
+  public void isNullableWithUnknownFieldId() {
+    Schema schema = new Schema(required(1, "id", IntegerType.get()));
+
+    assertThat(TypeUtil.isNullable(schema, 2)).isTrue();
+  }
+
+  @Test
+  public void isNullableWithTopLevelFields() {
+    Schema schema =
+        new Schema(
+            required(1, "id", IntegerType.get()), optional(2, "data", 
Types.StringType.get()));
+
+    assertThat(TypeUtil.isNullable(schema, 1)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 2)).isTrue();
+  }
+
+  @Test
+  public void isNullableWithNestedStructs() {
+    Schema schema =
+        new Schema(
+            required(
+                1,
+                "required_location",
+                Types.StructType.of(
+                    required(3, "required_lat", Types.DoubleType.get()),
+                    optional(4, "optional_lon", Types.DoubleType.get()),
+                    required(
+                        5,
+                        "required_inner",
+                        Types.StructType.of(required(6, "required_zip", 
IntegerType.get()))))),
+            optional(
+                2,
+                "optional_location",
+                Types.StructType.of(
+                    required(7, "required_lat", Types.DoubleType.get()),
+                    required(
+                        8,
+                        "required_inner",
+                        Types.StructType.of(required(9, "required_zip", 
IntegerType.get()))))));
+
+    // a required field is not null when every field that contains it is 
required

Review Comment:
   ```suggestion
       // a required field is not nullable when every field that contains it is 
required
   ```



##########
api/src/test/java/org/apache/iceberg/types/TestTypeUtil.java:
##########
@@ -972,6 +972,151 @@ public void ancestorFieldsInNestedSchema() {
     assertThat(TypeUtil.ancestorFields(schema, 
17)).containsExactly(pointsElement, points);
   }
 
+  @Test
+  public void isNullableWithUnknownFieldId() {
+    Schema schema = new Schema(required(1, "id", IntegerType.get()));
+
+    assertThat(TypeUtil.isNullable(schema, 2)).isTrue();
+  }
+
+  @Test
+  public void isNullableWithTopLevelFields() {
+    Schema schema =
+        new Schema(
+            required(1, "id", IntegerType.get()), optional(2, "data", 
Types.StringType.get()));
+
+    assertThat(TypeUtil.isNullable(schema, 1)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 2)).isTrue();
+  }
+
+  @Test
+  public void isNullableWithNestedStructs() {
+    Schema schema =
+        new Schema(
+            required(
+                1,
+                "required_location",
+                Types.StructType.of(
+                    required(3, "required_lat", Types.DoubleType.get()),
+                    optional(4, "optional_lon", Types.DoubleType.get()),
+                    required(
+                        5,
+                        "required_inner",
+                        Types.StructType.of(required(6, "required_zip", 
IntegerType.get()))))),
+            optional(
+                2,
+                "optional_location",
+                Types.StructType.of(
+                    required(7, "required_lat", Types.DoubleType.get()),
+                    required(
+                        8,
+                        "required_inner",
+                        Types.StructType.of(required(9, "required_zip", 
IntegerType.get()))))));
+
+    // a required field is not null when every field that contains it is 
required
+    assertThat(TypeUtil.isNullable(schema, 1)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 3)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 5)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 6)).isFalse();
+
+    // an optional field is null regardless of the fields that contain it

Review Comment:
   ```suggestion
       // an optional field is nullable regardless of the fields that contain it
   ```



##########
api/src/test/java/org/apache/iceberg/types/TestTypeUtil.java:
##########
@@ -972,6 +972,151 @@ public void ancestorFieldsInNestedSchema() {
     assertThat(TypeUtil.ancestorFields(schema, 
17)).containsExactly(pointsElement, points);
   }
 
+  @Test
+  public void isNullableWithUnknownFieldId() {
+    Schema schema = new Schema(required(1, "id", IntegerType.get()));
+
+    assertThat(TypeUtil.isNullable(schema, 2)).isTrue();
+  }
+
+  @Test
+  public void isNullableWithTopLevelFields() {
+    Schema schema =
+        new Schema(
+            required(1, "id", IntegerType.get()), optional(2, "data", 
Types.StringType.get()));
+
+    assertThat(TypeUtil.isNullable(schema, 1)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 2)).isTrue();
+  }
+
+  @Test
+  public void isNullableWithNestedStructs() {
+    Schema schema =
+        new Schema(
+            required(
+                1,
+                "required_location",
+                Types.StructType.of(
+                    required(3, "required_lat", Types.DoubleType.get()),
+                    optional(4, "optional_lon", Types.DoubleType.get()),
+                    required(
+                        5,
+                        "required_inner",
+                        Types.StructType.of(required(6, "required_zip", 
IntegerType.get()))))),
+            optional(
+                2,
+                "optional_location",
+                Types.StructType.of(
+                    required(7, "required_lat", Types.DoubleType.get()),
+                    required(
+                        8,
+                        "required_inner",
+                        Types.StructType.of(required(9, "required_zip", 
IntegerType.get()))))));
+
+    // a required field is not null when every field that contains it is 
required
+    assertThat(TypeUtil.isNullable(schema, 1)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 3)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 5)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 6)).isFalse();
+
+    // an optional field is null regardless of the fields that contain it
+    assertThat(TypeUtil.isNullable(schema, 4)).isTrue();
+
+    // a required field nested in an optional struct is null when the struct 
is null

Review Comment:
   ```suggestion
       // a required field nested in an optional struct is nullable
   ```



##########
api/src/test/java/org/apache/iceberg/types/TestTypeUtil.java:
##########
@@ -972,6 +972,151 @@ public void ancestorFieldsInNestedSchema() {
     assertThat(TypeUtil.ancestorFields(schema, 
17)).containsExactly(pointsElement, points);
   }
 
+  @Test
+  public void isNullableWithUnknownFieldId() {
+    Schema schema = new Schema(required(1, "id", IntegerType.get()));
+
+    assertThat(TypeUtil.isNullable(schema, 2)).isTrue();
+  }
+
+  @Test
+  public void isNullableWithTopLevelFields() {
+    Schema schema =
+        new Schema(
+            required(1, "id", IntegerType.get()), optional(2, "data", 
Types.StringType.get()));
+
+    assertThat(TypeUtil.isNullable(schema, 1)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 2)).isTrue();
+  }
+
+  @Test
+  public void isNullableWithNestedStructs() {
+    Schema schema =
+        new Schema(
+            required(
+                1,
+                "required_location",
+                Types.StructType.of(
+                    required(3, "required_lat", Types.DoubleType.get()),
+                    optional(4, "optional_lon", Types.DoubleType.get()),
+                    required(
+                        5,
+                        "required_inner",
+                        Types.StructType.of(required(6, "required_zip", 
IntegerType.get()))))),
+            optional(
+                2,
+                "optional_location",
+                Types.StructType.of(
+                    required(7, "required_lat", Types.DoubleType.get()),
+                    required(
+                        8,
+                        "required_inner",
+                        Types.StructType.of(required(9, "required_zip", 
IntegerType.get()))))));
+
+    // a required field is not null when every field that contains it is 
required
+    assertThat(TypeUtil.isNullable(schema, 1)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 3)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 5)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 6)).isFalse();
+
+    // an optional field is null regardless of the fields that contain it
+    assertThat(TypeUtil.isNullable(schema, 4)).isTrue();
+
+    // a required field nested in an optional struct is null when the struct 
is null
+    assertThat(TypeUtil.isNullable(schema, 2)).isTrue();
+    assertThat(TypeUtil.isNullable(schema, 7)).isTrue();
+    assertThat(TypeUtil.isNullable(schema, 8)).isTrue();
+    assertThat(TypeUtil.isNullable(schema, 9)).isTrue();
+  }
+
+  @Test
+  public void isNullableWithListsAndMaps() {
+    Schema schema =
+        new Schema(
+            required(
+                1,
+                "required_points",
+                Types.ListType.ofRequired(
+                    4, Types.StructType.of(required(5, "required_x", 
Types.LongType.get())))),
+            optional(
+                2,
+                "optional_points",
+                Types.ListType.ofOptional(
+                    6, Types.StructType.of(required(7, "required_x", 
Types.LongType.get())))),
+            required(
+                3,
+                "locations",
+                Types.MapType.ofRequired(
+                    8,
+                    9,
+                    Types.StringType.get(),
+                    Types.StructType.of(required(10, "required_lat", 
Types.DoubleType.get())))),
+            optional(
+                11,
+                "optional_lines",
+                Types.ListType.ofRequired(
+                    12, Types.StructType.of(required(13, "required_x", 
Types.LongType.get())))),
+            required(
+                14,
+                "required_shapes",
+                Types.ListType.ofOptional(
+                    15, Types.StructType.of(required(16, "required_x", 
Types.LongType.get())))),
+            optional(
+                17,
+                "optional_locations",
+                Types.MapType.ofRequired(
+                    18,
+                    19,
+                    Types.StringType.get(),
+                    Types.StructType.of(required(20, "required_lat", 
Types.DoubleType.get())))),
+            required(
+                21,
+                "required_locations",
+                Types.MapType.ofOptional(
+                    22,
+                    23,
+                    Types.StringType.get(),
+                    Types.StructType.of(required(24, "required_lat", 
Types.DoubleType.get())))));
+
+    // a required element of a required list is not null, nor is anything it 
contains

Review Comment:
   ```suggestion
       // a required element of a required list is not nullable, nor is 
anything it contains
   ```



##########
api/src/test/java/org/apache/iceberg/types/TestTypeUtil.java:
##########
@@ -972,6 +972,151 @@ public void ancestorFieldsInNestedSchema() {
     assertThat(TypeUtil.ancestorFields(schema, 
17)).containsExactly(pointsElement, points);
   }
 
+  @Test
+  public void isNullableWithUnknownFieldId() {
+    Schema schema = new Schema(required(1, "id", IntegerType.get()));
+
+    assertThat(TypeUtil.isNullable(schema, 2)).isTrue();
+  }
+
+  @Test
+  public void isNullableWithTopLevelFields() {
+    Schema schema =
+        new Schema(
+            required(1, "id", IntegerType.get()), optional(2, "data", 
Types.StringType.get()));
+
+    assertThat(TypeUtil.isNullable(schema, 1)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 2)).isTrue();
+  }
+
+  @Test
+  public void isNullableWithNestedStructs() {
+    Schema schema =
+        new Schema(
+            required(
+                1,
+                "required_location",
+                Types.StructType.of(
+                    required(3, "required_lat", Types.DoubleType.get()),
+                    optional(4, "optional_lon", Types.DoubleType.get()),
+                    required(
+                        5,
+                        "required_inner",
+                        Types.StructType.of(required(6, "required_zip", 
IntegerType.get()))))),
+            optional(
+                2,
+                "optional_location",
+                Types.StructType.of(
+                    required(7, "required_lat", Types.DoubleType.get()),
+                    required(
+                        8,
+                        "required_inner",
+                        Types.StructType.of(required(9, "required_zip", 
IntegerType.get()))))));
+
+    // a required field is not null when every field that contains it is 
required
+    assertThat(TypeUtil.isNullable(schema, 1)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 3)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 5)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 6)).isFalse();
+
+    // an optional field is null regardless of the fields that contain it
+    assertThat(TypeUtil.isNullable(schema, 4)).isTrue();
+
+    // a required field nested in an optional struct is null when the struct 
is null
+    assertThat(TypeUtil.isNullable(schema, 2)).isTrue();
+    assertThat(TypeUtil.isNullable(schema, 7)).isTrue();
+    assertThat(TypeUtil.isNullable(schema, 8)).isTrue();
+    assertThat(TypeUtil.isNullable(schema, 9)).isTrue();
+  }
+
+  @Test
+  public void isNullableWithListsAndMaps() {
+    Schema schema =
+        new Schema(
+            required(
+                1,
+                "required_points",
+                Types.ListType.ofRequired(
+                    4, Types.StructType.of(required(5, "required_x", 
Types.LongType.get())))),
+            optional(
+                2,
+                "optional_points",
+                Types.ListType.ofOptional(
+                    6, Types.StructType.of(required(7, "required_x", 
Types.LongType.get())))),
+            required(
+                3,
+                "locations",
+                Types.MapType.ofRequired(
+                    8,
+                    9,
+                    Types.StringType.get(),
+                    Types.StructType.of(required(10, "required_lat", 
Types.DoubleType.get())))),
+            optional(
+                11,
+                "optional_lines",
+                Types.ListType.ofRequired(
+                    12, Types.StructType.of(required(13, "required_x", 
Types.LongType.get())))),
+            required(
+                14,
+                "required_shapes",
+                Types.ListType.ofOptional(
+                    15, Types.StructType.of(required(16, "required_x", 
Types.LongType.get())))),
+            optional(
+                17,
+                "optional_locations",
+                Types.MapType.ofRequired(
+                    18,
+                    19,
+                    Types.StringType.get(),
+                    Types.StructType.of(required(20, "required_lat", 
Types.DoubleType.get())))),
+            required(
+                21,
+                "required_locations",
+                Types.MapType.ofOptional(
+                    22,
+                    23,
+                    Types.StringType.get(),
+                    Types.StructType.of(required(24, "required_lat", 
Types.DoubleType.get())))));
+
+    // a required element of a required list is not null, nor is anything it 
contains
+    assertThat(TypeUtil.isNullable(schema, 1)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 4)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 5)).isFalse();
+
+    // an optional element is null, as is anything it contains
+    assertThat(TypeUtil.isNullable(schema, 2)).isTrue();
+    assertThat(TypeUtil.isNullable(schema, 6)).isTrue();
+    assertThat(TypeUtil.isNullable(schema, 7)).isTrue();
+
+    // required map keys and values are not null
+    assertThat(TypeUtil.isNullable(schema, 3)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 8)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 9)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 10)).isFalse();
+
+    // a required element of an optional list is null when the list is null

Review Comment:
   ```suggestion
       // a required element of an optional list is nullable
   ```



##########
api/src/test/java/org/apache/iceberg/types/TestTypeUtil.java:
##########
@@ -972,6 +972,151 @@ public void ancestorFieldsInNestedSchema() {
     assertThat(TypeUtil.ancestorFields(schema, 
17)).containsExactly(pointsElement, points);
   }
 
+  @Test
+  public void isNullableWithUnknownFieldId() {
+    Schema schema = new Schema(required(1, "id", IntegerType.get()));
+
+    assertThat(TypeUtil.isNullable(schema, 2)).isTrue();
+  }
+
+  @Test
+  public void isNullableWithTopLevelFields() {
+    Schema schema =
+        new Schema(
+            required(1, "id", IntegerType.get()), optional(2, "data", 
Types.StringType.get()));
+
+    assertThat(TypeUtil.isNullable(schema, 1)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 2)).isTrue();
+  }
+
+  @Test
+  public void isNullableWithNestedStructs() {
+    Schema schema =
+        new Schema(
+            required(
+                1,
+                "required_location",
+                Types.StructType.of(
+                    required(3, "required_lat", Types.DoubleType.get()),
+                    optional(4, "optional_lon", Types.DoubleType.get()),
+                    required(
+                        5,
+                        "required_inner",
+                        Types.StructType.of(required(6, "required_zip", 
IntegerType.get()))))),
+            optional(
+                2,
+                "optional_location",
+                Types.StructType.of(
+                    required(7, "required_lat", Types.DoubleType.get()),
+                    required(
+                        8,
+                        "required_inner",
+                        Types.StructType.of(required(9, "required_zip", 
IntegerType.get()))))));
+
+    // a required field is not null when every field that contains it is 
required
+    assertThat(TypeUtil.isNullable(schema, 1)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 3)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 5)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 6)).isFalse();
+
+    // an optional field is null regardless of the fields that contain it
+    assertThat(TypeUtil.isNullable(schema, 4)).isTrue();
+
+    // a required field nested in an optional struct is null when the struct 
is null
+    assertThat(TypeUtil.isNullable(schema, 2)).isTrue();
+    assertThat(TypeUtil.isNullable(schema, 7)).isTrue();
+    assertThat(TypeUtil.isNullable(schema, 8)).isTrue();
+    assertThat(TypeUtil.isNullable(schema, 9)).isTrue();
+  }
+
+  @Test
+  public void isNullableWithListsAndMaps() {
+    Schema schema =
+        new Schema(
+            required(
+                1,
+                "required_points",
+                Types.ListType.ofRequired(
+                    4, Types.StructType.of(required(5, "required_x", 
Types.LongType.get())))),
+            optional(
+                2,
+                "optional_points",
+                Types.ListType.ofOptional(
+                    6, Types.StructType.of(required(7, "required_x", 
Types.LongType.get())))),
+            required(
+                3,
+                "locations",
+                Types.MapType.ofRequired(
+                    8,
+                    9,
+                    Types.StringType.get(),
+                    Types.StructType.of(required(10, "required_lat", 
Types.DoubleType.get())))),
+            optional(
+                11,
+                "optional_lines",
+                Types.ListType.ofRequired(
+                    12, Types.StructType.of(required(13, "required_x", 
Types.LongType.get())))),
+            required(
+                14,
+                "required_shapes",
+                Types.ListType.ofOptional(
+                    15, Types.StructType.of(required(16, "required_x", 
Types.LongType.get())))),
+            optional(
+                17,
+                "optional_locations",
+                Types.MapType.ofRequired(
+                    18,
+                    19,
+                    Types.StringType.get(),
+                    Types.StructType.of(required(20, "required_lat", 
Types.DoubleType.get())))),
+            required(
+                21,
+                "required_locations",
+                Types.MapType.ofOptional(
+                    22,
+                    23,
+                    Types.StringType.get(),
+                    Types.StructType.of(required(24, "required_lat", 
Types.DoubleType.get())))));
+
+    // a required element of a required list is not null, nor is anything it 
contains
+    assertThat(TypeUtil.isNullable(schema, 1)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 4)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 5)).isFalse();
+
+    // an optional element is null, as is anything it contains

Review Comment:
   ```suggestion
       // an optional element is nullable, as is anything it contains
   ```



##########
api/src/test/java/org/apache/iceberg/types/TestTypeUtil.java:
##########
@@ -972,6 +972,151 @@ public void ancestorFieldsInNestedSchema() {
     assertThat(TypeUtil.ancestorFields(schema, 
17)).containsExactly(pointsElement, points);
   }
 
+  @Test
+  public void isNullableWithUnknownFieldId() {
+    Schema schema = new Schema(required(1, "id", IntegerType.get()));
+
+    assertThat(TypeUtil.isNullable(schema, 2)).isTrue();
+  }
+
+  @Test
+  public void isNullableWithTopLevelFields() {
+    Schema schema =
+        new Schema(
+            required(1, "id", IntegerType.get()), optional(2, "data", 
Types.StringType.get()));
+
+    assertThat(TypeUtil.isNullable(schema, 1)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 2)).isTrue();
+  }
+
+  @Test
+  public void isNullableWithNestedStructs() {
+    Schema schema =
+        new Schema(
+            required(
+                1,
+                "required_location",
+                Types.StructType.of(
+                    required(3, "required_lat", Types.DoubleType.get()),
+                    optional(4, "optional_lon", Types.DoubleType.get()),
+                    required(
+                        5,
+                        "required_inner",
+                        Types.StructType.of(required(6, "required_zip", 
IntegerType.get()))))),
+            optional(
+                2,
+                "optional_location",
+                Types.StructType.of(
+                    required(7, "required_lat", Types.DoubleType.get()),
+                    required(
+                        8,
+                        "required_inner",
+                        Types.StructType.of(required(9, "required_zip", 
IntegerType.get()))))));
+
+    // a required field is not null when every field that contains it is 
required
+    assertThat(TypeUtil.isNullable(schema, 1)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 3)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 5)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 6)).isFalse();
+
+    // an optional field is null regardless of the fields that contain it
+    assertThat(TypeUtil.isNullable(schema, 4)).isTrue();
+
+    // a required field nested in an optional struct is null when the struct 
is null
+    assertThat(TypeUtil.isNullable(schema, 2)).isTrue();
+    assertThat(TypeUtil.isNullable(schema, 7)).isTrue();
+    assertThat(TypeUtil.isNullable(schema, 8)).isTrue();
+    assertThat(TypeUtil.isNullable(schema, 9)).isTrue();
+  }
+
+  @Test
+  public void isNullableWithListsAndMaps() {
+    Schema schema =
+        new Schema(
+            required(
+                1,
+                "required_points",
+                Types.ListType.ofRequired(
+                    4, Types.StructType.of(required(5, "required_x", 
Types.LongType.get())))),
+            optional(
+                2,
+                "optional_points",
+                Types.ListType.ofOptional(
+                    6, Types.StructType.of(required(7, "required_x", 
Types.LongType.get())))),
+            required(
+                3,
+                "locations",
+                Types.MapType.ofRequired(
+                    8,
+                    9,
+                    Types.StringType.get(),
+                    Types.StructType.of(required(10, "required_lat", 
Types.DoubleType.get())))),
+            optional(
+                11,
+                "optional_lines",
+                Types.ListType.ofRequired(
+                    12, Types.StructType.of(required(13, "required_x", 
Types.LongType.get())))),
+            required(
+                14,
+                "required_shapes",
+                Types.ListType.ofOptional(
+                    15, Types.StructType.of(required(16, "required_x", 
Types.LongType.get())))),
+            optional(
+                17,
+                "optional_locations",
+                Types.MapType.ofRequired(
+                    18,
+                    19,
+                    Types.StringType.get(),
+                    Types.StructType.of(required(20, "required_lat", 
Types.DoubleType.get())))),
+            required(
+                21,
+                "required_locations",
+                Types.MapType.ofOptional(
+                    22,
+                    23,
+                    Types.StringType.get(),
+                    Types.StructType.of(required(24, "required_lat", 
Types.DoubleType.get())))));
+
+    // a required element of a required list is not null, nor is anything it 
contains
+    assertThat(TypeUtil.isNullable(schema, 1)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 4)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 5)).isFalse();
+
+    // an optional element is null, as is anything it contains
+    assertThat(TypeUtil.isNullable(schema, 2)).isTrue();
+    assertThat(TypeUtil.isNullable(schema, 6)).isTrue();
+    assertThat(TypeUtil.isNullable(schema, 7)).isTrue();
+
+    // required map keys and values are not null

Review Comment:
   ```suggestion
       // required map keys and values are not nullable
   ```



##########
api/src/test/java/org/apache/iceberg/types/TestTypeUtil.java:
##########
@@ -972,6 +972,151 @@ public void ancestorFieldsInNestedSchema() {
     assertThat(TypeUtil.ancestorFields(schema, 
17)).containsExactly(pointsElement, points);
   }
 
+  @Test
+  public void isNullableWithUnknownFieldId() {
+    Schema schema = new Schema(required(1, "id", IntegerType.get()));
+
+    assertThat(TypeUtil.isNullable(schema, 2)).isTrue();
+  }
+
+  @Test
+  public void isNullableWithTopLevelFields() {
+    Schema schema =
+        new Schema(
+            required(1, "id", IntegerType.get()), optional(2, "data", 
Types.StringType.get()));
+
+    assertThat(TypeUtil.isNullable(schema, 1)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 2)).isTrue();
+  }
+
+  @Test
+  public void isNullableWithNestedStructs() {
+    Schema schema =
+        new Schema(
+            required(
+                1,
+                "required_location",
+                Types.StructType.of(
+                    required(3, "required_lat", Types.DoubleType.get()),
+                    optional(4, "optional_lon", Types.DoubleType.get()),
+                    required(
+                        5,
+                        "required_inner",
+                        Types.StructType.of(required(6, "required_zip", 
IntegerType.get()))))),
+            optional(
+                2,
+                "optional_location",
+                Types.StructType.of(
+                    required(7, "required_lat", Types.DoubleType.get()),
+                    required(
+                        8,
+                        "required_inner",
+                        Types.StructType.of(required(9, "required_zip", 
IntegerType.get()))))));
+
+    // a required field is not null when every field that contains it is 
required
+    assertThat(TypeUtil.isNullable(schema, 1)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 3)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 5)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 6)).isFalse();
+
+    // an optional field is null regardless of the fields that contain it
+    assertThat(TypeUtil.isNullable(schema, 4)).isTrue();
+
+    // a required field nested in an optional struct is null when the struct 
is null
+    assertThat(TypeUtil.isNullable(schema, 2)).isTrue();
+    assertThat(TypeUtil.isNullable(schema, 7)).isTrue();
+    assertThat(TypeUtil.isNullable(schema, 8)).isTrue();
+    assertThat(TypeUtil.isNullable(schema, 9)).isTrue();
+  }
+
+  @Test
+  public void isNullableWithListsAndMaps() {
+    Schema schema =
+        new Schema(
+            required(
+                1,
+                "required_points",
+                Types.ListType.ofRequired(
+                    4, Types.StructType.of(required(5, "required_x", 
Types.LongType.get())))),
+            optional(
+                2,
+                "optional_points",
+                Types.ListType.ofOptional(
+                    6, Types.StructType.of(required(7, "required_x", 
Types.LongType.get())))),
+            required(
+                3,
+                "locations",
+                Types.MapType.ofRequired(
+                    8,
+                    9,
+                    Types.StringType.get(),
+                    Types.StructType.of(required(10, "required_lat", 
Types.DoubleType.get())))),
+            optional(
+                11,
+                "optional_lines",
+                Types.ListType.ofRequired(
+                    12, Types.StructType.of(required(13, "required_x", 
Types.LongType.get())))),
+            required(
+                14,
+                "required_shapes",
+                Types.ListType.ofOptional(
+                    15, Types.StructType.of(required(16, "required_x", 
Types.LongType.get())))),
+            optional(
+                17,
+                "optional_locations",
+                Types.MapType.ofRequired(
+                    18,
+                    19,
+                    Types.StringType.get(),
+                    Types.StructType.of(required(20, "required_lat", 
Types.DoubleType.get())))),
+            required(
+                21,
+                "required_locations",
+                Types.MapType.ofOptional(
+                    22,
+                    23,
+                    Types.StringType.get(),
+                    Types.StructType.of(required(24, "required_lat", 
Types.DoubleType.get())))));
+
+    // a required element of a required list is not null, nor is anything it 
contains
+    assertThat(TypeUtil.isNullable(schema, 1)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 4)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 5)).isFalse();
+
+    // an optional element is null, as is anything it contains
+    assertThat(TypeUtil.isNullable(schema, 2)).isTrue();
+    assertThat(TypeUtil.isNullable(schema, 6)).isTrue();
+    assertThat(TypeUtil.isNullable(schema, 7)).isTrue();
+
+    // required map keys and values are not null
+    assertThat(TypeUtil.isNullable(schema, 3)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 8)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 9)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 10)).isFalse();
+
+    // a required element of an optional list is null when the list is null
+    assertThat(TypeUtil.isNullable(schema, 11)).isTrue();
+    assertThat(TypeUtil.isNullable(schema, 12)).isTrue();
+    assertThat(TypeUtil.isNullable(schema, 13)).isTrue();
+
+    // an optional element of a required list is null, as is anything it 
contains
+    assertThat(TypeUtil.isNullable(schema, 14)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 15)).isTrue();
+    assertThat(TypeUtil.isNullable(schema, 16)).isTrue();
+
+    // required keys and values of an optional map are null when the map is 
null

Review Comment:
   ```suggestion
       // required keys and values of an optional map are nullable
   ```



##########
api/src/test/java/org/apache/iceberg/types/TestTypeUtil.java:
##########
@@ -972,6 +972,151 @@ public void ancestorFieldsInNestedSchema() {
     assertThat(TypeUtil.ancestorFields(schema, 
17)).containsExactly(pointsElement, points);
   }
 
+  @Test
+  public void isNullableWithUnknownFieldId() {
+    Schema schema = new Schema(required(1, "id", IntegerType.get()));
+
+    assertThat(TypeUtil.isNullable(schema, 2)).isTrue();
+  }
+
+  @Test
+  public void isNullableWithTopLevelFields() {
+    Schema schema =
+        new Schema(
+            required(1, "id", IntegerType.get()), optional(2, "data", 
Types.StringType.get()));
+
+    assertThat(TypeUtil.isNullable(schema, 1)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 2)).isTrue();
+  }
+
+  @Test
+  public void isNullableWithNestedStructs() {
+    Schema schema =
+        new Schema(
+            required(
+                1,
+                "required_location",
+                Types.StructType.of(
+                    required(3, "required_lat", Types.DoubleType.get()),
+                    optional(4, "optional_lon", Types.DoubleType.get()),
+                    required(
+                        5,
+                        "required_inner",
+                        Types.StructType.of(required(6, "required_zip", 
IntegerType.get()))))),
+            optional(
+                2,
+                "optional_location",
+                Types.StructType.of(
+                    required(7, "required_lat", Types.DoubleType.get()),
+                    required(
+                        8,
+                        "required_inner",
+                        Types.StructType.of(required(9, "required_zip", 
IntegerType.get()))))));
+
+    // a required field is not null when every field that contains it is 
required
+    assertThat(TypeUtil.isNullable(schema, 1)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 3)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 5)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 6)).isFalse();
+
+    // an optional field is null regardless of the fields that contain it
+    assertThat(TypeUtil.isNullable(schema, 4)).isTrue();
+
+    // a required field nested in an optional struct is null when the struct 
is null
+    assertThat(TypeUtil.isNullable(schema, 2)).isTrue();
+    assertThat(TypeUtil.isNullable(schema, 7)).isTrue();
+    assertThat(TypeUtil.isNullable(schema, 8)).isTrue();
+    assertThat(TypeUtil.isNullable(schema, 9)).isTrue();
+  }
+
+  @Test
+  public void isNullableWithListsAndMaps() {
+    Schema schema =
+        new Schema(
+            required(
+                1,
+                "required_points",
+                Types.ListType.ofRequired(
+                    4, Types.StructType.of(required(5, "required_x", 
Types.LongType.get())))),
+            optional(
+                2,
+                "optional_points",
+                Types.ListType.ofOptional(
+                    6, Types.StructType.of(required(7, "required_x", 
Types.LongType.get())))),
+            required(
+                3,
+                "locations",
+                Types.MapType.ofRequired(
+                    8,
+                    9,
+                    Types.StringType.get(),
+                    Types.StructType.of(required(10, "required_lat", 
Types.DoubleType.get())))),
+            optional(
+                11,
+                "optional_lines",
+                Types.ListType.ofRequired(
+                    12, Types.StructType.of(required(13, "required_x", 
Types.LongType.get())))),
+            required(
+                14,
+                "required_shapes",
+                Types.ListType.ofOptional(
+                    15, Types.StructType.of(required(16, "required_x", 
Types.LongType.get())))),
+            optional(
+                17,
+                "optional_locations",
+                Types.MapType.ofRequired(
+                    18,
+                    19,
+                    Types.StringType.get(),
+                    Types.StructType.of(required(20, "required_lat", 
Types.DoubleType.get())))),
+            required(
+                21,
+                "required_locations",
+                Types.MapType.ofOptional(
+                    22,
+                    23,
+                    Types.StringType.get(),
+                    Types.StructType.of(required(24, "required_lat", 
Types.DoubleType.get())))));
+
+    // a required element of a required list is not null, nor is anything it 
contains
+    assertThat(TypeUtil.isNullable(schema, 1)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 4)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 5)).isFalse();
+
+    // an optional element is null, as is anything it contains
+    assertThat(TypeUtil.isNullable(schema, 2)).isTrue();
+    assertThat(TypeUtil.isNullable(schema, 6)).isTrue();
+    assertThat(TypeUtil.isNullable(schema, 7)).isTrue();
+
+    // required map keys and values are not null
+    assertThat(TypeUtil.isNullable(schema, 3)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 8)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 9)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 10)).isFalse();
+
+    // a required element of an optional list is null when the list is null
+    assertThat(TypeUtil.isNullable(schema, 11)).isTrue();
+    assertThat(TypeUtil.isNullable(schema, 12)).isTrue();
+    assertThat(TypeUtil.isNullable(schema, 13)).isTrue();
+
+    // an optional element of a required list is null, as is anything it 
contains
+    assertThat(TypeUtil.isNullable(schema, 14)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 15)).isTrue();
+    assertThat(TypeUtil.isNullable(schema, 16)).isTrue();
+
+    // required keys and values of an optional map are null when the map is 
null
+    assertThat(TypeUtil.isNullable(schema, 17)).isTrue();
+    assertThat(TypeUtil.isNullable(schema, 18)).isTrue();
+    assertThat(TypeUtil.isNullable(schema, 19)).isTrue();
+    assertThat(TypeUtil.isNullable(schema, 20)).isTrue();
+
+    // an optional value of a required map is null, as is anything it 
contains, but keys are not

Review Comment:
   ```suggestion
       // an optional value of a required map is nullable, as is anything it 
contains, but keys are not
   ```



##########
api/src/test/java/org/apache/iceberg/types/TestTypeUtil.java:
##########
@@ -972,6 +972,151 @@ public void ancestorFieldsInNestedSchema() {
     assertThat(TypeUtil.ancestorFields(schema, 
17)).containsExactly(pointsElement, points);
   }
 
+  @Test
+  public void isNullableWithUnknownFieldId() {
+    Schema schema = new Schema(required(1, "id", IntegerType.get()));
+
+    assertThat(TypeUtil.isNullable(schema, 2)).isTrue();
+  }
+
+  @Test
+  public void isNullableWithTopLevelFields() {
+    Schema schema =
+        new Schema(
+            required(1, "id", IntegerType.get()), optional(2, "data", 
Types.StringType.get()));
+
+    assertThat(TypeUtil.isNullable(schema, 1)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 2)).isTrue();
+  }
+
+  @Test
+  public void isNullableWithNestedStructs() {
+    Schema schema =
+        new Schema(
+            required(
+                1,
+                "required_location",
+                Types.StructType.of(
+                    required(3, "required_lat", Types.DoubleType.get()),
+                    optional(4, "optional_lon", Types.DoubleType.get()),
+                    required(
+                        5,
+                        "required_inner",
+                        Types.StructType.of(required(6, "required_zip", 
IntegerType.get()))))),
+            optional(
+                2,
+                "optional_location",
+                Types.StructType.of(
+                    required(7, "required_lat", Types.DoubleType.get()),
+                    required(
+                        8,
+                        "required_inner",
+                        Types.StructType.of(required(9, "required_zip", 
IntegerType.get()))))));
+
+    // a required field is not null when every field that contains it is 
required
+    assertThat(TypeUtil.isNullable(schema, 1)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 3)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 5)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 6)).isFalse();
+
+    // an optional field is null regardless of the fields that contain it
+    assertThat(TypeUtil.isNullable(schema, 4)).isTrue();
+
+    // a required field nested in an optional struct is null when the struct 
is null
+    assertThat(TypeUtil.isNullable(schema, 2)).isTrue();
+    assertThat(TypeUtil.isNullable(schema, 7)).isTrue();
+    assertThat(TypeUtil.isNullable(schema, 8)).isTrue();
+    assertThat(TypeUtil.isNullable(schema, 9)).isTrue();
+  }
+
+  @Test
+  public void isNullableWithListsAndMaps() {
+    Schema schema =
+        new Schema(
+            required(
+                1,
+                "required_points",
+                Types.ListType.ofRequired(
+                    4, Types.StructType.of(required(5, "required_x", 
Types.LongType.get())))),
+            optional(
+                2,
+                "optional_points",
+                Types.ListType.ofOptional(
+                    6, Types.StructType.of(required(7, "required_x", 
Types.LongType.get())))),
+            required(
+                3,
+                "locations",
+                Types.MapType.ofRequired(
+                    8,
+                    9,
+                    Types.StringType.get(),
+                    Types.StructType.of(required(10, "required_lat", 
Types.DoubleType.get())))),
+            optional(
+                11,
+                "optional_lines",
+                Types.ListType.ofRequired(
+                    12, Types.StructType.of(required(13, "required_x", 
Types.LongType.get())))),
+            required(
+                14,
+                "required_shapes",
+                Types.ListType.ofOptional(
+                    15, Types.StructType.of(required(16, "required_x", 
Types.LongType.get())))),
+            optional(
+                17,
+                "optional_locations",
+                Types.MapType.ofRequired(
+                    18,
+                    19,
+                    Types.StringType.get(),
+                    Types.StructType.of(required(20, "required_lat", 
Types.DoubleType.get())))),
+            required(
+                21,
+                "required_locations",
+                Types.MapType.ofOptional(
+                    22,
+                    23,
+                    Types.StringType.get(),
+                    Types.StructType.of(required(24, "required_lat", 
Types.DoubleType.get())))));
+
+    // a required element of a required list is not null, nor is anything it 
contains
+    assertThat(TypeUtil.isNullable(schema, 1)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 4)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 5)).isFalse();
+
+    // an optional element is null, as is anything it contains
+    assertThat(TypeUtil.isNullable(schema, 2)).isTrue();
+    assertThat(TypeUtil.isNullable(schema, 6)).isTrue();
+    assertThat(TypeUtil.isNullable(schema, 7)).isTrue();
+
+    // required map keys and values are not null
+    assertThat(TypeUtil.isNullable(schema, 3)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 8)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 9)).isFalse();
+    assertThat(TypeUtil.isNullable(schema, 10)).isFalse();
+
+    // a required element of an optional list is null when the list is null
+    assertThat(TypeUtil.isNullable(schema, 11)).isTrue();
+    assertThat(TypeUtil.isNullable(schema, 12)).isTrue();
+    assertThat(TypeUtil.isNullable(schema, 13)).isTrue();
+
+    // an optional element of a required list is null, as is anything it 
contains

Review Comment:
   ```suggestion
       // an optional element of a required list is nullable, as is anything it 
contains
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to