rdblue commented on code in PR #17413:
URL: https://github.com/apache/iceberg/pull/17413#discussion_r3779497865


##########
api/src/test/java/org/apache/iceberg/expressions/TestInclusiveMetricsEvaluator.java:
##########
@@ -393,789 +546,525 @@ public void testZeroRecordFile() {
         };
 
     for (Expression expr : exprs) {
-      boolean shouldRead = new InclusiveMetricsEvaluator(SCHEMA, 
expr).eval(empty);
+      boolean shouldRead = shouldRead(SCHEMA, expr, emptyFile());
       assertThat(shouldRead).as("Should never read 0-record file: " + 
expr).isFalse();
     }
   }
 
   @Test
   public void testNot() {
     // this test case must use a real predicate, not alwaysTrue(), or binding 
will simplify it out
-    boolean shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, not(lessThan("id", INT_MIN_VALUE 
- 25))).eval(FILE);
+    boolean shouldRead = shouldRead(SCHEMA, not(lessThan("id", INT_MIN_VALUE - 
25)), file());
     assertThat(shouldRead).as("Should read: not(false)").isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, not(greaterThan("id", 
INT_MIN_VALUE - 25)))
-            .eval(FILE);
+    shouldRead = shouldRead(SCHEMA, not(greaterThan("id", INT_MIN_VALUE - 
25)), file());
     assertThat(shouldRead).as("Should skip: not(true)").isFalse();
   }
 
   @Test
   public void testAnd() {
     // this test case must use a real predicate, not alwaysTrue(), or binding 
will simplify it out
     boolean shouldRead =
-        new InclusiveMetricsEvaluator(
-                SCHEMA,
-                and(
-                    lessThan("id", INT_MIN_VALUE - 25),
-                    greaterThanOrEqual("id", INT_MIN_VALUE - 30)))
-            .eval(FILE);
+        shouldRead(
+            SCHEMA,
+            and(lessThan("id", INT_MIN_VALUE - 25), greaterThanOrEqual("id", 
INT_MIN_VALUE - 30)),
+            file());
     assertThat(shouldRead).as("Should skip: and(false, true)").isFalse();
 
     shouldRead =
-        new InclusiveMetricsEvaluator(
-                SCHEMA,
-                and(
-                    lessThan("id", INT_MIN_VALUE - 25),
-                    greaterThanOrEqual("id", INT_MAX_VALUE + 1)))
-            .eval(FILE);
+        shouldRead(
+            SCHEMA,
+            and(lessThan("id", INT_MIN_VALUE - 25), greaterThanOrEqual("id", 
INT_MAX_VALUE + 1)),
+            file());
     assertThat(shouldRead).as("Should skip: and(false, false)").isFalse();
 
     shouldRead =
-        new InclusiveMetricsEvaluator(
-                SCHEMA,
-                and(greaterThan("id", INT_MIN_VALUE - 25), 
lessThanOrEqual("id", INT_MIN_VALUE)))
-            .eval(FILE);
+        shouldRead(
+            SCHEMA,
+            and(greaterThan("id", INT_MIN_VALUE - 25), lessThanOrEqual("id", 
INT_MIN_VALUE)),
+            file());
     assertThat(shouldRead).as("Should read: and(true, true)").isTrue();
   }
 
   @Test
   public void testOr() {
     // this test case must use a real predicate, not alwaysTrue(), or binding 
will simplify it out
     boolean shouldRead =
-        new InclusiveMetricsEvaluator(
-                SCHEMA,
-                or(lessThan("id", INT_MIN_VALUE - 25), 
greaterThanOrEqual("id", INT_MAX_VALUE + 1)))
-            .eval(FILE);
+        shouldRead(
+            SCHEMA,
+            or(lessThan("id", INT_MIN_VALUE - 25), greaterThanOrEqual("id", 
INT_MAX_VALUE + 1)),
+            file());
     assertThat(shouldRead).as("Should skip: or(false, false)").isFalse();
 
     shouldRead =
-        new InclusiveMetricsEvaluator(
-                SCHEMA,
-                or(
-                    lessThan("id", INT_MIN_VALUE - 25),
-                    greaterThanOrEqual("id", INT_MAX_VALUE - 19)))
-            .eval(FILE);
+        shouldRead(
+            SCHEMA,
+            or(lessThan("id", INT_MIN_VALUE - 25), greaterThanOrEqual("id", 
INT_MAX_VALUE - 19)),
+            file());
     assertThat(shouldRead).as("Should read: or(false, true)").isTrue();
   }
 
   @Test
   public void testIntegerLt() {
-    boolean shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, lessThan("id", INT_MIN_VALUE - 
25)).eval(FILE);
+    boolean shouldRead = shouldRead(SCHEMA, lessThan("id", INT_MIN_VALUE - 
25), file());
     assertThat(shouldRead).as("Should not read: id range below lower bound (5 
< 30)").isFalse();
 
-    shouldRead = new InclusiveMetricsEvaluator(SCHEMA, lessThan("id", 
INT_MIN_VALUE)).eval(FILE);
+    shouldRead = shouldRead(SCHEMA, lessThan("id", INT_MIN_VALUE), file());
     assertThat(shouldRead)
         .as("Should not read: id range below lower bound (30 is not < 30)")
         .isFalse();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, lessThan("id", INT_MIN_VALUE + 
1)).eval(FILE);
+    shouldRead = shouldRead(SCHEMA, lessThan("id", INT_MIN_VALUE + 1), file());
     assertThat(shouldRead).as("Should read: one possible id").isTrue();
 
