Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
* <code>Key.Evaluated</code> containing the empty list.
*/
@API(API.Status.UNSTABLE)
public class FieldKeyExpression extends BaseKeyExpression implements AtomKeyExpression, KeyExpressionWithoutChildren {
public class FieldKeyExpression extends BaseKeyExpression implements AtomKeyExpression, KeyExpressionWithoutChildren, GroupableKeyExpression {
private static final ObjectPlanHash BASE_HASH = new ObjectPlanHash("Field-Key-Expression");

@Nonnull
Expand Down Expand Up @@ -290,16 +290,14 @@ public NestingKeyExpression nest(@Nonnull KeyExpression child) {
return new NestingKeyExpression(this, child);
}

/**
* Get this field as a group without any grouping keys.
* @return this field without any grouping keys
*/
@Nonnull
@Override
public GroupingKeyExpression ungrouped() {
return new GroupingKeyExpression(this, 1);
}

@Nonnull
@Override
public GroupingKeyExpression groupBy(@Nonnull KeyExpression groupByFirst, @Nonnull KeyExpression... groupByRest) {
return GroupingKeyExpression.of(this, groupByFirst, groupByRest);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* GroupableKeyExpression.java
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2015-2025 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.apple.foundationdb.record.metadata.expressions;

import com.apple.foundationdb.annotation.API;

import javax.annotation.Nonnull;

/**
* A {@link KeyExpression} that can be grouped by another {@link KeyExpression}.
*/
@API(API.Status.EXPERIMENTAL)
public interface GroupableKeyExpression extends KeyExpression {

@Nonnull
GroupingKeyExpression groupBy(@Nonnull KeyExpression groupByFirst, @Nonnull KeyExpression... groupByRest);

Check failure on line 34 in fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/metadata/expressions/GroupableKeyExpression.java

View check run for this annotation

fdb.teamscale.io / Teamscale | Findings

fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/metadata/expressions/GroupableKeyExpression.java#L34

[New] Do not use varargs https://fdb.teamscale.io/findings/details/foundationdb-fdb-record-layer?t=FORK_MR%2F3549%2Fhatyo%2Faggregate-index-nested-column%3AHEAD&id=23F1DF8705468FD987A9F850792DD54D

/**
* Get {@code this} nesting as a group without any grouping keys.
*
* @return this nesting without any grouping keys
*/
GroupingKeyExpression ungrouped();
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
* record, then it will evaluate the same as if the parent field is unset or empty (depending on the fan type).
*/
@API(API.Status.UNSTABLE)
public class NestingKeyExpression extends BaseKeyExpression implements KeyExpressionWithChild, AtomKeyExpression {
public class NestingKeyExpression extends BaseKeyExpression implements KeyExpressionWithChild, AtomKeyExpression, GroupableKeyExpression {
private static final ObjectPlanHash BASE_HASH = new ObjectPlanHash("Nesting-Key-Expression");

@Nonnull
Expand Down Expand Up @@ -167,16 +167,14 @@ public KeyExpression getChild() {
return child;
}

/**
* Get this nesting as a group without any grouping keys.
* @return this nesting without any grouping keys
*/
@Nonnull
@Override
public GroupingKeyExpression ungrouped() {
return new GroupingKeyExpression(this, getColumnSize());
}

@Nonnull
@Override
public GroupingKeyExpression groupBy(@Nonnull KeyExpression groupByFirst, @Nonnull KeyExpression... groupByRest) {
return GroupingKeyExpression.of(this, groupByFirst, groupByRest);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
* against the <code>null</code> record.
*/
@API(API.Status.UNSTABLE)
public class ThenKeyExpression extends BaseKeyExpression implements KeyExpressionWithChildren {
public class ThenKeyExpression extends BaseKeyExpression implements KeyExpressionWithChildren, GroupableKeyExpression {
private static final ObjectPlanHash BASE_HASH = new ObjectPlanHash("Then-Key-Expression");

@Nonnull
Expand Down Expand Up @@ -146,16 +146,14 @@ public int getColumnSize() {
return columnSize;
}

/**
* Get this entire concatenation as a group without any grouping keys.
* @return this concatenation without any grouping keys
*/
@Nonnull
@Override
public GroupingKeyExpression ungrouped() {
return new GroupingKeyExpression(this, getColumnSize());
}

@Nonnull
@Override
public GroupingKeyExpression groupBy(@Nonnull KeyExpression groupByFirst, @Nonnull KeyExpression... groupByRest) {
return GroupingKeyExpression.of(this, groupByFirst, groupByRest);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import com.apple.foundationdb.record.metadata.expressions.EmptyKeyExpression;
import com.apple.foundationdb.record.metadata.expressions.FieldKeyExpression;
import com.apple.foundationdb.record.metadata.expressions.FunctionKeyExpression;
import com.apple.foundationdb.record.metadata.expressions.GroupableKeyExpression;
import com.apple.foundationdb.record.metadata.expressions.GroupingKeyExpression;
import com.apple.foundationdb.record.metadata.expressions.KeyExpression;
import com.apple.foundationdb.record.metadata.expressions.ThenKeyExpression;
Expand Down Expand Up @@ -391,7 +392,6 @@ private NonnullPair<KeyExpression, String> generateAggregateIndexKeyExpression(@
final var indexableAggregateValue = (IndexableAggregateValue) aggregateValue;
final var child = Iterables.getOnlyElement(aggregateValue.getChildren());
var indexTypeName = indexableAggregateValue.getIndexTypeName();
final KeyExpression groupedValue;
final GroupingKeyExpression keyExpression;
// COUNT(*) is a special case.
if (aggregateValue instanceof CountValue && IndexTypes.COUNT.equals(indexTypeName)) {
Expand All @@ -402,7 +402,7 @@ private NonnullPair<KeyExpression, String> generateAggregateIndexKeyExpression(@
}
} else if (aggregateValue instanceof NumericAggregationValue.BitmapConstructAgg && IndexTypes.BITMAP_VALUE.equals(indexTypeName)) {
Assert.thatUnchecked(child instanceof FieldValue || child instanceof ArithmeticValue, "Unsupported index definition, expecting a column argument in aggregation function");
groupedValue = generate(List.of(child), Collections.emptyMap());
final var groupedValue = generate(List.of(child), Collections.emptyMap());
// only support bitmap_construct_agg(bitmap_bit_position(column))
// doesn't support bitmap_construct_agg(column)
Assert.thatUnchecked(groupedValue instanceof FunctionKeyExpression, "Unsupported index definition, expecting a bitmap_bit_position function in bitmap_construct_agg function");
Expand All @@ -422,16 +422,11 @@ private NonnullPair<KeyExpression, String> generateAggregateIndexKeyExpression(@
}
} else {
Assert.thatUnchecked(child instanceof FieldValue, "Unsupported index definition, expecting a column argument in aggregation function");
groupedValue = generate(List.of(child), Collections.emptyMap());
Assert.thatUnchecked(groupedValue instanceof FieldKeyExpression || groupedValue instanceof ThenKeyExpression);
final var groupedValue = Assert.castUnchecked(generate(List.of(child), Collections.emptyMap()), GroupableKeyExpression.class);
if (maybeGroupingExpression.isPresent()) {
keyExpression = (groupedValue instanceof FieldKeyExpression) ?
((FieldKeyExpression) groupedValue).groupBy(maybeGroupingExpression.get()) :
((ThenKeyExpression) groupedValue).groupBy(maybeGroupingExpression.get());
keyExpression = groupedValue.groupBy(maybeGroupingExpression.get());
} else {
keyExpression = (groupedValue instanceof FieldKeyExpression) ?
((FieldKeyExpression) groupedValue).ungrouped() :
((ThenKeyExpression) groupedValue).ungrouped();
keyExpression = groupedValue.ungrouped();
}
}
// special handling of min_ever and max_ever, depending on index attributes we either create the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,32 @@ void createAggregateIndexWithComplexGroupingExpressionCase1() throws Exception {
function("add", concat(field("B"), value(3))))), IndexTypes.PERMUTED_MAX);
}

@Test
void createAggregateIndexWithComplexGroupingExpressionCase2() throws Exception {
final String stmt = "CREATE SCHEMA TEMPLATE test_template " +
"CREATE TYPE AS STRUCT A(x BIGINT)" +
"CREATE TYPE AS STRUCT B(y A)" +
"CREATE TYPE AS STRUCT C(z B)" +
"CREATE TABLE T1(p1 bigint, a bigint, b bigint, c C, primary key(p1)) " +
"CREATE INDEX mv1 AS SELECT a & 2, b + 3, MAX(c.z.y.x) FROM T1 GROUP BY a & 2, b + 3";
indexIs(stmt, field("C").nest(field("Z").nest(field("Y").nest(field("X")))).groupBy(concat(function("bitand", concat(field("A"), value(2))),
function("add", concat(field("B"), value(3))))), IndexTypes.PERMUTED_MAX);
}

@Test
void createAggregateIndexWithComplexGroupingExpressionCase3() throws Exception {
final String stmt = "CREATE SCHEMA TEMPLATE test_template " +
"CREATE TYPE AS STRUCT A(x BIGINT)" +
"CREATE TYPE AS STRUCT B(y A)" +
"CREATE TYPE AS STRUCT C(z B)" +
"CREATE TABLE T1(p1 bigint, a bigint, b bigint, c C, primary key(p1)) " +
"CREATE INDEX mv1 AS SELECT a & 2, b + 3, c.z.y.x, MAX(b) FROM T1 GROUP BY a & 2, b + 3, c.z.y.x";
indexIs(stmt, field("B").groupBy(concat(
function("bitand", concat(field("A"), value(2))),
function("add", concat(field("B"), value(3))),
field("C").nest(field("Z").nest(field("Y").nest(field("X")))))), IndexTypes.PERMUTED_MAX);
}

@Test
void createSimpleValueIndex() throws Exception {
final String stmt = "CREATE SCHEMA TEMPLATE test_template " +
Expand Down Expand Up @@ -853,6 +879,19 @@ void createMaxEverLong() throws Exception {
);
}

@Test
void createMaxEvenNestedNonrepeatedField() throws Exception {
final String stmt = "CREATE SCHEMA TEMPLATE test_template " +
"CREATE TYPE AS STRUCT S(x bigint) " +
"CREATE TYPE AS STRUCT Q(y bigint)" +
"CREATE TABLE T1(col1 bigint, col2 S, col3 Q, primary key(col1)) " +
"CREATE INDEX mv1 AS SELECT MAX_EVER(col3.y) FROM T1 group by col2.x";
indexIs(stmt,
field("COL3").nest("Y").groupBy(field("COL2").nest("X")),
IndexTypes.MAX_EVER_TUPLE
);
}

@Test
void createMaxEverTupleIncorrectType() throws Exception {
final String stmt = "CREATE SCHEMA TEMPLATE test_template " +
Expand All @@ -875,6 +914,19 @@ void createMinEverTupleIncorrectType() throws Exception {
);
}

@Test
void createMinEvenNestedNonrepeatedField() throws Exception {
final String stmt = "CREATE SCHEMA TEMPLATE test_template " +
"CREATE TYPE AS STRUCT S(x bigint) " +
"CREATE TYPE AS STRUCT Q(y bigint)" +
"CREATE TABLE T1(col1 bigint, col2 S, col3 Q, primary key(col1)) " +
"CREATE INDEX mv1 AS SELECT MIN_EVER(col3.y) FROM T1 group by col2.x";
indexIs(stmt,
field("COL3").nest("Y").groupBy(field("COL2").nest("X")),
IndexTypes.MIN_EVER_TUPLE
);
}

@Test
void createMaxEverLongIncorrectType() throws Exception {
final String stmt = "CREATE SCHEMA TEMPLATE test_template " +
Expand Down
Loading