public <T> R predicate(BoundPredicate<T> pred) { switch (pred.op()) { case IS_NULL: return isNull(pred.ref()); case NOT_NULL: return notNull(pred.ref()); case LT: return lt(pred.ref(), pred.literal()); case LT_EQ: return ltEq(pred.ref(), pred.literal()); case GT: return gt(pred.ref(), pred.literal()); case GT_EQ: return gtEq(pred.ref(), pred.literal()); case EQ: return eq(pred.ref(), pred.literal()); case NOT_EQ: return notEq(pred.ref(), pred.literal()); case IN: return in(pred.ref(), pred.literal()); case NOT_IN: return notIn(pred.ref(), pred.literal()); default: throw new UnsupportedOperationException( "Unknown operation for predicate: " + pred.op()); } }
@Override public Expression negate() { return new BoundPredicate<>(op().negate(), ref(), literal()); } }
@Override public <T> Set<Integer> predicate(BoundPredicate<T> pred) { references.add(pred.ref().fieldId()); return references; } }
BoundPredicate<Float> ltMax = assertAndUnwrap(ltExpr); Assert.assertEquals("Should translate bound to Float", (Float) Float.MAX_VALUE, ltMax.literal().value()); BoundPredicate<Float> lteqMax = assertAndUnwrap(lteqExpr); Assert.assertEquals("Should translate bound to Float", (Float) Float.MAX_VALUE, lteqMax.literal().value()); Float.valueOf(-Float.MAX_VALUE), gtMin.literal().value()); BoundPredicate<Float> gteqMin = assertAndUnwrap(gteqExpr); Assert.assertEquals("Should translate bound to Float", Float.valueOf(-Float.MAX_VALUE), gteqMin.literal().value());
return Expressions.alwaysFalse(); return new BoundPredicate<>(IS_NULL, new BoundReference<>(struct, field.fieldId())); case NOT_NULL: if (field.isRequired()) { return Expressions.alwaysTrue(); return new BoundPredicate<>(NOT_NULL, new BoundReference<>(struct, field.fieldId())); default: throw new ValidationException("Operation must be IS_NULL or NOT_NULL"); return new BoundPredicate<>(op(), new BoundReference<>(struct, field.fieldId()), lit);
@Override public UnboundPredicate<Integer> projectStrict(String name, BoundPredicate<T> predicate) { switch (predicate.op()) { case NOT_EQ: // TODO: need to translate not(eq(...)) into notEq in expressions return Expressions.predicate(predicate.op(), name, apply(predicate.literal().value())); // case NOT_IN: // return null; default: // no strict projection for comparison or equality return null; } }
@Override @SuppressWarnings("unchecked") public <T> Expression predicate(BoundPredicate<T> pred) { PartitionField part = spec.getFieldBySourceId(pred.ref().fieldId()); if (part == null) { // the predicate has no partition column return alwaysTrue(); } UnboundPredicate<?> result = ((Transform<T, ?>) part.transform()).project(part.name(), pred); if (result != null) { return result; } // if the predicate could not be projected, it always matches return alwaysTrue(); } }
BoundPredicate<Integer> ltMax = assertAndUnwrap(ltExpr); Assert.assertEquals("Should translate bound to Integer", (Integer) Integer.MAX_VALUE, ltMax.literal().value()); BoundPredicate<Integer> lteqMax = assertAndUnwrap(lteqExpr); Assert.assertEquals("Should translate bound to Integer", (Integer) Integer.MAX_VALUE, lteqMax.literal().value()); (Integer) Integer.MIN_VALUE, gtMin.literal().value()); BoundPredicate<Integer> gteqMin = assertAndUnwrap(gteqExpr); Assert.assertEquals("Should translate bound to Integer", (Integer) Integer.MIN_VALUE, gteqMin.literal().value());
@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()); } }
@Override public UnboundPredicate<Integer> project(String name, BoundPredicate<T> predicate) { switch (predicate.op()) { case EQ: return Expressions.predicate( predicate.op(), name, apply(predicate.literal().value())); // case IN: // return Expressions.predicate(); default: // comparison predicates can't be projected, notEq can't be projected // TODO: small ranges can be projected. // for example, (x > 0) and (x < 3) can be turned into in({1, 2}) and projected. return null; } }
@Override @SuppressWarnings("unchecked") public <T> Expression predicate(BoundPredicate<T> pred) { PartitionField part = spec.getFieldBySourceId(pred.ref().fieldId()); if (part == null) { // the predicate has no partition column return alwaysFalse(); } UnboundPredicate<?> result = ((Transform<T, ?>) part.transform()) .projectStrict(part.name(), pred); if (result != null) { return result; } // if the predicate could not be projected, it never matches return alwaysFalse(); } }
@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()); } }
static <T> UnboundPredicate<T> truncateDecimal( String name, BoundPredicate<BigDecimal> pred, Transform<BigDecimal, T> transform) { BigDecimal boundary = pred.literal().value(); switch (pred.op()) { case LT: // adjust closed and then transform ltEq BigDecimal minusOne = new BigDecimal( boundary.unscaledValue().subtract(BigInteger.ONE), boundary.scale()); return predicate(Expression.Operation.LT_EQ, name, transform.apply(minusOne)); case LT_EQ: return predicate(Expression.Operation.LT_EQ, name, transform.apply(boundary)); case GT: // adjust closed and then transform gtEq BigDecimal plusOne = new BigDecimal( boundary.unscaledValue().add(BigInteger.ONE), boundary.scale()); return predicate(Expression.Operation.GT_EQ, name, transform.apply(plusOne)); case GT_EQ: return predicate(Expression.Operation.GT_EQ, name, transform.apply(boundary)); case EQ: return predicate(pred.op(), name, transform.apply(boundary)); default: return null; } }
@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; }