-    shouldRead = new InclusiveMetricsEvaluator(SCHEMA, lessThan("id", 
INT_MAX_VALUE)).eval(FILE);
+    shouldRead = shouldRead(SCHEMA, lessThan("id", INT_MAX_VALUE), file());
     assertThat(shouldRead).as("Should read: many possible ids").isTrue();
   }
 
   @Test
   public void testIntegerLtEq() {
-    boolean shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, lessThanOrEqual("id", 
INT_MIN_VALUE - 25)).eval(FILE);
+    boolean shouldRead = shouldRead(SCHEMA, lessThanOrEqual("id", 
INT_MIN_VALUE - 25), file());
     assertThat(shouldRead).as("Should not read: id range below lower bound (5 
< 30)").isFalse();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, lessThanOrEqual("id", 
INT_MIN_VALUE - 1)).eval(FILE);
+    shouldRead = shouldRead(SCHEMA, lessThanOrEqual("id", INT_MIN_VALUE - 1), 
file());
     assertThat(shouldRead).as("Should not read: id range below lower bound (29 
< 30)").isFalse();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, lessThanOrEqual("id", 
INT_MIN_VALUE)).eval(FILE);
+    shouldRead = shouldRead(SCHEMA, lessThanOrEqual("id", INT_MIN_VALUE), 
file());
     assertThat(shouldRead).as("Should read: one possible id").isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, lessThanOrEqual("id", 
INT_MAX_VALUE)).eval(FILE);
+    shouldRead = shouldRead(SCHEMA, lessThanOrEqual("id", INT_MAX_VALUE), 
file());
     assertThat(shouldRead).as("Should read: many possible ids").isTrue();
   }
 
   @Test
   public void testIntegerGt() {
-    boolean shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, greaterThan("id", INT_MAX_VALUE 
+ 6)).eval(FILE);
+    boolean shouldRead = shouldRead(SCHEMA, greaterThan("id", INT_MAX_VALUE + 
6), file());
     assertThat(shouldRead).as("Should not read: id range above upper bound (85 
< 79)").isFalse();
 
