Skip to content

Commit af99307

Browse files
authored
[8.x] ESQL - CSV tests for invalid ranges (elastic#125714) (elastic#125862)
* ESQL - CSV tests for invalid ranges (elastic#125714) Add CSV tests for invalid ranges (i.e. where the lower bound is greater than the upper bound) for several data types. It looks at first glance like there should be a bug here with Date Nanos, but I cannot trigger one, and analysis suggests the code that has the bug is in fact unreachable. I've left comments to that effect. * add capability checks
1 parent 55c91d6 commit af99307

File tree

3 files changed

+82
-0
lines changed

3 files changed

+82
-0
lines changed
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
Invalid boundaries integer literal
2+
3+
FROM employees
4+
| WHERE salary > 200 AND salary < 100
5+
| KEEP salary;
6+
7+
salary:integer
8+
;
9+
10+
Invalid boundaries date
11+
12+
FROM employees
13+
| WHERE hire_date > TO_DATETIME("2025-01-01") AND hire_date < TO_DATETIME("2020-01-01")
14+
| KEEP hire_date;
15+
16+
hire_date:datetime
17+
;
18+
19+
Invalid boundaries, date with implicit casting
20+
21+
FROM employees
22+
| WHERE hire_date > "2025-01-01" AND hire_date < "2020-01-01"
23+
| KEEP hire_date;
24+
25+
hire_date:datetime
26+
;
27+
28+
Invalid Boundaries, date nanos
29+
required_capability: date_nanos_type
30+
required_capability: to_date_nanos
31+
required_capability: date_nanos_binary_comparison
32+
required_capability: fix_date_nanos_lucene_pushdown_bug
33+
required_capability: fix_date_nanos_mixed_range_pushdown_bug
34+
35+
FROM date_nanos
36+
| WHERE nanos > TO_DATE_NANOS("2025-01-01") and nanos < TO_DATE_NANOS("2020-01-01")
37+
| KEEP nanos;
38+
warningRegex:Line 2:49: evaluation of \[nanos < TO_DATE_NANOS\(\\\"2020-01-01\\\"\)\] failed, treating result as null\. Only first 20 failures recorded\.
39+
warningRegex:Line 2:49: java\.lang\.IllegalArgumentException: single-value function encountered multi-value
40+
warningRegex:Line 2:9: evaluation of \[nanos > TO_DATE_NANOS\(\\\"2025-01-01\\\"\)\] failed, treating result as null\. Only first 20 failures recorded\.
41+
warningRegex:Line 2:9: java\.lang\.IllegalArgumentException: single-value function encountered multi-value
42+
43+
nanos:date_nanos
44+
;
45+
46+
Invalid Boundaries, date nanos implicit casting
47+
required_capability: date_nanos_type
48+
required_capability: date_nanos_binary_comparison
49+
required_capability: date_nanos_implicit_casting
50+
required_capability: fix_date_nanos_lucene_pushdown_bug
51+
required_capability: fix_date_nanos_mixed_range_pushdown_bug
52+
53+
FROM date_nanos
54+
| WHERE nanos > "2025-01-01" and nanos < "2020-01-01"
55+
| KEEP nanos;
56+
warningRegex:Line 2:34: evaluation of \[nanos < \\\"2020-01-01\\\"\] failed, treating result as null\. Only first 20 failures recorded\.
57+
warningRegex:Line 2:34: java\.lang\.IllegalArgumentException: single-value function encountered multi-value
58+
warningRegex:Line 2:9: evaluation of \[nanos > \\\"2025-01-01\\\"\] failed, treating result as null\. Only first 20 failures recorded\.
59+
warningRegex:Line 2:9: java\.lang\.IllegalArgumentException: single-value function encountered multi-value
60+
61+
nanos:date_nanos
62+
;

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/Range.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ public ZoneId zoneId() {
115115
*/
116116
@Override
117117
public boolean foldable() {
118+
// NB: this is likely dead code. See note in areBoundariesInvalid
118119
if (lower.foldable() && upper.foldable()) {
119120
if (value().foldable()) {
120121
return true;
@@ -130,6 +131,7 @@ public boolean foldable() {
130131

131132
@Override
132133
public Object fold(FoldContext ctx) {
134+
// NB: this is likely dead code. See note in areBoundariesInvalid
133135
Object lowerValue = lower.fold(ctx);
134136
Object upperValue = upper.fold(ctx);
135137
if (areBoundariesInvalid(lowerValue, upperValue)) {
@@ -149,6 +151,19 @@ public Object fold(FoldContext ctx) {
149151
* If they are, the value does not have to be evaluated.
150152
*/
151153
protected boolean areBoundariesInvalid(Object lowerValue, Object upperValue) {
154+
/*
155+
NB: I am reasonably sure this code is dead. It can only be reached from foldable(), and as far as I can tell
156+
we never fold ranges. There's no ES|QL syntax for ranges, so they can never be created by the parser. The
157+
PropagateEquals optimizer rule can in theory create ranges, but only from existing ranges. The fact that this
158+
class is not serializable (note that writeTo throws UnsupportedOperationException) is a clear indicator that
159+
logical planning cannot output Range nodes.
160+
161+
PushFiltersToSource can also create ranges, but that is a Physical optimizer rule. Folding happens in the
162+
Logical optimization layer, and should be done by the time we are calling PushFiltersToSource.
163+
164+
That said, if somehow you have arrived here while debugging something, know that this method is likely
165+
completely broken for date_nanos, and possibly other types.
166+
*/
152167
if (DataType.isDateTime(value.dataType()) || DataType.isDateTime(lower.dataType()) || DataType.isDateTime(upper.dataType())) {
153168
try {
154169
if (upperValue instanceof String upperString) {

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/rules/logical/PropagateEquals.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,11 @@ private static Expression propagate(Or or, LogicalOptimizerContext ctx) {
256256
}
257257

258258
// Equals OR Range
259+
/*
260+
NB: this loop is probably dead code. There's no syntax for ranges, so the parser never produces them. This
261+
rule can create ranges, but only in this loop, which iterates over the existing ranges. In short,
262+
ranges.size() should always be zero at this point.
263+
*/
259264
for (int i = 0; i < ranges.size(); i++) { // might modify list, so use index loop
260265
Range range = ranges.get(i);
261266
if (eq.left().semanticEquals(range.value())) {

0 commit comments

Comments
 (0)