|
14 | 14 | import org.elasticsearch.xpack.esql.core.expression.Literal; |
15 | 15 | import org.elasticsearch.xpack.esql.core.expression.Nullability; |
16 | 16 | import org.elasticsearch.xpack.esql.core.expression.predicate.BinaryOperator; |
| 17 | +import org.elasticsearch.xpack.esql.core.expression.predicate.Range; |
17 | 18 | import org.elasticsearch.xpack.esql.core.expression.predicate.logical.And; |
18 | 19 | import org.elasticsearch.xpack.esql.core.expression.predicate.logical.Not; |
19 | 20 | import org.elasticsearch.xpack.esql.core.expression.predicate.logical.Or; |
|
27 | 28 | import org.elasticsearch.xpack.esql.expression.predicate.operator.arithmetic.Mod; |
28 | 29 | import org.elasticsearch.xpack.esql.expression.predicate.operator.arithmetic.Mul; |
29 | 30 | import org.elasticsearch.xpack.esql.expression.predicate.operator.arithmetic.Sub; |
| 31 | +import org.elasticsearch.xpack.esql.plan.logical.Filter; |
| 32 | +import org.elasticsearch.xpack.esql.plan.logical.LogicalPlan; |
30 | 33 |
|
31 | 34 | import static org.elasticsearch.xpack.esql.EsqlTestUtils.FIVE; |
32 | 35 | import static org.elasticsearch.xpack.esql.EsqlTestUtils.THREE; |
33 | 36 | import static org.elasticsearch.xpack.esql.EsqlTestUtils.TWO; |
| 37 | +import static org.elasticsearch.xpack.esql.EsqlTestUtils.as; |
| 38 | +import static org.elasticsearch.xpack.esql.EsqlTestUtils.emptySource; |
34 | 39 | import static org.elasticsearch.xpack.esql.EsqlTestUtils.equalsOf; |
| 40 | +import static org.elasticsearch.xpack.esql.EsqlTestUtils.fieldAttribute; |
35 | 41 | import static org.elasticsearch.xpack.esql.EsqlTestUtils.greaterThanOf; |
36 | 42 | import static org.elasticsearch.xpack.esql.EsqlTestUtils.greaterThanOrEqualOf; |
37 | 43 | import static org.elasticsearch.xpack.esql.EsqlTestUtils.lessThanOf; |
@@ -111,6 +117,53 @@ public void testArithmeticFolding() { |
111 | 117 | assertEquals(1, foldOperator(new Mod(EMPTY, new Literal(EMPTY, 7, DataType.INTEGER), THREE))); |
112 | 118 | } |
113 | 119 |
|
| 120 | + public void testFoldRange() { |
| 121 | + // 1 + 9 < value AND value < 20-1 |
| 122 | + // with value = 12 and randomly replacing the `<` by `<=` |
| 123 | + Expression lowerBound = new Add(EMPTY, new Literal(EMPTY, 1, DataType.INTEGER), new Literal(EMPTY, 9, DataType.INTEGER)); |
| 124 | + Expression upperBound = new Sub(EMPTY, new Literal(EMPTY, 20, DataType.INTEGER), new Literal(EMPTY, 1, DataType.INTEGER)); |
| 125 | + Expression value = new Literal(EMPTY, 12, DataType.INTEGER); |
| 126 | + Range range = new Range(EMPTY, value, lowerBound, randomBoolean(), upperBound, randomBoolean(), randomZone()); |
| 127 | + |
| 128 | + Expression folded = new ConstantFolding().rule(range); |
| 129 | + assertTrue((Boolean) as(folded, Literal.class).value()); |
| 130 | + } |
| 131 | + |
| 132 | + public void testFoldRangeWithInvalidBoundaries() { |
| 133 | + // 1 + 9 < value AND value <= 11 - 1 |
| 134 | + // This is always false. We also randomly test versions with `<=`. |
| 135 | + Expression lowerBound; |
| 136 | + boolean includeLowerBound = randomBoolean(); |
| 137 | + if (includeLowerBound) { |
| 138 | + // 1 + 10 <= value |
| 139 | + lowerBound = new Add(EMPTY, new Literal(EMPTY, 1, DataType.INTEGER), new Literal(EMPTY, 10, DataType.INTEGER)); |
| 140 | + } else { |
| 141 | + // 1 + 9 < value |
| 142 | + lowerBound = new Add(EMPTY, new Literal(EMPTY, 1, DataType.INTEGER), new Literal(EMPTY, 9, DataType.INTEGER)); |
| 143 | + } |
| 144 | + |
| 145 | + boolean includeUpperBound = randomBoolean(); |
| 146 | + // value < 11 - 1 |
| 147 | + // or |
| 148 | + // value <= 11 - 1 |
| 149 | + Expression upperBound = new Sub(EMPTY, new Literal(EMPTY, 11, DataType.INTEGER), new Literal(EMPTY, 1, DataType.INTEGER)); |
| 150 | + |
| 151 | + Expression value = fieldAttribute(); |
| 152 | + |
| 153 | + Range range = new Range(EMPTY, value, lowerBound, includeLowerBound, upperBound, includeUpperBound, randomZone()); |
| 154 | + |
| 155 | + // We need to test this as part of a logical plan, to correctly simulate how we traverse down the expression tree. |
| 156 | + // Just applying this to the range directly won't perform a transformDown. |
| 157 | + LogicalPlan filter = new Filter(EMPTY, emptySource(), range); |
| 158 | + |
| 159 | + Filter foldedOnce = as(new ConstantFolding().apply(filter), Filter.class); |
| 160 | + // We need to run the rule twice, because during the first run only the boundaries can be folded - the range doesn't know it's |
| 161 | + // foldable, yet. |
| 162 | + Filter foldedTwice = as(new ConstantFolding().apply(foldedOnce), Filter.class); |
| 163 | + |
| 164 | + assertFalse((Boolean) as(foldedTwice.condition(), Literal.class).value()); |
| 165 | + } |
| 166 | + |
114 | 167 | private static Object foldOperator(BinaryOperator<?, ?, ?, ?> b) { |
115 | 168 | return ((Literal) new ConstantFolding().rule(b)).value(); |
116 | 169 | } |
|
0 commit comments