-    shouldRead = new InclusiveMetricsEvaluator(SCHEMA, greaterThan("id", 
INT_MAX_VALUE)).eval(FILE);
+    shouldRead = shouldRead(SCHEMA, greaterThan("id", INT_MAX_VALUE), file());
     assertThat(shouldRead)
         .as("Should not read: id range above upper bound (79 is not > 79)")
         .isFalse();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, greaterThan("id", INT_MAX_VALUE 
- 1)).eval(FILE);
+    shouldRead = shouldRead(SCHEMA, greaterThan("id", INT_MAX_VALUE - 1), 
file());
     assertThat(shouldRead).as("Should read: one possible id").isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, greaterThan("id", INT_MAX_VALUE 
- 4)).eval(FILE);
+    shouldRead = shouldRead(SCHEMA, greaterThan("id", INT_MAX_VALUE - 4), 
file());
     assertThat(shouldRead).as("Should read: many possible ids").isTrue();
   }
 
   @Test
   public void testIntegerGtEq() {
-    boolean shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, greaterThanOrEqual("id", 
INT_MAX_VALUE + 6))
-            .eval(FILE);
+    boolean shouldRead = shouldRead(SCHEMA, greaterThanOrEqual("id", 
INT_MAX_VALUE + 6), file());
     assertThat(shouldRead).as("Should not read: id range above upper bound (85 
< 79)").isFalse();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, greaterThanOrEqual("id", 
INT_MAX_VALUE + 1))
-            .eval(FILE);
+    shouldRead = shouldRead(SCHEMA, greaterThanOrEqual("id", INT_MAX_VALUE + 
1), file());
     assertThat(shouldRead).as("Should not read: id range above upper bound (80 
> 79)").isFalse();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, greaterThanOrEqual("id", 
INT_MAX_VALUE)).eval(FILE);
+    shouldRead = shouldRead(SCHEMA, greaterThanOrEqual("id", INT_MAX_VALUE), 
file());
     assertThat(shouldRead).as("Should read: one possible id").isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, greaterThanOrEqual("id", 
INT_MAX_VALUE - 4))
-            .eval(FILE);
+    shouldRead = shouldRead(SCHEMA, greaterThanOrEqual("id", INT_MAX_VALUE - 
4), file());
     assertThat(shouldRead).as("Should read: many possible ids").isTrue();
   }
 
   @Test
   public void testIntegerEq() {
-    boolean shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, equal("id", INT_MIN_VALUE - 
25)).eval(FILE);
+    boolean shouldRead = shouldRead(SCHEMA, equal("id", INT_MIN_VALUE - 25), 
file());
     assertThat(shouldRead).as("Should not read: id below lower 
bound").isFalse();
 
-    shouldRead = new InclusiveMetricsEvaluator(SCHEMA, equal("id", 
INT_MIN_VALUE - 1)).eval(FILE);
+    shouldRead = shouldRead(SCHEMA, equal("id", INT_MIN_VALUE - 1), file());
     assertThat(shouldRead).as("Should not read: id below lower 
bound").isFalse();
 
-    shouldRead = new InclusiveMetricsEvaluator(SCHEMA, equal("id", 
INT_MIN_VALUE)).eval(FILE);
+    shouldRead = shouldRead(SCHEMA, equal("id", INT_MIN_VALUE), file());
     assertThat(shouldRead).as("Should read: id equal to lower bound").isTrue();
 
-    shouldRead = new InclusiveMetricsEvaluator(SCHEMA, equal("id", 
INT_MAX_VALUE - 4)).eval(FILE);
+    shouldRead = shouldRead(SCHEMA, equal("id", INT_MAX_VALUE - 4), file());
     assertThat(shouldRead).as("Should read: id between lower and upper 
bounds").isTrue();
 
-    shouldRead = new InclusiveMetricsEvaluator(SCHEMA, equal("id", 
INT_MAX_VALUE)).eval(FILE);
+    shouldRead = shouldRead(SCHEMA, equal("id", INT_MAX_VALUE), file());
     assertThat(shouldRead).as("Should read: id equal to upper bound").isTrue();
 
-    shouldRead = new InclusiveMetricsEvaluator(SCHEMA, equal("id", 
INT_MAX_VALUE + 1)).eval(FILE);
+    shouldRead = shouldRead(SCHEMA, equal("id", INT_MAX_VALUE + 1), file());
     assertThat(shouldRead).as("Should not read: id above upper 
bound").isFalse();
 
-    shouldRead = new InclusiveMetricsEvaluator(SCHEMA, equal("id", 
INT_MAX_VALUE + 6)).eval(FILE);
+    shouldRead = shouldRead(SCHEMA, equal("id", INT_MAX_VALUE + 6), file());
     assertThat(shouldRead).as("Should not read: id above upper 
bound").isFalse();
   }
 
   @Test
   public void testIntegerNotEq() {
-    boolean shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, notEqual("id", INT_MIN_VALUE - 
25)).eval(FILE);
+    boolean shouldRead = shouldRead(SCHEMA, notEqual("id", INT_MIN_VALUE - 
25), file());
     assertThat(shouldRead).as("Should read: id below lower bound").isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, notEqual("id", INT_MIN_VALUE - 
1)).eval(FILE);
+    shouldRead = shouldRead(SCHEMA, notEqual("id", INT_MIN_VALUE - 1), file());
     assertThat(shouldRead).as("Should read: id below lower bound").isTrue();
 
-    shouldRead = new InclusiveMetricsEvaluator(SCHEMA, notEqual("id", 
INT_MIN_VALUE)).eval(FILE);
+    shouldRead = shouldRead(SCHEMA, notEqual("id", INT_MIN_VALUE), file());
     assertThat(shouldRead).as("Should read: id equal to lower bound").isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, notEqual("id", INT_MAX_VALUE - 
4)).eval(FILE);
+    shouldRead = shouldRead(SCHEMA, notEqual("id", INT_MAX_VALUE - 4), file());
     assertThat(shouldRead).as("Should read: id between lower and upper 
bounds").isTrue();
 
-    shouldRead = new InclusiveMetricsEvaluator(SCHEMA, notEqual("id", 
INT_MAX_VALUE)).eval(FILE);
+    shouldRead = shouldRead(SCHEMA, notEqual("id", INT_MAX_VALUE), file());
     assertThat(shouldRead).as("Should read: id equal to upper bound").isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, notEqual("id", INT_MAX_VALUE + 
1)).eval(FILE);
+    shouldRead = shouldRead(SCHEMA, notEqual("id", INT_MAX_VALUE + 1), file());
     assertThat(shouldRead).as("Should read: id above upper bound").isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, notEqual("id", INT_MAX_VALUE + 
6)).eval(FILE);
+    shouldRead = shouldRead(SCHEMA, notEqual("id", INT_MAX_VALUE + 6), file());
     assertThat(shouldRead).as("Should read: id above upper bound").isTrue();
   }
 
   @Test
   public void testIntegerNotEqRewritten() {
-    boolean shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, not(equal("id", INT_MIN_VALUE - 
25))).eval(FILE);
+    boolean shouldRead = shouldRead(SCHEMA, not(equal("id", INT_MIN_VALUE - 
25)), file());
     assertThat(shouldRead).as("Should read: id below lower bound").isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, not(equal("id", INT_MIN_VALUE - 
1))).eval(FILE);
+    shouldRead = shouldRead(SCHEMA, not(equal("id", INT_MIN_VALUE - 1)), 
file());
     assertThat(shouldRead).as("Should read: id below lower bound").isTrue();
 
-    shouldRead = new InclusiveMetricsEvaluator(SCHEMA, not(equal("id", 
INT_MIN_VALUE))).eval(FILE);
+    shouldRead = shouldRead(SCHEMA, not(equal("id", INT_MIN_VALUE)), file());
     assertThat(shouldRead).as("Should read: id equal to lower bound").isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, not(equal("id", INT_MAX_VALUE - 
4))).eval(FILE);
+    shouldRead = shouldRead(SCHEMA, not(equal("id", INT_MAX_VALUE - 4)), 
file());
     assertThat(shouldRead).as("Should read: id between lower and upper 
bounds").isTrue();
 
-    shouldRead = new InclusiveMetricsEvaluator(SCHEMA, not(equal("id", 
INT_MAX_VALUE))).eval(FILE);
+    shouldRead = shouldRead(SCHEMA, not(equal("id", INT_MAX_VALUE)), file());
     assertThat(shouldRead).as("Should read: id equal to upper bound").isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, not(equal("id", INT_MAX_VALUE + 
1))).eval(FILE);
+    shouldRead = shouldRead(SCHEMA, not(equal("id", INT_MAX_VALUE + 1)), 
file());
     assertThat(shouldRead).as("Should read: id above upper bound").isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, not(equal("id", INT_MAX_VALUE + 
6))).eval(FILE);
+    shouldRead = shouldRead(SCHEMA, not(equal("id", INT_MAX_VALUE + 6)), 
file());
     assertThat(shouldRead).as("Should read: id above upper bound").isTrue();
   }
 
   @Test
   public void testCaseInsensitiveIntegerNotEqRewritten() {
-    boolean shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, not(equal("ID", INT_MIN_VALUE - 
25)), false)
-            .eval(FILE);
+    boolean shouldRead = shouldRead(SCHEMA, not(equal("ID", INT_MIN_VALUE - 
25)), false, file());
     assertThat(shouldRead).as("Should read: id below lower bound").isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, not(equal("ID", INT_MIN_VALUE - 
1)), false)
-            .eval(FILE);
+    shouldRead = shouldRead(SCHEMA, not(equal("ID", INT_MIN_VALUE - 1)), 
false, file());
     assertThat(shouldRead).as("Should read: id below lower bound").isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, not(equal("ID", INT_MIN_VALUE)), 
