Skip to content

Commit 2af19d8

Browse files
authored
ES|QL: Restrict sorting for _source and counter field types (#114638)
1 parent 30ff474 commit 2af19d8

File tree

10 files changed

+80
-9
lines changed

10 files changed

+80
-9
lines changed

docs/changelog/114638.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
pr: 114638
2+
summary: "ES|QL: Restrict sorting for `_source` and counter field types"
3+
area: ES|QL
4+
type: bug
5+
issues:
6+
- 114423
7+
- 111976

x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/type/DataType.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,10 @@ public static boolean isRepresentable(DataType t) {
425425
&& t.isCounter() == false;
426426
}
427427

428+
public static boolean isCounter(DataType t) {
429+
return t == COUNTER_DOUBLE || t == COUNTER_INTEGER || t == COUNTER_LONG;
430+
}
431+
428432
public static boolean isSpatialPoint(DataType t) {
429433
return t == GEO_POINT || t == CARTESIAN_POINT;
430434
}
@@ -437,6 +441,10 @@ public static boolean isSpatial(DataType t) {
437441
return t == GEO_POINT || t == CARTESIAN_POINT || t == GEO_SHAPE || t == CARTESIAN_SHAPE;
438442
}
439443

444+
public static boolean isSortable(DataType t) {
445+
return false == (t == SOURCE || isCounter(t) || isSpatial(t));
446+
}
447+
440448
public String nameUpper() {
441449
return name;
442450
}

x-pack/plugin/esql/qa/testFixtures/src/main/resources/tsdb-mapping.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@
2727
"message_in": {
2828
"type": "float",
2929
"time_series_metric": "counter"
30+
},
31+
"message_out": {
32+
"type": "integer",
33+
"time_series_metric": "counter"
3034
}
3135
}
3236
}

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,12 @@ public enum Cap {
365365
/**
366366
* Support named parameters for field names.
367367
*/
368-
NAMED_PARAMETER_FOR_FIELD_AND_FUNCTION_NAMES;
368+
NAMED_PARAMETER_FOR_FIELD_AND_FUNCTION_NAMES,
369+
370+
/**
371+
* Fix sorting not allowed on _source and counters.
372+
*/
373+
SORTING_ON_SOURCE_AND_COUNTERS_FORBIDDEN;
369374

370375
private final boolean snapshotOnly;
371376
private final FeatureFlag featureFlag;

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/Verifier.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ else if (p instanceof Lookup lookup) {
190190

191191
checkOperationsOnUnsignedLong(p, failures);
192192
checkBinaryComparison(p, failures);
193-
checkForSortOnSpatialTypes(p, failures);
193+
checkForSortableDataTypes(p, failures);
194194

195195
checkFilterMatchConditions(p, failures);
196196
checkFullTextQueryFunctions(p, failures);
@@ -555,12 +555,12 @@ private static Failure validateUnsignedLongNegation(Neg neg) {
555555
}
556556

557557
/**
558-
* Makes sure that spatial types do not appear in sorting contexts.
558+
* Some datatypes are not sortable
559559
*/
560-
private static void checkForSortOnSpatialTypes(LogicalPlan p, Set<Failure> localFailures) {
560+
private static void checkForSortableDataTypes(LogicalPlan p, Set<Failure> localFailures) {
561561
if (p instanceof OrderBy ob) {
562562
ob.order().forEach(order -> {
563-
if (DataType.isSpatial(order.dataType())) {
563+
if (DataType.isSortable(order.dataType()) == false) {
564564
localFailures.add(fail(order, "cannot sort on " + order.dataType().typeName()));
565565
}
566566
});

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/Rate.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ public DataType dataType() {
115115
protected TypeResolution resolveType() {
116116
TypeResolution resolution = isType(
117117
field(),
118-
dt -> dt == DataType.COUNTER_LONG || dt == DataType.COUNTER_INTEGER || dt == DataType.COUNTER_DOUBLE,
118+
dt -> DataType.isCounter(dt),
119119
sourceText(),
120120
FIRST,
121121
"counter_long",

x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/analysis/AnalyzerTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1638,7 +1638,7 @@ public void testCounterTypes() {
16381638
var attributes = limit.output().stream().collect(Collectors.toMap(NamedExpression::name, a -> a));
16391639
assertThat(
16401640
attributes.keySet(),
1641-
equalTo(Set.of("network.connections", "network.bytes_in", "network.bytes_out", "network.message_in"))
1641+
equalTo(Set.of("network.connections", "network.bytes_in", "network.bytes_out", "network.message_in", "network.message_out"))
16421642
);
16431643
assertThat(attributes.get("network.connections").dataType(), equalTo(DataType.LONG));
16441644
assertThat(attributes.get("network.bytes_in").dataType(), equalTo(DataType.COUNTER_LONG));

x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/analysis/VerifierTests.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,16 @@
2626
import java.util.LinkedHashMap;
2727
import java.util.LinkedHashSet;
2828
import java.util.List;
29+
import java.util.Locale;
2930
import java.util.Map;
3031
import java.util.Set;
3132

3233
import static org.elasticsearch.xpack.esql.EsqlTestUtils.paramAsConstant;
3334
import static org.elasticsearch.xpack.esql.EsqlTestUtils.withDefaultLimitWarning;
3435
import static org.elasticsearch.xpack.esql.analysis.AnalyzerTestUtils.loadMapping;
36+
import static org.elasticsearch.xpack.esql.core.type.DataType.COUNTER_DOUBLE;
37+
import static org.elasticsearch.xpack.esql.core.type.DataType.COUNTER_INTEGER;
38+
import static org.elasticsearch.xpack.esql.core.type.DataType.COUNTER_LONG;
3539
import static org.elasticsearch.xpack.esql.core.type.DataType.UNSIGNED_LONG;
3640
import static org.hamcrest.Matchers.containsString;
3741
import static org.hamcrest.Matchers.equalTo;
@@ -909,6 +913,25 @@ public void testSpatialSort() {
909913
assertEquals("1:42: cannot sort on cartesian_shape", error("FROM countries_bbox_web | LIMIT 5 | sort shape", countriesBboxWeb));
910914
}
911915

916+
public void testSourceSorting() {
917+
assertEquals("1:35: cannot sort on _source", error("from test metadata _source | sort _source"));
918+
}
919+
920+
public void testCountersSorting() {
921+
Map<DataType, String> counterDataTypes = Map.of(
922+
COUNTER_DOUBLE,
923+
"network.message_in",
924+
COUNTER_INTEGER,
925+
"network.message_out",
926+
COUNTER_LONG,
927+
"network.bytes_out"
928+
);
929+
for (DataType counterDT : counterDataTypes.keySet()) {
930+
var fieldName = counterDataTypes.get(counterDT);
931+
assertEquals("1:18: cannot sort on " + counterDT.name().toLowerCase(Locale.ROOT), error("from test | sort " + fieldName, tsdb));
932+
}
933+
}
934+
912935
public void testInlineImpossibleConvert() {
913936
assertEquals("1:5: argument of [false::ip] must be [ip or string], found value [false] type [boolean]", error("ROW false::ip"));
914937
}

x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/esql/140_metadata.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,3 +155,19 @@ setup:
155155
esql.query:
156156
body:
157157
query: 'FROM test [metadata _source] | STATS COUNT_DISTINCT(_source)'
158+
159+
---
160+
"sort on _source not allowed":
161+
- requires:
162+
test_runner_features: [capabilities]
163+
capabilities:
164+
- method: POST
165+
path: /_query
166+
parameters: []
167+
capabilities: [sorting_on_source_and_counters_forbidden]
168+
reason: "Sorting on _source shouldn't have been possible"
169+
- do:
170+
catch: /cannot sort on _source/
171+
esql.query:
172+
body:
173+
query: 'FROM test metadata _source | sort _source'

x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/esql/40_tsdb.yml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,11 +178,19 @@ cast counter then filter:
178178

179179
---
180180
sort on counter without cast:
181+
- requires:
182+
test_runner_features: [capabilities]
183+
capabilities:
184+
- method: POST
185+
path: /_query
186+
parameters: []
187+
capabilities: [sorting_on_source_and_counters_forbidden]
188+
reason: "Sorting on counters shouldn't have been possible"
181189
- do:
182-
catch: bad_request
190+
catch: /cannot sort on counter_long/
183191
esql.query:
184192
body:
185-
query: 'from test | KEEP k8s.pod.network.tx | sort @k8s.pod.network.tx | limit 1'
193+
query: 'from test | KEEP k8s.pod.network.tx | sort k8s.pod.network.tx | limit 1'
186194

187195
---
188196
cast then sort on counter:

0 commit comments

Comments
 (0)