You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iceberg.apache.org by bl...@apache.org on 2019/01/23 19:19:20 UTC

[incubator-iceberg] branch master updated: Add case sensitivity flag to expression binding (#82)

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

blue pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-iceberg.git


The following commit(s) were added to refs/heads/master by this push:
     new 022fe36  Add case sensitivity flag to expression binding (#82)
022fe36 is described below

commit 022fe36ba4afa7f22231df7a544eaae915a5153b
Author: Xabriel J. Collazo Mojica <xc...@adobe.com>
AuthorDate: Wed Jan 23 11:19:16 2019 -0800

    Add case sensitivity flag to expression binding (#82)
    
    This doesn't change default behavior. Configuring case sensitivity for processing engines will be added in future commits.
---
 .../com/netflix/iceberg/expressions/Binder.java    | 44 +++++++++++++++++++---
 .../com/netflix/iceberg/expressions/Evaluator.java |  2 +-
 .../expressions/InclusiveManifestEvaluator.java    |  2 +-
 .../expressions/InclusiveMetricsEvaluator.java     |  2 +-
 .../netflix/iceberg/expressions/Projections.java   |  2 +-
 .../iceberg/expressions/ResidualEvaluator.java     |  4 +-
 .../expressions/StrictMetricsEvaluator.java        |  2 +-
 .../iceberg/expressions/UnboundPredicate.java      | 31 ++++++++++++++-
 .../main/java/com/netflix/iceberg/types/Types.java | 17 ++++++++-
 .../iceberg/expressions/TestExpressionBinding.java | 14 ++++++-
 .../iceberg/expressions/TestPredicateBinding.java  |  2 +-
 .../netflix/iceberg/transforms/TestProjection.java |  4 +-
 .../java/com/netflix/iceberg/BaseTableScan.java    |  2 +-
 .../parquet/ParquetDictionaryRowGroupFilter.java   |  2 +-
 .../netflix/iceberg/parquet/ParquetFilters.java    |  4 +-
 .../parquet/ParquetMetricsRowGroupFilter.java      |  2 +-
 .../netflix/iceberg/spark/SparkExpressions.java    |  2 +-
 .../com/netflix/iceberg/spark/SparkSchemaUtil.java |  4 +-
 18 files changed, 114 insertions(+), 28 deletions(-)

diff --git a/api/src/main/java/com/netflix/iceberg/expressions/Binder.java b/api/src/main/java/com/netflix/iceberg/expressions/Binder.java
index e024a7c..b896e0c 100644
--- a/api/src/main/java/com/netflix/iceberg/expressions/Binder.java
+++ b/api/src/main/java/com/netflix/iceberg/expressions/Binder.java
@@ -51,31 +51,63 @@ public class Binder {
    *
    * @param struct The {@link StructType struct type} to resolve references by name.
    * @param expr An {@link Expression expression} to rewrite with bound references.
+   * @param caseSensitive A boolean flag to control whether the bind should enforce case sensitivity.
    * @return the expression rewritten with bound references
    * @throws ValidationException if literals do not match bound references
    * @throws IllegalStateException if any references are already bound
    */
   public static Expression bind(StructType struct,
-                                Expression expr) {
-    return ExpressionVisitors.visit(expr, new BindVisitor(struct));
+                                Expression expr,
+                                boolean caseSensitive) {
+    return ExpressionVisitors.visit(expr, new BindVisitor(struct, caseSensitive));
   }
 
-  public static Set<Integer> boundReferences(StructType struct, List<Expression> exprs) {
+  /**
+   * Replaces all unbound/named references with bound references to fields in the given struct,
+   * defaulting to case sensitive mode.
+   *
+   * Access modifier is package-private, to only allow use from existing tests.
+   *
+   * <p>
+   * When a reference is resolved, any literal used in a predicate for that field is converted to
+   * the field's type using {@link Literal#to(Type)}. If automatic conversion to that type isn't
+   * allowed, a {@link ValidationException validation exception} is thrown.
+   * <p>
+   * The result expression may be simplified when constructed. For example, {@code isNull("a")} is
+   * replaced with {@code alwaysFalse()} when {@code "a"} is resolved to a required field.
+   * <p>
+   * The expression cannot contain references that are already bound, or an
+   * {@link IllegalStateException} will be thrown.
+   *
+   * @param struct The {@link StructType struct type} to resolve references by name.
+   * @param expr An {@link Expression expression} to rewrite with bound references.
+   * @return the expression rewritten with bound references
+   *
+   * @throws IllegalStateException if any references are already bound
+   */
+  static Expression bind(StructType struct,
+                                   Expression expr) {
+    return Binder.bind(struct, expr, true);
+  }
+
+  public static Set<Integer> boundReferences(StructType struct, List<Expression> exprs, boolean caseSensitive) {
     if (exprs == null) {
       return ImmutableSet.of();
     }
     ReferenceVisitor visitor = new ReferenceVisitor();
     for (Expression expr : exprs) {
-      ExpressionVisitors.visit(bind(struct, expr), visitor);
+      ExpressionVisitors.visit(bind(struct, expr, caseSensitive), visitor);
     }
     return visitor.references;
   }
 
   private static class BindVisitor extends ExpressionVisitor<Expression> {
     private final StructType struct;
+    private final boolean caseSensitive;
 
-    private BindVisitor(StructType struct) {
+    private BindVisitor(StructType struct, boolean caseSensitive) {
       this.struct = struct;
+      this.caseSensitive = caseSensitive;
     }
 
     @Override
@@ -110,7 +142,7 @@ public class Binder {
 
     @Override
     public <T> Expression predicate(UnboundPredicate<T> pred) {
-      return pred.bind(struct);
+      return pred.bind(struct, caseSensitive);
     }
   }
 
diff --git a/api/src/main/java/com/netflix/iceberg/expressions/Evaluator.java b/api/src/main/java/com/netflix/iceberg/expressions/Evaluator.java
index 3854166..fc6eacd 100644
--- a/api/src/main/java/com/netflix/iceberg/expressions/Evaluator.java
+++ b/api/src/main/java/com/netflix/iceberg/expressions/Evaluator.java
@@ -44,7 +44,7 @@ public class Evaluator implements Serializable {
   }
 
   public Evaluator(Types.StructType struct, Expression unbound) {
-    this.expr = Binder.bind(struct, unbound);
+    this.expr = Binder.bind(struct, unbound, true);
   }
 
   public boolean eval(StructLike data) {
diff --git a/api/src/main/java/com/netflix/iceberg/expressions/InclusiveManifestEvaluator.java b/api/src/main/java/com/netflix/iceberg/expressions/InclusiveManifestEvaluator.java
index cac617d..6493273 100644
--- a/api/src/main/java/com/netflix/iceberg/expressions/InclusiveManifestEvaluator.java
+++ b/api/src/main/java/com/netflix/iceberg/expressions/InclusiveManifestEvaluator.java
@@ -54,7 +54,7 @@ public class InclusiveManifestEvaluator {
 
   public InclusiveManifestEvaluator(PartitionSpec spec, Expression rowFilter) {
     this.struct = spec.partitionType();
-    this.expr = Binder.bind(struct, rewriteNot(Projections.inclusive(spec).project(rowFilter)));
+    this.expr = Binder.bind(struct, rewriteNot(Projections.inclusive(spec).project(rowFilter)), true);
   }
 
   /**
diff --git a/api/src/main/java/com/netflix/iceberg/expressions/InclusiveMetricsEvaluator.java b/api/src/main/java/com/netflix/iceberg/expressions/InclusiveMetricsEvaluator.java
index 26c17e4..54cc0be 100644
--- a/api/src/main/java/com/netflix/iceberg/expressions/InclusiveMetricsEvaluator.java
+++ b/api/src/main/java/com/netflix/iceberg/expressions/InclusiveMetricsEvaluator.java
@@ -56,7 +56,7 @@ public class InclusiveMetricsEvaluator {
   public InclusiveMetricsEvaluator(Schema schema, Expression unbound) {
     this.schema = schema;
     this.struct = schema.asStruct();
-    this.expr = Binder.bind(struct, rewriteNot(unbound));
+    this.expr = Binder.bind(struct, rewriteNot(unbound), true);
   }
 
   /**
diff --git a/api/src/main/java/com/netflix/iceberg/expressions/Projections.java b/api/src/main/java/com/netflix/iceberg/expressions/Projections.java
index d9da053..b811e27 100644
--- a/api/src/main/java/com/netflix/iceberg/expressions/Projections.java
+++ b/api/src/main/java/com/netflix/iceberg/expressions/Projections.java
@@ -135,7 +135,7 @@ public class Projections {
 
     @Override
     public <T> Expression predicate(UnboundPredicate<T> pred) {
-      Expression bound = pred.bind(spec.schema().asStruct());
+      Expression bound = pred.bind(spec.schema().asStruct(), true);
 
       if (bound instanceof BoundPredicate) {
         return predicate((BoundPredicate<?>) bound);
diff --git a/api/src/main/java/com/netflix/iceberg/expressions/ResidualEvaluator.java b/api/src/main/java/com/netflix/iceberg/expressions/ResidualEvaluator.java
index 290ae4a..610bdc5 100644
--- a/api/src/main/java/com/netflix/iceberg/expressions/ResidualEvaluator.java
+++ b/api/src/main/java/com/netflix/iceberg/expressions/ResidualEvaluator.java
@@ -170,7 +170,7 @@ public class ResidualEvaluator implements Serializable {
           .projectStrict(part.name(), pred);
 
       if (strictProjection != null) {
-        Expression bound = strictProjection.bind(spec.partitionType());
+        Expression bound = strictProjection.bind(spec.partitionType(), true);
         if (bound instanceof BoundPredicate) {
           // the predicate methods will evaluate and return alwaysTrue or alwaysFalse
           return super.predicate((BoundPredicate<?>) bound);
@@ -184,7 +184,7 @@ public class ResidualEvaluator implements Serializable {
 
     @Override
     public <T> Expression predicate(UnboundPredicate<T> pred) {
-      Expression bound = pred.bind(spec.schema().asStruct());
+      Expression bound = pred.bind(spec.schema().asStruct(), true);
 
       if (bound instanceof BoundPredicate) {
         Expression boundResidual = predicate((BoundPredicate<?>) bound);
diff --git a/api/src/main/java/com/netflix/iceberg/expressions/StrictMetricsEvaluator.java b/api/src/main/java/com/netflix/iceberg/expressions/StrictMetricsEvaluator.java
index d3fa1df..702c255 100644
--- a/api/src/main/java/com/netflix/iceberg/expressions/StrictMetricsEvaluator.java
+++ b/api/src/main/java/com/netflix/iceberg/expressions/StrictMetricsEvaluator.java
@@ -57,7 +57,7 @@ public class StrictMetricsEvaluator {
   public StrictMetricsEvaluator(Schema schema, Expression unbound) {
     this.schema = schema;
     this.struct = schema.asStruct();
-    this.expr = Binder.bind(struct, rewriteNot(unbound));
+    this.expr = Binder.bind(struct, rewriteNot(unbound), true);
   }
 
   /**
diff --git a/api/src/main/java/com/netflix/iceberg/expressions/UnboundPredicate.java b/api/src/main/java/com/netflix/iceberg/expressions/UnboundPredicate.java
index 3523d1d..da6f981 100644
--- a/api/src/main/java/com/netflix/iceberg/expressions/UnboundPredicate.java
+++ b/api/src/main/java/com/netflix/iceberg/expressions/UnboundPredicate.java
@@ -44,8 +44,35 @@ public class UnboundPredicate<T> extends Predicate<T, NamedReference> {
     return new UnboundPredicate<>(op().negate(), ref(), literal());
   }
 
-  public Expression bind(Types.StructType struct) {
-    Types.NestedField field = struct.field(ref().name());
+  /**
+   * Bind this UnboundPredicate, defaulting to case sensitive mode.
+   *
+   * Access modifier is package-private, to only allow use from existing tests.
+   *
+   * @param struct The {@link Types.StructType struct type} to resolve references by name.
+   * @return an {@link Expression}
+   * @throws ValidationException if literals do not match bound references, or if comparison on expression is invalid
+   */
+  Expression bind(Types.StructType struct) {
+    return bind(struct, true);
+  }
+
+  /**
+   * Bind this UnboundPredicate.
+   *
+   * @param struct The {@link Types.StructType struct type} to resolve references by name.
+   * @param caseSensitive A boolean flag to control whether the bind should enforce case sensitivity.
+   * @return an {@link Expression}
+   * @throws ValidationException if literals do not match bound references, or if comparison on expression is invalid
+   */
+  public Expression bind(Types.StructType struct, boolean caseSensitive) {
+    Types.NestedField field;
+    if (caseSensitive) {
+      field = struct.field(ref().name());
+    } else {
+      field = struct.caseInsensitiveField(ref().name());
+    }
+
     ValidationException.check(field != null,
         "Cannot find field '%s' in struct: %s", ref().name(), struct);
 
diff --git a/api/src/main/java/com/netflix/iceberg/types/Types.java b/api/src/main/java/com/netflix/iceberg/types/Types.java
index 22111cf..a4ef5ac 100644
--- a/api/src/main/java/com/netflix/iceberg/types/Types.java
+++ b/api/src/main/java/com/netflix/iceberg/types/Types.java
@@ -55,7 +55,7 @@ public class Types {
   private static final Pattern DECIMAL = Pattern.compile("decimal\\((\\d+),\\s+(\\d+)\\)");
 
   public static PrimitiveType fromPrimitiveString(String typeString) {
-    String lowerTypeString = typeString.toLowerCase(Locale.ENGLISH);
+    String lowerTypeString = typeString.toLowerCase(Locale.ROOT);
     if (TYPES.containsKey(lowerTypeString)) {
       return TYPES.get(lowerTypeString);
     }
@@ -516,6 +516,7 @@ public class Types {
     // lazy values
     private transient List<NestedField> fieldList = null;
     private transient Map<String, NestedField> fieldsByName = null;
+    private transient Map<String, NestedField> fieldsByLowerCaseName = null;
     private transient Map<Integer, NestedField> fieldsById = null;
 
     private StructType(List<NestedField> fields) {
@@ -535,6 +536,10 @@ public class Types {
       return lazyFieldsByName().get(name);
     }
 
+    public NestedField caseInsensitiveField(String name) {
+        return lazyFieldsByLowerCaseName().get(name.toLowerCase(Locale.ROOT));
+    }
+
     @Override
     public Type fieldType(String name) {
       NestedField field = field(name);
@@ -600,6 +605,13 @@ public class Types {
       return fieldsByName;
     }
 
+    private Map<String, NestedField> lazyFieldsByLowerCaseName() {
+        if (fieldsByLowerCaseName == null) {
+          indexFields();
+        }
+        return fieldsByLowerCaseName;
+    }
+
     private Map<Integer, NestedField> lazyFieldsById() {
       if (fieldsById == null) {
         indexFields();
@@ -609,12 +621,15 @@ public class Types {
 
     private void indexFields() {
       ImmutableMap.Builder<String, NestedField> byNameBuilder = ImmutableMap.builder();
+      ImmutableMap.Builder<String, NestedField> byLowerCaseNameBuilder = ImmutableMap.builder();
       ImmutableMap.Builder<Integer, NestedField> byIdBuilder = ImmutableMap.builder();
       for (NestedField field : fields) {
         byNameBuilder.put(field.name(), field);
+        byLowerCaseNameBuilder.put(field.name().toLowerCase(Locale.ROOT), field);
         byIdBuilder.put(field.fieldId(), field);
       }
       this.fieldsByName = byNameBuilder.build();
+      this.fieldsByLowerCaseName = byLowerCaseNameBuilder.build();
       this.fieldsById = byIdBuilder.build();
     }
   }
diff --git a/api/src/test/java/com/netflix/iceberg/expressions/TestExpressionBinding.java b/api/src/test/java/com/netflix/iceberg/expressions/TestExpressionBinding.java
index 14b95b0..266e863 100644
--- a/api/src/test/java/com/netflix/iceberg/expressions/TestExpressionBinding.java
+++ b/api/src/test/java/com/netflix/iceberg/expressions/TestExpressionBinding.java
@@ -64,7 +64,19 @@ public class TestExpressionBinding {
   @Test
   public void testSingleReference() {
     Expression expr = not(equal("x", 7));
-    TestHelpers.assertAllReferencesBound("Single reference", Binder.bind(STRUCT, expr));
+    TestHelpers.assertAllReferencesBound("Single reference", Binder.bind(STRUCT, expr, true));
+  }
+
+  @Test
+  public void testCaseInsensitiveReference() {
+    Expression expr = not(equal("X", 7));
+    TestHelpers.assertAllReferencesBound("Single reference", Binder.bind(STRUCT, expr, false));
+  }
+
+  @Test(expected = ValidationException.class)
+  public void testCaseSensitiveReference() {
+    Expression expr = not(equal("X", 7));
+    Binder.bind(STRUCT, expr, true);
   }
 
   @Test
diff --git a/api/src/test/java/com/netflix/iceberg/expressions/TestPredicateBinding.java b/api/src/test/java/com/netflix/iceberg/expressions/TestPredicateBinding.java
index 433d20e..c30986f 100644
--- a/api/src/test/java/com/netflix/iceberg/expressions/TestPredicateBinding.java
+++ b/api/src/test/java/com/netflix/iceberg/expressions/TestPredicateBinding.java
@@ -179,7 +179,7 @@ public class TestPredicateBinding {
     Assert.assertEquals("Less than or equal below min should be alwaysFalse",
         Expressions.alwaysFalse(), lteqMin.bind(struct));
 
-    Expression ltExpr = new UnboundPredicate<>(LT, ref("i"), (long) Integer.MAX_VALUE).bind(struct);
+    Expression ltExpr = new UnboundPredicate<>(LT, ref("i"), (long) Integer.MAX_VALUE).bind(struct, true);
     BoundPredicate<Integer> ltMax = assertAndUnwrap(ltExpr);
     Assert.assertEquals("Should translate bound to Integer",
         (Integer) Integer.MAX_VALUE, ltMax.literal().value());
diff --git a/api/src/test/java/com/netflix/iceberg/transforms/TestProjection.java b/api/src/test/java/com/netflix/iceberg/transforms/TestProjection.java
index 8d8a34d..f08fe3f 100644
--- a/api/src/test/java/com/netflix/iceberg/transforms/TestProjection.java
+++ b/api/src/test/java/com/netflix/iceberg/transforms/TestProjection.java
@@ -71,7 +71,7 @@ public class TestProjection {
       UnboundPredicate<?> projected = assertAndUnwrapUnbound(expr);
 
       // check inclusive the bound predicate to ensure the types are correct
-      BoundPredicate<?> bound = assertAndUnwrap(predicate.bind(spec.schema().asStruct()));
+      BoundPredicate<?> bound = assertAndUnwrap(predicate.bind(spec.schema().asStruct(), true));
 
       Assert.assertEquals("Field name should match partition struct field",
           "id", projected.ref().name());
@@ -109,7 +109,7 @@ public class TestProjection {
       UnboundPredicate<?> projected = assertAndUnwrapUnbound(expr);
 
       // check inclusive the bound predicate to ensure the types are correct
-      BoundPredicate<?> bound = assertAndUnwrap(predicate.bind(spec.schema().asStruct()));
+      BoundPredicate<?> bound = assertAndUnwrap(predicate.bind(spec.schema().asStruct(), true));
 
       Assert.assertEquals("Field name should match partition struct field",
           "id", projected.ref().name());
diff --git a/core/src/main/java/com/netflix/iceberg/BaseTableScan.java b/core/src/main/java/com/netflix/iceberg/BaseTableScan.java
index bbfdc4f..0c44df7 100644
--- a/core/src/main/java/com/netflix/iceberg/BaseTableScan.java
+++ b/core/src/main/java/com/netflix/iceberg/BaseTableScan.java
@@ -129,7 +129,7 @@ class BaseTableScan implements TableScan {
 
     // all of the filter columns are required
     requiredFieldIds.addAll(
-        Binder.boundReferences(table.schema().asStruct(), Collections.singletonList(rowFilter)));
+        Binder.boundReferences(table.schema().asStruct(), Collections.singletonList(rowFilter), true));
 
     // all of the projection columns are required
     requiredFieldIds.addAll(TypeUtil.getProjectedIds(table.schema().select(columns)));
diff --git a/parquet/src/main/java/com/netflix/iceberg/parquet/ParquetDictionaryRowGroupFilter.java b/parquet/src/main/java/com/netflix/iceberg/parquet/ParquetDictionaryRowGroupFilter.java
index f7db1fb..51d8c12 100644
--- a/parquet/src/main/java/com/netflix/iceberg/parquet/ParquetDictionaryRowGroupFilter.java
+++ b/parquet/src/main/java/com/netflix/iceberg/parquet/ParquetDictionaryRowGroupFilter.java
@@ -68,7 +68,7 @@ public class ParquetDictionaryRowGroupFilter {
   public ParquetDictionaryRowGroupFilter(Schema schema, Expression unbound) {
     this.schema = schema;
     this.struct = schema.asStruct();
-    this.expr = Binder.bind(struct, rewriteNot(unbound));
+    this.expr = Binder.bind(struct, rewriteNot(unbound), true);
   }
 
   /**
diff --git a/parquet/src/main/java/com/netflix/iceberg/parquet/ParquetFilters.java b/parquet/src/main/java/com/netflix/iceberg/parquet/ParquetFilters.java
index 11613aa..b93a8d8 100644
--- a/parquet/src/main/java/com/netflix/iceberg/parquet/ParquetFilters.java
+++ b/parquet/src/main/java/com/netflix/iceberg/parquet/ParquetFilters.java
@@ -161,7 +161,7 @@ class ParquetFilters {
     }
 
     protected Expression bind(UnboundPredicate<?> pred) {
-      return pred.bind(schema.asStruct());
+      return pred.bind(schema.asStruct(), true);
     }
 
     @Override
@@ -189,7 +189,7 @@ class ParquetFilters {
 
     protected Expression bind(UnboundPredicate<?> pred) {
       // instead of binding the predicate using the top-level schema, bind it to the partition data
-      return pred.bind(partitionStruct);
+      return pred.bind(partitionStruct, true);
     }
   }
 
diff --git a/parquet/src/main/java/com/netflix/iceberg/parquet/ParquetMetricsRowGroupFilter.java b/parquet/src/main/java/com/netflix/iceberg/parquet/ParquetMetricsRowGroupFilter.java
index 8694390..490eb16 100644
--- a/parquet/src/main/java/com/netflix/iceberg/parquet/ParquetMetricsRowGroupFilter.java
+++ b/parquet/src/main/java/com/netflix/iceberg/parquet/ParquetMetricsRowGroupFilter.java
@@ -57,7 +57,7 @@ public class ParquetMetricsRowGroupFilter {
   public ParquetMetricsRowGroupFilter(Schema schema, Expression unbound) {
     this.schema = schema;
     this.struct = schema.asStruct();
-    this.expr = Binder.bind(struct, rewriteNot(unbound));
+    this.expr = Binder.bind(struct, rewriteNot(unbound), true);
   }
 
   /**
diff --git a/spark/src/main/java/com/netflix/iceberg/spark/SparkExpressions.java b/spark/src/main/java/com/netflix/iceberg/spark/SparkExpressions.java
index 99f0a81..25f38ff 100644
--- a/spark/src/main/java/com/netflix/iceberg/spark/SparkExpressions.java
+++ b/spark/src/main/java/com/netflix/iceberg/spark/SparkExpressions.java
@@ -336,7 +336,7 @@ public class SparkExpressions {
 
   public static Expression convert(com.netflix.iceberg.expressions.Expression filter,
                                    Schema schema) {
-    return visit(Binder.bind(schema.asStruct(), filter), new ExpressionToSpark(schema));
+    return visit(Binder.bind(schema.asStruct(), filter, true), new ExpressionToSpark(schema));
   }
 
   private static class ExpressionToSpark extends ExpressionVisitors.
diff --git a/spark/src/main/java/com/netflix/iceberg/spark/SparkSchemaUtil.java b/spark/src/main/java/com/netflix/iceberg/spark/SparkSchemaUtil.java
index b63329f..bfe9390 100644
--- a/spark/src/main/java/com/netflix/iceberg/spark/SparkSchemaUtil.java
+++ b/spark/src/main/java/com/netflix/iceberg/spark/SparkSchemaUtil.java
@@ -202,7 +202,7 @@ public class SparkSchemaUtil {
    * @throws IllegalArgumentException if the Spark type does not match the Schema
    */
   public static Schema prune(Schema schema, StructType requestedType, List<Expression> filters) {
-    Set<Integer> filterRefs = Binder.boundReferences(schema.asStruct(), filters);
+    Set<Integer> filterRefs = Binder.boundReferences(schema.asStruct(), filters, true);
     return new Schema(visit(schema, new PruneColumnsWithoutReordering(requestedType, filterRefs))
         .asNestedType()
         .asStructType()
@@ -225,7 +225,7 @@ public class SparkSchemaUtil {
    * @throws IllegalArgumentException if the Spark type does not match the Schema
    */
   public static Schema prune(Schema schema, StructType requestedType, Expression filter) {
-    Set<Integer> filterRefs = Binder.boundReferences(schema.asStruct(), Collections.singletonList(filter));
+    Set<Integer> filterRefs = Binder.boundReferences(schema.asStruct(), Collections.singletonList(filter), true);
     return new Schema(visit(schema, new PruneColumnsWithoutReordering(requestedType, filterRefs))
         .asNestedType()
         .asStructType()