false).eval(FILE);
+    shouldRead = shouldRead(SCHEMA, not(equal("ID", INT_MIN_VALUE)), false, 
file());
     assertThat(shouldRead).as("Should read: id equal to lower bound").isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, not(equal("ID", INT_MAX_VALUE - 
4)), false)
-            .eval(FILE);
+    shouldRead = shouldRead(SCHEMA, not(equal("ID", INT_MAX_VALUE - 4)), 
false, file());
     assertThat(shouldRead).as("Should read: id between lower and upper 
bounds").isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, not(equal("ID", INT_MAX_VALUE)), 
false).eval(FILE);
+    shouldRead = shouldRead(SCHEMA, not(equal("ID", INT_MAX_VALUE)), false, 
file());
     assertThat(shouldRead).as("Should read: id equal to upper bound").isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, not(equal("ID", INT_MAX_VALUE + 
1)), false)
-            .eval(FILE);
+    shouldRead = shouldRead(SCHEMA, not(equal("ID", INT_MAX_VALUE + 1)), 
false, file());
     assertThat(shouldRead).as("Should read: id above upper bound").isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, not(equal("ID", INT_MAX_VALUE + 
6)), false)
-            .eval(FILE);
+    shouldRead = shouldRead(SCHEMA, not(equal("ID", INT_MAX_VALUE + 6)), 
false, file());
     assertThat(shouldRead).as("Should read: id above upper bound").isTrue();
   }
 
   @Test
   public void testCaseSensitiveIntegerNotEqRewritten() {
-    assertThatThrownBy(
-            () -> new InclusiveMetricsEvaluator(SCHEMA, not(equal("ID", 5)), 
true).eval(FILE))
+    assertThatThrownBy(() -> shouldRead(SCHEMA, not(equal("ID", 5)), true, 
file()))
         .isInstanceOf(ValidationException.class)
         .hasMessageContaining("Cannot find field 'ID'");
   }
 
   @Test
   public void testStringStartsWith() {
-    boolean shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, startsWith("required", "a"), 
true).eval(FILE);
+    boolean shouldRead = shouldRead(SCHEMA, startsWith("required", "a"), true, 
file());
     assertThat(shouldRead).as("Should read: no stats").isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, startsWith("required", "a"), 
true).eval(FILE_2);
+    shouldRead = shouldRead(SCHEMA, startsWith("required", "a"), true, 
file2());
     assertThat(shouldRead).as("Should read: range matches").isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, startsWith("required", "aa"), 
true).eval(FILE_2);
+    shouldRead = shouldRead(SCHEMA, startsWith("required", "aa"), true, 
file2());
     assertThat(shouldRead).as("Should read: range matches").isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, startsWith("required", "aaa"), 
true).eval(FILE_2);
+    shouldRead = shouldRead(SCHEMA, startsWith("required", "aaa"), true, 
file2());
     assertThat(shouldRead).as("Should read: range matches").isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, startsWith("required", "1s"), 
true).eval(FILE_3);
+    shouldRead = shouldRead(SCHEMA, startsWith("required", "1s"), true, 
file3());
     assertThat(shouldRead).as("Should read: range matches").isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, startsWith("required", 
"1str1x"), true).eval(FILE_3);
+    shouldRead = shouldRead(SCHEMA, startsWith("required", "1str1x"), true, 
file3());
     assertThat(shouldRead).as("Should read: range matches").isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, startsWith("required", "ff"), 
true).eval(FILE_4);
+    shouldRead = shouldRead(SCHEMA, startsWith("required", "ff"), true, 
file4());
     assertThat(shouldRead).as("Should read: range matches").isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, startsWith("required", "aB"), 
true).eval(FILE_2);
+    shouldRead = shouldRead(SCHEMA, startsWith("required", "aB"), true, 
file2());
     assertThat(shouldRead).as("Should not read: range doesn't 
match").isFalse();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, startsWith("required", "dWX"), 
true).eval(FILE_2);
+    shouldRead = shouldRead(SCHEMA, startsWith("required", "dWX"), true, 
file2());
     assertThat(shouldRead).as("Should not read: range doesn't 
match").isFalse();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, startsWith("required", "5"), 
true).eval(FILE_3);
+    shouldRead = shouldRead(SCHEMA, startsWith("required", "5"), true, 
file3());
     assertThat(shouldRead).as("Should not read: range doesn't 
match").isFalse();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, startsWith("required", 
"3str3x"), true).eval(FILE_3);
+    shouldRead = shouldRead(SCHEMA, startsWith("required", "3str3x"), true, 
file3());
     assertThat(shouldRead).as("Should not read: range doesn't 
match").isFalse();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, startsWith("some_empty", 
"房东整租霍"), true).eval(FILE);
+    shouldRead = shouldRead(SCHEMA, startsWith("some_empty", "房东整租霍"), true, 
file());
     assertThat(shouldRead).as("Should read: range matches").isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, startsWith("all_nulls", ""), 
