Skip to content

Commit b720ded

Browse files
authored
Fix off by one in ValuesBytesRefAggregator (#132032)
There are two bugs introduced in #130510 and #131390 affecting the VALUES aggregator. The random tests do not cover these edge cases: 1. The check should be firstValues.size() <= group instead of firstValues.size() < group when reading values from the firstValues array. We need to inject nulls with repeated values (to simulate ordinals) to trigger this case. 2. We incorrectly added positionOffset when reading the group ID. We need to generate more groups to trigger chunking. Relates #130510 Relates #131390 Closes #131878
1 parent 6a64375 commit b720ded

File tree

8 files changed

+29
-24
lines changed

8 files changed

+29
-24
lines changed

muted-tests.yml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -515,9 +515,6 @@ tests:
515515
- class: org.elasticsearch.xpack.esql.vector.VectorSimilarityFunctionsIT
516516
method: testDifferentDimensions {functionName=v_dot_product similarityFunction=DOT_PRODUCT}
517517
issue: https://github.com/elastic/elasticsearch/issues/131845
518-
- class: org.elasticsearch.compute.aggregation.ValuesBytesRefGroupingAggregatorFunctionTests
519-
method: testSomeFiltered
520-
issue: https://github.com/elastic/elasticsearch/issues/131878
521518
- class: org.elasticsearch.xpack.restart.FullClusterRestartIT
522519
method: testWatcherWithApiKey {cluster=UPGRADED}
523520
issue: https://github.com/elastic/elasticsearch/issues/131964

x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/aggregation/ValuesBytesRefAggregator.java

Lines changed: 2 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/ValuesBytesRefAggregators.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,8 @@ static void addOrdinalInputBlock(
160160
IntVector hashIds
161161
) {
162162
for (int p = 0; p < groupIds.getPositionCount(); p++) {
163+
final int groupId = groupIds.getInt(p);
163164
final int valuePosition = p + positionOffset;
164-
final int groupId = groupIds.getInt(valuePosition);
165165
final int start = ordinalIds.getFirstValueIndex(valuePosition);
166166
final int end = start + ordinalIds.getValueCount(valuePosition);
167167
for (int i = start; i < end; i++) {
@@ -212,8 +212,8 @@ static void combineIntermediateInputValues(
212212
} else {
213213
final BytesRef scratch = new BytesRef();
214214
for (int p = 0; p < groupIds.getPositionCount(); p++) {
215+
final int groupId = groupIds.getInt(p);
215216
final int valuePosition = p + positionOffset;
216-
final int groupId = groupIds.getInt(valuePosition);
217217
final int start = values.getFirstValueIndex(valuePosition);
218218
final int end = start + values.getValueCount(valuePosition);
219219
for (int i = start; i < end; i++) {

x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/X-ValuesAggregator.java.st

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -525,12 +525,8 @@ $if(BytesRef)$
525525
try (var builder = blockFactory.newIntBlockBuilder(estimateSize)) {
526526
int nextValuesStart = 0;
527527
for (int s = 0; s < selected.getPositionCount(); s++) {
528-
int group = selected.getInt(s);
529-
if (firstValues.size() < group) {
530-
builder.appendNull();
531-
continue;
532-
}
533-
int firstValue = firstValues.get(group) - 1;
528+
final int group = selected.getInt(s);
529+
final int firstValue = group >= firstValues.size() ? -1 : firstValues.get(group) - 1;
534530
if (firstValue < 0) {
535531
builder.appendNull();
536532
continue;

x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/ValuesBytesRefGroupingAggregatorFunctionTests.java

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@
2727
import static org.hamcrest.Matchers.nullValue;
2828

2929
public class ValuesBytesRefGroupingAggregatorFunctionTests extends GroupingAggregatorFunctionTestCase {
30+
final boolean ordinals = randomBoolean();
31+
final boolean withNulls = randomBoolean();
32+
3033
@Override
3134
protected AggregatorFunctionSupplier aggregatorFunction() {
3235
return new ValuesBytesRefAggregatorFunctionSupplier();
@@ -39,10 +42,17 @@ protected String expectedDescriptionOfAggregator() {
3942

4043
@Override
4144
protected SourceOperator simpleInput(BlockFactory blockFactory, int size) {
42-
return new LongBytesRefTupleBlockSourceOperator(
43-
blockFactory,
44-
IntStream.range(0, size).mapToObj(l -> Tuple.tuple(randomLongBetween(0, 4), new BytesRef(randomAlphaOfLengthBetween(0, 100))))
45-
);
45+
46+
return new LongBytesRefTupleBlockSourceOperator(blockFactory, IntStream.range(0, size).mapToObj(l -> {
47+
long groupId = randomLongBetween(0, 100);
48+
if (withNulls && randomBoolean()) {
49+
return Tuple.tuple(groupId, null);
50+
}
51+
if (ordinals && randomBoolean()) {
52+
return Tuple.tuple(groupId, new BytesRef("v" + between(1, 5)));
53+
}
54+
return Tuple.tuple(groupId, new BytesRef(randomAlphaOfLengthBetween(0, 100)));
55+
}));
4656
}
4757

4858
@Override

x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/ValuesDoubleGroupingAggregatorFunctionTests.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
import static org.hamcrest.Matchers.nullValue;
2727

2828
public class ValuesDoubleGroupingAggregatorFunctionTests extends GroupingAggregatorFunctionTestCase {
29+
private final boolean withNulls = randomBoolean();
30+
2931
@Override
3032
protected AggregatorFunctionSupplier aggregatorFunction() {
3133
return new ValuesDoubleAggregatorFunctionSupplier();
@@ -40,7 +42,8 @@ protected String expectedDescriptionOfAggregator() {
4042
protected SourceOperator simpleInput(BlockFactory blockFactory, int size) {
4143
return new LongDoubleTupleBlockSourceOperator(
4244
blockFactory,
43-
LongStream.range(0, size).mapToObj(l -> Tuple.tuple(randomLongBetween(0, 4), randomDouble()))
45+
LongStream.range(0, size)
46+
.mapToObj(l -> Tuple.tuple(randomLongBetween(0, 100), withNulls && randomBoolean() ? null : randomDouble()))
4447
);
4548
}
4649

x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/ValuesFloatGroupingAggregatorFunctionTests.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
import static org.hamcrest.Matchers.nullValue;
2727

2828
public class ValuesFloatGroupingAggregatorFunctionTests extends GroupingAggregatorFunctionTestCase {
29+
private final boolean withNulls = randomBoolean();
30+
2931
@Override
3032
protected AggregatorFunctionSupplier aggregatorFunction() {
3133
return new ValuesFloatAggregatorFunctionSupplier();
@@ -40,7 +42,8 @@ protected String expectedDescriptionOfAggregator() {
4042
protected SourceOperator simpleInput(BlockFactory blockFactory, int size) {
4143
return new LongFloatTupleBlockSourceOperator(
4244
blockFactory,
43-
LongStream.range(0, size).mapToObj(l -> Tuple.tuple(randomLongBetween(0, 4), randomFloat()))
45+
LongStream.range(0, size)
46+
.mapToObj(l -> Tuple.tuple(randomLongBetween(0, 100), withNulls && randomBoolean() ? null : randomFloat()))
4447
);
4548
}
4649

x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/ValuesIntGroupingAggregatorFunctionTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ protected String expectedDescriptionOfAggregator() {
4040
protected SourceOperator simpleInput(BlockFactory blockFactory, int size) {
4141
return new LongIntBlockSourceOperator(
4242
blockFactory,
43-
LongStream.range(0, size).mapToObj(l -> Tuple.tuple(randomLongBetween(0, 4), randomInt()))
43+
LongStream.range(0, size).mapToObj(l -> Tuple.tuple(randomLongBetween(0, 100), randomInt()))
4444
);
4545
}
4646

0 commit comments

Comments
 (0)