This is an automated email from the ASF dual-hosted git repository.
morrysnow pushed a commit to branch branch-3.1
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-3.1 by this push:
new 0b7cbdab96d branch-3.1: [feature](Nereids) support turn off
ONLY_FULL_GROUP_BY sql mode #49341 (#53535)
0b7cbdab96d is described below
commit 0b7cbdab96dbfa1ac2df27ebec46b1624c0f4b0c
Author: morrySnow <[email protected]>
AuthorDate: Tue Jul 22 13:59:07 2025 +0800
branch-3.1: [feature](Nereids) support turn off ONLY_FULL_GROUP_BY sql mode
#49341 (#53535)
cherry-picked from #49341
---
.../nereids/rules/analysis/BindExpression.java | 104 ++++++++++--
.../nereids/rules/analysis/CheckAfterBind.java | 4 -
.../nereids/rules/analysis/FillUpMissingSlots.java | 14 +-
.../nereids/rules/analysis/NormalizeAggregate.java | 75 +++++----
.../nereids/rules/analysis/NormalizeRepeat.java | 16 +-
.../nereids/trees/plans/logical/LogicalHaving.java | 10 +-
.../java/org/apache/doris/qe/GlobalVariable.java | 3 +-
.../java/org/apache/doris/qe/SessionVariable.java | 2 +-
.../java/org/apache/doris/qe/SqlModeHelper.java | 8 +
.../main/java/org/apache/doris/qe/VariableMgr.java | 33 ++--
.../org/apache/doris/analysis/SetVariableTest.java | 8 +-
.../java/org/apache/doris/qe/VariableMgrTest.java | 2 +-
.../aggregate/non_standard_aggregate.groovy | 187 +++++++++++++++++++++
13 files changed, 396 insertions(+), 70 deletions(-)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindExpression.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindExpression.java
index b2d7779f902..24772ed5f14 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindExpression.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindExpression.java
@@ -55,6 +55,7 @@ import
org.apache.doris.nereids.trees.expressions.functions.BoundFunction;
import org.apache.doris.nereids.trees.expressions.functions.Function;
import org.apache.doris.nereids.trees.expressions.functions.FunctionBuilder;
import
org.apache.doris.nereids.trees.expressions.functions.agg.AggregateFunction;
+import org.apache.doris.nereids.trees.expressions.functions.agg.AnyValue;
import
org.apache.doris.nereids.trees.expressions.functions.agg.NullableAggregateFunction;
import
org.apache.doris.nereids.trees.expressions.functions.generator.TableGeneratingFunction;
import
org.apache.doris.nereids.trees.expressions.functions.scalar.GroupingScalarFunction;
@@ -96,8 +97,10 @@ import org.apache.doris.nereids.types.StructField;
import org.apache.doris.nereids.types.StructType;
import org.apache.doris.nereids.util.ExpressionUtils;
import org.apache.doris.nereids.util.PlanUtils;
+import org.apache.doris.nereids.util.PlanUtils.CollectNonWindowedAggFuncs;
import org.apache.doris.nereids.util.TypeCoercionUtils;
import org.apache.doris.nereids.util.Utils;
+import org.apache.doris.qe.SqlModeHelper;
import com.google.common.base.Preconditions;
import com.google.common.base.Suppliers;
@@ -106,6 +109,7 @@ import com.google.common.collect.ImmutableList.Builder;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
@@ -116,6 +120,7 @@ import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
@@ -367,7 +372,41 @@ public class BindExpression implements AnalysisRuleFactory
{
Supplier<Scope> childChildrenOutput = Suppliers.memoize(() ->
toScope(cascadesContext,
PlanUtils.fastGetChildrenOutputs(childPlan.children()))
);
- return bindHavingByScopes(having, cascadesContext, childOutput,
childChildrenOutput);
+ LogicalHaving<Plan> boundHaving = bindHavingByScopes(having,
having.child(),
+ cascadesContext, childOutput, childChildrenOutput);
+ if (!SqlModeHelper.hasOnlyFullGroupBy() && childPlan instanceof
LogicalProject) {
+ // ATTN: process having(project) that have aggregate function in
having
+ LogicalProject<?> project = (LogicalProject<?>) childPlan;
+ List<AggregateFunction> aggFuncs =
CollectNonWindowedAggFuncs.collect(boundHaving.getConjuncts());
+ if (!aggFuncs.isEmpty()) {
+ Map<Expression, Expression> replaceMap = Maps.newHashMap();
+ for (AggregateFunction aggFunc : aggFuncs) {
+ // ATTN: this is a little trick here. since replace check
replace successful with equal operator
+ // see:
org.apache.doris.nereids.trees.TreeNode.rewriteDownShortCircuit
+ // here, we generate a new aggFunc to replace to avoid
rewrite its child.
+ // because we do not want to replace agg func agg(child)
to agg(any_value(child))
+ replaceMap.put(aggFunc,
aggFunc.withChildren(aggFunc.children()));
+ }
+ Builder<NamedExpression> boundProjectionsBuilder
+ =
ImmutableList.builderWithExpectedSize(project.getProjects().size());
+ for (NamedExpression expr : project.getProjects()) {
+ if (expr instanceof SlotReference) {
+ Alias alias = new Alias(new AnyValue(expr),
expr.getName());
+ boundProjectionsBuilder.add(alias);
+ replaceMap.put(expr, alias);
+ } else {
+ boundProjectionsBuilder.add(expr);
+ }
+ }
+ Plan newChildPlan =
project.withProjects(boundProjectionsBuilder.build());
+ ImmutableSet.Builder<Expression> newConjunctsBuilder =
ImmutableSet.builder();
+ for (Expression conjunct : boundHaving.getConjuncts()) {
+ newConjunctsBuilder.add(ExpressionUtils.replace(conjunct,
replaceMap));
+ }
+ boundHaving =
boundHaving.withConjunctsAndChild(newConjunctsBuilder.build(), newChildPlan);
+ }
+ }
+ return boundHaving;
}
private LogicalHaving<Plan> bindHavingAggregate(
@@ -466,10 +505,8 @@ public class BindExpression implements AnalysisRuleFactory
{
}
private LogicalHaving<Plan> bindHavingByScopes(
- LogicalHaving<? extends Plan> having,
+ LogicalHaving<? extends Plan> having, Plan child,
CascadesContext cascadesContext, Scope defaultScope,
Supplier<Scope> backupScope) {
- Plan child = having.child();
-
SimpleExprAnalyzer analyzer = buildCustomSlotBinderAnalyzer(
having, cascadesContext, defaultScope, false, true,
(self, unboundSlot) -> {
@@ -488,7 +525,7 @@ public class BindExpression implements AnalysisRuleFactory {
}
checkIfOutputAliasNameDuplicatedForGroupBy(boundConjuncts.build(),
child instanceof LogicalProject ? ((LogicalProject<?>)
child).getOutputs() : child.getOutput());
- return new LogicalHaving<>(boundConjuncts.build(), having.child());
+ return new LogicalHaving<>(boundConjuncts.build(), child);
}
private LogicalSort<LogicalSetOperation> bindSortWithSetOperation(
@@ -621,19 +658,20 @@ public class BindExpression implements
AnalysisRuleFactory {
Supplier<Set<NamedExpression>> boundExcepts = Suppliers.memoize(
() -> analyzer.analyzeToSet(project.getExcepts()));
- Builder<NamedExpression> boundProjections =
ImmutableList.builderWithExpectedSize(project.getProjects().size());
+ Builder<NamedExpression> boundProjectionsBuilder
+ =
ImmutableList.builderWithExpectedSize(project.getProjects().size());
StatementContext statementContext = ctx.statementContext;
for (Expression expression : project.getProjects()) {
Expression expr = analyzer.analyze(expression);
if (!(expr instanceof BoundStar)) {
- boundProjections.add((NamedExpression) expr);
+ boundProjectionsBuilder.add((NamedExpression) expr);
} else {
BoundStar boundStar = (BoundStar) expr;
List<Slot> slots = boundStar.getSlots();
if (!excepts.isEmpty()) {
slots = Utils.filterImmutableList(slots, slot ->
!boundExcepts.get().contains(slot));
}
- boundProjections.addAll(slots);
+ boundProjectionsBuilder.addAll(slots);
// for create view stmt expand star
List<Slot> slotsForLambda = slots;
@@ -643,7 +681,7 @@ public class BindExpression implements AnalysisRuleFactory {
});
}
}
- List<NamedExpression> projects =
adjustProjectionAggNullable(boundProjections.build());
+ List<NamedExpression> projects =
adjustProjectionAggNullable(boundProjectionsBuilder.build());
return project.withProjects(projects);
}
@@ -653,6 +691,7 @@ public class BindExpression implements AnalysisRuleFactory {
if (!hasAggregation) {
return expressions;
}
+ boolean hasOnlyFullGroupBy = SqlModeHelper.hasOnlyFullGroupBy();
Builder<NamedExpression> newExpressionsBuilder =
ImmutableList.builderWithExpectedSize(expressions.size());
for (NamedExpression expr : expressions) {
expr = (NamedExpression) expr.rewriteDownShortCircuit(e -> {
@@ -662,6 +701,9 @@ public class BindExpression implements AnalysisRuleFactory {
}
return e;
});
+ if (!hasOnlyFullGroupBy && expr instanceof SlotReference) {
+ expr = new Alias(expr, expr.getName());
+ }
newExpressionsBuilder.add(expr);
}
return newExpressionsBuilder.build();
@@ -702,7 +744,7 @@ public class BindExpression implements AnalysisRuleFactory {
buildAggOutputScopeWithoutAggFun(boundProjections,
cascadesContext);
List<Expression> boundGroupBy = bindGroupBy(
agg, agg.getGroupByExpressions(), boundProjections,
aggOutputScopeWithoutAggFun, cascadesContext);
- return agg.withGroupByAndOutput(boundGroupBy, boundProjections);
+ return agg.withGroupByAndOutput(boundGroupBy,
processNonStandardAggregate(boundProjections, boundGroupBy));
}
private Plan bindRepeat(MatchingContext<LogicalRepeat<Plan>> ctx) {
@@ -717,10 +759,12 @@ public class BindExpression implements
AnalysisRuleFactory {
Builder<List<Expression>> boundGroupingSetsBuilder =
ImmutableList.builderWithExpectedSize(repeat.getGroupingSets().size());
+ Set<Expression> flatBoundGroupingSet = Sets.newHashSet();
for (List<Expression> groupingSet : repeat.getGroupingSets()) {
List<Expression> boundGroupingSet = bindGroupBy(
repeat, groupingSet, boundRepeatOutput,
aggOutputScopeWithoutAggFun, cascadesContext);
boundGroupingSetsBuilder.add(boundGroupingSet);
+ flatBoundGroupingSet.addAll(boundGroupingSet);
}
List<List<Expression>> boundGroupingSets =
boundGroupingSetsBuilder.build();
List<NamedExpression> nullableOutput =
PlanUtils.adjustNullableForRepeat(boundGroupingSets, boundRepeatOutput);
@@ -730,7 +774,7 @@ public class BindExpression implements AnalysisRuleFactory {
// check all GroupingScalarFunction inputSlots must be from
groupingExprs
Set<Slot> groupingExprs = boundGroupingSets.stream()
- .flatMap(Collection::stream).map(expr -> expr.getInputSlots())
+ .flatMap(Collection::stream).map(Expression::getInputSlots)
.flatMap(Collection::stream).collect(Collectors.toSet());
Set<GroupingScalarFunction> groupingScalarFunctions = ExpressionUtils
.collect(nullableOutput,
GroupingScalarFunction.class::isInstance);
@@ -740,7 +784,43 @@ public class BindExpression implements AnalysisRuleFactory
{
+ " does not exist in GROUP BY clause.");
}
}
- return repeat.withGroupSetsAndOutput(boundGroupingSets,
nullableOutput);
+ return repeat.withGroupSetsAndOutput(boundGroupingSets,
+ processNonStandardAggregate(nullableOutput,
flatBoundGroupingSet));
+ }
+
+ /**
+ * for support non-standard aggregate, such as SELECT c1, c2 FROM t GROUP
BY c1.
+ * we around an extra Alias on SlotReference in output expression list
+ * to avoid ExprId conflict. Because we will do a transform later like:
+ * <p>
+ * Repeat([c1#1, c2#2 as c2#3], [[c1#1], [c3#4]])
+ * ---
+ * Project(c1#1, c2#3)
+ * +-- Aggregate([c1#1, any_value(c2#2) as c2#3], [c1#1])
+ * +-- Project(c1#1, c2#2)
+ *
+ * @param originalProjections original projections in aggregation
+ * @param groupingExprs all expressions for group by key
+ *
+ * @return output with wrapped scalar slots
+ */
+ private List<NamedExpression> processNonStandardAggregate(
+ List<NamedExpression> originalProjections, Collection<Expression>
groupingExprs) {
+ if (SqlModeHelper.hasOnlyFullGroupBy()) {
+ return originalProjections;
+ } else {
+
+ ImmutableList.Builder<NamedExpression> finalProjectionsBuilder =
ImmutableList.builder();
+ for (NamedExpression projection : originalProjections) {
+ // we do a trick here
+ if (projection instanceof SlotReference &&
!groupingExprs.contains(projection)) {
+ finalProjectionsBuilder.add(new Alias(projection,
projection.getName()));
+ } else {
+ finalProjectionsBuilder.add(projection);
+ }
+ }
+ return finalProjectionsBuilder.build();
+ }
}
private List<Expression> bindGroupBy(
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/CheckAfterBind.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/CheckAfterBind.java
index 9658bfef20c..e90e868a510 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/CheckAfterBind.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/CheckAfterBind.java
@@ -25,7 +25,6 @@ import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.InSubquery;
import org.apache.doris.nereids.trees.plans.Plan;
import org.apache.doris.nereids.trees.plans.logical.LogicalHaving;
-import org.apache.doris.nereids.util.ExpressionUtils;
import com.google.common.collect.ImmutableList;
@@ -61,9 +60,6 @@ public class CheckAfterBind implements AnalysisRuleFactory {
throw new AnalysisException(Type.OnlyMetricTypeErrorMsg);
}
}
- if (ExpressionUtils.hasOnlyMetricType(predicate.getArguments())) {
- throw new AnalysisException(Type.OnlyMetricTypeErrorMsg);
- }
}
}
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/FillUpMissingSlots.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/FillUpMissingSlots.java
index 24eeaef6dfa..05784b011da 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/FillUpMissingSlots.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/FillUpMissingSlots.java
@@ -29,6 +29,7 @@ import
org.apache.doris.nereids.trees.expressions.NamedExpression;
import org.apache.doris.nereids.trees.expressions.Slot;
import org.apache.doris.nereids.trees.expressions.SlotReference;
import
org.apache.doris.nereids.trees.expressions.functions.agg.AggregateFunction;
+import org.apache.doris.nereids.trees.expressions.functions.agg.AnyValue;
import org.apache.doris.nereids.trees.plans.Plan;
import org.apache.doris.nereids.trees.plans.algebra.Aggregate;
import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
@@ -36,6 +37,7 @@ import
org.apache.doris.nereids.trees.plans.logical.LogicalHaving;
import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
import org.apache.doris.nereids.trees.plans.logical.LogicalSort;
import org.apache.doris.nereids.util.ExpressionUtils;
+import org.apache.doris.qe.SqlModeHelper;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
@@ -310,9 +312,17 @@ public class FillUpMissingSlots implements
AnalysisRuleFactory {
// We couldn't find the equivalent expression in output
expressions and group-by expressions,
// so we should check whether the expression is valid.
if (expression instanceof SlotReference) {
- if (checkSlot && (!outerScope.isPresent()
+ if ((!outerScope.isPresent()
||
!outerScope.get().getCorrelatedSlots().contains(expression))) {
- throw new AnalysisException(expression.toSql() + "
should be grouped by.");
+ if (!SqlModeHelper.hasOnlyFullGroupBy()) {
+ // ATTN: we should add any_value to agg's output
here, but not add slot directly.
+ // because normalize agg cannot replace upper
slot with new output.
+ Alias alias = new Alias(new AnyValue(expression));
+ newOutputSlots.add(alias);
+ substitution.put(expression, alias.toSlot());
+ } else if (checkSlot) {
+ throw new AnalysisException(expression.toSql() + "
should be grouped by.");
+ }
}
} else if (expression instanceof AggregateFunction) {
if
(checkWhetherNestedAggregateFunctionsExist((AggregateFunction) expression)) {
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/NormalizeAggregate.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/NormalizeAggregate.java
index 4a2e226caae..ad13f19d69b 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/NormalizeAggregate.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/NormalizeAggregate.java
@@ -36,6 +36,7 @@ import
org.apache.doris.nereids.trees.expressions.SlotReference;
import org.apache.doris.nereids.trees.expressions.SubqueryExpr;
import org.apache.doris.nereids.trees.expressions.WindowExpression;
import
org.apache.doris.nereids.trees.expressions.functions.agg.AggregateFunction;
+import org.apache.doris.nereids.trees.expressions.functions.agg.AnyValue;
import
org.apache.doris.nereids.trees.expressions.functions.agg.MultiDistinction;
import org.apache.doris.nereids.trees.expressions.literal.Literal;
import org.apache.doris.nereids.trees.expressions.literal.TinyIntLiteral;
@@ -47,10 +48,12 @@ import
org.apache.doris.nereids.trees.plans.logical.LogicalProject;
import org.apache.doris.nereids.util.ExpressionUtils;
import org.apache.doris.nereids.util.PlanUtils.CollectNonWindowedAggFuncs;
import org.apache.doris.nereids.util.Utils;
+import org.apache.doris.qe.SqlModeHelper;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import java.util.ArrayList;
@@ -218,14 +221,6 @@ public class NormalizeAggregate implements
RewriteRuleFactory, NormalizeToSlot {
Set<NamedExpression> bottomProjects = Sets.union(pushedGroupByExprs,
Sets.union(pushedTrivialAggChildren,
pushedTrivialAggInputSlots));
- // create bottom project
- Plan bottomPlan;
- if (!bottomProjects.isEmpty()) {
- bottomPlan = new
LogicalProject<>(ImmutableList.copyOf(bottomProjects), aggregate.child());
- } else {
- bottomPlan = aggregate.child();
- }
-
// use group by context to normalize agg functions to process
// sql like: select sum(a + 1) from t group by a + 1
//
@@ -275,8 +270,6 @@ public class NormalizeAggregate implements
RewriteRuleFactory, NormalizeToSlot {
newAggOutputBuilder.add((NamedExpression) rewrittenExpr);
}
ImmutableList<NamedExpression> normalizedAggOutput =
newAggOutputBuilder.build();
- LogicalAggregate<?> newAggregate =
- aggregate.withNormalized(normalizedGroupExprs,
normalizedAggOutput, bottomPlan);
// create upper projects by normalize all output exprs in old
LogicalAggregate
// In aggregateOutput, the expressions inside the agg function can be
rewritten
@@ -286,36 +279,58 @@ public class NormalizeAggregate implements
RewriteRuleFactory, NormalizeToSlot {
List<NamedExpression> upperProjects = normalizeOutput(aggregateOutput,
groupByExprContext, argsOfAggFuncNeedPushDownContext,
normalizedAggFuncsToSlotContext);
- ExpressionRewriteContext rewriteContext = new
ExpressionRewriteContext(ctx);
- LogicalProject<Plan> project =
eliminateGroupByConstant(groupByExprContext, rewriteContext,
- normalizedGroupExprs, normalizedAggOutput, bottomProjects,
aggregate, upperProjects, newAggregate);
-
// verify project used slots are all coming from agg's output
- List<Slot> slots = collectAllUsedSlots(upperProjects);
- if (!slots.isEmpty()) {
- Set<ExprId> aggOutputExprIds = new HashSet<>(slots.size());
+ List<Slot> slotsUsedInUpperProject =
collectAllUsedSlots(upperProjects);
+ if (!slotsUsedInUpperProject.isEmpty()) {
+ Set<ExprId> aggOutputExprIds = new
HashSet<>(slotsUsedInUpperProject.size());
for (NamedExpression expression : normalizedAggOutput) {
aggOutputExprIds.add(expression.getExprId());
}
- List<Slot> errorSlots = new ArrayList<>(slots.size());
- for (Slot slot : slots) {
+ Set<Slot> missingSlotsInAggregate = new
HashSet<>(slotsUsedInUpperProject.size());
+ for (Slot slot : slotsUsedInUpperProject) {
if (!aggOutputExprIds.contains(slot.getExprId()) && !(slot
instanceof SlotNotFromChildren)) {
- errorSlots.add(slot);
+ missingSlotsInAggregate.add(slot);
}
}
- if (!errorSlots.isEmpty()) {
- throw new AnalysisException(String.format("%s not in
aggregate's output", errorSlots
-
.stream().map(NamedExpression::getName).collect(Collectors.joining(", "))));
+ if (!missingSlotsInAggregate.isEmpty()) {
+ if (SqlModeHelper.hasOnlyFullGroupBy()) {
+ throw new AnalysisException(String.format("%s not in
aggregate's output", missingSlotsInAggregate
+
.stream().map(NamedExpression::getName).collect(Collectors.joining(", "))));
+ } else {
+ // for any slots missing in aggregate's output, we should
add a any_value(slot) into
+ // aggregate's output list and slot itself into bottom
project's output list
+ bottomProjects = Sets.union(bottomProjects,
missingSlotsInAggregate);
+ Map<Expression, Expression> replaceMap = Maps.newHashMap();
+ for (Slot slot : missingSlotsInAggregate) {
+ Alias anyValue = new Alias(new AnyValue(slot),
slot.getName());
+ replaceMap.put(slot, anyValue.toSlot());
+ newAggOutputBuilder.add(anyValue);
+ }
+ upperProjects = upperProjects.stream()
+ .map(e -> (NamedExpression)
ExpressionUtils.replace(e, replaceMap))
+ .collect(ImmutableList.toImmutableList());
+ }
}
}
+ // create normalized plan
+ Plan bottomPlan;
+ if (!bottomProjects.isEmpty()) {
+ bottomPlan = new
LogicalProject<>(ImmutableList.copyOf(bottomProjects), aggregate.child());
+ } else {
+ bottomPlan = aggregate.child();
+ }
+ // NOTICE: we must call newAggOutputBuilder.build() here,
newAggOutputBuilder could be updated if we need
+ // to process non-standard aggregate: SELECT c1, c2 FROM t GROUP BY c1
+ LogicalAggregate<?> newAggregate =
+ aggregate.withNormalized(normalizedGroupExprs,
newAggOutputBuilder.build(), bottomPlan);
+ ExpressionRewriteContext rewriteContext = new
ExpressionRewriteContext(ctx);
+ LogicalProject<Plan> project =
eliminateGroupByConstant(groupByExprContext, rewriteContext,
+ normalizedGroupExprs, normalizedAggOutput, bottomProjects,
aggregate, upperProjects, newAggregate);
+
if (having.isPresent()) {
- Set<Slot> havingUsedSlots =
ExpressionUtils.getInputSlotSet(having.get().getExpressions());
- Set<ExprId> havingUsedExprIds = new
HashSet<>(havingUsedSlots.size());
- for (Slot slot : havingUsedSlots) {
- havingUsedExprIds.add(slot.getExprId());
- }
- Set<ExprId> aggOutputExprIds = newAggregate.getOutputExprIdSet();
- if (aggOutputExprIds.containsAll(havingUsedExprIds)) {
+ Set<Slot> havingUsedSlots = having.get().getInputSlots();
+ Set<Slot> aggOutputExprIds = newAggregate.getOutputSet();
+ if (aggOutputExprIds.containsAll(havingUsedSlots)) {
// when having just use output slots from agg, we push down
having as parent of agg
return project.withChildren(ImmutableList.of(
new LogicalHaving<>(
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/NormalizeRepeat.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/NormalizeRepeat.java
index 96ea874f259..36bcbea1f12 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/NormalizeRepeat.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/NormalizeRepeat.java
@@ -40,6 +40,7 @@ import
org.apache.doris.nereids.trees.plans.logical.LogicalProject;
import org.apache.doris.nereids.trees.plans.logical.LogicalRepeat;
import org.apache.doris.nereids.util.ExpressionUtils;
import org.apache.doris.nereids.util.PlanUtils.CollectNonWindowedAggFuncs;
+import org.apache.doris.qe.SqlModeHelper;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
@@ -58,6 +59,7 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
+import java.util.stream.Collectors;
import javax.annotation.Nullable;
/** NormalizeRepeat
@@ -177,12 +179,12 @@ public class NormalizeRepeat extends
OneAnalysisRuleFactory {
Set<Slot> groupingSetsUsedSlot = ImmutableSet.copyOf(
ExpressionUtils.flatExpressions(normalizedGroupingSets));
- SetView<SlotReference> aggUsedSlotInAggFunction
+ SetView<SlotReference> aggUsedSlotNotInGroupBy
= Sets.difference(aggUsedNonVirtualSlots,
groupingSetsUsedSlot);
List<Slot> normalizedRepeatOutput = ImmutableList.<Slot>builder()
.addAll(groupingSetsUsedSlot)
- .addAll(aggUsedSlotInAggFunction)
+ .addAll(aggUsedSlotNotInGroupBy)
.addAll(allVirtualSlots)
.build();
@@ -192,6 +194,16 @@ public class NormalizeRepeat extends
OneAnalysisRuleFactory {
NormalizeToSlotContext fullContext =
argsContext.mergeContext(groupingExprContext);
Set<NamedExpression> pushedProject =
fullContext.pushDownToNamedExpression(needToSlots);
+ if (!SqlModeHelper.hasOnlyFullGroupBy()) {
+ // in non-standard aggregate, we need to add all missing slot into
pushed project
+ // we should not use aggUsedSlotNotInGroupBy directly to avoid
duplicate materialization
+ // TODO: refactor NormalizeRepeat and NormalizeAggregate for
reading friendly
+ SetView<SlotReference> missingSlots
+ = Sets.difference(aggUsedSlotNotInGroupBy,
+
pushedProject.stream().map(NamedExpression::toSlot).collect(Collectors.toSet()));
+ pushedProject = Sets.union(pushedProject, missingSlots);
+ }
+
Plan normalizedChild = pushDownProject(pushedProject, repeat.child());
LogicalRepeat<Plan> normalizedRepeat = repeat.withNormalizedExpr(
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalHaving.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalHaving.java
index 7dd227c6d6c..fedd2b9c62e 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalHaving.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalHaving.java
@@ -69,7 +69,7 @@ public class LogicalHaving<CHILD_TYPE extends Plan> extends
LogicalUnary<CHILD_T
}
@Override
- public Plan withChildren(List<Plan> children) {
+ public LogicalHaving<Plan> withChildren(List<Plan> children) {
Preconditions.checkArgument(children.size() == 1);
return new LogicalHaving<>(conjuncts, children.get(0));
}
@@ -91,11 +91,15 @@ public class LogicalHaving<CHILD_TYPE extends Plan> extends
LogicalUnary<CHILD_T
return new LogicalHaving<>(conjuncts, groupExpression,
logicalProperties, children.get(0));
}
- public Plan withConjuncts(Set<Expression> expressions) {
- return new LogicalHaving<Plan>(expressions, Optional.empty(),
+ public LogicalHaving<Plan> withConjuncts(Set<Expression> conjuncts) {
+ return new LogicalHaving<>(conjuncts, Optional.empty(),
Optional.of(getLogicalProperties()), child());
}
+ public LogicalHaving<Plan> withConjunctsAndChild(Set<Expression>
conjuncts, Plan child) {
+ return new LogicalHaving<>(conjuncts, child);
+ }
+
@Override
public List<Slot> computeOutput() {
return child().getOutput();
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/GlobalVariable.java
b/fe/fe-core/src/main/java/org/apache/doris/qe/GlobalVariable.java
index b942ae3f657..9523e0a822c 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/GlobalVariable.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/GlobalVariable.java
@@ -37,7 +37,8 @@ public final class GlobalVariable {
public static final int VARIABLE_VERSION_100 = 100;
public static final int VARIABLE_VERSION_101 = 101;
public static final int VARIABLE_VERSION_200 = 200;
- public static final int CURRENT_VARIABLE_VERSION = VARIABLE_VERSION_200;
+ public static final int VARIABLE_VERSION_300 = 300;
+ public static final int CURRENT_VARIABLE_VERSION = VARIABLE_VERSION_300;
public static final String VARIABLE_VERSION = "variable_version";
public static final String VERSION_COMMENT = "version_comment";
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java
b/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java
index f91ddfd7cf5..92f1ae85f6d 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java
@@ -861,7 +861,7 @@ public class SessionVariable implements Serializable,
Writable {
// Set sqlMode to empty string
@VariableMgr.VarAttr(name = SQL_MODE, needForward = true)
- public long sqlMode = SqlModeHelper.MODE_DEFAULT;
+ public long sqlMode = SqlModeHelper.MODE_ONLY_FULL_GROUP_BY;
@VariableMgr.VarAttr(name = WORKLOAD_VARIABLE, needForward = true)
public String workloadGroup = "";
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/SqlModeHelper.java
b/fe/fe-core/src/main/java/org/apache/doris/qe/SqlModeHelper.java
index d33800eeb8c..2b66297333b 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/SqlModeHelper.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/SqlModeHelper.java
@@ -225,4 +225,12 @@ public class SqlModeHelper {
& MODE_PIPES_AS_CONCAT) != 0;
}
+ public static boolean hasOnlyFullGroupBy() {
+ SessionVariable sessionVariable = ConnectContext.get() == null
+ ? VariableMgr.newSessionVariable()
+ : ConnectContext.get().getSessionVariable();
+ return ((sessionVariable.getSqlMode() & MODE_ALLOWED_MASK)
+ & MODE_ONLY_FULL_GROUP_BY) != 0;
+ }
+
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/VariableMgr.java
b/fe/fe-core/src/main/java/org/apache/doris/qe/VariableMgr.java
index f27b8561278..03e09637fad 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/VariableMgr.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/VariableMgr.java
@@ -954,53 +954,66 @@ public class VariableMgr {
public static void forceUpdateVariables() {
int currentVariableVersion = GlobalVariable.variableVersion;
+ String updateInfo = currentVariableVersion + "to" +
GlobalVariable.CURRENT_VARIABLE_VERSION;
if (currentVariableVersion == GlobalVariable.VARIABLE_VERSION_0) {
// update from 2.0.15 or below to 2.0.16 or higher
if (VariableMgr.newSessionVariable().nereidsTimeoutSecond == 5) {
- VariableMgr.refreshDefaultSessionVariables("update variable
version",
+ VariableMgr.refreshDefaultSessionVariables(updateInfo,
SessionVariable.NEREIDS_TIMEOUT_SECOND, "30");
}
}
if (currentVariableVersion < GlobalVariable.VARIABLE_VERSION_100) {
// update from 2.1.6 or below to 2.1.7 or higher
- VariableMgr.refreshDefaultSessionVariables("update variable
version",
+ VariableMgr.refreshDefaultSessionVariables(updateInfo,
SessionVariable.ENABLE_NEREIDS_DML,
String.valueOf(true));
- VariableMgr.refreshDefaultSessionVariables("update variable
version",
+ VariableMgr.refreshDefaultSessionVariables(updateInfo,
SessionVariable.ENABLE_NEREIDS_DML_WITH_PIPELINE,
String.valueOf(true));
- VariableMgr.refreshDefaultSessionVariables("update variable
version",
+ VariableMgr.refreshDefaultSessionVariables(updateInfo,
SessionVariable.ENABLE_NEREIDS_PLANNER,
String.valueOf(true));
- VariableMgr.refreshDefaultSessionVariables("update variable
version",
+ VariableMgr.refreshDefaultSessionVariables(updateInfo,
SessionVariable.ENABLE_FALLBACK_TO_ORIGINAL_PLANNER,
String.valueOf(true));
- VariableMgr.refreshDefaultSessionVariables("update variable
version",
+ VariableMgr.refreshDefaultSessionVariables(updateInfo,
SessionVariable.ENABLE_PIPELINE_X_ENGINE,
String.valueOf(true));
}
if (currentVariableVersion < GlobalVariable.VARIABLE_VERSION_101) {
if (StatisticsUtil.getAutoAnalyzeTableWidthThreshold()
< StatisticConstants.AUTO_ANALYZE_TABLE_WIDTH_THRESHOLD) {
- VariableMgr.refreshDefaultSessionVariables("update variable
version",
+ VariableMgr.refreshDefaultSessionVariables(updateInfo,
SessionVariable.AUTO_ANALYZE_TABLE_WIDTH_THRESHOLD,
String.valueOf(StatisticConstants.AUTO_ANALYZE_TABLE_WIDTH_THRESHOLD));
}
if (StatisticsUtil.getTableStatsHealthThreshold()
< StatisticConstants.TABLE_STATS_HEALTH_THRESHOLD) {
- VariableMgr.refreshDefaultSessionVariables("update variable
version",
+ VariableMgr.refreshDefaultSessionVariables(updateInfo,
SessionVariable.TABLE_STATS_HEALTH_THRESHOLD,
String.valueOf(StatisticConstants.TABLE_STATS_HEALTH_THRESHOLD));
}
}
if (currentVariableVersion < GlobalVariable.VARIABLE_VERSION_200) {
// update from 3.0.2 or below to 3.0.3 or higher
- VariableMgr.refreshDefaultSessionVariables("update variable
version",
+ VariableMgr.refreshDefaultSessionVariables(updateInfo,
SessionVariable.ENABLE_FALLBACK_TO_ORIGINAL_PLANNER,
String.valueOf(false));
}
+ if (currentVariableVersion < GlobalVariable.VARIABLE_VERSION_300) {
+ // update to master
+ long sqlMode = defaultSessionVariable.sqlMode;
+ // remove mode_default flag
+ if ((sqlMode & SqlModeHelper.MODE_DEFAULT) != 0) {
+ sqlMode ^= SqlModeHelper.MODE_DEFAULT;
+ }
+ sqlMode |= SqlModeHelper.MODE_ONLY_FULL_GROUP_BY;
+ VariableMgr.refreshDefaultSessionVariables(updateInfo,
+ SessionVariable.SQL_MODE,
+ String.valueOf(sqlMode));
+ }
if (currentVariableVersion < GlobalVariable.CURRENT_VARIABLE_VERSION) {
- VariableMgr.refreshDefaultSessionVariables("update variable
version",
+ VariableMgr.refreshDefaultSessionVariables(updateInfo,
GlobalVariable.VARIABLE_VERSION,
String.valueOf(GlobalVariable.CURRENT_VARIABLE_VERSION));
}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/analysis/SetVariableTest.java
b/fe/fe-core/src/test/java/org/apache/doris/analysis/SetVariableTest.java
index 1148f64de3f..d23421840d0 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/analysis/SetVariableTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/analysis/SetVariableTest.java
@@ -45,14 +45,14 @@ public class SetVariableTest {
@Test
public void testSqlMode() throws Exception {
- String setStr = "set sql_mode = concat(@@sql_mode,
'STRICT_TRANS_TABLES');";
+ String setStr = "set sql_mode = concat_ws(',', @@sql_mode,
'STRICT_TRANS_TABLES');";
connectContext.getState().reset();
StmtExecutor stmtExecutor = new StmtExecutor(connectContext, setStr);
stmtExecutor.execute();
- Assert.assertEquals("STRICT_TRANS_TABLES",
-
SqlModeHelper.decode(connectContext.getSessionVariable().getSqlMode()));
+ Assert.assertNotEquals(0,
+ connectContext.getSessionVariable().getSqlMode() &
SqlModeHelper.MODE_STRICT_TRANS_TABLES);
- String selectStr = "explain select /*+
SET_VAR(enable_nereids_planner=false) */ @@sql_mode;";
+ String selectStr = "explain select @@sql_mode;";
connectContext.getState().reset();
stmtExecutor = new StmtExecutor(connectContext, selectStr);
stmtExecutor.execute();
diff --git a/fe/fe-core/src/test/java/org/apache/doris/qe/VariableMgrTest.java
b/fe/fe-core/src/test/java/org/apache/doris/qe/VariableMgrTest.java
index 39f76b86c64..adf6b6d5416 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/qe/VariableMgrTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/qe/VariableMgrTest.java
@@ -82,7 +82,7 @@ public class VariableMgrTest {
} else if (row.get(0).equalsIgnoreCase("query_timeout")) {
Assert.assertEquals(String.valueOf(originQueryTimeOut),
row.get(1));
} else if (row.get(0).equalsIgnoreCase("sql_mode")) {
- Assert.assertEquals("", row.get(1));
+ Assert.assertEquals("ONLY_FULL_GROUP_BY", row.get(1));
} else if (row.get(0).equalsIgnoreCase("insert_timeout")) {
Assert.assertEquals(String.valueOf(originInsertTimeout),
row.get(1));
}
diff --git
a/regression-test/suites/nereids_p0/aggregate/non_standard_aggregate.groovy
b/regression-test/suites/nereids_p0/aggregate/non_standard_aggregate.groovy
new file mode 100644
index 00000000000..d3ed17a8b99
--- /dev/null
+++ b/regression-test/suites/nereids_p0/aggregate/non_standard_aggregate.groovy
@@ -0,0 +1,187 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you 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.
+
+suite("non_standard_aggregate") {
+ sql """
+ set sql_mode = "";
+ """
+
+ sql """
+ DROP TABLE IF EXISTS non_standard_aggregate
+ """
+
+ sql """
+ DROP TABLE IF EXISTS non_standard_aggregate_2
+ """
+
+ sql """
+ CREATE TABLE non_standard_aggregate (
+ c1 int,
+ c2 int,
+ c3 string,
+ c4 array<int>
+ )
+ PROPERTIES (
+ 'replication_num' = '1'
+ )
+ """
+
+ sql """
+ CREATE TABLE non_standard_aggregate_2 (
+ c5 int,
+ c6 int,
+ c7 string,
+ c8 array<int>
+ )
+ PROPERTIES (
+ 'replication_num' = '1'
+ )
+ """
+
+ sql """
+ INSERT INTO non_standard_aggregate VALUES (1, 2, 'hello,world', [1, 2,
3, 4])
+ """
+
+ sql """
+ INSERT INTO non_standard_aggregate_2 VALUES (1, 2, 'hello,world', [1,
2, 3, 4])
+ """
+
+ // simple case
+ sql """SELECT c1, c2, c3 FROM non_standard_aggregate GROUP BY c1"""
+
+ // not group by key
+ sql """SELECT c1, sum(c2) FROM non_standard_aggregate"""
+
+ // use two scalar column as group by key
+ sql """SELECT c1, c2, c3 FROM non_standard_aggregate GROUP BY c1 + c2"""
+
+ // both group by key and scalar column in one expression
+ sql """SELECT c1 + c2, c3 FROM non_standard_aggregate GROUP BY c1"""
+
+ // all scalar column not in group by key
+ sql """SELECT c1, c2, c3 FROM non_standard_aggregate GROUP BY c1 + 1"""
+
+ // scalar column with function
+ sql """SELECT c1, c2 + 1 FROM non_standard_aggregate GROUP BY c1 + 1"""
+
+ // use two scalar column as group by key
+ sql """SELECT c1, c2, c3 FROM non_standard_aggregate GROUP BY c1 + c2
HAVING c1 < 5"""
+
+ // use function with two scalar column as group by key, function with two
scalar column in having
+ sql """SELECT c1, c2, c3 FROM non_standard_aggregate GROUP BY c1 + c2
HAVING c1 + c2 < 5"""
+
+ // use function with two scalar column as group by key, function with two
scalar column in having
+ sql """SELECT c1, c2, c3 FROM non_standard_aggregate GROUP BY c1 + c2
HAVING c1 + c3 < 5"""
+
+ // use function with two scalar column as group by key, function with both
scalar column and group by key in having
+ sql """SELECT c1, c2, c3 FROM non_standard_aggregate GROUP BY c1 + c2
HAVING c1 + c2 + c1 < 10"""
+
+ // both group by key and scalar column in one expression and use it in
having
+ sql """SELECT c1 + c2, c3 FROM non_standard_aggregate GROUP BY c1 HAVING
c1 + c2 < 5"""
+
+ // having with aggregate function but project not
+ sql """SELECT c1 FROM non_standard_aggregate HAVING max(c1) = c1"""
+ sql """SELECT c2 FROM non_standard_aggregate HAVING max(c1) = c2"""
+
+ // having with both aggregate function and scalar column
+ sql """SELECT max(c1) FROM non_standard_aggregate HAVING c1 = count(c1)"""
+ sql """SELECT max(c1) FROM non_standard_aggregate HAVING c2 = count(c1)"""
+
+ // lateral view, be do not support any_value(array)
+ // sql """SELECT c1, c4, c5 FROM non_standard_aggregate LATERAL VIEW
explode(c4) tmp as c5 GROUP BY c1 HAVING c5 < 5"""
+ sql """SELECT c1, c5 FROM non_standard_aggregate LATERAL VIEW explode(c4)
tmp as c5 GROUP BY c1 HAVING c5 < 5"""
+
+ // join
+ sql """SELECT c1, c5 FROM non_standard_aggregate JOIN
non_standard_aggregate_2 ON c2 = c6 GROUP BY c1"""
+
+ // filter
+ sql """SELECT c1, c2 FROM non_standard_aggregate WHERE c3 = 'hello,world'
GROUP BY c2"""
+
+ // window as scalar column
+ sql """SELECT c1 + 1, LAG(c2, 0, NULL) OVER(PARTITION BY c1 ORDER BY c3)
FROM non_standard_aggregate GROUP BY c1 + 1"""
+
+ // having to filter group by key
+ sql """SELECT c1, c2 + 1 FROM non_standard_aggregate GROUP BY c1 + 1
HAVING c1 + 1 < 5"""
+
+ // having to filter scalar column
+ sql """SELECT c1, c2 + 1 FROM non_standard_aggregate GROUP BY c1 + 1
HAVING c2 + 1 < 5"""
+
+ // having with neither group by key nor scalar column
+ sql """SELECT c1, c2 + 1 FROM non_standard_aggregate GROUP BY c1 + 1
HAVING c2 + 2 < 5"""
+
+ // having to filter window
+ sql """SELECT c1 + 1, LAG(c2, 0, NULL) OVER(PARTITION BY c1 ORDER BY c3)
AS c3 FROM non_standard_aggregate GROUP BY c1 + 1 HAVING c3 < 10"""
+
+ // order by with group by key
+ sql """SELECT c1, c2, c3 FROM non_standard_aggregate GROUP BY c1 ORDER BY
c1"""
+
+ // order by with scalar column
+ sql """SELECT c1, c2, c3 FROM non_standard_aggregate GROUP BY c1 ORDER BY
c2"""
+
+ // order by with function of group by key
+ sql """SELECT c1, c2, c3 FROM non_standard_aggregate GROUP BY c1 ORDER BY
c1 + 1"""
+
+ // order by with function of scalar column
+ sql """SELECT c1, c2, c3 FROM non_standard_aggregate GROUP BY c1 ORDER BY
c2 + 1"""
+
+ // order by with aggregate function
+ sql """SELECT c1, c2, c3 FROM non_standard_aggregate GROUP BY c1 ORDER BY
sum(c2)"""
+
+ // top-n with group by key
+ sql """SELECT c1, c2, c3 FROM non_standard_aggregate GROUP BY c1 ORDER BY
c1 LIMIT 10"""
+
+ // top-n with scalar column
+ sql """SELECT c1, c2, c3 FROM non_standard_aggregate GROUP BY c1 ORDER BY
c2 LIMIT 10"""
+
+ // top-n with function of group by key
+ sql """SELECT c1, c2, c3 FROM non_standard_aggregate GROUP BY c1 ORDER BY
c1 + 1 LIMIT 10"""
+
+ // top-n with function of scalar column
+ sql """SELECT c1, c2, c3 FROM non_standard_aggregate GROUP BY c1 ORDER BY
c2 + 1 LIMIT 10"""
+
+ // top-n with aggregate function
+ sql """SELECT c1, c2, c3 FROM non_standard_aggregate GROUP BY c1 ORDER BY
sum(c2) LIMIT 10"""
+
+ // having scalar column + order by aggregate function
+ sql """SELECT c1, c2, c3 FROM non_standard_aggregate GROUP BY c1 HAVING c2
< 10 ORDER BY sum(c2)"""
+
+ // having scalar column + order by scalar column
+ sql """SELECT c1, c2, c3 FROM non_standard_aggregate GROUP BY c1 HAVING c2
< 10 ORDER BY c3"""
+
+ // having aggregate function + order by scalar column
+ sql """SELECT c1, c2, c3 FROM non_standard_aggregate GROUP BY c1 HAVING
sum(c2) < 10 ORDER BY c3"""
+
+
+ // repeat
+ sql """SELECT c1, c2, c3 FROM non_standard_aggregate GROUP BY GROUPING
SETS((c1), (c1, c2), ())"""
+
+ // repeat with having on group by key
+ sql """SELECT c1, c2, c3 FROM non_standard_aggregate GROUP BY GROUPING
SETS((c1), (c1, c2), ()) HAVING c1 < 5"""
+
+ // repeat with having on function with group by key
+ sql """SELECT c1, c2, c3 FROM non_standard_aggregate GROUP BY GROUPING
SETS((c1), (c1, c2), ()) HAVING c1 + 1 < 5"""
+
+ // repeat with having on scalar column
+ sql """SELECT c1, c2, c3 FROM non_standard_aggregate GROUP BY GROUPING
SETS((c1), (c1, c2), ()) HAVING c3 < 5"""
+
+ // repeat with having on function with scalar column
+ sql """SELECT c1, c2, c3 FROM non_standard_aggregate GROUP BY GROUPING
SETS((c1), (c1, c2), ()) HAVING c3 + 1 < 5"""
+
+ // repeat with having with neither group by key nor scalar column
+ sql """SELECT c1, c2, c3 + 1 FROM non_standard_aggregate GROUP BY GROUPING
SETS((c1), (c1, c2), ()) HAVING c3 < 5"""
+
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]