true).eval(FILE);
+    shouldRead = shouldRead(SCHEMA, startsWith("all_nulls", ""), true, file());
     assertThat(shouldRead).as("Should not read: range doesn't 
match").isFalse();
 
     String aboveMax = UnicodeUtil.truncateStringMax(Literal.of("イロハニホヘト"), 
4).value().toString();
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, startsWith("required", 
aboveMax), true).eval(FILE_4);
+    shouldRead = shouldRead(SCHEMA, startsWith("required", aboveMax), true, 
file4());
     assertThat(shouldRead).as("Should not read: range doesn't 
match").isFalse();
   }
 
   @Test
   public void testStringNotStartsWith() {
-    boolean shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, notStartsWith("required", "a"), 
true).eval(FILE);
+    boolean shouldRead = shouldRead(SCHEMA, notStartsWith("required", "a"), 
true, file());
     assertThat(shouldRead).as("Should read: no stats").isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, notStartsWith("required", "a"), 
true).eval(FILE_2);
+    shouldRead = shouldRead(SCHEMA, notStartsWith("required", "a"), true, 
file2());
     assertThat(shouldRead).as("Should read: range matches").isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, notStartsWith("required", "aa"), 
true).eval(FILE_2);
+    shouldRead = shouldRead(SCHEMA, notStartsWith("required", "aa"), true, 
file2());
     assertThat(shouldRead).as("Should read: range matches").isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, notStartsWith("required", 
"aaa"), true).eval(FILE_2);
+    shouldRead = shouldRead(SCHEMA, notStartsWith("required", "aaa"), true, 
file2());
     assertThat(shouldRead).as("Should read: range matches").isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, notStartsWith("required", "1s"), 
true).eval(FILE_3);
+    shouldRead = shouldRead(SCHEMA, notStartsWith("required", "1s"), true, 
file3());
     assertThat(shouldRead).as("Should read: range matches").isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, notStartsWith("required", 
"1str1x"), true)
-            .eval(FILE_3);
+    shouldRead = shouldRead(SCHEMA, notStartsWith("required", "1str1x"), true, 
file3());
     assertThat(shouldRead).as("Should read: range matches").isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, notStartsWith("required", "ff"), 
true).eval(FILE_4);
+    shouldRead = shouldRead(SCHEMA, notStartsWith("required", "ff"), true, 
file4());
     assertThat(shouldRead).as("Should read: range matches").isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, notStartsWith("required", "aB"), 
true).eval(FILE_2);
+    shouldRead = shouldRead(SCHEMA, notStartsWith("required", "aB"), true, 
file2());
     assertThat(shouldRead).as("Should read: range matches").isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, notStartsWith("required", 
"dWX"), true).eval(FILE_2);
+    shouldRead = shouldRead(SCHEMA, notStartsWith("required", "dWX"), true, 
file2());
     assertThat(shouldRead).as("Should read: range matches").isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, notStartsWith("required", "5"), 
true).eval(FILE_3);
+    shouldRead = shouldRead(SCHEMA, notStartsWith("required", "5"), true, 
file3());
     assertThat(shouldRead).as("Should read: range matches").isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, notStartsWith("required", 
"3str3x"), true)
-            .eval(FILE_3);
+    shouldRead = shouldRead(SCHEMA, notStartsWith("required", "3str3x"), true, 
file3());
     assertThat(shouldRead).as("Should read: range matches").isTrue();
 
     String aboveMax = UnicodeUtil.truncateStringMax(Literal.of("イロハニホヘト"), 
4).value().toString();
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, notStartsWith("required", 
aboveMax), true)
-            .eval(FILE_4);
+    shouldRead = shouldRead(SCHEMA, notStartsWith("required", aboveMax), true, 
file4());
     assertThat(shouldRead).as("Should read: range matches").isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, notStartsWith("required", 
"abc"), true).eval(FILE_5);
+    shouldRead = shouldRead(SCHEMA, notStartsWith("required", "abc"), true, 
file5());
     assertThat(shouldRead).as("Should not read: all strings start with 
prefix").isFalse();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, notStartsWith("required", 
"abcd"), true).eval(FILE_5);
+    shouldRead = shouldRead(SCHEMA, notStartsWith("required", "abcd"), true, 
file5());
     assertThat(shouldRead).as("Should not read: lower shorter than prefix, 
cannot match").isTrue();
   }
 
   @Test
   public void testIntegerIn() {
     boolean shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, in("id", INT_MIN_VALUE - 25, 
INT_MIN_VALUE - 24))
-            .eval(FILE);
+        shouldRead(SCHEMA, in("id", INT_MIN_VALUE - 25, INT_MIN_VALUE - 24), 
file());
     assertThat(shouldRead).as("Should not read: id below lower bound (5 < 30, 
6 < 30)").isFalse();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, in("id", INT_MIN_VALUE - 2, 
INT_MIN_VALUE - 1))
-            .eval(FILE);
+    shouldRead = shouldRead(SCHEMA, in("id", INT_MIN_VALUE - 2, INT_MIN_VALUE 
- 1), file());
     assertThat(shouldRead).as("Should not read: id below lower bound (28 < 30, 
29 < 30)").isFalse();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, in("id", INT_MIN_VALUE - 1, 
INT_MIN_VALUE))
-            .eval(FILE);
+    shouldRead = shouldRead(SCHEMA, in("id", INT_MIN_VALUE - 1, 
INT_MIN_VALUE), file());
     assertThat(shouldRead).as("Should read: id equal to lower bound (30 == 
30)").isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, in("id", INT_MAX_VALUE - 4, 
INT_MAX_VALUE - 3))
-            .eval(FILE);
+    shouldRead = shouldRead(SCHEMA, in("id", INT_MAX_VALUE - 4, INT_MAX_VALUE 
- 3), file());
     assertThat(shouldRead)
         .as("Should read: id between lower and upper bounds (30 < 75 < 79, 30 
< 76 < 79)")
         .isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, in("id", INT_MAX_VALUE, 
INT_MAX_VALUE + 1))
-            .eval(FILE);
+    shouldRead = shouldRead(SCHEMA, in("id", INT_MAX_VALUE, INT_MAX_VALUE + 
1), file());
     assertThat(shouldRead).as("Should read: id equal to upper bound (79 == 
79)").isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, in("id", INT_MAX_VALUE + 1, 
INT_MAX_VALUE + 2))
-            .eval(FILE);
+    shouldRead = shouldRead(SCHEMA, in("id", INT_MAX_VALUE + 1, INT_MAX_VALUE 
+ 2), file());
     assertThat(shouldRead).as("Should not read: id above upper bound (80 > 79, 
81 > 79)").isFalse();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, in("id", INT_MAX_VALUE + 6, 
INT_MAX_VALUE + 7))
-            .eval(FILE);
+    shouldRead = shouldRead(SCHEMA, in("id", INT_MAX_VALUE + 6, INT_MAX_VALUE 
+ 7), file());
     assertThat(shouldRead).as("Should not read: id above upper bound (85 > 79, 
86 > 79)").isFalse();
 
