Skip to content

Commit a5e2206

Browse files
committed
Fix off by one
1 parent 523321a commit a5e2206

File tree

2 files changed

+75
-5
lines changed
  • server/src/main/java/org/elasticsearch/index/mapper
  • x-pack/plugin/esql/qa/server/single-node/src/javaRestTest/java/org/elasticsearch/xpack/esql/qa/single_node

2 files changed

+75
-5
lines changed

server/src/main/java/org/elasticsearch/index/mapper/TextFieldMapper.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -996,7 +996,7 @@ public boolean canUseSyntheticSourceDelegateForQueryingEquality(String str) {
996996
return false;
997997
}
998998
// Can't push equality if the field we're checking for is so big we'd ignore it.
999-
return str.length() < syntheticSourceDelegate.ignoreAbove();
999+
return str.length() <= syntheticSourceDelegate.ignoreAbove();
10001000
}
10011001

10021002
@Override

x-pack/plugin/esql/qa/server/single-node/src/javaRestTest/java/org/elasticsearch/xpack/esql/qa/single_node/RestEsqlIT.java

Lines changed: 74 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,10 @@ public void testProfileParsing() throws IOException {
414414
public void testPushEqualityOnDefaults() throws IOException {
415415
indexTimestampData(1);
416416

417-
RequestObjectBuilder builder = requestObjectBuilder().query(fromIndex() + " | WHERE test == \"value1\"");
417+
String value = "v".repeat(between(0, 256));
418+
indexValue(value);
419+
420+
RequestObjectBuilder builder = requestObjectBuilder().query(fromIndex() + " | WHERE test == \"" + value + "\"");
418421
builder.profile(true);
419422
Map<String, Object> result = runEsql(builder);
420423
assertResultMap(
@@ -424,7 +427,7 @@ public void testPushEqualityOnDefaults() throws IOException {
424427
.item(matchesMap().entry("name", "test").entry("type", "text"))
425428
.item(matchesMap().entry("name", "test.keyword").entry("type", "keyword"))
426429
.item(matchesMap().entry("name", "value").entry("type", "long")),
427-
equalTo(List.of(List.of("2020-12-12T00:00:00.000Z", "value1", "value1", 1)))
430+
equalTo(List.of(Arrays.asList(null, value, value, null)))
428431
);
429432

430433
@SuppressWarnings("unchecked")
@@ -437,7 +440,7 @@ public void testPushEqualityOnDefaults() throws IOException {
437440
List<Map<String, Object>> operators = (List<Map<String, Object>>) p.get("operators");
438441
for (Map<String, Object> o : operators) {
439442
// The query here is the most important bit - we *do* push to lucene.
440-
sig.add(checkOperatorProfile(o, "test.keyword:value1"));
443+
sig.add(checkOperatorProfile(o, "test.keyword:" + value));
441444
}
442445
String description = p.get("description").toString();
443446
switch (description) {
@@ -460,6 +463,73 @@ public void testPushEqualityOnDefaults() throws IOException {
460463
}
461464
}
462465

466+
public void testPushEqualityOnDefaultsTooBigToPush() throws IOException {
467+
indexTimestampData(1);
468+
469+
String value = "a".repeat(between(257, 1000));
470+
indexValue(value);
471+
472+
RequestObjectBuilder builder = requestObjectBuilder().query(fromIndex() + " | WHERE test == \"" + value + "\"");
473+
builder.profile(true);
474+
Map<String, Object> result = runEsql(builder);
475+
assertResultMap(
476+
result,
477+
getResultMatcher(result).entry("profile", matchesMap().entry("drivers", instanceOf(List.class))),
478+
matchesList().item(matchesMap().entry("name", "@timestamp").entry("type", "date"))
479+
.item(matchesMap().entry("name", "test").entry("type", "text"))
480+
.item(matchesMap().entry("name", "test.keyword").entry("type", "keyword"))
481+
.item(matchesMap().entry("name", "value").entry("type", "long")),
482+
equalTo(List.of(Arrays.asList(null, value, null, null)))
483+
);
484+
485+
@SuppressWarnings("unchecked")
486+
List<Map<String, Object>> profiles = (List<Map<String, Object>>) ((Map<String, Object>) result.get("profile")).get("drivers");
487+
for (Map<String, Object> p : profiles) {
488+
fixTypesOnProfile(p);
489+
assertThat(p, commonProfile());
490+
List<String> sig = new ArrayList<>();
491+
@SuppressWarnings("unchecked")
492+
List<Map<String, Object>> operators = (List<Map<String, Object>>) p.get("operators");
493+
for (Map<String, Object> o : operators) {
494+
// The query here is the most important bit - we do *not* push to lucene.
495+
sig.add(checkOperatorProfile(o, "*:*"));
496+
}
497+
String description = p.get("description").toString();
498+
switch (description) {
499+
case "data" -> assertMap(
500+
sig,
501+
matchesList().item("LuceneSourceOperator")
502+
.item("ValuesSourceReaderOperator")
503+
.item("FilterOperator")
504+
.item("LimitOperator")
505+
.item("ValuesSourceReaderOperator")
506+
.item("ProjectOperator")
507+
.item("ExchangeSinkOperator")
508+
);
509+
case "node_reduce" -> assertThat(
510+
sig,
511+
either(matchesList().item("ExchangeSourceOperator").item("ExchangeSinkOperator")).or(
512+
matchesList().item("ExchangeSourceOperator").item("AggregationOperator").item("ExchangeSinkOperator")
513+
)
514+
);
515+
case "final" -> assertMap(sig, matchesList().item("ExchangeSourceOperator").item("LimitOperator").item("OutputOperator"));
516+
default -> throw new IllegalArgumentException("can't match " + description);
517+
}
518+
}
519+
}
520+
521+
private void indexValue(String value) throws IOException {
522+
Request bulk = new Request("POST", "/_bulk");
523+
bulk.addParameter("filter_path", "errors");
524+
bulk.addParameter("refresh", "");
525+
bulk.setJsonEntity(String.format("""
526+
{"create":{"_index":"%s"}}
527+
{"test":"%s"}
528+
""", testIndexName(), value));
529+
Response response = client().performRequest(bulk);
530+
Assert.assertEquals("{\"errors\":false}", EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8));
531+
}
532+
463533
@SuppressWarnings("unchecked")
464534
public void assertProcessMetadataForNextNode(Map<String, Object> nodeMetadata, Set<String> expectedNamesForNodes, int seenNodes) {
465535
assertEquals("M", nodeMetadata.get("ph"));
@@ -748,7 +818,7 @@ private String checkOperatorProfile(Map<String, Object> o, String query) {
748818
case "ExchangeSourceOperator" -> matchesMap().entry("pages_waiting", 0)
749819
.entry("pages_emitted", greaterThan(0))
750820
.entry("rows_emitted", greaterThan(0));
751-
case "ProjectOperator", "EvalOperator" -> basicProfile();
821+
case "ProjectOperator", "EvalOperator", "FilterOperator" -> basicProfile();
752822
case "LimitOperator" -> matchesMap().entry("pages_processed", greaterThan(0))
753823
.entry("limit", 1000)
754824
.entry("limit_remaining", 999)

0 commit comments

Comments
 (0)