Skip to content

Commit 08911fb

Browse files
committed
Support GROUP BY ALL in relational queries
1 parent bb68d50 commit 08911fb

6 files changed

Lines changed: 354 additions & 5 deletions

File tree

iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2595,6 +2595,12 @@ private void analyzeSelectSingleColumn(
25952595
private Analysis.GroupingSetAnalysis analyzeGroupBy(
25962596
QuerySpecification node, Scope scope, List<Expression> outputExpressions) {
25972597
if (node.getGroupBy().isPresent()) {
2598+
2599+
// Handle GROUP BY ALL: infer grouping columns from SELECT expressions
2600+
if (node.getGroupBy().get().isAll()) {
2601+
return analyzeGroupByAll(node, scope, outputExpressions);
2602+
}
2603+
25982604
ImmutableList.Builder<List<Set<FieldId>>> cubes = ImmutableList.builder();
25992605
ImmutableList.Builder<List<Set<FieldId>>> rollups = ImmutableList.builder();
26002606
ImmutableList.Builder<List<Set<FieldId>>> sets = ImmutableList.builder();
@@ -2718,6 +2724,69 @@ private Analysis.GroupingSetAnalysis analyzeGroupBy(
27182724
return result;
27192725
}
27202726

2727+
private Analysis.GroupingSetAnalysis analyzeGroupByAll(
2728+
QuerySpecification node, Scope scope, List<Expression> outputExpressions) {
2729+
ImmutableList.Builder<List<Set<FieldId>>> sets = ImmutableList.builder();
2730+
ImmutableList.Builder<Expression> complexExpressions = ImmutableList.builder();
2731+
ImmutableList.Builder<Expression> groupingExpressions = ImmutableList.builder();
2732+
FunctionCall gapFillColumn = null;
2733+
ImmutableList.Builder<Expression> gapFillGroupingExpressions = ImmutableList.builder();
2734+
2735+
for (Expression outputExpression : outputExpressions) {
2736+
List<FunctionCall> aggregates =
2737+
extractAggregateFunctions(ImmutableList.of(outputExpression));
2738+
List<FunctionCall> windowFunctions =
2739+
extractWindowFunctions(ImmutableList.of(outputExpression));
2740+
if (!aggregates.isEmpty() || !windowFunctions.isEmpty()) {
2741+
continue;
2742+
}
2743+
2744+
analyzeExpression(outputExpression, scope);
2745+
ResolvedField field =
2746+
analysis.getColumnReferenceFields().get(NodeRef.of(outputExpression));
2747+
if (field != null) {
2748+
sets.add(ImmutableList.of(ImmutableSet.of(field.getFieldId())));
2749+
} else {
2750+
complexExpressions.add(outputExpression);
2751+
}
2752+
2753+
if (isDateBinGapFill(outputExpression)) {
2754+
if (gapFillColumn != null) {
2755+
throw new SemanticException("multiple date_bin_gapfill calls not allowed");
2756+
}
2757+
gapFillColumn = (FunctionCall) outputExpression;
2758+
} else {
2759+
gapFillGroupingExpressions.add(outputExpression);
2760+
}
2761+
2762+
groupingExpressions.add(outputExpression);
2763+
}
2764+
2765+
List<Expression> expressions = groupingExpressions.build();
2766+
for (Expression expression : expressions) {
2767+
Type type = analysis.getType(expression);
2768+
if (!type.isComparable()) {
2769+
throw new SemanticException(
2770+
String.format(
2771+
"%s is not comparable, and therefore cannot be used in GROUP BY", type));
2772+
}
2773+
}
2774+
2775+
Analysis.GroupingSetAnalysis groupingSets =
2776+
new Analysis.GroupingSetAnalysis(
2777+
expressions,
2778+
ImmutableList.of(),
2779+
ImmutableList.of(),
2780+
sets.build(),
2781+
complexExpressions.build());
2782+
analysis.setGroupingSets(node, groupingSets);
2783+
if (gapFillColumn != null) {
2784+
analysis.setGapFill(node, gapFillColumn);
2785+
analysis.setGapFillGroupingKeys(node, gapFillGroupingExpressions.build());
2786+
}
2787+
return groupingSets;
2788+
}
2789+
27212790
private boolean isDateBinGapFill(Expression column) {
27222791
return column instanceof FunctionCall
27232792
&& DATE_BIN

iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/GroupBy.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,25 +33,39 @@ public class GroupBy extends Node {
3333
private static final long INSTANCE_SIZE = RamUsageEstimator.shallowSizeOfInstance(GroupBy.class);
3434

3535
private final boolean isDistinct;
36+
private final boolean isAll;
3637
private final List<GroupingElement> groupingElements;
3738

3839
public GroupBy(boolean isDistinct, List<GroupingElement> groupingElements) {
3940
super(null);
4041
this.isDistinct = isDistinct;
42+
this.isAll = false;
4143
this.groupingElements = ImmutableList.copyOf(requireNonNull(groupingElements));
4244
}
4345

4446
public GroupBy(
4547
NodeLocation location, boolean isDistinct, List<GroupingElement> groupingElements) {
4648
super(requireNonNull(location, "location is null"));
4749
this.isDistinct = isDistinct;
50+
this.isAll = false;
4851
this.groupingElements = ImmutableList.copyOf(requireNonNull(groupingElements));
4952
}
5053

54+
public GroupBy(NodeLocation location, boolean isAll) {
55+
super(requireNonNull(location, "location is null"));
56+
this.isDistinct = false;
57+
this.isAll = isAll;
58+
this.groupingElements = ImmutableList.of();
59+
}
60+
5161
public boolean isDistinct() {
5262
return isDistinct;
5363
}
5464

65+
public boolean isAll() {
66+
return isAll;
67+
}
68+
5569
public List<GroupingElement> getGroupingElements() {
5670
return groupingElements;
5771
}
@@ -76,18 +90,20 @@ public boolean equals(Object o) {
7690
}
7791
GroupBy groupBy = (GroupBy) o;
7892
return isDistinct == groupBy.isDistinct
93+
&& isAll == groupBy.isAll
7994
&& Objects.equals(groupingElements, groupBy.groupingElements);
8095
}
8196

8297
@Override
8398
public int hashCode() {
84-
return Objects.hash(isDistinct, groupingElements);
99+
return Objects.hash(isDistinct, isAll, groupingElements);
85100
}
86101

87102
@Override
88103
public String toString() {
89104
return toStringHelper(this)
90105
.add("isDistinct", isDistinct)
106+
.add("isAll", isAll)
91107
.add("groupingElements", groupingElements)
92108
.toString();
93109
}
@@ -98,7 +114,7 @@ public boolean shallowEquals(Node other) {
98114
return false;
99115
}
100116

101-
return isDistinct == ((GroupBy) other).isDistinct;
117+
return isDistinct == ((GroupBy) other).isDistinct && isAll == ((GroupBy) other).isAll;
102118
}
103119

104120
@Override

iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/AstBuilder.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2419,6 +2419,9 @@ public Node visitSelectAll(RelationalSqlParser.SelectAllContext ctx) {
24192419

24202420
@Override
24212421
public Node visitGroupBy(RelationalSqlParser.GroupByContext ctx) {
2422+
if (ctx.ALL() != null && ctx.groupingElement().isEmpty()) {
2423+
return new GroupBy(getLocation(ctx), true);
2424+
}
24222425
return new GroupBy(
24232426
getLocation(ctx),
24242427
isDistinct(ctx.setQuantifier()),

iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/SqlFormatter.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -248,13 +248,18 @@ protected Void visitQuerySpecification(QuerySpecification node, Integer indent)
248248

249249
node.getGroupBy()
250250
.ifPresent(
251-
groupBy ->
251+
groupBy -> {
252+
if (groupBy.isAll()) {
253+
append(indent, "GROUP BY ALL").append('\n');
254+
} else {
252255
append(
253256
indent,
254257
"GROUP BY "
255258
+ (groupBy.isDistinct() ? " DISTINCT " : "")
256259
+ formatGroupBy(groupBy.getGroupingElements()))
257-
.append('\n'));
260+
.append('\n');
261+
}
262+
});
258263

259264
node.getHaving()
260265
.ifPresent(having -> append(indent, "HAVING " + formatExpression(having)).append('\n'));

0 commit comments

Comments
 (0)