-    shouldRead = new InclusiveMetricsEvaluator(SCHEMA, in("all_nulls", "abc", 
"def")).eval(FILE);
+    shouldRead = shouldRead(SCHEMA, in("all_nulls", "abc", "def"), file());
     assertThat(shouldRead).as("Should skip: in on all nulls column").isFalse();
 
-    shouldRead = new InclusiveMetricsEvaluator(SCHEMA, in("some_nulls", "abc", 
"def")).eval(FILE);
+    shouldRead = shouldRead(SCHEMA, in("some_nulls", "abc", "def"), file());
     assertThat(shouldRead).as("Should read: in on some nulls column").isTrue();
 
-    shouldRead = new InclusiveMetricsEvaluator(SCHEMA, in("no_nulls", "abc", 
"def")).eval(FILE);
+    shouldRead = shouldRead(SCHEMA, in("no_nulls", "abc", "def"), file());
     assertThat(shouldRead).as("Should read: in on no nulls column").isTrue();
 
     // should read as the number of elements in the in expression is too big
     List<Integer> ids = Lists.newArrayListWithExpectedSize(400);
     for (int id = -400; id <= 0; id++) {
       ids.add(id);
     }
-    shouldRead = new InclusiveMetricsEvaluator(SCHEMA, in("id", 
ids)).eval(FILE);
+    shouldRead = shouldRead(SCHEMA, in("id", ids), file());
     assertThat(shouldRead).as("Should read: large in expression").isTrue();
   }
 
   @Test
   public void testIntegerNotIn() {
     boolean shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, notIn("id", INT_MIN_VALUE - 25, 
INT_MIN_VALUE - 24))
-            .eval(FILE);
+        shouldRead(SCHEMA, notIn("id", INT_MIN_VALUE - 25, INT_MIN_VALUE - 
24), file());
     assertThat(shouldRead).as("Should read: id below lower bound (5 < 30, 6 < 
30)").isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, notIn("id", INT_MIN_VALUE - 2, 
INT_MIN_VALUE - 1))
-            .eval(FILE);
+    shouldRead = shouldRead(SCHEMA, notIn("id", INT_MIN_VALUE - 2, 
INT_MIN_VALUE - 1), file());
     assertThat(shouldRead).as("Should read: id below lower bound (28 < 30, 29 
< 30)").isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, notIn("id", INT_MIN_VALUE - 1, 
INT_MIN_VALUE))
-            .eval(FILE);
+    shouldRead = shouldRead(SCHEMA, notIn("id", INT_MIN_VALUE - 1, 
INT_MIN_VALUE), file());
     assertThat(shouldRead).as("Should read: id equal to lower bound (30 == 
30)").isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, notIn("id", INT_MAX_VALUE - 4, 
INT_MAX_VALUE - 3))
-            .eval(FILE);
+    shouldRead = shouldRead(SCHEMA, notIn("id", INT_MAX_VALUE - 4, 
INT_MAX_VALUE - 3), file());
     assertThat(shouldRead)
         .as("Should read: id between lower and upper bounds (30 < 75 < 79, 30 
< 76 < 79)")
         .isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, notIn("id", INT_MAX_VALUE, 
INT_MAX_VALUE + 1))
-            .eval(FILE);
+    shouldRead = shouldRead(SCHEMA, notIn("id", INT_MAX_VALUE, INT_MAX_VALUE + 
1), file());
     assertThat(shouldRead).as("Should read: id equal to upper bound (79 == 
79)").isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, notIn("id", INT_MAX_VALUE + 1, 
INT_MAX_VALUE + 2))
-            .eval(FILE);
+    shouldRead = shouldRead(SCHEMA, notIn("id", INT_MAX_VALUE + 1, 
INT_MAX_VALUE + 2), file());
     assertThat(shouldRead).as("Should read: id above upper bound (80 > 79, 81 
> 79)").isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, notIn("id", INT_MAX_VALUE + 6, 
INT_MAX_VALUE + 7))
-            .eval(FILE);
+    shouldRead = shouldRead(SCHEMA, notIn("id", INT_MAX_VALUE + 6, 
INT_MAX_VALUE + 7), file());
     assertThat(shouldRead).as("Should read: id above upper bound (85 > 79, 86 
> 79)").isTrue();
 
