@Override public <T> Expression predicate(UnboundPredicate<T> pred) { return pred.bind(struct); } }
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); } }
protected Expression bind(UnboundPredicate<?> pred) { return pred.bind(schema.asStruct()); }
@Override public <T> Expression predicate(UnboundPredicate<T> pred) { Expression bound = pred.bind(spec.schema().asStruct()); if (bound instanceof BoundPredicate) { return predicate((BoundPredicate<?>) bound); } return bound; } }
@Override public <T> Expression predicate(UnboundPredicate<T> pred) { Expression bound = pred.bind(spec.schema().asStruct()); if (bound instanceof BoundPredicate) { Expression boundResidual = predicate((BoundPredicate<?>) bound); if (boundResidual instanceof Predicate) { return pred; // replace inclusive original unbound predicate } return boundResidual; // use the non-predicate residual (e.g. alwaysTrue) } // if binding didn't result in a Predicate, return the expression return bound; } }
@Override @SuppressWarnings("unchecked") public <T> Expression predicate(BoundPredicate<T> pred) { // Get the strict projection of this predicate in partition data, then use it to determine // whether to return the original predicate. The strict projection returns true iff the // original predicate would have returned true, so the predicate can be eliminated if the // strict projection evaluates to true. // // If there is no strict projection or if it evaluates to false, then return the predicate. PartitionField part = spec.getFieldBySourceId(pred.ref().fieldId()); if (part == null) { return pred; // not associated inclusive a partition field, can't be evaluated } UnboundPredicate<?> strictProjection = ((Transform<T, ?>) part.transform()) .projectStrict(part.name(), pred); if (strictProjection != null) { Expression bound = strictProjection.bind(spec.partitionType()); if (bound instanceof BoundPredicate) { // the predicate methods will evaluate and return alwaysTrue or alwaysFalse return super.predicate((BoundPredicate<?>) bound); } return bound; // use the non-predicate residual (e.g. alwaysTrue) } // if the predicate could not be projected, it must be in the residual return pred; }
@Test public void testMissingField() { StructType struct = StructType.of( required(13, "x", Types.IntegerType.get()) ); UnboundPredicate<Integer> unbound = new UnboundPredicate<>(LT, ref("missing"), 6); try { unbound.bind(struct); Assert.fail("Binding a missing field should fail"); } catch (ValidationException e) { Assert.assertTrue("Validation should complain about missing field", e.getMessage().contains("Cannot find field 'missing' in struct:")); } }
LT, ref("f"), (double) Float.MAX_VALUE * 2); Assert.assertEquals("Less than above max should be alwaysTrue", Expressions.alwaysTrue(), lt.bind(struct)); Expressions.alwaysTrue(), lteq.bind(struct)); Expressions.alwaysTrue(), gt.bind(struct)); Expressions.alwaysTrue(), gteq.bind(struct)); Expressions.alwaysFalse(), gtMax.bind(struct)); Expressions.alwaysFalse(), gteqMax.bind(struct)); Expressions.alwaysFalse(), ltMin.bind(struct)); Expressions.alwaysFalse(), lteqMin.bind(struct)); Expression ltExpr = new UnboundPredicate<>(LT, ref("f"), (double) Float.MAX_VALUE).bind(struct); BoundPredicate<Float> ltMax = assertAndUnwrap(ltExpr); Assert.assertEquals("Should translate bound to Float", .bind(struct); BoundPredicate<Float> lteqMax = assertAndUnwrap(lteqExpr); Assert.assertEquals("Should translate bound to Float", (Float) Float.MAX_VALUE, lteqMax.literal().value()); Expression gtExpr = new UnboundPredicate<>(GT, ref("f"), (double) -Float.MAX_VALUE).bind(struct); BoundPredicate<Float> gtMin = assertAndUnwrap(gtExpr);
LT, ref("i"), (long) Integer.MAX_VALUE + 1L); Assert.assertEquals("Less than above max should be alwaysTrue", Expressions.alwaysTrue(), lt.bind(struct)); Expressions.alwaysTrue(), lteq.bind(struct)); Expressions.alwaysTrue(), gt.bind(struct)); Expressions.alwaysTrue(), gteq.bind(struct)); Expressions.alwaysFalse(), gtMax.bind(struct)); Expressions.alwaysFalse(), gteqMax.bind(struct)); Expressions.alwaysFalse(), ltMin.bind(struct)); Expressions.alwaysFalse(), lteqMin.bind(struct)); Expression ltExpr = new UnboundPredicate<>(LT, ref("i"), (long) Integer.MAX_VALUE).bind(struct); BoundPredicate<Integer> ltMax = assertAndUnwrap(ltExpr); Assert.assertEquals("Should translate bound to Integer", .bind(struct); BoundPredicate<Integer> lteqMax = assertAndUnwrap(lteqExpr); Assert.assertEquals("Should translate bound to Integer", (Integer) Integer.MAX_VALUE, lteqMax.literal().value()); Expression gtExpr = new UnboundPredicate<>(GT, ref("i"), (long) Integer.MIN_VALUE).bind(struct); BoundPredicate<Integer> gtMin = assertAndUnwrap(gtExpr);
@Test public void testInvalidConversions() { StructType struct = StructType.of(required(16, "f", Types.FloatType.get())); for (Expression.Operation op : COMPARISONS) { UnboundPredicate<String> unbound = new UnboundPredicate<>(op, ref("f"), "12.40"); try { unbound.bind(struct); Assert.fail("Should not convert string to float"); } catch (ValidationException e) { Assert.assertEquals("Should ", e.getMessage(), "Invalid value for comparison inclusive type float: 12.40 (java.lang.String)"); } } }
@Test @SuppressWarnings("unchecked") public void testIsNull() { StructType optional = StructType.of(optional(19, "s", Types.StringType.get())); UnboundPredicate<?> unbound = new UnboundPredicate<>(IS_NULL, ref("s")); Expression expr = unbound.bind(optional); BoundPredicate<?> bound = assertAndUnwrap(expr); Assert.assertEquals("Should use the same operation", IS_NULL, bound.op()); Assert.assertEquals("Should use the correct field", 19, bound.ref().fieldId()); Assert.assertNull("Should not have a literal value", bound.literal()); StructType required = StructType.of(required(20, "s", Types.StringType.get())); Assert.assertEquals("IsNull inclusive a required field should be alwaysFalse", Expressions.alwaysFalse(), unbound.bind(required)); }
@Test public void testNotNull() { StructType optional = StructType.of(optional(21, "s", Types.StringType.get())); UnboundPredicate<?> unbound = new UnboundPredicate<>(NOT_NULL, ref("s")); Expression expr = unbound.bind(optional); BoundPredicate<?> bound = assertAndUnwrap(expr); Assert.assertEquals("Should use the same operation", NOT_NULL, bound.op()); Assert.assertEquals("Should use the correct field", 21, bound.ref().fieldId()); Assert.assertNull("Should not have a literal value", bound.literal()); StructType required = StructType.of(required(22, "s", Types.StringType.get())); Assert.assertEquals("NotNull inclusive a required field should be alwaysTrue", Expressions.alwaysTrue(), unbound.bind(required)); } }
@Test @SuppressWarnings("unchecked") public void testLiteralConversion() { StructType struct = StructType.of(required(15, "d", Types.DecimalType.of(9, 2))); for (Expression.Operation op : COMPARISONS) { UnboundPredicate<String> unbound = new UnboundPredicate<>(op, ref("d"), "12.40"); Expression expr = unbound.bind(struct); BoundPredicate<BigDecimal> bound = assertAndUnwrap(expr); Assert.assertEquals("Should convert literal value to decimal", new BigDecimal("12.40"), bound.literal().value()); Assert.assertEquals("Should reference correct field ID", 15, bound.ref().fieldId()); Assert.assertEquals("Should not change the comparison operation", op, bound.op()); } }
@Test @SuppressWarnings("unchecked") public void testComparisonPredicateBinding() { StructType struct = StructType.of(required(14, "x", Types.IntegerType.get())); for (Expression.Operation op : COMPARISONS) { UnboundPredicate<Integer> unbound = new UnboundPredicate<>(op, ref("x"), 5); Expression expr = unbound.bind(struct); BoundPredicate<Integer> bound = assertAndUnwrap(expr); Assert.assertEquals("Should not alter literal value", Integer.valueOf(5), bound.literal().value()); Assert.assertEquals("Should reference correct field ID", 14, bound.ref().fieldId()); Assert.assertEquals("Should not change the comparison operation", op, bound.op()); } }
@Test @SuppressWarnings("unchecked") public void testMultipleFields() { StructType struct = StructType.of( required(10, "x", Types.IntegerType.get()), required(11, "y", Types.IntegerType.get()), required(12, "z", Types.IntegerType.get()) ); UnboundPredicate<Integer> unbound = new UnboundPredicate<>(LT, ref("y"), 6); Expression expr = unbound.bind(struct); BoundPredicate<Integer> bound = assertAndUnwrap(expr); Assert.assertEquals("Should reference correct field ID", 11, bound.ref().fieldId()); Assert.assertEquals("Should not change the comparison operation", LT, bound.op()); Assert.assertEquals("Should not alter literal value", Integer.valueOf(6), bound.literal().value()); }
BoundPredicate<?> bound = assertAndUnwrap(predicate.bind(spec.schema().asStruct()));
BoundPredicate<?> bound = assertAndUnwrap(predicate.bind(spec.schema().asStruct()));
@Test public void testExpressions() throws Exception { Schema schema = new Schema( Types.NestedField.optional(34, "a", Types.IntegerType.get()) ); Expression[] expressions = new Expression[] { Expressions.alwaysFalse(), Expressions.alwaysTrue(), Expressions.lessThan("x", 5), Expressions.lessThanOrEqual("y", -3), Expressions.greaterThan("z", 0), Expressions.greaterThanOrEqual("t", 129), Expressions.equal("col", "data"), Expressions.notEqual("col", "abc"), Expressions.notNull("maybeNull"), Expressions.isNull("maybeNull2"), Expressions.not(Expressions.greaterThan("a", 10)), Expressions.and(Expressions.greaterThanOrEqual("a", 0), Expressions.lessThan("a", 3)), Expressions.or(Expressions.lessThan("a", 0), Expressions.greaterThan("a", 10)), Expressions.equal("a", 5).bind(schema.asStruct()) }; for (Expression expression : expressions) { Expression copy = TestHelpers.roundTripSerialize(expression); Assert.assertTrue( "Expression should equal the deserialized copy: " + expression + " != " + copy, equals(expression, copy)); } }