-    shouldRead = new InclusiveMetricsEvaluator(SCHEMA, notIn("all_nulls", 
"abc", "def")).eval(FILE);
+    shouldRead = shouldRead(SCHEMA, notIn("all_nulls", "abc", "def"), file());
     assertThat(shouldRead).as("Should read: notIn on all nulls 
column").isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(SCHEMA, notIn("some_nulls", "abc", 
"def")).eval(FILE);
+    shouldRead = shouldRead(SCHEMA, notIn("some_nulls", "abc", "def"), file());
     assertThat(shouldRead).as("Should read: notIn on some nulls 
column").isTrue();
 
-    shouldRead = new InclusiveMetricsEvaluator(SCHEMA, notIn("no_nulls", 
"abc", "def")).eval(FILE);
+    shouldRead = shouldRead(SCHEMA, notIn("no_nulls", "abc", "def"), file());
     assertThat(shouldRead).as("Should read: notIn on no nulls 
column").isTrue();
   }
 
   @Test
   public void testIsNullInNestedStruct() {
     // read required_address and its nested fields
-    boolean shouldRead =
-        new InclusiveMetricsEvaluator(NESTED_SCHEMA, 
isNull("required_address")).eval(FILE_6);
+    boolean shouldRead = shouldRead(NESTED_SCHEMA, isNull("required_address"), 
file6());
     assertThat(shouldRead).as("Should not read: required_address is 
required").isFalse();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(NESTED_SCHEMA, 
isNull("required_address.required_street1"))
-            .eval(FILE_6);
+    shouldRead = shouldRead(NESTED_SCHEMA, 
isNull("required_address.required_street1"), file6());
     assertThat(shouldRead)
         .as("Should not read: required_address.required_street1 is required")
         .isFalse();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(NESTED_SCHEMA, 
isNull("required_address.optional_street1"))
-            .eval(FILE_6);
+    shouldRead = shouldRead(NESTED_SCHEMA, 
isNull("required_address.optional_street1"), file6());
     assertThat(shouldRead)
         .as("Should read: required_address.optional_street1 is optional")
         .isTrue();
 
     // read optional_address and its nested fields
-    shouldRead =
-        new InclusiveMetricsEvaluator(NESTED_SCHEMA, 
isNull("optional_address")).eval(FILE_6);
-    assertThat(shouldRead).as("Should read: metrics are not tracked for 
structs").isTrue();
+    shouldRead = shouldRead(NESTED_SCHEMA, isNull("optional_address"), 
file6());
+    assertThat(shouldRead).as("Should read: optional_address is 
optional").isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(NESTED_SCHEMA, 
isNull("optional_address.required_street2"))
-            .eval(FILE_6);
+    shouldRead = shouldRead(NESTED_SCHEMA, 
isNull("optional_address.required_street2"), file6());
     assertThat(shouldRead).as("Should read: optional_address is 
optional").isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(NESTED_SCHEMA, 
isNull("optional_address.optional_street2"))
-            .eval(FILE_6);
+    shouldRead = shouldRead(NESTED_SCHEMA, 
isNull("optional_address.optional_street2"), file6());
     assertThat(shouldRead).as("Should read: optional_address is 
optional").isTrue();
   }
 
   @Test
   public void testNotNullInNestedStruct() {
     // read required_address and its nested fields
-    boolean shouldRead =
-        new InclusiveMetricsEvaluator(NESTED_SCHEMA, 
notNull("required_address")).eval(FILE_6);
+    boolean shouldRead = shouldRead(NESTED_SCHEMA, 
notNull("required_address"), file6());
     assertThat(shouldRead).as("Should read: required_address is 
required").isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(NESTED_SCHEMA, 
notNull("required_address.required_street1"))
-            .eval(FILE_6);
+    shouldRead = shouldRead(NESTED_SCHEMA, 
notNull("required_address.required_street1"), file6());
     assertThat(shouldRead)
         .as("Should read: required_address.required_street1 is required")
         .isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(NESTED_SCHEMA, 
notNull("required_address.optional_street1"))
-            .eval(FILE_6);
+    shouldRead = shouldRead(NESTED_SCHEMA, 
notNull("required_address.optional_street1"), file6());
     assertThat(shouldRead)
         .as("Should not read: required_address.optional_street1 is optional")
         .isFalse();
 
     // read optional_address and its nested fields
-    shouldRead =
-        new InclusiveMetricsEvaluator(NESTED_SCHEMA, 
notNull("optional_address")).eval(FILE_6);
+    shouldRead = shouldRead(NESTED_SCHEMA, notNull("optional_address"), 
file6());
     assertThat(shouldRead).as("Should read: metrics are not tracked for 
structs").isTrue();
 
-    shouldRead =
-        new InclusiveMetricsEvaluator(NESTED_SCHEMA, 
notNull("optional_address.required_street2"))
-            .eval(FILE_6);
-    assertThat(shouldRead).as("Should not read: optional_address is 
optional").isFalse();
-
-    shouldRead =
-        new InclusiveMetricsEvaluator(NESTED_SCHEMA, 
notNull("optional_address.optional_street2"))
-            .eval(FILE_6);
+    shouldRead = shouldRead(NESTED_SCHEMA, 
notNull("optional_address.optional_street2"), file6());
     assertThat(shouldRead)
         .as("Should not read: optional_address.optional_street2 is optional")
         .isFalse();
   }
 
+  @Test
+  public void notNullForRequiredFieldInOptionalStruct() {

Review Comment:
   Sorry about the confusion, this was my fault. When I looked at what we 
currently do, I got it wrong because I used a `float` for the test.
   
   > Having this value align with the parquet stats will be less confusing.
   
   I thought that our stats did align with Parquet, so this statement was a 
surprise. After looking into this more, I can confirm that the stats actually 
do align with what Parquet produces. The problem above was that I used `float`, 
which collects metrics differently to accumulate a NaN count.
   
   If you look at stats for an `int` column that is produced directly from 
Parquet, they match:
   
   ```
   In [8]: %sql select * from nested_test;
   Out[8]:
   +----+-----------------------------+
   | id |                       point |
   +----+-----------------------------+
   |  1 |       Row(x=1, y=1, z=None) |
   |  2 |          Row(x=2, y=2, z=0) |
   |  3 |       Row(x=3, y=3, z=None) |
   |  4 | Row(x=None, y=None, z=None) |
   |  5 | Row(x=None, y=None, z=None) |
   +----+-----------------------------+
   ```
   
   Parquet:
   
   ```
   Row group 0:  count: 5  42.60 B records  start: 4  total(compressed): 213 B 
total(uncompressed):165 B
   
--------------------------------------------------------------------------------
                 type      encodings count     avg size   nulls   min / max
   id            INT32     Z   _     5         12.20 B    0       "1" / "5"
   location.lat  FLOAT     Z   _     5         10.60 B    2       "1.0" / "3.0"
   location.lon  FLOAT     Z   _     5         10.60 B    2       "1.0" / "3.0"
   location.alt  FLOAT     Z   _     5         9.20 B     4       "-0.0" / "0.0"
   ```
   
   Iceberg:
   
   ```
   id=Row(column_size=61, value_count=5, null_value_count=0, 
nan_value_count=None, lower_bound=1, upper_bound=5),
   point.x=Row(column_size=53, value_count=5, null_value_count=2, 
nan_value_count=None, lower_bound=1, upper_bound=3),
   point.y=Row(column_size=53, value_count=5, null_value_count=2, 
nan_value_count=None, lower_bound=1, upper_bound=3),
   point.z=Row(column_size=45, value_count=5, null_value_count=4, 
nan_value_count=None, lower_bound=0, upper_bound=0)
   ```
   
   I also ran both tests for ORC and the counts agree with the Parquet counts.
   
   > Are we able to fix this with the new v4 stats format and just use 
`null_value_count` for null detection?
   
   The most important consideration is to be consistent with what we currently 
write. Since we are writing counts in metrics maps that include null parents, 
we should continue to do that if possible. And luckily, that behavior aligns 
with row-level evaluation already.
   
   The existing behavior is:
   - There are no null values if the null count is zero
   - There is at least one null value if null count is non-zero (including null 
parents)
   - There is at least one non-null value if the null count is less than the 
value count
   - There are no non-null values if the null count is equal to the value count
   
   Accessors return `null` when a parent is missing, so this behavior matches 
the row-level evaluator. For `float` where we use leaf counts for values and 
nulls, the row-level evaluator behavior does not match when a parent is null. 
`point.x IS NULL` will return true when `point=null`, but a file containing 
only null values for `point` will be skipped because null count is 0.
   
   We will need to address the issue with `float` and `double`. To fix this for 
existing files, I think we should check whether the value count is less than 
the row count and use that to signal that parents are null. We should also 
update how we store stats and use values from Parquet for these types as well.
   
   For v4, we could change to use leaf-based counts rather than including null 
parents in the value and null value counts. That would save us from needing an 
extra null count column to detect the case where all parents are defined (null 
count = 0). It's reasonable to be consistent here.
   
   > Also checking value count == row count won't work as we have array of 
struct case.
   
   I don't think this case is relevant to the choice for structs that have at 
most 1 value for a field. We also don't currently store counts for array 
elements or map key/value pairs, which is a good thing.
   
   Evaluating `point.x IS NULL` returns true when `point` is null, which means 
null parents can be handled as null values in stats. (Keep in mind that this 
wasn't deliberate, it was a result of null handling in `Accessor`. It just 
happens to align with SQL behavior.)
   
   Predicates for arrays and maps would behave differently. For an array `arr`, 
you can't directly reference the element like in `arr.element IS NULL`. You'd 
have to translate to something like `arr CONTAINS NULL` or `arr[0] IS NULL`. 
These aren't necessarily true when the array itself is null because these imply 
that the array is not null. In Spark, `arr[0] IS NOT NULL` is false when `arr` 
is null: a null array's first element is not null!
   
   Getting back to the point, we could infer the number of missing parents in 
structs by comparing row count and value count if we know that there is always 
1 value for the field when parents are present. That logic is based on the type 
structure and it doesn't hold for arrays, which could contain 0 elements.



-- 
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