You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by jh...@apache.org on 2018/11/08 18:00:56 UTC
[1/6] calcite git commit: [CALCITE-2654] In RelBuilder,
add a fluent API for building complex aggregate calls
Repository: calcite
Updated Branches:
refs/heads/master 425fa7b7f -> ebc43d928
[CALCITE-2654] In RelBuilder, add a fluent API for building complex aggregate calls
To interface AggCall in RelBuilder, add methods distinct(boolean),
filter(RexNode), approximate(boolean), alias(String). And simplify the
RelBuilder.aggregateCall method to just two arguments:
aggregateCall(op, operands). Thus you only specify the arguments that
are of interest. Similar changes to count, countStar, min, max, sum,
avg.
Project: http://git-wip-us.apache.org/repos/asf/calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/4cc46130
Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/4cc46130
Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/4cc46130
Branch: refs/heads/master
Commit: 4cc46130f71d21e4f5b76f6e645380cd83d2c921
Parents: 425fa7b
Author: Julian Hyde <jh...@apache.org>
Authored: Sat Nov 3 19:59:11 2018 -0700
Committer: Julian Hyde <jh...@apache.org>
Committed: Wed Nov 7 16:33:06 2018 -0800
----------------------------------------------------------------------
.../rel/rules/AbstractMaterializedViewRule.java | 21 +-
.../AggregateExpandDistinctAggregatesRule.java | 21 +-
.../rel/rules/AggregateExtractProjectRule.java | 9 +-
.../calcite/rel/rules/SubQueryRemoveRule.java | 8 +-
.../apache/calcite/sql2rel/RelFieldTrimmer.java | 8 +-
.../org/apache/calcite/tools/PigRelBuilder.java | 3 +-
.../org/apache/calcite/tools/RelBuilder.java | 249 ++++++++++++++++---
.../rel/rel2sql/RelToSqlConverterTest.java | 4 +-
.../org/apache/calcite/test/RelBuilderTest.java | 66 +++--
site/_docs/algebra.md | 22 +-
10 files changed, 298 insertions(+), 113 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/calcite/blob/4cc46130/core/src/main/java/org/apache/calcite/rel/rules/AbstractMaterializedViewRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AbstractMaterializedViewRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AbstractMaterializedViewRule.java
index 3a82f95..68a9382 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/AbstractMaterializedViewRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/AbstractMaterializedViewRule.java
@@ -1194,13 +1194,14 @@ public abstract class AbstractMaterializedViewRule extends RelOptRule {
// Cannot rollup this aggregate, bail out
return null;
}
+ final RexInputRef operand =
+ rexBuilder.makeInputRef(relBuilder.peek(),
+ aggregate.getGroupCount() + i);
aggregateCalls.add(
- relBuilder.aggregateCall(
- rollupAgg,
- aggCall.isDistinct(), aggCall.isApproximate(), null,
- aggCall.name,
- rexBuilder.makeInputRef(relBuilder.peek(),
- aggregate.getGroupCount() + i)));
+ relBuilder.aggregateCall(rollupAgg, operand)
+ .distinct(aggCall.isDistinct())
+ .approximate(aggCall.isApproximate())
+ .as(aggCall.name));
}
RelNode prevNode = relBuilder.peek();
RelNode result = relBuilder
@@ -1466,10 +1467,12 @@ public abstract class AbstractMaterializedViewRule extends RelOptRule {
// Cannot rollup this aggregate, bail out
return null;
}
+ final RexInputRef operand = rexBuilder.makeInputRef(input, k);
aggregateCalls.add(
- relBuilder.aggregateCall(
- rollupAgg, queryAggCall.isDistinct(), queryAggCall.isApproximate(),
- null, queryAggCall.name, rexBuilder.makeInputRef(input, k)));
+ relBuilder.aggregateCall(rollupAgg, operand)
+ .approximate(queryAggCall.isApproximate())
+ .distinct(queryAggCall.isDistinct())
+ .as(queryAggCall.name));
rewritingMapping.set(k, sourceIdx);
added = true;
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/4cc46130/core/src/main/java/org/apache/calcite/rel/rules/AggregateExpandDistinctAggregatesRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateExpandDistinctAggregatesRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateExpandDistinctAggregatesRule.java
index d150989..6d974d8 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateExpandDistinctAggregatesRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateExpandDistinctAggregatesRule.java
@@ -274,14 +274,15 @@ public final class AggregateExpandDistinctAggregatesRule extends RelOptRule {
// Add the distinct aggregate column(s) to the group-by columns,
// if not already a part of the group-by
- final SortedSet<Integer> bottomGroupSet = new TreeSet<>();
- bottomGroupSet.addAll(aggregate.getGroupSet().asList());
+ final SortedSet<Integer> bottomGroups = new TreeSet<>();
+ bottomGroups.addAll(aggregate.getGroupSet().asList());
for (AggregateCall aggCall : originalAggCalls) {
if (aggCall.isDistinct()) {
- bottomGroupSet.addAll(aggCall.getArgList());
+ bottomGroups.addAll(aggCall.getArgList());
break; // since we only have single distinct call
}
}
+ final ImmutableBitSet bottomGroupSet = ImmutableBitSet.of(bottomGroups);
// Generate the intermediate aggregate B, the one on the bottom that converts
// a distinct call to group by call.
@@ -295,16 +296,15 @@ public final class AggregateExpandDistinctAggregatesRule extends RelOptRule {
final AggregateCall newCall =
AggregateCall.create(aggCall.getAggregation(), false,
aggCall.isApproximate(), aggCall.getArgList(), -1,
- ImmutableBitSet.of(bottomGroupSet).cardinality(),
+ bottomGroupSet.cardinality(),
relBuilder.peek(), null, aggCall.name);
bottomAggregateCalls.add(newCall);
}
}
// Generate the aggregate B (see the reference example above)
relBuilder.push(
- aggregate.copy(
- aggregate.getTraitSet(), relBuilder.build(),
- false, ImmutableBitSet.of(bottomGroupSet), null, bottomAggregateCalls));
+ aggregate.copy(aggregate.getTraitSet(), relBuilder.build(),
+ false, bottomGroupSet, null, bottomAggregateCalls));
// Add aggregate A (see the reference example above), the top aggregate
// to handle the rest of the aggregation that the bottom aggregate hasn't handled
@@ -316,7 +316,7 @@ public final class AggregateExpandDistinctAggregatesRule extends RelOptRule {
if (aggCall.isDistinct()) {
List<Integer> newArgList = new ArrayList<>();
for (int arg : aggCall.getArgList()) {
- newArgList.add(bottomGroupSet.headSet(arg).size());
+ newArgList.add(bottomGroups.headSet(arg).size());
}
newCall =
AggregateCall.create(aggCall.getAggregation(),
@@ -332,7 +332,8 @@ public final class AggregateExpandDistinctAggregatesRule extends RelOptRule {
// If aggregate B had a COUNT aggregate call the corresponding aggregate at
// aggregate A must be SUM. For other aggregates, it remains the same.
final List<Integer> newArgs =
- Lists.newArrayList(bottomGroupSet.size() + nonDistinctAggCallProcessedSoFar);
+ Lists.newArrayList(bottomGroups.size()
+ + nonDistinctAggCallProcessedSoFar);
if (aggCall.getAggregation().getKind() == SqlKind.COUNT) {
newCall =
AggregateCall.create(new SqlSumEmptyIsZeroAggFunction(), false,
@@ -357,7 +358,7 @@ public final class AggregateExpandDistinctAggregatesRule extends RelOptRule {
// output), minus the distinct aggCall's input.
final Set<Integer> topGroupSet = new HashSet<>();
int groupSetToAdd = 0;
- for (int bottomGroup : bottomGroupSet) {
+ for (int bottomGroup : bottomGroups) {
if (originalGroupSet.get(bottomGroup)) {
topGroupSet.add(groupSetToAdd);
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/4cc46130/core/src/main/java/org/apache/calcite/rel/rules/AggregateExtractProjectRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateExtractProjectRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateExtractProjectRule.java
index 5559c97..08b12dd 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateExtractProjectRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateExtractProjectRule.java
@@ -116,9 +116,12 @@ public class AggregateExtractProjectRule extends RelOptRule {
final RexNode filterArg = aggCall.filterArg < 0 ? null
: relBuilder.field(Mappings.apply(mapping, aggCall.filterArg));
newAggCallList.add(
- relBuilder.aggregateCall(aggCall.getAggregation(),
- aggCall.isDistinct(), aggCall.isApproximate(),
- filterArg, aggCall.name, args));
+ relBuilder.aggregateCall(aggCall.getAggregation(), args)
+ .distinct(aggCall.isDistinct())
+ .filter(filterArg)
+ .approximate(aggCall.isApproximate())
+ .approximate(aggCall.isApproximate())
+ .as(aggCall.name));
}
final RelBuilder.GroupKey groupKey =
http://git-wip-us.apache.org/repos/asf/calcite/blob/4cc46130/core/src/main/java/org/apache/calcite/rel/rules/SubQueryRemoveRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/SubQueryRemoveRule.java b/core/src/main/java/org/apache/calcite/rel/rules/SubQueryRemoveRule.java
index 974c57d..39bccbf 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/SubQueryRemoveRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/SubQueryRemoveRule.java
@@ -124,8 +124,8 @@ public abstract class SubQueryRemoveRule extends RelOptRule {
ImmutableBitSet.of());
if (unique == null || !unique) {
builder.aggregate(builder.groupKey(),
- builder.aggregateCall(SqlStdOperatorTable.SINGLE_VALUE, false,
- false, null, null, builder.field(0)));
+ builder.aggregateCall(SqlStdOperatorTable.SINGLE_VALUE,
+ builder.field(0)));
}
builder.join(JoinRelType.LEFT, builder.literal(true), variablesSet);
return field(builder, inputCount, offset);
@@ -385,8 +385,8 @@ public abstract class SubQueryRemoveRule extends RelOptRule {
// Builds the cross join
builder.aggregate(builder.groupKey(),
builder.count(false, "c"),
- builder.aggregateCall(SqlStdOperatorTable.COUNT, false, false, null,
- "ck", builder.fields()));
+ builder.aggregateCall(SqlStdOperatorTable.COUNT, builder.fields())
+ .as("ck"));
builder.as("ct");
if (!variablesSet.isEmpty()) {
builder.join(JoinRelType.LEFT, builder.literal(true), variablesSet);
http://git-wip-us.apache.org/repos/asf/calcite/blob/4cc46130/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java b/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java
index 733ee02..e9031ba 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java
@@ -867,9 +867,11 @@ public class RelFieldTrimmer implements ReflectiveVisitor {
final RexNode filterArg = aggCall.filterArg < 0 ? null
: relBuilder.field(Mappings.apply(inputMapping, aggCall.filterArg));
RelBuilder.AggCall newAggCall =
- relBuilder.aggregateCall(aggCall.getAggregation(),
- aggCall.isDistinct(), aggCall.isApproximate(),
- filterArg, aggCall.name, args);
+ relBuilder.aggregateCall(aggCall.getAggregation(), args)
+ .distinct(aggCall.isDistinct())
+ .filter(filterArg)
+ .approximate(aggCall.isApproximate())
+ .as(aggCall.name);
mapping.set(j, groupCount + indicatorCount + newAggCallList.size());
newAggCallList.add(newAggCall);
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/4cc46130/core/src/main/java/org/apache/calcite/tools/PigRelBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/tools/PigRelBuilder.java b/core/src/main/java/org/apache/calcite/tools/PigRelBuilder.java
index dcac18b..50738cd 100644
--- a/core/src/main/java/org/apache/calcite/tools/PigRelBuilder.java
+++ b/core/src/main/java/org/apache/calcite/tools/PigRelBuilder.java
@@ -146,8 +146,7 @@ public class PigRelBuilder extends RelBuilder {
cluster.getRexBuilder().makeCall(peek(1, 0).getRowType(),
SqlStdOperatorTable.ROW, fields());
aggregate(groupKey.e,
- aggregateCall(SqlStdOperatorTable.COLLECT, false, false, null,
- getAlias(), row));
+ aggregateCall(SqlStdOperatorTable.COLLECT, row).as(getAlias()));
if (groupKey.i < n - 1) {
push(r);
List<RexNode> predicates = new ArrayList<>();
http://git-wip-us.apache.org/repos/asf/calcite/blob/4cc46130/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
index d2b5d5d..323f65a 100644
--- a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
+++ b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
@@ -763,72 +763,129 @@ public class RelBuilder {
@Deprecated // to be removed before 2.0
public AggCall aggregateCall(SqlAggFunction aggFunction, boolean distinct,
RexNode filter, String alias, RexNode... operands) {
- return aggregateCall(aggFunction, distinct, false, filter, alias,
- ImmutableList.copyOf(operands));
+ return aggregateCall(aggFunction, distinct, false, filter,
+ ImmutableList.of(), alias, ImmutableList.copyOf(operands));
}
- /** Creates a call to an aggregate function. */
+ @Deprecated // to be removed before 2.0
public AggCall aggregateCall(SqlAggFunction aggFunction, boolean distinct,
boolean approximate, RexNode filter, String alias, RexNode... operands) {
- return aggregateCall(aggFunction, distinct, approximate, filter, alias,
- ImmutableList.copyOf(operands));
+ return aggregateCall(aggFunction, distinct, approximate, filter,
+ ImmutableList.of(), alias, ImmutableList.copyOf(operands));
}
@Deprecated // to be removed before 2.0
public AggCall aggregateCall(SqlAggFunction aggFunction, boolean distinct,
RexNode filter, String alias, Iterable<? extends RexNode> operands) {
- return aggregateCall(aggFunction, distinct, false, filter, alias, operands);
+ return aggregateCall(aggFunction, distinct, false, filter,
+ ImmutableList.of(), alias, ImmutableList.copyOf(operands));
}
- /** Creates a call to an aggregate function. */
+ @Deprecated // to be removed before 2.0
public AggCall aggregateCall(SqlAggFunction aggFunction, boolean distinct,
boolean approximate, RexNode filter, String alias,
Iterable<? extends RexNode> operands) {
- if (filter != null) {
- if (filter.getType().getSqlTypeName() != SqlTypeName.BOOLEAN) {
- throw RESOURCE.filterMustBeBoolean().ex();
- }
- if (filter.getType().isNullable()) {
- filter = call(SqlStdOperatorTable.IS_TRUE, filter);
- }
- }
+ return aggregateCall(aggFunction, distinct, filter, alias, operands)
+ .approximate(approximate);
+ }
+
+ /** Creates a call to an aggregate function.
+ *
+ * <p>To add other operands, apply
+ * {@link AggCall#filter(RexNode...)},
+ * {@link AggCall#distinct()},
+ * {@link AggCall#as},
+ * {@link AggCall#sort} to the result. */
+ public AggCall aggregateCall(SqlAggFunction aggFunction,
+ Iterable<? extends RexNode> operands) {
+ return aggregateCall(aggFunction, false, false, null, ImmutableList.of(),
+ null, ImmutableList.copyOf(operands));
+ }
+
+ /** Creates a call to an aggregate function.
+ *
+ * <p>To add other operands, apply
+ * {@link AggCall#filter(RexNode...)},
+ * {@link AggCall#distinct()},
+ * {@link AggCall#as},
+ * {@link AggCall#sort} to the result. */
+ public AggCall aggregateCall(SqlAggFunction aggFunction,
+ RexNode... operands) {
+ return aggregateCall(aggFunction, false, false, null, ImmutableList.of(),
+ null, ImmutableList.copyOf(operands));
+ }
+
+ /** Creates a call to an aggregate function with all applicable operands. */
+ AggCall aggregateCall(SqlAggFunction aggFunction, boolean distinct,
+ boolean approximate, RexNode filter, ImmutableList<RexNode> orderKeys,
+ String alias, ImmutableList<RexNode> operands) {
return new AggCallImpl(aggFunction, distinct, approximate, filter, alias,
- ImmutableList.copyOf(operands));
+ operands, orderKeys);
+ }
+
+ /** Creates a call to the {@code COUNT} aggregate function. */
+ public AggCall count(RexNode... operands) {
+ return count(false, null, operands);
}
- /** Creates a call to the COUNT aggregate function. */
+ /** Creates a call to the {@code COUNT} aggregate function,
+ * optionally distinct and with an alias. */
public AggCall count(boolean distinct, String alias, RexNode... operands) {
- return aggregateCall(SqlStdOperatorTable.COUNT, distinct, false, null,
- alias, operands);
+ return aggregateCall(SqlStdOperatorTable.COUNT, distinct, null, alias,
+ ImmutableList.copyOf(operands));
}
- /** Creates a call to the COUNT(*) aggregate function. */
+ /** Creates a call to the {@code COUNT(*)} aggregate function. */
public AggCall countStar(String alias) {
- return aggregateCall(SqlStdOperatorTable.COUNT, false, false, null, alias);
+ return count(false, alias);
}
- /** Creates a call to the SUM aggregate function. */
+ /** Creates a call to the {@code SUM} aggregate function. */
+ public AggCall sum(RexNode operand) {
+ return sum(false, null, operand);
+ }
+
+ /** Creates a call to the {@code SUM} aggregate function,
+ * optionally distinct and with an alias. */
public AggCall sum(boolean distinct, String alias, RexNode operand) {
- return aggregateCall(SqlStdOperatorTable.SUM, distinct, false, null, alias,
- operand);
+ return aggregateCall(SqlStdOperatorTable.SUM, distinct, null, alias,
+ ImmutableList.of(operand));
}
- /** Creates a call to the AVG aggregate function. */
+ /** Creates a call to the {@code AVG} aggregate function. */
+ public AggCall avg(RexNode operand) {
+ return avg(false, null, operand);
+ }
+
+ /** Creates a call to the {@code AVG} aggregate function,
+ * optionally distinct and with an alias. */
public AggCall avg(boolean distinct, String alias, RexNode operand) {
- return aggregateCall(SqlStdOperatorTable.AVG, distinct, false, null, alias,
- operand);
+ return aggregateCall(SqlStdOperatorTable.AVG, distinct, null, alias,
+ ImmutableList.of(operand));
}
- /** Creates a call to the MIN aggregate function. */
+ /** Creates a call to the {@code MIN} aggregate function. */
+ public AggCall min(RexNode operand) {
+ return min(null, operand);
+ }
+
+ /** Creates a call to the {@code MIN} aggregate function,
+ * optionally with an alias. */
public AggCall min(String alias, RexNode operand) {
- return aggregateCall(SqlStdOperatorTable.MIN, false, false, null, alias,
- operand);
+ return aggregateCall(SqlStdOperatorTable.MIN, false, null, alias,
+ ImmutableList.of(operand));
+ }
+
+ /** Creates a call to the {@code MAX} aggregate function,
+ * optionally with an alias. */
+ public AggCall max(RexNode operand) {
+ return max(null, operand);
}
- /** Creates a call to the MAX aggregate function. */
+ /** Creates a call to the {@code MAX} aggregate function. */
public AggCall max(String alias, RexNode operand) {
- return aggregateCall(SqlStdOperatorTable.MAX, false, false, null, alias,
- operand);
+ return aggregateCall(SqlStdOperatorTable.MAX, false, null, alias,
+ ImmutableList.of(operand));
}
// Methods for patterns
@@ -2041,6 +2098,32 @@ public class RelBuilder {
*
* @see RelBuilder#aggregateCall */
public interface AggCall {
+ /** Returns a copy of this AggCall that applies a filter before aggregating
+ * values. */
+ AggCall filter(RexNode condition);
+
+ /** Returns a copy of this AggCall that sorts its input values by
+ * {@code orderKeys} before aggregating, as in SQL's {@code WITHIN GROUP}
+ * clause. */
+ AggCall sort(Iterable<RexNode> orderKeys);
+
+ /** Returns a copy of this AggCall that sorts its input values by
+ * {@code orderKeys} before aggregating, as in SQL's {@code WITHIN GROUP}
+ * clause. */
+ AggCall sort(RexNode... orderKeys);
+
+ /** Returns a copy of this AggCall that may return approximate results
+ * if {@code approximate} is true. */
+ AggCall approximate(boolean approximate);
+
+ /** Returns a copy of this AggCall with a given alias. */
+ AggCall as(String alias);
+
+ /** Returns a copy of this AggCall that is optionally distinct. */
+ AggCall distinct(boolean distinct);
+
+ /** Returns a copy of this AggCall that is distinct. */
+ AggCall distinct();
}
/** Information necessary to create the GROUP BY clause of an Aggregate.
@@ -2081,23 +2164,79 @@ public class RelBuilder {
}
/** Implementation of {@link AggCall}. */
- private static class AggCallImpl implements AggCall {
+ private class AggCallImpl implements AggCall {
private final SqlAggFunction aggFunction;
private final boolean distinct;
private final boolean approximate;
- private final RexNode filter;
- private final String alias;
- private final ImmutableList<RexNode> operands;
+ private final RexNode filter; // may be null
+ private final String alias; // may be null
+ private final ImmutableList<RexNode> operands; // may be empty, never null
+ private final ImmutableList<RexNode> orderKeys; // may be empty, never null
AggCallImpl(SqlAggFunction aggFunction, boolean distinct,
boolean approximate, RexNode filter,
- String alias, ImmutableList<RexNode> operands) {
- this.aggFunction = aggFunction;
+ String alias, ImmutableList<RexNode> operands,
+ ImmutableList<RexNode> orderKeys) {
+ this.aggFunction = Objects.requireNonNull(aggFunction);
this.distinct = distinct;
this.approximate = approximate;
- this.filter = filter;
this.alias = alias;
- this.operands = operands;
+ this.operands = Objects.requireNonNull(operands);
+ this.orderKeys = Objects.requireNonNull(orderKeys);
+ if (filter != null) {
+ if (filter.getType().getSqlTypeName() != SqlTypeName.BOOLEAN) {
+ throw RESOURCE.filterMustBeBoolean().ex();
+ }
+ if (filter.getType().isNullable()) {
+ filter = call(SqlStdOperatorTable.IS_TRUE, filter);
+ }
+ }
+ this.filter = filter;
+ }
+
+ public AggCall sort(Iterable<RexNode> orderKeys) {
+ final ImmutableList<RexNode> orderKeyList =
+ ImmutableList.copyOf(orderKeys);
+ return orderKeyList.equals(this.orderKeys)
+ ? this
+ : new AggCallImpl(aggFunction, distinct, approximate, filter,
+ alias, operands, orderKeyList);
+ }
+
+ public AggCall sort(RexNode... orderKeys) {
+ return sort(ImmutableList.copyOf(orderKeys));
+ }
+
+ public AggCall approximate(boolean approximate) {
+ return approximate == this.approximate
+ ? this
+ : new AggCallImpl(aggFunction, distinct, approximate, filter,
+ alias, operands, orderKeys);
+ }
+
+ public AggCall filter(RexNode condition) {
+ return Objects.equals(condition, this.filter)
+ ? this
+ : new AggCallImpl(aggFunction, distinct, approximate, condition,
+ alias, operands, orderKeys);
+ }
+
+ public AggCall as(String alias) {
+ return Objects.equals(alias, this.alias)
+ ? this
+ : new AggCallImpl(aggFunction, distinct, approximate, filter,
+ alias, operands, orderKeys);
+ }
+
+ public AggCall distinct(boolean distinct) {
+ return distinct == this.distinct
+ ? this
+ : new AggCallImpl(aggFunction, distinct, approximate, filter,
+ alias, operands, orderKeys);
+ }
+
+ public AggCall distinct() {
+ return distinct(true);
}
}
@@ -2109,6 +2248,34 @@ public class RelBuilder {
AggCallImpl2(AggregateCall aggregateCall) {
this.aggregateCall = Objects.requireNonNull(aggregateCall);
}
+
+ public AggCall sort(Iterable<RexNode> orderKeys) {
+ throw new UnsupportedOperationException();
+ }
+
+ public AggCall sort(RexNode... orderKeys) {
+ throw new UnsupportedOperationException();
+ }
+
+ public AggCall approximate(boolean approximate) {
+ throw new UnsupportedOperationException();
+ }
+
+ public AggCall filter(RexNode condition) {
+ throw new UnsupportedOperationException();
+ }
+
+ public AggCall as(String alias) {
+ throw new UnsupportedOperationException();
+ }
+
+ public AggCall distinct(boolean distinct) {
+ throw new UnsupportedOperationException();
+ }
+
+ public AggCall distinct() {
+ throw new UnsupportedOperationException();
+ }
}
/** Collects the extra expressions needed for {@link #aggregate}.
http://git-wip-us.apache.org/repos/asf/calcite/blob/4cc46130/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
index 70bf317..94b98b7 100644
--- a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
+++ b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
@@ -274,8 +274,8 @@ public class RelToSqlConverterTest {
final RelNode root = builder
.scan("EMP")
.aggregate(builder.groupKey(),
- builder.aggregateCall(SqlStdOperatorTable.SUM0, false, false, null,
- "s", builder.field(3)))
+ builder.aggregateCall(SqlStdOperatorTable.SUM0, false, null, "s",
+ builder.field(3)))
.build();
final String expectedMysql = "SELECT COALESCE(SUM(`MGR`), 0) AS `s`\n"
+ "FROM `scott`.`EMP`";
http://git-wip-us.apache.org/repos/asf/calcite/blob/4cc46130/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java b/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
index 3624204..e3d5b95 100644
--- a/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
@@ -505,7 +505,6 @@ public class RelBuilderTest {
builder.literal(20)))
.aggregate(builder.groupKey(0, 1, 2),
builder.aggregateCall(SqlStdOperatorTable.SUM,
- false, false, null, null,
builder.field(0)))
.project(builder.field("c"),
builder.field("a"))
@@ -661,8 +660,7 @@ public class RelBuilderTest {
RelNode root =
builder.scan("EMP")
.aggregate(builder.groupKey(),
- builder.aggregateCall(SqlStdOperatorTable.COUNT, true, false,
- null, "C", builder.field("DEPTNO")))
+ builder.count(true, "C", builder.field("DEPTNO")))
.build();
final String expected = ""
+ "LogicalAggregate(group=[{}], C=[COUNT(DISTINCT $7)])\n"
@@ -684,12 +682,10 @@ public class RelBuilderTest {
builder.field(4),
builder.field(3)),
builder.field(1)),
- builder.aggregateCall(SqlStdOperatorTable.COUNT, false, false,
- null, "C"),
- builder.aggregateCall(SqlStdOperatorTable.SUM, false, false,
- null, "S",
+ builder.countStar("C"),
+ builder.sum(
builder.call(SqlStdOperatorTable.PLUS, builder.field(3),
- builder.literal(1))))
+ builder.literal(1))).as("S"))
.build();
final String expected = ""
+ "LogicalAggregate(group=[{1, 8}], C=[COUNT()], S=[SUM($9)])\n"
@@ -711,12 +707,9 @@ public class RelBuilderTest {
final RelBuilder builder = RelBuilder.create(config().build());
RelNode root =
builder.scan("EMP")
- .aggregate(
- builder.groupKey(builder.field(1)),
- builder.aggregateCall(SqlStdOperatorTable.COUNT, false, false,
- null, "C"))
- .aggregate(
- builder.groupKey(builder.field(0)))
+ .aggregate(builder.groupKey(builder.field(1)),
+ builder.count().as("C"))
+ .aggregate(builder.groupKey(builder.field(0)))
.build();
final String expected = ""
+ "LogicalProject(ENAME=[$0])\n"
@@ -738,8 +731,7 @@ public class RelBuilderTest {
builder.scan("EMP")
.aggregate(
builder.groupKey(builder.field(1)),
- builder.aggregateCall(SqlStdOperatorTable.COUNT, false, false,
- null, "C"))
+ builder.count().as("C"))
.filter(
builder.call(SqlStdOperatorTable.GREATER_THAN, builder.field(1),
builder.literal(3)))
@@ -766,9 +758,11 @@ public class RelBuilderTest {
builder.groupKey(ImmutableBitSet.of(7),
ImmutableList.of(ImmutableBitSet.of(7),
ImmutableBitSet.of())),
- builder.aggregateCall(SqlStdOperatorTable.COUNT, false, false,
- builder.call(SqlStdOperatorTable.GREATER_THAN,
- builder.field("EMPNO"), builder.literal(100)), "C"))
+ builder.count()
+ .filter(
+ builder.call(SqlStdOperatorTable.GREATER_THAN,
+ builder.field("EMPNO"), builder.literal(100)))
+ .as("C"))
.build();
final String expected = ""
+ "LogicalAggregate(group=[{7}], groups=[[{7}, {}]], C=[COUNT() FILTER $8])\n"
@@ -788,8 +782,9 @@ public class RelBuilderTest {
builder.scan("EMP")
.aggregate(
builder.groupKey(builder.field("DEPTNO")),
- builder.aggregateCall(SqlStdOperatorTable.SUM, false, false,
- builder.field("COMM"), "C", builder.field("SAL")))
+ builder.sum(builder.field("SAL"))
+ .filter(builder.field("COMM"))
+ .as("C"))
.build();
fail("expected error, got " + root);
} catch (CalciteException e) {
@@ -808,10 +803,11 @@ public class RelBuilderTest {
builder.scan("EMP")
.aggregate(
builder.groupKey(builder.field("DEPTNO")),
- builder.aggregateCall(SqlStdOperatorTable.SUM, false, false,
- builder.call(SqlStdOperatorTable.LESS_THAN,
- builder.field("COMM"), builder.literal(100)), "C",
- builder.field("SAL")))
+ builder.sum(builder.field("SAL"))
+ .filter(
+ builder.call(SqlStdOperatorTable.LESS_THAN,
+ builder.field("COMM"), builder.literal(100)))
+ .as("C"))
.build();
final String expected = ""
+ "LogicalAggregate(group=[{7}], C=[SUM($5) FILTER $8])\n"
@@ -912,8 +908,8 @@ public class RelBuilderTest {
RelNode root =
builder.scan("EMP")
.aggregate(builder.groupKey(6, 7),
- builder.aggregateCall(SqlStdOperatorTable.GROUPING, false,
- false, null, "g", builder.field("DEPTNO")))
+ builder.aggregateCall(SqlStdOperatorTable.GROUPING,
+ builder.field("DEPTNO")).as("g"))
.build();
final String expected = ""
+ "LogicalAggregate(group=[{6, 7}], g=[GROUPING($7)])\n"
@@ -927,8 +923,10 @@ public class RelBuilderTest {
RelNode root =
builder.scan("EMP")
.aggregate(builder.groupKey(6, 7),
- builder.aggregateCall(SqlStdOperatorTable.GROUPING, true,
- false, null, "g", builder.field("DEPTNO")))
+ builder.aggregateCall(SqlStdOperatorTable.GROUPING,
+ builder.field("DEPTNO"))
+ .distinct(true)
+ .as("g"))
.build();
fail("expected error, got " + root);
} catch (IllegalArgumentException e) {
@@ -942,9 +940,10 @@ public class RelBuilderTest {
RelNode root =
builder.scan("EMP")
.aggregate(builder.groupKey(6, 7),
- builder.aggregateCall(SqlStdOperatorTable.GROUPING, false,
- false, builder.literal(true), "g",
- builder.field("DEPTNO")))
+ builder.aggregateCall(SqlStdOperatorTable.GROUPING,
+ builder.field("DEPTNO"))
+ .filter(builder.literal(true))
+ .as("g"))
.build();
fail("expected error, got " + root);
} catch (IllegalArgumentException e) {
@@ -1482,8 +1481,7 @@ public class RelBuilderTest {
.project(builder.field("DEPTNO"),
builder.literal(20))
.aggregate(builder.groupKey(builder.field("EMP_alias", "DEPTNO")),
- builder.aggregateCall(SqlStdOperatorTable.SUM, false, false,
- null, null, builder.field(1)))
+ builder.sum(builder.field(1)))
.project(builder.alias(builder.field(1), "sum"),
builder.field("EMP_alias", "DEPTNO"))
.build();
http://git-wip-us.apache.org/repos/asf/calcite/blob/4cc46130/site/_docs/algebra.md
----------------------------------------------------------------------
diff --git a/site/_docs/algebra.md b/site/_docs/algebra.md
index 2b5c66a..e5b505b 100644
--- a/site/_docs/algebra.md
+++ b/site/_docs/algebra.md
@@ -390,9 +390,21 @@ The following methods return an
| Method | Description
|:------------------- |:-----------
-| `aggregateCall(op, distinct, approximate, filter, alias, expr...)`<br/>`aggregateCall(op, distinct, approximate, filter, alias, exprList)` | Creates a call to a given aggregate function, with an optional filter expression
-| `count(distinct, alias, expr...)` | Creates a call to the COUNT aggregate function
+| `aggregateCall(op, distinct, approximate, filter, orderKeys, alias, expr...)`<br/>`aggregateCall(op, distinct, approximate, filter, orderKeys, alias, exprList)` | Creates a call to a given aggregate function, with an optional filter expression and a list of optional ordering keys (for sorting input values before aggregation)
+| `count( [ distinct, alias, ] expr...)` | Creates a call to the COUNT aggregate function
| `countStar(alias)` | Creates a call to the COUNT(*) aggregate function
-| `sum(distinct, alias, expr)` | Creates a call to the SUM aggregate function
-| `min(alias, expr)` | Creates a call to the MIN aggregate function
-| `max(alias, expr)` | Creates a call to the MAX aggregate function
+| `sum( [ distinct, alias, ] expr)` | Creates a call to the SUM aggregate function
+| `min( [ alias, ] expr)` | Creates a call to the MIN aggregate function
+| `max( [ alias, ] expr)` | Creates a call to the MAX aggregate function
+
+To further modify the `AggCall`, call its methods:
+
+| Method | Description
+|:-------------------- |:-----------
+| `distinct()` | Eliminates duplicate values before aggregating (see SQL `DISTINCT`)
+| `distinct(distinct)` | Eliminates duplicate values before aggregating if `distinct`
+| `as(alias)` | Assigns a column alias to this expression (see SQL `AS`)
+| `filter(expr)` | Filters rows before aggregating (see SQL `FILTER (WHERE ...)`)
+| `sort(expr, ...)`<br/>`sort(exprList)` | Sorts rows before aggregating (see SQL `WITHIN GROUP`)
+| `approximate(approximate)` | Allows approximate value for the aggregate of `approximate`
+
[2/6] calcite git commit: [CALCITE-2224] Support WITHIN GROUP clause
for aggregate functions (Hongze Zhang)
Posted by jh...@apache.org.
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/druid/src/main/java/org/apache/calcite/adapter/druid/DruidRules.java
----------------------------------------------------------------------
diff --git a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidRules.java b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidRules.java
index c385c02..b6a7270 100644
--- a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidRules.java
+++ b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidRules.java
@@ -595,13 +595,12 @@ public class DruidRules {
// Erase references to filters
for (AggregateCall aggCall : aggregate.getAggCallList()) {
- int newFilterArg = aggCall.filterArg;
- if (!aggCall.hasFilter()
- || (uniqueFilterRefs.size() == 1 && allHaveFilters) // filters get extracted
- || project.getProjects().get(newFilterArg).isAlwaysTrue()) {
- newFilterArg = -1;
+ if ((uniqueFilterRefs.size() == 1
+ && allHaveFilters) // filters get extracted
+ || project.getProjects().get(aggCall.filterArg).isAlwaysTrue()) {
+ aggCall = aggCall.copy(aggCall.getArgList(), -1, aggCall.collation);
}
- newCalls.add(aggCall.copy(aggCall.getArgList(), newFilterArg));
+ newCalls.add(aggCall);
}
aggregate = aggregate.copy(aggregate.getTraitSet(), aggregate.getInput(),
aggregate.indicator, aggregate.getGroupSet(), aggregate.getGroupSets(),
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 98fd037..4d238ff 100644
--- a/pom.xml
+++ b/pom.xml
@@ -134,7 +134,7 @@ limitations under the License.
<slf4j.version>1.7.13</slf4j.version>
<log4j2.version>2.11.0</log4j2.version>
<spark.version>2.2.0</spark.version>
- <sqlline.version>1.5.0</sqlline.version>
+ <sqlline.version>1.6.0-SNAPSHOT</sqlline.version>
<tpcds.version>1.2</tpcds.version>
<xalan.version>2.7.1</xalan.version>
<xerces.version>2.9.1</xerces.version>
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/site/_docs/algebra.md
----------------------------------------------------------------------
diff --git a/site/_docs/algebra.md b/site/_docs/algebra.md
index e5b505b..8ff2339 100644
--- a/site/_docs/algebra.md
+++ b/site/_docs/algebra.md
@@ -216,7 +216,7 @@ If you have a particular `RelNode` instance, you can rely on the field names not
changing. In fact, the whole relational expression is immutable.
But if a relational expression has passed through several rewrite rules (see
-([RelOptRule]({{ site.apiRoot }}/org/apache/calcite/plan/RelOptRule.html)), the field
+[RelOptRule]({{ site.apiRoot }}/org/apache/calcite/plan/RelOptRule.html)), the field
names of the resulting expression might not look much like the originals.
At that point it is better to reference fields by ordinal.
@@ -315,7 +315,7 @@ expression on the stack:
| `as(alias)` | Assigns a table alias to the top relational expression on the stack
| `variable(varHolder)` | Creates a correlation variable referencing the top relational expression
-### Stack methods
+#### Stack methods
| Method | Description
|:------------------- |:-----------
@@ -372,7 +372,7 @@ The following methods return patterns for use in `match`.
| `patternPermute(pattern...)` | Permutes a pattern
| `patternExclude(pattern)` | Excludes a pattern
-### Group key methods
+#### Group key methods
The following methods return a
[RelBuilder.GroupKey]({{ site.apiRoot }}/org/apache/calcite/tools/RelBuilder.GroupKey.html).
@@ -383,28 +383,28 @@ The following methods return a
| `groupKey(exprList, exprListList)` | Creates a group key of the given expressions with grouping sets
| `groupKey(bitSet [, bitSets])` | Creates a group key of the given input columns, with multiple grouping sets if `bitSets` is specified
-### Aggregate call methods
+#### Aggregate call methods
The following methods return an
[RelBuilder.AggCall]({{ site.apiRoot }}/org/apache/calcite/tools/RelBuilder.AggCall.html).
| Method | Description
|:------------------- |:-----------
-| `aggregateCall(op, distinct, approximate, filter, orderKeys, alias, expr...)`<br/>`aggregateCall(op, distinct, approximate, filter, orderKeys, alias, exprList)` | Creates a call to a given aggregate function, with an optional filter expression and a list of optional ordering keys (for sorting input values before aggregation)
-| `count( [ distinct, alias, ] expr...)` | Creates a call to the COUNT aggregate function
-| `countStar(alias)` | Creates a call to the COUNT(*) aggregate function
-| `sum( [ distinct, alias, ] expr)` | Creates a call to the SUM aggregate function
-| `min( [ alias, ] expr)` | Creates a call to the MIN aggregate function
-| `max( [ alias, ] expr)` | Creates a call to the MAX aggregate function
+| `aggregateCall(op, expr...)`<br/>`aggregateCall(op, exprList)` | Creates a call to a given aggregate function
+| `count([ distinct, alias, ] expr...)`<br/>`count([ distinct, alias, ] exprList)` | Creates a call to the `COUNT` aggregate function
+| `countStar(alias)` | Creates a call to the `COUNT(*)` aggregate function
+| `sum([ distinct, alias, ] expr)` | Creates a call to the `SUM` aggregate function
+| `min([ alias, ] expr)` | Creates a call to the `MIN` aggregate function
+| `max([ alias, ] expr)` | Creates a call to the `MAX` aggregate function
To further modify the `AggCall`, call its methods:
| Method | Description
|:-------------------- |:-----------
+| `approximate(approximate)` | Allows approximate value for the aggregate of `approximate`
+| `as(alias)` | Assigns a column alias to this expression (see SQL `AS`)
| `distinct()` | Eliminates duplicate values before aggregating (see SQL `DISTINCT`)
| `distinct(distinct)` | Eliminates duplicate values before aggregating if `distinct`
-| `as(alias)` | Assigns a column alias to this expression (see SQL `AS`)
| `filter(expr)` | Filters rows before aggregating (see SQL `FILTER (WHERE ...)`)
-| `sort(expr, ...)`<br/>`sort(exprList)` | Sorts rows before aggregating (see SQL `WITHIN GROUP`)
-| `approximate(approximate)` | Allows approximate value for the aggregate of `approximate`
+| `sort(expr...)`<br/>`sort(exprList)` | Sorts rows before aggregating (see SQL `WITHIN GROUP`)
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/site/_docs/reference.md
----------------------------------------------------------------------
diff --git a/site/_docs/reference.md b/site/_docs/reference.md
index 81d21c3..1e48292 100644
--- a/site/_docs/reference.md
+++ b/site/_docs/reference.md
@@ -1482,7 +1482,9 @@ Syntax:
{% highlight sql %}
aggregateCall:
- agg( [ ALL | DISTINCT ] value [, value ]*) [ FILTER (WHERE condition) ]
+ agg( [ ALL | DISTINCT ] value [, value ]*)
+ [ WITHIN GROUP (ORDER BY orderItem [, orderItem ]*) ]
+ [ FILTER (WHERE condition) ]
| agg(*) [ FILTER (WHERE condition) ]
{% endhighlight %}
@@ -1492,6 +1494,13 @@ If `FILTER` is present, the aggregate function only considers rows for which
If `DISTINCT` is present, duplicate argument values are eliminated before being
passed to the aggregate function.
+If `WITHIN GROUP` is present, the aggregate function sorts the input rows
+according to the `ORDER BY` clause inside `WITHIN GROUP` before aggregating
+values. `WITHIN GROUP` is only allowed for hypothetical set functions (`RANK`,
+`DENSE_RANK`, `PERCENT_RANK` and `CUME_DIST`), inverse distribution functions
+(`PERCENTILE_CONT` and `PERCENTILE_DISC`) and collection functions (`COLLECT`
+and `LISTAGG`).
+
| Operator syntax | Description
|:---------------------------------- |:-----------
| COLLECT( [ ALL | DISTINCT ] value) | Returns a multiset of the values
@@ -1516,6 +1525,7 @@ passed to the aggregate function.
Not implemented:
+* LISTAGG(string)
* REGR_AVGX(numeric1, numeric2)
* REGR_AVGY(numeric1, numeric2)
* REGR_INTERCEPT(numeric1, numeric2)
[6/6] calcite git commit: [CALCITE-2652] SqlNode to SQL conversion
fails if the join condition references a BOOLEAN column (Zoltan Haindrich)
Posted by jh...@apache.org.
[CALCITE-2652] SqlNode to SQL conversion fails if the join condition references a BOOLEAN column (Zoltan Haindrich)
Previously when a join ON condition have contained an exact reference it ended up with an exception.
Close apache/calcite#899
Project: http://git-wip-us.apache.org/repos/asf/calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/ebc43d92
Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/ebc43d92
Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/ebc43d92
Branch: refs/heads/master
Commit: ebc43d9288dca1990992b16a1fa8bbfdd193f939
Parents: 042fa6b
Author: Zoltan Haindrich <ki...@rxd.hu>
Authored: Thu Nov 1 23:15:57 2018 +0100
Committer: Julian Hyde <jh...@apache.org>
Committed: Thu Nov 8 10:00:37 2018 -0800
----------------------------------------------------------------------
.../org/apache/calcite/rel/rel2sql/SqlImplementor.java | 4 ++++
.../calcite/rel/rel2sql/RelToSqlConverterTest.java | 13 +++++++++++++
2 files changed, 17 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/calcite/blob/ebc43d92/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java b/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java
index 1de1255..99e9220 100644
--- a/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java
+++ b/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java
@@ -186,6 +186,10 @@ public abstract class SqlImplementor {
if (node.isAlwaysFalse()) {
return SqlLiteral.createBoolean(false, POS);
}
+ if (node instanceof RexInputRef) {
+ Context joinContext = leftContext.implementor().joinContext(leftContext, rightContext);
+ return joinContext.toSql(null, node);
+ }
if (!(node instanceof RexCall)) {
throw new AssertionError(node);
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/ebc43d92/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
index 7a5181e..351550a 100644
--- a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
+++ b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
@@ -61,6 +61,7 @@ import java.util.function.Function;
import static org.apache.calcite.test.Matchers.isLinux;
import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
import static org.junit.Assert.assertThat;
/**
@@ -990,6 +991,18 @@ public class RelToSqlConverterTest {
sql(query).ok(expected);
}
+ /** Test case for
+ * <a href="https://issues.apache.org/jira/browse/CALCITE-2652">[CALCITE-2652]
+ * SqlNode to SQL conversion fails if the join condition references a BOOLEAN
+ * column</a>. */
+ @Test public void testJoinOnBoolean() {
+ final String sql = "SELECT 1\n"
+ + "from emps\n"
+ + "join emp on (emp.deptno = emps.empno and manager)";
+ final String s = sql(sql).schema(CalciteAssert.SchemaSpec.POST).exec();
+ assertThat(s, notNullValue()); // sufficient that conversion did not throw
+ }
+
@Test public void testCartesianProductWithInnerJoinSyntax() {
String query = "select * from \"department\"\n"
+ "INNER JOIN \"employee\" ON TRUE";
[4/6] calcite git commit: [CALCITE-2224] Support WITHIN GROUP clause
for aggregate functions (Hongze Zhang)
Posted by jh...@apache.org.
[CALCITE-2224] Support WITHIN GROUP clause for aggregate functions (Hongze Zhang)
Close apache/calcite#871
Project: http://git-wip-us.apache.org/repos/asf/calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/7bc9f140
Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/7bc9f140
Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/7bc9f140
Branch: refs/heads/master
Commit: 7bc9f14032b7cf0761c0b2eefdb6bb588047ec8e
Parents: 4cc4613
Author: hongzezhang <ho...@tencent.com>
Authored: Thu Sep 27 17:55:02 2018 +0800
Committer: Julian Hyde <jh...@apache.org>
Committed: Thu Nov 8 10:00:28 2018 -0800
----------------------------------------------------------------------
core/src/main/codegen/templates/Parser.jj | 15 +-
.../calcite/adapter/enumerable/AggImpState.java | 1 +
.../enumerable/AggregateLambdaFactory.java | 48 +++++
.../adapter/enumerable/EnumerableAggregate.java | 195 ++++++++++++++-----
.../OrderedAggregateLambdaFactory.java | 105 ++++++++++
.../SequencedAdderAggregateLambdaFactory.java | 88 +++++++++
.../adapter/enumerable/SourceSorter.java | 60 ++++++
.../org/apache/calcite/plan/RelOptUtil.java | 10 +-
.../calcite/plan/SubstitutionVisitor.java | 13 +-
.../calcite/prepare/CalciteCatalogReader.java | 4 +-
.../org/apache/calcite/rel/RelCollations.java | 20 ++
.../apache/calcite/rel/core/AggregateCall.java | 112 ++++++++---
.../org/apache/calcite/rel/core/Window.java | 1 +
.../calcite/rel/externalize/RelJsonReader.java | 5 +-
.../calcite/rel/rel2sql/SqlImplementor.java | 51 +++--
.../rel/rules/AbstractMaterializedViewRule.java | 2 +
.../AggregateExpandDistinctAggregatesRule.java | 27 +--
.../rel/rules/AggregateExtractProjectRule.java | 2 +-
.../rel/rules/AggregateFilterTransposeRule.java | 1 +
.../rel/rules/AggregateProjectMergeRule.java | 59 +++---
.../rel/rules/AggregateReduceFunctionsRule.java | 12 +-
.../rel/rules/AggregateStarTableRule.java | 3 +-
.../rel/rules/AggregateUnionTransposeRule.java | 8 +-
.../calcite/rel/rules/SubQueryRemoveRule.java | 3 +-
.../java/org/apache/calcite/rex/RexBuilder.java | 3 +-
.../apache/calcite/runtime/CalciteResource.java | 12 ++
.../org/apache/calcite/sql/SqlAggFunction.java | 59 +++++-
.../apache/calcite/sql/SqlFilterOperator.java | 47 +++--
.../java/org/apache/calcite/sql/SqlKind.java | 5 +
.../org/apache/calcite/sql/SqlRankFunction.java | 3 +-
.../calcite/sql/SqlSplittableAggFunction.java | 16 +-
.../calcite/sql/SqlWithinGroupOperator.java | 85 ++++++++
.../sql/fun/SqlAbstractGroupFunction.java | 3 +-
.../calcite/sql/fun/SqlAnyValueAggFunction.java | 4 +-
.../calcite/sql/fun/SqlAvgAggFunction.java | 7 +-
.../calcite/sql/fun/SqlCountAggFunction.java | 7 +-
.../calcite/sql/fun/SqlCovarAggFunction.java | 4 +-
.../sql/fun/SqlFirstLastValueAggFunction.java | 4 +-
.../sql/fun/SqlHistogramAggFunction.java | 4 +-
.../calcite/sql/fun/SqlLeadLagAggFunction.java | 4 +-
.../calcite/sql/fun/SqlMinMaxAggFunction.java | 4 +-
.../calcite/sql/fun/SqlNthValueAggFunction.java | 3 +-
.../calcite/sql/fun/SqlNtileAggFunction.java | 4 +-
.../sql/fun/SqlSingleValueAggFunction.java | 4 +-
.../calcite/sql/fun/SqlStdOperatorTable.java | 11 +-
.../calcite/sql/fun/SqlSumAggFunction.java | 4 +-
.../sql/fun/SqlSumEmptyIsZeroAggFunction.java | 4 +-
.../apache/calcite/sql/validate/AggChecker.java | 4 +
.../sql/validate/SqlUserDefinedAggFunction.java | 7 +-
.../calcite/sql/validate/SqlValidator.java | 8 +-
.../calcite/sql/validate/SqlValidatorImpl.java | 39 +++-
.../apache/calcite/sql2rel/RelFieldTrimmer.java | 7 +-
.../calcite/sql2rel/SqlToRelConverter.java | 67 ++++++-
.../org/apache/calcite/tools/RelBuilder.java | 65 +++++--
.../org/apache/calcite/util/BuiltInMethod.java | 20 +-
.../org/apache/calcite/util/Optionality.java | 41 ++++
.../calcite/runtime/CalciteResource.properties | 4 +
.../org/apache/calcite/plan/RelWriterTest.java | 5 +-
.../plan/volcano/TraitPropagationTest.java | 3 +-
.../rel/rel2sql/RelToSqlConverterTest.java | 51 ++++-
.../calcite/sql/parser/SqlParserTest.java | 58 ++++++
.../calcite/sql/test/SqlOperatorBaseTest.java | 2 +
.../java/org/apache/calcite/test/JdbcTest.java | 75 +++++++
.../apache/calcite/test/RelMetadataTest.java | 2 +-
.../calcite/test/SqlToRelConverterTest.java | 28 +++
.../apache/calcite/test/SqlValidatorTest.java | 35 ++++
.../org/apache/calcite/tools/PlannerTest.java | 4 +-
.../calcite/test/SqlToRelConverterTest.xml | 38 ++++
core/src/test/resources/sql/agg.iq | 123 ++++++++++++
.../calcite/adapter/druid/DruidRules.java | 11 +-
pom.xml | 2 +-
site/_docs/algebra.md | 26 +--
site/_docs/reference.md | 12 +-
73 files changed, 1612 insertions(+), 271 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/codegen/templates/Parser.jj
----------------------------------------------------------------------
diff --git a/core/src/main/codegen/templates/Parser.jj b/core/src/main/codegen/templates/Parser.jj
index bcc9e56..7bfc02c 100644
--- a/core/src/main/codegen/templates/Parser.jj
+++ b/core/src/main/codegen/templates/Parser.jj
@@ -4976,6 +4976,8 @@ SqlNode NamedFunctionCall() :
final SqlNode filter;
final SqlNode over;
SqlLiteral quantifier = null;
+ SqlNodeList orderList = null;
+ final Span withinGroupSpan;
}
{
(
@@ -5004,10 +5006,19 @@ SqlNode NamedFunctionCall() :
}
)
{
- call = createCall(qualifiedName, s.end(this), funcType, quantifier,
- args);
+ call = createCall(qualifiedName, s.end(this), funcType, quantifier, args);
}
[
+ <WITHIN> { withinGroupSpan = span(); }
+ <GROUP>
+ <LPAREN>
+ orderList = OrderBy(true)
+ <RPAREN> {
+ call = SqlStdOperatorTable.WITHIN_GROUP.createCall(
+ withinGroupSpan.end(this), call, orderList);
+ }
+ ]
+ [
<FILTER> { filterSpan = span(); }
<LPAREN>
<WHERE>
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/adapter/enumerable/AggImpState.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/AggImpState.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/AggImpState.java
index b100cec..aac5277 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/AggImpState.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/AggImpState.java
@@ -31,6 +31,7 @@ public class AggImpState {
public AggContext context;
public Expression result;
public List<Expression> state;
+ public Expression accumulatorAdder;
public AggImpState(int aggIdx, AggregateCall call, boolean windowContext) {
this.aggIdx = aggIdx;
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/adapter/enumerable/AggregateLambdaFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/AggregateLambdaFactory.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/AggregateLambdaFactory.java
new file mode 100644
index 0000000..c5f87b4
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/AggregateLambdaFactory.java
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+package org.apache.calcite.adapter.enumerable;
+
+import org.apache.calcite.linq4j.function.Function0;
+import org.apache.calcite.linq4j.function.Function1;
+import org.apache.calcite.linq4j.function.Function2;
+
+/**
+ * Generates lambda functions used in {@link EnumerableAggregate}.
+ *
+ * <p>This interface allows a implicit accumulator type variation.
+ * ({@code OAccumulate} {@literal ->} {@code TAccumulate})
+ *
+ * @param <TSource> Type of the enumerable input source
+ * @param <TOrigAccumulate> Type of the original accumulator
+ * @param <TAccumulate> Type of the varied accumulator
+ * @param <TResult> Type of the enumerable output result
+ * @param <TKey> Type of the group-by key
+ */
+public interface AggregateLambdaFactory<TSource, TOrigAccumulate, TAccumulate,
+ TResult, TKey> {
+ Function0<TAccumulate> accumulatorInitializer();
+
+ Function2<TAccumulate, TSource, TAccumulate> accumulatorAdder();
+
+ Function1<TAccumulate, TResult> singleGroupResultSelector(
+ Function1<TOrigAccumulate, TResult> resultSelector);
+
+ Function2<TKey, TAccumulate, TResult> resultSelector(
+ Function2<TKey, TOrigAccumulate, TResult> resultSelector);
+}
+
+// End AggregateLambdaFactory.java
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableAggregate.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableAggregate.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableAggregate.java
index 49af4ba..1caf4e3 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableAggregate.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableAggregate.java
@@ -33,6 +33,7 @@ import org.apache.calcite.plan.RelOptCluster;
import org.apache.calcite.plan.RelTraitSet;
import org.apache.calcite.prepare.CalcitePrepareImpl;
import org.apache.calcite.rel.InvalidRelException;
+import org.apache.calcite.rel.RelCollations;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.core.Aggregate;
import org.apache.calcite.rel.core.AggregateCall;
@@ -52,6 +53,7 @@ import com.google.common.collect.ImmutableList;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.LinkedList;
import java.util.List;
/** Implementation of {@link org.apache.calcite.rel.core.Aggregate} in
@@ -231,37 +233,10 @@ public class EnumerableAggregate extends Aggregate implements EnumerableRel {
PhysTypeImpl.of(typeFactory,
typeFactory.createSyntheticType(aggStateTypes));
-
- if (accPhysType.getJavaRowType() instanceof JavaTypeFactoryImpl.SyntheticRecordType) {
- // We have to initialize the SyntheticRecordType instance this way, to avoid using
- // class constructor with too many parameters.
- JavaTypeFactoryImpl.SyntheticRecordType synType =
- (JavaTypeFactoryImpl.SyntheticRecordType)
- accPhysType.getJavaRowType();
- final ParameterExpression record0_ =
- Expressions.parameter(accPhysType.getJavaRowType(), "record0");
- initBlock.add(Expressions.declare(0, record0_, null));
- initBlock.add(
- Expressions.statement(
- Expressions.assign(record0_,
- Expressions.new_(accPhysType.getJavaRowType()))));
- List<Types.RecordField> fieldList = synType.getRecordFields();
- for (int i = 0; i < initExpressions.size(); i++) {
- Expression right = initExpressions.get(i);
- initBlock.add(
- Expressions.statement(
- Expressions.assign(
- Expressions.field(record0_, fieldList.get(i)),
- right)));
- }
- initBlock.add(record0_);
- } else {
- initBlock.add(accPhysType.record(initExpressions));
- }
+ declareParentAccumulator(initExpressions, initBlock, accPhysType);
final Expression accumulatorInitializer =
- builder.append(
- "accumulatorInitializer",
+ builder.append("accumulatorInitializer",
Expressions.lambda(
Function0.class,
initBlock.toBlock()));
@@ -274,12 +249,12 @@ public class EnumerableAggregate extends Aggregate implements EnumerableRel {
// return acc;
// }
// };
- final BlockBuilder builder2 = new BlockBuilder();
final ParameterExpression inParameter =
Expressions.parameter(inputPhysType.getJavaRowType(), "in");
final ParameterExpression acc_ =
Expressions.parameter(accPhysType.getJavaRowType(), "acc");
for (int i = 0, stateOffset = 0; i < aggs.size(); i++) {
+ final BlockBuilder builder2 = new BlockBuilder();
final AggImpState agg = aggs.get(i);
final int stateSize = agg.state.size();
@@ -315,24 +290,25 @@ public class EnumerableAggregate extends Aggregate implements EnumerableRel {
currentBlock(),
new RexToLixTranslator.InputGetterImpl(
Collections.singletonList(
- Pair.of((Expression) inParameter, inputPhysType))),
+ Pair.of(inParameter, inputPhysType))),
implementor.getConformance())
.setNullable(currentNullables());
}
};
agg.implementor.implementAdd(agg.context, addContext);
+ builder2.add(acc_);
+ agg.accumulatorAdder = builder.append("accumulatorAdder",
+ Expressions.lambda(Function2.class, builder2.toBlock(), acc_,
+ inParameter));
}
- builder2.add(acc_);
- final Expression accumulatorAdder =
- builder.append(
- "accumulatorAdder",
- Expressions.lambda(
- Function2.class,
- builder2.toBlock(),
- acc_,
- inParameter));
+ final ParameterExpression lambdaFactory =
+ Expressions.parameter(AggregateLambdaFactory.class,
+ builder.newName("lambdaFactory"));
+
+ implementLambdaFactory(builder, inputPhysType, aggs, accumulatorInitializer,
+ hasOrderedCall(aggs), lambdaFactory);
// Function2<Integer, Object[], Object[]> resultSelector =
// new Function2<Integer, Object[], Object[]>() {
// public Object[] apply(Integer key, Object[] acc) {
@@ -390,9 +366,13 @@ public class EnumerableAggregate extends Aggregate implements EnumerableRel {
BuiltInMethod.GROUP_BY_MULTIPLE.method,
Expressions.list(childExp,
keySelectors_,
- accumulatorInitializer,
- accumulatorAdder,
- resultSelector)
+ Expressions.call(lambdaFactory,
+ BuiltInMethod.AGG_LAMBDA_FACTORY_ACC_INITIALIZER.method),
+ Expressions.call(lambdaFactory,
+ BuiltInMethod.AGG_LAMBDA_FACTORY_ACC_ADDER.method),
+ Expressions.call(lambdaFactory,
+ BuiltInMethod.AGG_LAMBDA_FACTORY_ACC_RESULT_SELECTOR.method,
+ resultSelector))
.appendIfNotNull(keyPhysType.comparer()))));
} else if (groupCount == 0) {
final Expression resultSelector =
@@ -410,9 +390,15 @@ public class EnumerableAggregate extends Aggregate implements EnumerableRel {
Expressions.call(
childExp,
BuiltInMethod.AGGREGATE.method,
- Expressions.call(accumulatorInitializer, "apply"),
- accumulatorAdder,
- resultSelector))));
+ Expressions.call(
+ Expressions.call(lambdaFactory,
+ BuiltInMethod.AGG_LAMBDA_FACTORY_ACC_INITIALIZER.method),
+ BuiltInMethod.FUNCTION0_APPLY.method),
+ Expressions.call(lambdaFactory,
+ BuiltInMethod.AGG_LAMBDA_FACTORY_ACC_ADDER.method),
+ Expressions.call(lambdaFactory,
+ BuiltInMethod.AGG_LAMBDA_FACTORY_ACC_SINGLE_GROUP_RESULT_SELECTOR.method,
+ resultSelector)))));
} else if (aggCalls.isEmpty()
&& groupSet.equals(
ImmutableBitSet.range(child.getRowType().getFieldCount()))) {
@@ -441,14 +427,125 @@ public class EnumerableAggregate extends Aggregate implements EnumerableRel {
Expressions.call(childExp,
BuiltInMethod.GROUP_BY2.method,
Expressions.list(keySelector_,
- accumulatorInitializer,
- accumulatorAdder,
- resultSelector_)
+ Expressions.call(lambdaFactory,
+ BuiltInMethod.AGG_LAMBDA_FACTORY_ACC_INITIALIZER.method),
+ Expressions.call(lambdaFactory,
+ BuiltInMethod.AGG_LAMBDA_FACTORY_ACC_ADDER.method),
+ Expressions.call(lambdaFactory,
+ BuiltInMethod.AGG_LAMBDA_FACTORY_ACC_RESULT_SELECTOR.method,
+ resultSelector_))
.appendIfNotNull(keyPhysType.comparer()))));
}
return implementor.result(physType, builder.toBlock());
}
+ private static boolean hasOrderedCall(List<AggImpState> aggs) {
+ for (AggImpState agg : aggs) {
+ if (!agg.call.collation.equals(RelCollations.EMPTY)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void declareParentAccumulator(List<Expression> initExpressions,
+ BlockBuilder initBlock, PhysType accPhysType) {
+ if (accPhysType.getJavaRowType()
+ instanceof JavaTypeFactoryImpl.SyntheticRecordType) {
+ // We have to initialize the SyntheticRecordType instance this way, to
+ // avoid using a class constructor with too many parameters.
+ final JavaTypeFactoryImpl.SyntheticRecordType synType =
+ (JavaTypeFactoryImpl.SyntheticRecordType)
+ accPhysType.getJavaRowType();
+ final ParameterExpression record0_ =
+ Expressions.parameter(accPhysType.getJavaRowType(), "record0");
+ initBlock.add(Expressions.declare(0, record0_, null));
+ initBlock.add(
+ Expressions.statement(
+ Expressions.assign(record0_,
+ Expressions.new_(accPhysType.getJavaRowType()))));
+ List<Types.RecordField> fieldList = synType.getRecordFields();
+ for (int i = 0; i < initExpressions.size(); i++) {
+ Expression right = initExpressions.get(i);
+ initBlock.add(
+ Expressions.statement(
+ Expressions.assign(
+ Expressions.field(record0_, fieldList.get(i)), right)));
+ }
+ initBlock.add(record0_);
+ } else {
+ initBlock.add(accPhysType.record(initExpressions));
+ }
+ }
+
+ /**
+ * Implements the {@link AggregateLambdaFactory}.
+ *
+ * <p>Behavior depends upon ordering:
+ * <ul>
+ *
+ * <li>{@code hasOrderedCall == true} means there is at least one aggregate
+ * call including sort spec. We use {@link OrderedAggregateLambdaFactory}
+ * implementation to implement sorted aggregates for that.
+ *
+ * <li>{@code hasOrderedCall == false} indicates to use
+ * {@link SequencedAdderAggregateLambdaFactory} to implement a non-sort
+ * aggregate.
+ *
+ * </ul>
+ */
+ private void implementLambdaFactory(BlockBuilder builder,
+ PhysType inputPhysType,
+ List<AggImpState> aggs,
+ Expression accumulatorInitializer,
+ boolean hasOrderedCall,
+ ParameterExpression lambdaFactory) {
+ if (hasOrderedCall) {
+ ParameterExpression pe = Expressions.parameter(List.class,
+ builder.newName("sourceSorters"));
+ builder.add(
+ Expressions.declare(0, pe, Expressions.new_(LinkedList.class)));
+
+ for (AggImpState agg : aggs) {
+ if (agg.call.collation.equals(RelCollations.EMPTY)) {
+ continue;
+ }
+ final Pair<Expression, Expression> pair =
+ inputPhysType.generateCollationKey(
+ agg.call.collation.getFieldCollations());
+ builder.add(
+ Expressions.statement(
+ Expressions.call(pe,
+ BuiltInMethod.COLLECTION_ADD.method,
+ Expressions.new_(BuiltInMethod.SOURCE_SORTER.constructor,
+ agg.accumulatorAdder, pair.left, pair.right))));
+ }
+ builder.add(
+ Expressions.declare(0, lambdaFactory,
+ Expressions.new_(
+ BuiltInMethod.ORDERED_AGGREGATE_LAMBDA_FACTORY.constructor,
+ accumulatorInitializer, pe)));
+ } else {
+ // when hasOrderedCall == false
+ ParameterExpression pe = Expressions.parameter(List.class,
+ builder.newName("accumulatorAdders"));
+ builder.add(
+ Expressions.declare(0, pe, Expressions.new_(LinkedList.class)));
+
+ for (AggImpState agg : aggs) {
+ builder.add(
+ Expressions.statement(
+ Expressions.call(pe, BuiltInMethod.COLLECTION_ADD.method,
+ agg.accumulatorAdder)));
+ }
+ builder.add(
+ Expressions.declare(0, lambdaFactory,
+ Expressions.new_(
+ BuiltInMethod.SEQUENCED_ADDER_AGGREGATE_LAMBDA_FACTORY.constructor,
+ accumulatorInitializer, pe)));
+ }
+ }
+
/** An implementation of {@link AggContext}. */
private class AggContextImpl implements AggContext {
private final AggImpState agg;
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/adapter/enumerable/OrderedAggregateLambdaFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/OrderedAggregateLambdaFactory.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/OrderedAggregateLambdaFactory.java
new file mode 100644
index 0000000..3c7c72a
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/OrderedAggregateLambdaFactory.java
@@ -0,0 +1,105 @@
+/*
+ * 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.
+ */
+package org.apache.calcite.adapter.enumerable;
+
+import org.apache.calcite.linq4j.function.Function0;
+import org.apache.calcite.linq4j.function.Function1;
+import org.apache.calcite.linq4j.function.Function2;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Generate aggregate lambdas that sorts the input source before calling each
+ * aggregate adder.
+ *
+ * @param <TSource> Type of the enumerable input source
+ * @param <TKey> Type of the group-by key
+ * @param <TSortKey> Type of the sort key
+ * @param <TOrigAccumulate> Type of the original accumulator
+ * @param <TResult> Type of the enumerable output result
+ */
+public class OrderedAggregateLambdaFactory<TSource, TKey, TSortKey,
+ TOrigAccumulate, TResult>
+ implements AggregateLambdaFactory<TSource, TOrigAccumulate,
+ OrderedAggregateLambdaFactory.LazySource<TSource>, TResult, TKey> {
+
+ private final Function0<TOrigAccumulate> accumulatorInitializer;
+ private final List<SourceSorter<TOrigAccumulate, TSource, TSortKey>> sourceSorters;
+
+ public OrderedAggregateLambdaFactory(
+ Function0<TOrigAccumulate> accumulatorInitializer,
+ List<SourceSorter<TOrigAccumulate, TSource, TSortKey>> sourceSorters) {
+ this.accumulatorInitializer = accumulatorInitializer;
+ this.sourceSorters = sourceSorters;
+ }
+
+ public Function0<LazySource<TSource>> accumulatorInitializer() {
+ return LazySource::new;
+ }
+
+ public Function2<LazySource<TSource>,
+ TSource, LazySource<TSource>> accumulatorAdder() {
+ return (lazySource, source) -> {
+ lazySource.add(source);
+ return lazySource;
+ };
+ }
+
+ public Function1<LazySource<TSource>, TResult> singleGroupResultSelector(
+ Function1<TOrigAccumulate, TResult> resultSelector) {
+ return lazySource -> {
+ final TOrigAccumulate accumulator = accumulatorInitializer.apply();
+ for (SourceSorter<TOrigAccumulate, TSource, TSortKey> acc : sourceSorters) {
+ acc.sortAndAccumulate(lazySource, accumulator);
+ }
+ return resultSelector.apply(accumulator);
+ };
+ }
+
+ public Function2<TKey, LazySource<TSource>, TResult> resultSelector(
+ Function2<TKey, TOrigAccumulate, TResult> resultSelector) {
+ return (groupByKey, lazySource) -> {
+ final TOrigAccumulate accumulator = accumulatorInitializer.apply();
+ for (SourceSorter<TOrigAccumulate, TSource, TSortKey> acc : sourceSorters) {
+ acc.sortAndAccumulate(lazySource, accumulator);
+ }
+ return resultSelector.apply(groupByKey, accumulator);
+ };
+ }
+
+ /**
+ * Cache the input sources. (Will be sorted, aggregated in result selector.)
+ *
+ * @param <TSource> Type of the enumerable input source.
+ */
+ public static class LazySource<TSource> implements Iterable<TSource> {
+ private final List<TSource> list = new ArrayList<>();
+
+ private void add(TSource source) {
+ list.add(source);
+ }
+
+ @Override public Iterator<TSource> iterator() {
+ return list.iterator();
+ }
+ }
+
+}
+
+// End OrderedAggregateLambdaFactory.java
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/adapter/enumerable/SequencedAdderAggregateLambdaFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/SequencedAdderAggregateLambdaFactory.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/SequencedAdderAggregateLambdaFactory.java
new file mode 100644
index 0000000..4b4577c
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/SequencedAdderAggregateLambdaFactory.java
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+package org.apache.calcite.adapter.enumerable;
+
+import org.apache.calcite.linq4j.function.Function0;
+import org.apache.calcite.linq4j.function.Function1;
+import org.apache.calcite.linq4j.function.Function2;
+
+import java.util.List;
+
+/**
+ * Implementation of {@link AggregateLambdaFactory} that applies a sequence of
+ * accumulator adders to input source.
+ *
+ * @param <TSource> Type of the enumerable input source
+ * @param <TAccumulate> Type of the accumulator
+ * @param <TResult> Type of the enumerable output result
+ * @param <TKey> Type of the group-by key
+ */
+public class SequencedAdderAggregateLambdaFactory<TSource, TAccumulate, TResult, TKey>
+ implements AggregateLambdaFactory<TSource, TAccumulate, TAccumulate, TResult, TKey> {
+
+ private final Function0<TAccumulate> accumulatorInitializer;
+ private final Function2<TAccumulate, TSource, TAccumulate> accumulatorAdderDecorator;
+
+ public SequencedAdderAggregateLambdaFactory(
+ Function0<TAccumulate> accumulatorInitializer,
+ List<Function2<TAccumulate, TSource, TAccumulate>> accumulatorAdders) {
+ this.accumulatorInitializer = accumulatorInitializer;
+ this.accumulatorAdderDecorator = new AccumulatorAdderSeq(accumulatorAdders);
+ }
+
+ @Override public Function0<TAccumulate> accumulatorInitializer() {
+ return accumulatorInitializer;
+ }
+
+ @Override public Function2<TAccumulate, TSource, TAccumulate> accumulatorAdder() {
+ return accumulatorAdderDecorator;
+ }
+
+ @Override public Function1<TAccumulate, TResult> singleGroupResultSelector(
+ Function1<TAccumulate, TResult> resultSelector) {
+ return resultSelector;
+ }
+
+ @Override public Function2<TKey, TAccumulate, TResult> resultSelector(
+ Function2<TKey, TAccumulate, TResult> resultSelector) {
+ return resultSelector;
+ }
+
+ /**
+ * Decorator class of a sequence of accumulator adder functions.
+ */
+ private class AccumulatorAdderSeq
+ implements Function2<TAccumulate, TSource, TAccumulate> {
+ private final List<Function2<TAccumulate, TSource, TAccumulate>> accumulatorAdders;
+
+ AccumulatorAdderSeq(
+ List<Function2<TAccumulate, TSource, TAccumulate>> accumulatorAdders) {
+ this.accumulatorAdders = accumulatorAdders;
+ }
+
+ @Override public TAccumulate apply(TAccumulate accumulator, TSource source) {
+ TAccumulate result = accumulator;
+ for (Function2<TAccumulate, TSource, TAccumulate> accumulatorAdder
+ : accumulatorAdders) {
+ result = accumulatorAdder.apply(accumulator, source);
+ }
+ return result;
+ }
+ }
+}
+
+// End SequencedAdderAggregateLambdaFactory.java
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/adapter/enumerable/SourceSorter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/SourceSorter.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/SourceSorter.java
new file mode 100644
index 0000000..9345da4
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/SourceSorter.java
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+package org.apache.calcite.adapter.enumerable;
+
+import org.apache.calcite.linq4j.Linq4j;
+import org.apache.calcite.linq4j.function.Function1;
+import org.apache.calcite.linq4j.function.Function2;
+
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Helper that combines the sorting process and accumulating process against the
+ * aggregate execution, used with {@link OrderedAggregateLambdaFactory}.
+ *
+ * @param <TAccumulate> Type of the accumulator
+ * @param <TSource> Type of the enumerable input source
+ * @param <TSortKey> Type of the sort key
+ */
+public class SourceSorter<TAccumulate, TSource, TSortKey> {
+ private final Function2<TAccumulate, TSource, TAccumulate> accumulatorAdder;
+ private final Function1<TSource, TSortKey> keySelector;
+ private final Comparator<TSortKey> comparator;
+
+ public SourceSorter(
+ Function2<TAccumulate, TSource, TAccumulate> accumulatorAdder,
+ Function1<TSource, TSortKey> keySelector,
+ Comparator<TSortKey> comparator) {
+ this.accumulatorAdder = accumulatorAdder;
+ this.keySelector = keySelector;
+ this.comparator = comparator;
+ }
+
+ public void sortAndAccumulate(Iterable<TSource> sourceIterable,
+ TAccumulate accumulator) {
+ List<TSource> sorted = Linq4j.asEnumerable(sourceIterable)
+ .orderBy(keySelector, comparator)
+ .toList();
+ TAccumulate accumulator1 = accumulator;
+ for (TSource source : sorted) {
+ accumulator1 = accumulatorAdder.apply(accumulator1, source);
+ }
+ }
+}
+
+// End SourceSorter.java
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java b/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
index cda1e66..e15e491 100644
--- a/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
+++ b/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
@@ -18,6 +18,7 @@ package org.apache.calcite.plan;
import org.apache.calcite.avatica.AvaticaConnection;
import org.apache.calcite.linq4j.Ord;
+import org.apache.calcite.rel.RelCollations;
import org.apache.calcite.rel.RelHomogeneousShuttle;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.RelShuttle;
@@ -475,6 +476,7 @@ public abstract class RelOptUtil {
false,
ImmutableList.of(0),
-1,
+ RelCollations.EMPTY,
0,
ret,
null,
@@ -567,6 +569,7 @@ public abstract class RelOptUtil {
false,
ImmutableList.of(projectedKeyCount),
-1,
+ RelCollations.EMPTY,
projectedKeyCount,
ret,
null,
@@ -765,15 +768,14 @@ public abstract class RelOptUtil {
public static RelNode createSingleValueAggRel(
RelOptCluster cluster,
RelNode rel) {
- // assert (rel.getRowType().getFieldCount() == 1);
final int aggCallCnt = rel.getRowType().getFieldCount();
final List<AggregateCall> aggCalls = new ArrayList<>();
for (int i = 0; i < aggCallCnt; i++) {
aggCalls.add(
- AggregateCall.create(
- SqlStdOperatorTable.SINGLE_VALUE, false, false,
- ImmutableList.of(i), -1, 0, rel, null, null));
+ AggregateCall.create(SqlStdOperatorTable.SINGLE_VALUE, false, false,
+ ImmutableList.of(i), -1, RelCollations.EMPTY, 0, rel, null,
+ null));
}
return LogicalAggregate.create(rel, ImmutableBitSet.of(), null, aggCalls);
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java b/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java
index f37d3cd..4212fae 100644
--- a/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java
+++ b/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java
@@ -61,7 +61,6 @@ import org.apache.calcite.util.trace.CalciteTrace;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.LinkedHashMultimap;
-import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
@@ -1247,17 +1246,10 @@ public class SubstitutionVisitor {
ImmutableList<ImmutableBitSet> groupSets =
Mappings.apply2(mapping, aggregate.groupSets);
List<AggregateCall> aggregateCalls =
- apply(mapping, aggregate.aggCalls);
+ Util.transform(aggregate.aggCalls, call -> call.transform(mapping));
return MutableAggregate.of(input, groupSet, groupSets, aggregateCalls);
}
- private static List<AggregateCall> apply(final Mapping mapping,
- List<AggregateCall> aggCallList) {
- return Lists.transform(aggCallList,
- call -> call.copy(Mappings.apply2(mapping, call.getArgList()),
- Mappings.apply(mapping, call.filterArg)));
- }
-
public static MutableRel unifyAggregates(MutableAggregate query,
MutableAggregate target) {
if (query.getGroupType() != Aggregate.Group.SIMPLE
@@ -1304,7 +1296,8 @@ public class SubstitutionVisitor {
AggregateCall.create(getRollup(aggregateCall.getAggregation()),
aggregateCall.isDistinct(), aggregateCall.isApproximate(),
ImmutableList.of(target.groupSet.cardinality() + i), -1,
- aggregateCall.type, aggregateCall.name));
+ aggregateCall.collation, aggregateCall.type,
+ aggregateCall.name));
}
result = MutableAggregate.of(target, groupSet.build(), null,
aggregateCalls);
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/prepare/CalciteCatalogReader.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/prepare/CalciteCatalogReader.java b/core/src/main/java/org/apache/calcite/prepare/CalciteCatalogReader.java
index cbfd145..9685239 100644
--- a/core/src/main/java/org/apache/calcite/prepare/CalciteCatalogReader.java
+++ b/core/src/main/java/org/apache/calcite/prepare/CalciteCatalogReader.java
@@ -60,6 +60,7 @@ import org.apache.calcite.sql.validate.SqlUserDefinedFunction;
import org.apache.calcite.sql.validate.SqlUserDefinedTableFunction;
import org.apache.calcite.sql.validate.SqlUserDefinedTableMacro;
import org.apache.calcite.sql.validate.SqlValidatorUtil;
+import org.apache.calcite.util.Optionality;
import org.apache.calcite.util.Util;
import com.google.common.collect.ImmutableList;
@@ -324,7 +325,8 @@ public class CalciteCatalogReader implements Prepare.CatalogReader {
} else if (function instanceof AggregateFunction) {
return new SqlUserDefinedAggFunction(name,
infer((AggregateFunction) function), InferTypes.explicit(argTypes),
- typeChecker, (AggregateFunction) function, false, false, typeFactory);
+ typeChecker, (AggregateFunction) function, false, false,
+ Optionality.FORBIDDEN, typeFactory);
} else if (function instanceof TableMacro) {
return new SqlUserDefinedTableMacro(name, ReturnTypes.CURSOR,
InferTypes.explicit(argTypes), typeChecker, paramTypes,
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/rel/RelCollations.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/RelCollations.java b/core/src/main/java/org/apache/calcite/rel/RelCollations.java
index c3b16c6..5d7c7bb 100644
--- a/core/src/main/java/org/apache/calcite/rel/RelCollations.java
+++ b/core/src/main/java/org/apache/calcite/rel/RelCollations.java
@@ -19,6 +19,7 @@ package org.apache.calcite.rel;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.util.ImmutableIntList;
import org.apache.calcite.util.Util;
+import org.apache.calcite.util.mapping.Mappings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
@@ -26,6 +27,7 @@ import com.google.common.collect.Lists;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import java.util.Set;
/**
@@ -186,6 +188,24 @@ public class RelCollations {
}
return new RelCollationImpl(fieldCollations.build());
}
+
+ /** Creates a copy of this collation that changes the ordinals of input
+ * fields. */
+ public static RelCollation permute(RelCollation collation,
+ Map<Integer, Integer> mapping) {
+ return of(
+ Util.transform(collation.getFieldCollations(),
+ fc -> fc.copy(mapping.get(fc.getFieldIndex()))));
+ }
+
+ /** Creates a copy of this collation that changes the ordinals of input
+ * fields. */
+ public static RelCollation permute(RelCollation collation,
+ Mappings.TargetMapping mapping) {
+ return of(
+ Util.transform(collation.getFieldCollations(),
+ fc -> fc.copy(mapping.getTarget(fc.getFieldIndex()))));
+ }
}
// End RelCollations.java
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/rel/core/AggregateCall.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/core/AggregateCall.java b/core/src/main/java/org/apache/calcite/rel/core/AggregateCall.java
index 5df2a51..fb32cbc 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/AggregateCall.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/AggregateCall.java
@@ -16,6 +16,8 @@
*/
package org.apache.calcite.rel.core;
+import org.apache.calcite.rel.RelCollation;
+import org.apache.calcite.rel.RelCollations;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeFactory;
@@ -30,8 +32,8 @@ import java.util.List;
import java.util.Objects;
/**
- * Call to an aggFunction function within an
- * {@link org.apache.calcite.rel.logical.LogicalAggregate}.
+ * Call to an aggregate function within an
+ * {@link org.apache.calcite.rel.core.Aggregate}.
*/
public class AggregateCall {
//~ Instance fields --------------------------------------------------------
@@ -47,6 +49,7 @@ public class AggregateCall {
// since all values are small, ImmutableList uses cached Integer values.
private final ImmutableList<Integer> argList;
public final int filterArg;
+ public final RelCollation collation;
//~ Constructors -----------------------------------------------------------
@@ -66,7 +69,8 @@ public class AggregateCall {
List<Integer> argList,
RelDataType type,
String name) {
- this(aggFunction, distinct, false, argList, -1, type, name);
+ this(aggFunction, distinct, false,
+ argList, -1, RelCollations.EMPTY, type, name);
}
/**
@@ -76,23 +80,22 @@ public class AggregateCall {
* @param distinct Whether distinct
* @param approximate Whether approximate
* @param argList List of ordinals of arguments
- * @param filterArg Ordinal of filter argument, or -1
+ * @param filterArg Ordinal of filter argument (the
+ * {@code FILTER (WHERE ...)} clause in SQL), or -1
+ * @param collation How to sort values before aggregation (the
+ * {@code WITHIN GROUP} clause in SQL)
* @param type Result type
* @param name Name (may be null)
*/
- private AggregateCall(
- SqlAggFunction aggFunction,
- boolean distinct,
- boolean approximate,
- List<Integer> argList,
- int filterArg,
- RelDataType type,
- String name) {
+ private AggregateCall(SqlAggFunction aggFunction, boolean distinct,
+ boolean approximate, List<Integer> argList, int filterArg,
+ RelCollation collation, RelDataType type, String name) {
this.type = Objects.requireNonNull(type);
this.name = name;
this.aggFunction = Objects.requireNonNull(aggFunction);
this.argList = ImmutableList.copyOf(argList);
this.filterArg = filterArg;
+ this.collation = Objects.requireNonNull(collation);
this.distinct = distinct;
this.approximate = approximate;
}
@@ -103,23 +106,32 @@ public class AggregateCall {
public static AggregateCall create(SqlAggFunction aggFunction,
boolean distinct, List<Integer> argList, int groupCount, RelNode input,
RelDataType type, String name) {
- return create(aggFunction, distinct, false, argList, -1, groupCount, input,
- type, name);
+ return create(aggFunction, distinct, false, argList, -1,
+ RelCollations.EMPTY, groupCount, input, type, name);
}
@Deprecated // to be removed before 2.0
public static AggregateCall create(SqlAggFunction aggFunction,
boolean distinct, List<Integer> argList, int filterArg, int groupCount,
RelNode input, RelDataType type, String name) {
- return create(aggFunction, distinct, false, argList, -1, groupCount, input,
- type, name);
+ return create(aggFunction, distinct, false, argList, filterArg,
+ RelCollations.EMPTY, groupCount, input, type, name);
}
- /** Creates an AggregateCall, inferring its type if {@code type} is null. */
+ @Deprecated // to be removed before 2.0
public static AggregateCall create(SqlAggFunction aggFunction,
boolean distinct, boolean approximate, List<Integer> argList,
int filterArg, int groupCount,
RelNode input, RelDataType type, String name) {
+ return create(aggFunction, distinct, approximate, argList,
+ filterArg, RelCollations.EMPTY, groupCount, input, type, name);
+ }
+
+ /** Creates an AggregateCall, inferring its type if {@code type} is null. */
+ public static AggregateCall create(SqlAggFunction aggFunction,
+ boolean distinct, boolean approximate, List<Integer> argList,
+ int filterArg, RelCollation collation, int groupCount,
+ RelNode input, RelDataType type, String name) {
if (type == null) {
final RelDataTypeFactory typeFactory =
input.getCluster().getTypeFactory();
@@ -130,23 +142,32 @@ public class AggregateCall {
groupCount, filterArg >= 0);
type = aggFunction.inferReturnType(callBinding);
}
- return create(aggFunction, distinct, approximate, argList, filterArg, type,
- name);
+ return create(aggFunction, distinct, approximate, argList, filterArg,
+ collation, type, name);
}
@Deprecated // to be removed before 2.0
public static AggregateCall create(SqlAggFunction aggFunction,
boolean distinct, List<Integer> argList, int filterArg, RelDataType type,
String name) {
- return create(aggFunction, distinct, false, argList, filterArg, type, name);
+ return create(aggFunction, distinct, false, argList, filterArg,
+ RelCollations.EMPTY, type, name);
}
- /** Creates an AggregateCall. */
+ @Deprecated // to be removed before 2.0
public static AggregateCall create(SqlAggFunction aggFunction,
boolean distinct, boolean approximate, List<Integer> argList,
int filterArg, RelDataType type, String name) {
+ return create(aggFunction, distinct, approximate, argList, filterArg,
+ RelCollations.EMPTY, type, name);
+ }
+
+ /** Creates an AggregateCall. */
+ public static AggregateCall create(SqlAggFunction aggFunction,
+ boolean distinct, boolean approximate, List<Integer> argList,
+ int filterArg, RelCollation collation, RelDataType type, String name) {
return new AggregateCall(aggFunction, distinct, approximate, argList,
- filterArg, type, name);
+ filterArg, collation, type, name);
}
/**
@@ -179,6 +200,16 @@ public class AggregateCall {
}
/**
+ * Returns the aggregate ordering definition (the {@code WITHIN GROUP} clause
+ * in SQL), or the empty list if not specified.
+ *
+ * @return ordering definition
+ */
+ public RelCollation getCollation() {
+ return collation;
+ }
+
+ /**
* Returns the ordinals of the arguments to this call.
*
* <p>The list is immutable.
@@ -216,8 +247,10 @@ public class AggregateCall {
if (Objects.equals(this.name, name)) {
return this;
}
- return new AggregateCall(aggFunction, distinct, approximate, argList,
- filterArg, type, name);
+ return new AggregateCall(aggFunction, distinct, approximate,
+ argList,
+ filterArg, RelCollations.EMPTY, type,
+ name);
}
public String toString() {
@@ -235,6 +268,11 @@ public class AggregateCall {
buf.append(arg);
}
buf.append(")");
+ if (!collation.equals(RelCollations.EMPTY)) {
+ buf.append(" WITHIN GROUP (");
+ buf.append(collation);
+ buf.append(")");
+ }
if (hasFilter()) {
buf.append(" FILTER $");
buf.append(filterArg);
@@ -257,11 +295,12 @@ public class AggregateCall {
return aggFunction.equals(other.aggFunction)
&& (distinct == other.distinct)
&& argList.equals(other.argList)
- && filterArg == other.filterArg;
+ && filterArg == other.filterArg
+ && Objects.equals(collation, other.collation);
}
@Override public int hashCode() {
- return Objects.hash(aggFunction, distinct, argList, filterArg);
+ return Objects.hash(aggFunction, distinct, argList, filterArg, collation);
}
/**
@@ -282,17 +321,27 @@ public class AggregateCall {
/**
* Creates an equivalent AggregateCall with new argument ordinals.
*
+ * @see #transform(Mappings.TargetMapping)
+ *
* @param args Arguments
* @return AggregateCall that suits new inputs and GROUP BY columns
*/
- public AggregateCall copy(List<Integer> args, int filterArg) {
+ public AggregateCall copy(List<Integer> args, int filterArg,
+ RelCollation collation) {
return new AggregateCall(aggFunction, distinct, approximate, args,
- filterArg, type, name);
+ filterArg, collation, type, name);
+ }
+
+ @Deprecated // to be removed before 2.0
+ public AggregateCall copy(List<Integer> args, int filterArg) {
+ // ignoring collation is error-prone
+ return copy(args, filterArg, collation);
}
@Deprecated // to be removed before 2.0
public AggregateCall copy(List<Integer> args) {
- return copy(args, filterArg);
+ // ignoring filterArg and collation is error-prone
+ return copy(args, filterArg, collation);
}
/**
@@ -317,14 +366,15 @@ public class AggregateCall {
? type
: null;
return create(aggFunction, distinct, approximate, argList, filterArg,
- newGroupKeyCount, input, newType, getName());
+ collation, newGroupKeyCount, input, newType, getName());
}
/** Creates a copy of this aggregate call, applying a mapping to its
* arguments. */
public AggregateCall transform(Mappings.TargetMapping mapping) {
return copy(Mappings.apply2((Mapping) mapping, argList),
- hasFilter() ? Mappings.apply(mapping, filterArg) : -1);
+ hasFilter() ? Mappings.apply(mapping, filterArg) : -1,
+ RelCollations.permute(collation, mapping));
}
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/rel/core/Window.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/core/Window.java b/core/src/main/java/org/apache/calcite/rel/core/Window.java
index 1e00bba..186f7d3 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/Window.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/Window.java
@@ -316,6 +316,7 @@ public abstract class Window extends SingleRel {
final SqlAggFunction op = (SqlAggFunction) aggCall.getOperator();
return AggregateCall.create(op, aggCall.distinct,
false, getProjectOrdinals(aggCall.getOperands()), -1,
+ RelCollations.EMPTY,
aggCall.getType(), fieldNames.get(aggCall.ordinal));
}
};
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/rel/externalize/RelJsonReader.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/externalize/RelJsonReader.java b/core/src/main/java/org/apache/calcite/rel/externalize/RelJsonReader.java
index 695ab0b..29c3f44 100644
--- a/core/src/main/java/org/apache/calcite/rel/externalize/RelJsonReader.java
+++ b/core/src/main/java/org/apache/calcite/rel/externalize/RelJsonReader.java
@@ -22,6 +22,7 @@ import org.apache.calcite.plan.RelOptSchema;
import org.apache.calcite.plan.RelOptTable;
import org.apache.calcite.plan.RelTraitSet;
import org.apache.calcite.rel.RelCollation;
+import org.apache.calcite.rel.RelCollations;
import org.apache.calcite.rel.RelDistribution;
import org.apache.calcite.rel.RelInput;
import org.apache.calcite.rel.RelNode;
@@ -280,7 +281,9 @@ public class RelJsonReader {
final RelDataType type =
relJson.toType(cluster.getTypeFactory(), jsonAggCall.get("type"));
return AggregateCall.create(aggregation, distinct, false, operands,
- filterOperand == null ? -1 : filterOperand, type, null);
+ filterOperand == null ? -1 : filterOperand,
+ RelCollations.EMPTY,
+ type, null);
}
private RelNode lookupInput(String jsonInput) {
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java b/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java
index 0323a52..1de1255 100644
--- a/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java
+++ b/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java
@@ -768,6 +768,23 @@ public abstract class SqlImplementor {
};
}
+ void addOrderItem(List<SqlNode> orderByList, RelFieldCollation field) {
+ if (field.nullDirection != RelFieldCollation.NullDirection.UNSPECIFIED) {
+ final boolean first =
+ field.nullDirection == RelFieldCollation.NullDirection.FIRST;
+ SqlNode nullDirectionNode =
+ dialect.emulateNullDirection(field(field.getFieldIndex()),
+ first, field.direction.isDescending());
+ if (nullDirectionNode != null) {
+ orderByList.add(nullDirectionNode);
+ field = new RelFieldCollation(field.getFieldIndex(),
+ field.getDirection(),
+ RelFieldCollation.NullDirection.UNSPECIFIED);
+ }
+ }
+ orderByList.add(toSql(field));
+ }
+
/** Converts a call to an aggregate function to an expression. */
public SqlNode toSql(AggregateCall aggCall) {
final SqlOperator op = aggCall.getAggregation();
@@ -778,16 +795,32 @@ public abstract class SqlImplementor {
final SqlLiteral qualifier =
aggCall.isDistinct() ? SqlSelectKeyword.DISTINCT.symbol(POS) : null;
final SqlNode[] operands = operandList.toArray(new SqlNode[0]);
+ List<SqlNode> orderByList = Expressions.list();
+ for (RelFieldCollation field : aggCall.collation.getFieldCollations()) {
+ addOrderItem(orderByList, field);
+ }
+ SqlNodeList orderList = new SqlNodeList(orderByList, POS);
if (op instanceof SqlSumEmptyIsZeroAggFunction) {
final SqlNode node =
- SqlStdOperatorTable.SUM.createCall(qualifier, POS, operands);
+ withOrder(
+ SqlStdOperatorTable.SUM.createCall(qualifier, POS, operands),
+ orderList);
return SqlStdOperatorTable.COALESCE.createCall(POS, node,
SqlLiteral.createExactNumeric("0", POS));
} else {
- return op.createCall(qualifier, POS, operands);
+ return withOrder(op.createCall(qualifier, POS, operands), orderList);
}
}
+ /** Wraps a call in a {@link SqlKind#WITHIN_GROUP} call, if
+ * {@code orderList} is non-empty. */
+ private SqlNode withOrder(SqlCall call, SqlNodeList orderList) {
+ if (orderList == null || orderList.size() == 0) {
+ return call;
+ }
+ return SqlStdOperatorTable.WITHIN_GROUP.createCall(POS, call, orderList);
+ }
+
/** Converts a collation to an ORDER BY item. */
public SqlNode toSql(RelFieldCollation collation) {
SqlNode node = field(collation.getFieldIndex());
@@ -1217,19 +1250,7 @@ public abstract class SqlImplementor {
public void addOrderItem(List<SqlNode> orderByList,
RelFieldCollation field) {
- if (field.nullDirection != RelFieldCollation.NullDirection.UNSPECIFIED) {
- boolean first = field.nullDirection == RelFieldCollation.NullDirection.FIRST;
- SqlNode nullDirectionNode =
- dialect.emulateNullDirection(context.field(field.getFieldIndex()),
- first, field.direction.isDescending());
- if (nullDirectionNode != null) {
- orderByList.add(nullDirectionNode);
- field = new RelFieldCollation(field.getFieldIndex(),
- field.getDirection(),
- RelFieldCollation.NullDirection.UNSPECIFIED);
- }
- }
- orderByList.add(context.toSql(field));
+ context.addOrderItem(orderByList, field);
}
public Result result() {
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/rel/rules/AbstractMaterializedViewRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AbstractMaterializedViewRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AbstractMaterializedViewRule.java
index 68a9382..b164fe0 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/AbstractMaterializedViewRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/AbstractMaterializedViewRule.java
@@ -1198,6 +1198,7 @@ public abstract class AbstractMaterializedViewRule extends RelOptRule {
rexBuilder.makeInputRef(relBuilder.peek(),
aggregate.getGroupCount() + i);
aggregateCalls.add(
+ // TODO: handle aggregate ordering
relBuilder.aggregateCall(rollupAgg, operand)
.distinct(aggCall.isDistinct())
.approximate(aggCall.isApproximate())
@@ -1469,6 +1470,7 @@ public abstract class AbstractMaterializedViewRule extends RelOptRule {
}
final RexInputRef operand = rexBuilder.makeInputRef(input, k);
aggregateCalls.add(
+ // TODO: handle aggregate ordering
relBuilder.aggregateCall(rollupAgg, operand)
.approximate(queryAggCall.isApproximate())
.distinct(queryAggCall.isDistinct())
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/rel/rules/AggregateExpandDistinctAggregatesRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateExpandDistinctAggregatesRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateExpandDistinctAggregatesRule.java
index 6d974d8..7175a5f 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateExpandDistinctAggregatesRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateExpandDistinctAggregatesRule.java
@@ -20,6 +20,7 @@ import org.apache.calcite.linq4j.Ord;
import org.apache.calcite.plan.Contexts;
import org.apache.calcite.plan.RelOptRule;
import org.apache.calcite.plan.RelOptRuleCall;
+import org.apache.calcite.rel.RelCollations;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.core.Aggregate;
import org.apache.calcite.rel.core.Aggregate.Group;
@@ -296,7 +297,7 @@ public final class AggregateExpandDistinctAggregatesRule extends RelOptRule {
final AggregateCall newCall =
AggregateCall.create(aggCall.getAggregation(), false,
aggCall.isApproximate(), aggCall.getArgList(), -1,
- bottomGroupSet.cardinality(),
+ aggCall.collation, bottomGroupSet.cardinality(),
relBuilder.peek(), null, aggCall.name);
bottomAggregateCalls.add(newCall);
}
@@ -324,6 +325,7 @@ public final class AggregateExpandDistinctAggregatesRule extends RelOptRule {
aggCall.isApproximate(),
newArgList,
-1,
+ aggCall.collation,
originalGroupSet.cardinality(),
relBuilder.peek(),
aggCall.getType(),
@@ -331,19 +333,18 @@ public final class AggregateExpandDistinctAggregatesRule extends RelOptRule {
} else {
// If aggregate B had a COUNT aggregate call the corresponding aggregate at
// aggregate A must be SUM. For other aggregates, it remains the same.
- final List<Integer> newArgs =
- Lists.newArrayList(bottomGroups.size()
- + nonDistinctAggCallProcessedSoFar);
+ final int arg = bottomGroups.size() + nonDistinctAggCallProcessedSoFar;
+ final List<Integer> newArgs = ImmutableList.of(arg);
if (aggCall.getAggregation().getKind() == SqlKind.COUNT) {
newCall =
AggregateCall.create(new SqlSumEmptyIsZeroAggFunction(), false,
- aggCall.isApproximate(), newArgs, -1,
+ aggCall.isApproximate(), newArgs, -1, aggCall.collation,
originalGroupSet.cardinality(), relBuilder.peek(),
aggCall.getType(), aggCall.getName());
} else {
newCall =
AggregateCall.create(aggCall.getAggregation(), false,
- aggCall.isApproximate(), newArgs, -1,
+ aggCall.isApproximate(), newArgs, -1, aggCall.collation,
originalGroupSet.cardinality(),
relBuilder.peek(), aggCall.getType(), aggCall.name);
}
@@ -409,8 +410,8 @@ public final class AggregateExpandDistinctAggregatesRule extends RelOptRule {
final int z = groupCount + distinctAggCalls.size();
distinctAggCalls.add(
AggregateCall.create(SqlStdOperatorTable.GROUPING, false, false,
- ImmutableIntList.copyOf(fullGroupSet), -1, groupSets.size(),
- relBuilder.peek(), null, "$g"));
+ ImmutableIntList.copyOf(fullGroupSet), -1, RelCollations.EMPTY,
+ groupSets.size(), relBuilder.peek(), null, "$g"));
for (Ord<ImmutableBitSet> groupSet : Ord.zip(groupSets)) {
filters.put(groupSet.e, z + groupSet.i);
}
@@ -455,8 +456,8 @@ public final class AggregateExpandDistinctAggregatesRule extends RelOptRule {
}
final AggregateCall newCall =
AggregateCall.create(aggregation, false, aggCall.isApproximate(),
- newArgList, newFilterArg, aggregate.getGroupCount(), distinct,
- null, aggCall.name);
+ newArgList, newFilterArg, aggCall.collation,
+ aggregate.getGroupCount(), distinct, null, aggCall.name);
newCalls.add(newCall);
}
@@ -665,8 +666,8 @@ public final class AggregateExpandDistinctAggregatesRule extends RelOptRule {
aggCall.filterArg >= 0 ? sourceOf.get(aggCall.filterArg) : -1;
final AggregateCall newAggCall =
AggregateCall.create(aggCall.getAggregation(), false,
- aggCall.isApproximate(), newArgs,
- newFilterArg, aggCall.getType(), aggCall.getName());
+ aggCall.isApproximate(), newArgs, newFilterArg, aggCall.collation,
+ aggCall.getType(), aggCall.getName());
assert refs.get(i) == null;
if (n == 0) {
refs.set(i,
@@ -754,7 +755,7 @@ public final class AggregateExpandDistinctAggregatesRule extends RelOptRule {
}
final AggregateCall newAggCall =
AggregateCall.create(aggCall.getAggregation(), false,
- aggCall.isApproximate(), newArgs, -1,
+ aggCall.isApproximate(), newArgs, -1, aggCall.collation,
aggCall.getType(), aggCall.getName());
newAggCalls.set(i, newAggCall);
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/rel/rules/AggregateExtractProjectRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateExtractProjectRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateExtractProjectRule.java
index 08b12dd..bd5a348 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateExtractProjectRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateExtractProjectRule.java
@@ -120,7 +120,7 @@ public class AggregateExtractProjectRule extends RelOptRule {
.distinct(aggCall.isDistinct())
.filter(filterArg)
.approximate(aggCall.isApproximate())
- .approximate(aggCall.isApproximate())
+ .sort(relBuilder.fields(aggCall.collation))
.as(aggCall.name));
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/rel/rules/AggregateFilterTransposeRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateFilterTransposeRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateFilterTransposeRule.java
index 5c0e531..9b54f08 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateFilterTransposeRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateFilterTransposeRule.java
@@ -145,6 +145,7 @@ public class AggregateFilterTransposeRule extends RelOptRule {
topAggCallList.add(
AggregateCall.create(rollup, aggregateCall.isDistinct(),
aggregateCall.isApproximate(), ImmutableList.of(i++), -1,
+ aggregateCall.collation,
aggregateCall.type, aggregateCall.name));
}
final Aggregate topAggregate =
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/rel/rules/AggregateProjectMergeRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateProjectMergeRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateProjectMergeRule.java
index c5ec515..925afbe 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateProjectMergeRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateProjectMergeRule.java
@@ -18,6 +18,7 @@ package org.apache.calcite.rel.rules;
import org.apache.calcite.plan.RelOptRule;
import org.apache.calcite.plan.RelOptRuleCall;
+import org.apache.calcite.rel.RelCollations;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.core.Aggregate;
import org.apache.calcite.rel.core.Aggregate.Group;
@@ -29,13 +30,17 @@ import org.apache.calcite.rex.RexNode;
import org.apache.calcite.tools.RelBuilder;
import org.apache.calcite.tools.RelBuilderFactory;
import org.apache.calcite.util.ImmutableBitSet;
+import org.apache.calcite.util.mapping.Mappings;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
/**
* Planner rule that recognizes a {@link org.apache.calcite.rel.core.Aggregate}
@@ -73,18 +78,26 @@ public class AggregateProjectMergeRule extends RelOptRule {
public static RelNode apply(RelOptRuleCall call, Aggregate aggregate,
Project project) {
- final List<Integer> newKeys = new ArrayList<>();
+ // Find all fields which we need to be straightforward field projections.
+ final Set<Integer> interestingFields = new TreeSet<>();
+ interestingFields.addAll(aggregate.getGroupSet().asList());
+ for (AggregateCall aggregateCall : aggregate.getAggCallList()) {
+ interestingFields.addAll(aggregateCall.getArgList());
+ if (aggregateCall.filterArg >= 0) {
+ interestingFields.add(aggregateCall.filterArg);
+ }
+ interestingFields.addAll(RelCollations.ordinals(aggregateCall.collation));
+ }
+
+ // Build the map from old to new; abort if any entry is not a
+ // straightforward field projection.
final Map<Integer, Integer> map = new HashMap<>();
- for (int key : aggregate.getGroupSet()) {
- final RexNode rex = project.getProjects().get(key);
- if (rex instanceof RexInputRef) {
- final int newKey = ((RexInputRef) rex).getIndex();
- newKeys.add(newKey);
- map.put(key, newKey);
- } else {
- // Cannot handle "GROUP BY expression"
+ for (int source : interestingFields) {
+ final RexNode rex = project.getProjects().get(source);
+ if (!(rex instanceof RexInputRef)) {
return null;
}
+ map.put(source, ((RexInputRef) rex).getIndex());
}
final ImmutableBitSet newGroupSet = aggregate.getGroupSet().permute(map);
@@ -97,28 +110,12 @@ public class AggregateProjectMergeRule extends RelOptRule {
final ImmutableList.Builder<AggregateCall> aggCalls =
ImmutableList.builder();
+ final int sourceCount = aggregate.getInput().getRowType().getFieldCount();
+ final int targetCount = project.getInput().getRowType().getFieldCount();
+ final Mappings.TargetMapping targetMapping =
+ Mappings.target(map, sourceCount, targetCount);
for (AggregateCall aggregateCall : aggregate.getAggCallList()) {
- final ImmutableList.Builder<Integer> newArgs = ImmutableList.builder();
- for (int arg : aggregateCall.getArgList()) {
- final RexNode rex = project.getProjects().get(arg);
- if (rex instanceof RexInputRef) {
- newArgs.add(((RexInputRef) rex).getIndex());
- } else {
- // Cannot handle "AGG(expression)"
- return null;
- }
- }
- final int newFilterArg;
- if (aggregateCall.filterArg >= 0) {
- final RexNode rex = project.getProjects().get(aggregateCall.filterArg);
- if (!(rex instanceof RexInputRef)) {
- return null;
- }
- newFilterArg = ((RexInputRef) rex).getIndex();
- } else {
- newFilterArg = -1;
- }
- aggCalls.add(aggregateCall.copy(newArgs.build(), newFilterArg));
+ aggCalls.add(aggregateCall.transform(targetMapping));
}
final Aggregate newAggregate =
@@ -130,6 +127,8 @@ public class AggregateProjectMergeRule extends RelOptRule {
// contains duplicates.
final RelBuilder relBuilder = call.builder();
relBuilder.push(newAggregate);
+ final List<Integer> newKeys =
+ Lists.transform(aggregate.getGroupSet().asList(), map::get);
if (!newKeys.equals(newGroupSet.asList())) {
final List<Integer> posList = new ArrayList<>();
for (int newKey : newKeys) {
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/rel/rules/AggregateReduceFunctionsRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateReduceFunctionsRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateReduceFunctionsRule.java
index 68f6b16..2c9c4e5 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateReduceFunctionsRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateReduceFunctionsRule.java
@@ -316,6 +316,7 @@ public class AggregateReduceFunctionsRule extends RelOptRule {
oldCall.isApproximate(),
ImmutableIntList.of(argOrdinal),
filter,
+ oldCall.collation,
aggFunction.inferReturnType(binding),
null);
}
@@ -339,6 +340,7 @@ public class AggregateReduceFunctionsRule extends RelOptRule {
oldCall.isApproximate(),
oldCall.getArgList(),
oldCall.filterArg,
+ oldCall.collation,
oldAggRel.getGroupCount(),
oldAggRel.getInput(),
null,
@@ -349,6 +351,7 @@ public class AggregateReduceFunctionsRule extends RelOptRule {
oldCall.isApproximate(),
oldCall.getArgList(),
oldCall.filterArg,
+ oldCall.collation,
oldAggRel.getGroupCount(),
oldAggRel.getInput(),
null,
@@ -396,14 +399,15 @@ public class AggregateReduceFunctionsRule extends RelOptRule {
final AggregateCall sumZeroCall =
AggregateCall.create(SqlStdOperatorTable.SUM0, oldCall.isDistinct(),
oldCall.isApproximate(), oldCall.getArgList(), oldCall.filterArg,
- oldAggRel.getGroupCount(), oldAggRel.getInput(), null,
- oldCall.name);
+ oldCall.collation, oldAggRel.getGroupCount(), oldAggRel.getInput(),
+ null, oldCall.name);
final AggregateCall countCall =
AggregateCall.create(SqlStdOperatorTable.COUNT,
oldCall.isDistinct(),
oldCall.isApproximate(),
oldCall.getArgList(),
oldCall.filterArg,
+ oldCall.collation,
oldAggRel.getGroupCount(),
oldAggRel,
null,
@@ -494,6 +498,7 @@ public class AggregateReduceFunctionsRule extends RelOptRule {
oldCall.isApproximate(),
ImmutableIntList.of(argOrdinal),
oldCall.filterArg,
+ oldCall.collation,
oldAggRel.getGroupCount(),
oldAggRel.getInput(),
null,
@@ -517,6 +522,7 @@ public class AggregateReduceFunctionsRule extends RelOptRule {
oldCall.isApproximate(),
oldCall.getArgList(),
oldCall.filterArg,
+ oldCall.collation,
oldAggRel.getGroupCount(),
oldAggRel,
null,
@@ -589,6 +595,7 @@ public class AggregateReduceFunctionsRule extends RelOptRule {
oldCall.isApproximate(),
ImmutableIntList.of(argOrdinal),
filterArg,
+ oldCall.collation,
oldAggRel.getGroupCount(),
oldAggRel.getInput(),
null,
@@ -634,6 +641,7 @@ public class AggregateReduceFunctionsRule extends RelOptRule {
oldCall.isApproximate(),
argOrdinals,
filterArg,
+ oldCall.collation,
oldAggRel.getGroupCount(),
oldAggRel,
null,
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/rel/rules/AggregateStarTableRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateStarTableRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateStarTableRule.java
index e974763..317b0c5 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateStarTableRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateStarTableRule.java
@@ -236,6 +236,7 @@ public class AggregateStarTableRule extends RelOptRule {
}
return AggregateCall.create(roll, false,
aggregateCall.isApproximate(), ImmutableList.of(offset + i), -1,
+ aggregateCall.collation,
groupCount, relBuilder.peek(), null, aggregateCall.name);
}
@@ -251,7 +252,7 @@ public class AggregateStarTableRule extends RelOptRule {
newArgs.add(z);
}
return AggregateCall.create(aggregation, false,
- aggregateCall.isApproximate(), newArgs, -1,
+ aggregateCall.isApproximate(), newArgs, -1, aggregateCall.collation,
groupCount, relBuilder.peek(), null, aggregateCall.name);
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/rel/rules/AggregateUnionTransposeRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateUnionTransposeRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateUnionTransposeRule.java
index d6fd60e..0416338 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateUnionTransposeRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateUnionTransposeRule.java
@@ -173,8 +173,12 @@ public class AggregateUnionTransposeRule extends RelOptRule {
AggregateCall newCall =
AggregateCall.create(aggFun, origCall.isDistinct(),
origCall.isApproximate(),
- ImmutableList.of(groupCount + ord.i), -1, groupCount, input,
- aggType, origCall.getName());
+ ImmutableList.of(groupCount + ord.i), -1,
+ origCall.collation,
+ groupCount,
+ input,
+ aggType,
+ origCall.getName());
newCalls.add(newCall);
}
return newCalls;
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/rel/rules/SubQueryRemoveRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/SubQueryRemoveRule.java b/core/src/main/java/org/apache/calcite/rel/rules/SubQueryRemoveRule.java
index 39bccbf..3383f97 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/SubQueryRemoveRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/SubQueryRemoveRule.java
@@ -385,8 +385,7 @@ public abstract class SubQueryRemoveRule extends RelOptRule {
// Builds the cross join
builder.aggregate(builder.groupKey(),
builder.count(false, "c"),
- builder.aggregateCall(SqlStdOperatorTable.COUNT, builder.fields())
- .as("ck"));
+ builder.count(builder.fields()).as("ck"));
builder.as("ct");
if (!variablesSet.isEmpty()) {
builder.join(JoinRelType.LEFT, builder.literal(true), variablesSet);
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/rex/RexBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rex/RexBuilder.java b/core/src/main/java/org/apache/calcite/rex/RexBuilder.java
index 4a26cd8..5741a11 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexBuilder.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexBuilder.java
@@ -293,7 +293,8 @@ public class RexBuilder {
final List<Integer> args = aggCall.getArgList();
final List<Integer> nullableArgs = nullableArgs(args, aggArgTypes);
if (!nullableArgs.equals(args)) {
- aggCall = aggCall.copy(nullableArgs, aggCall.filterArg);
+ aggCall = aggCall.copy(nullableArgs, aggCall.filterArg,
+ aggCall.collation);
}
}
RexNode rex = aggCallMapping.get(aggCall);
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java b/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
index 122b39e..54a8c2d 100644
--- a/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
+++ b/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
@@ -326,6 +326,15 @@ public interface CalciteResource {
@BaseMessage("FILTER must not contain aggregate expression")
ExInst<SqlValidatorException> aggregateInFilterIllegal();
+ @BaseMessage("WITHIN GROUP must not contain aggregate expression")
+ ExInst<SqlValidatorException> aggregateInWithinGroupIllegal();
+
+ @BaseMessage("Aggregate expression ''{0}'' must contain a within group clause")
+ ExInst<SqlValidatorException> aggregateMissingWithinGroupClause(String a0);
+
+ @BaseMessage("Aggregate expression ''{0}'' must not contain a within group clause")
+ ExInst<SqlValidatorException> withinGroupClauseIllegalInAggregate(String a0);
+
@BaseMessage("Aggregate expression is illegal in ORDER BY clause of non-aggregating SELECT")
ExInst<SqlValidatorException> aggregateIllegalInOrderBy();
@@ -436,6 +445,9 @@ public interface CalciteResource {
@BaseMessage("DISTINCT/ALL not allowed with {0} function")
ExInst<SqlValidatorException> functionQuantifierNotAllowed(String a0);
+ @BaseMessage("WITHIN GROUP not allowed with {0} function")
+ ExInst<SqlValidatorException> withinGroupNotAllowed(String a0);
+
@BaseMessage("Some but not all arguments are named")
ExInst<SqlValidatorException> someButNotAllArgumentsAreNamed();
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/sql/SqlAggFunction.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlAggFunction.java b/core/src/main/java/org/apache/calcite/sql/SqlAggFunction.java
index 6c1f648..cca0ad0 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlAggFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlAggFunction.java
@@ -24,8 +24,11 @@ import org.apache.calcite.sql.type.SqlOperandTypeInference;
import org.apache.calcite.sql.type.SqlReturnTypeInference;
import org.apache.calcite.sql.validate.SqlValidator;
import org.apache.calcite.sql.validate.SqlValidatorScope;
+import org.apache.calcite.util.Optionality;
import java.util.List;
+import java.util.Objects;
+import javax.annotation.Nonnull;
/**
* Abstract base class for the definition of an aggregate function: an operator
@@ -34,6 +37,7 @@ import java.util.List;
public abstract class SqlAggFunction extends SqlFunction implements Context {
private final boolean requiresOrder;
private final boolean requiresOver;
+ private final Optionality requiresGroupOrder;
//~ Constructors -----------------------------------------------------------
@@ -48,7 +52,8 @@ public abstract class SqlAggFunction extends SqlFunction implements Context {
SqlFunctionCategory funcType) {
// We leave sqlIdentifier as null to indicate that this is a builtin.
this(name, null, kind, returnTypeInference, operandTypeInference,
- operandTypeChecker, funcType, false, false);
+ operandTypeChecker, funcType, false, false,
+ Optionality.FORBIDDEN);
}
/** Creates a user-defined SqlAggFunction. */
@@ -62,7 +67,24 @@ public abstract class SqlAggFunction extends SqlFunction implements Context {
SqlOperandTypeChecker operandTypeChecker,
SqlFunctionCategory funcType) {
this(name, sqlIdentifier, kind, returnTypeInference, operandTypeInference,
- operandTypeChecker, funcType, false, false);
+ operandTypeChecker, funcType, false, false,
+ Optionality.FORBIDDEN);
+ }
+
+ @Deprecated // to be removed before 2.0
+ protected SqlAggFunction(
+ String name,
+ SqlIdentifier sqlIdentifier,
+ SqlKind kind,
+ SqlReturnTypeInference returnTypeInference,
+ SqlOperandTypeInference operandTypeInference,
+ SqlOperandTypeChecker operandTypeChecker,
+ SqlFunctionCategory funcType,
+ boolean requiresOrder,
+ boolean requiresOver) {
+ this(name, sqlIdentifier, kind, returnTypeInference, operandTypeInference,
+ operandTypeChecker, funcType, requiresOrder, requiresOver,
+ Optionality.FORBIDDEN);
}
/** Creates a built-in or user-defined SqlAggFunction or window function.
@@ -78,11 +100,13 @@ public abstract class SqlAggFunction extends SqlFunction implements Context {
SqlOperandTypeChecker operandTypeChecker,
SqlFunctionCategory funcType,
boolean requiresOrder,
- boolean requiresOver) {
+ boolean requiresOver,
+ Optionality requiresGroupOrder) {
super(name, sqlIdentifier, kind, returnTypeInference, operandTypeInference,
operandTypeChecker, null, funcType);
this.requiresOrder = requiresOrder;
this.requiresOver = requiresOver;
+ this.requiresGroupOrder = Objects.requireNonNull(requiresGroupOrder);
}
//~ Methods ----------------------------------------------------------------
@@ -105,13 +129,40 @@ public abstract class SqlAggFunction extends SqlFunction implements Context {
SqlValidatorScope scope,
SqlValidatorScope operandScope) {
super.validateCall(call, validator, scope, operandScope);
- validator.validateAggregateParams(call, null, scope);
+ validator.validateAggregateParams(call, null, null, scope);
}
@Override public final boolean requiresOrder() {
return requiresOrder;
}
+ /** Returns whether this aggregate function must, may, or must not contain a
+ * {@code WITHIN GROUP (ORDER ...)} clause.
+ *
+ * <p>Cases:<ul>
+ *
+ * <li>If {@link Optionality#MANDATORY},
+ * then {@code AGG(x) WITHIN GROUP (ORDER BY 1)} is valid,
+ * and {@code AGG(x)} is invalid.
+ *
+ * <li>If {@link Optionality#OPTIONAL},
+ * then {@code AGG(x) WITHIN GROUP (ORDER BY 1)}
+ * and {@code AGG(x)} are both valid.
+ *
+ * <li>If {@link Optionality#IGNORED},
+ * then {@code AGG(x)} is valid,
+ * and {@code AGG(x) WITHIN GROUP (ORDER BY 1)} is valid but is
+ * treated the same as {@code AGG(x)}.
+ *
+ * <li>If {@link Optionality#FORBIDDEN},
+ * then {@code AGG(x) WITHIN GROUP (ORDER BY 1)} is invalid,
+ * and {@code AGG(x)} is valid.
+ * </ul>
+ */
+ public @Nonnull Optionality requiresGroupOrder() {
+ return requiresGroupOrder;
+ }
+
@Override public final boolean requiresOver() {
return requiresOver;
}
[5/6] calcite git commit: [CALCITE-2657] In RexShuttle,
use "RexCall.clone" instead of "new RexCall" (Chunwei Lei)
Posted by jh...@apache.org.
[CALCITE-2657] In RexShuttle, use "RexCall.clone" instead of "new RexCall" (Chunwei Lei)
Close apache/calcite#905
Project: http://git-wip-us.apache.org/repos/asf/calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/042fa6b7
Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/042fa6b7
Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/042fa6b7
Branch: refs/heads/master
Commit: 042fa6b7135fc8861816985253657dd685471f25
Parents: 7bc9f14
Author: chunwei.lcw <ch...@163.com>
Authored: Wed Nov 7 15:17:02 2018 +0800
Committer: Julian Hyde <jh...@apache.org>
Committed: Thu Nov 8 10:00:37 2018 -0800
----------------------------------------------------------------------
core/src/main/java/org/apache/calcite/rex/RexShuttle.java | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/calcite/blob/042fa6b7/core/src/main/java/org/apache/calcite/rex/RexShuttle.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rex/RexShuttle.java b/core/src/main/java/org/apache/calcite/rex/RexShuttle.java
index d79dcbf..b1f1593 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexShuttle.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexShuttle.java
@@ -104,10 +104,7 @@ public class RexShuttle implements RexVisitor<RexNode> {
// To do that, we would need to take a RexBuilder and
// watch out for special operators like CAST and NEW where
// the type is embedded in the original call.
- return new RexCall(
- call.getType(),
- call.getOperator(),
- clonedOperands);
+ return call.clone(call.getType(), clonedOperands);
} else {
return call;
}
[3/6] calcite git commit: [CALCITE-2224] Support WITHIN GROUP clause
for aggregate functions (Hongze Zhang)
Posted by jh...@apache.org.
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/sql/SqlFilterOperator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlFilterOperator.java b/core/src/main/java/org/apache/calcite/sql/SqlFilterOperator.java
index 3c0b527..8215dca 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlFilterOperator.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlFilterOperator.java
@@ -69,13 +69,18 @@ public class SqlFilterOperator extends SqlBinaryOperator {
SqlValidatorScope operandScope) {
assert call.getOperator() == this;
assert call.operandCount() == 2;
- SqlCall aggCall = call.operand(0);
+ SqlCall aggCall = getAggCall(call);
if (!aggCall.getOperator().isAggregator()) {
throw validator.newValidationError(aggCall,
RESOURCE.filterNonAggregate());
}
final SqlNode condition = call.operand(1);
- validator.validateAggregateParams(aggCall, condition, scope);
+ SqlNodeList orderList = null;
+ if (hasWithinGroupCall(call)) {
+ SqlCall withinGroupCall = getWithinGroupCall(call);
+ orderList = withinGroupCall.operand(1);
+ }
+ validator.validateAggregateParams(aggCall, condition, orderList, scope);
final RelDataType type = validator.deriveType(scope, condition);
if (!SqlTypeUtil.inBooleanFamily(type)) {
@@ -92,14 +97,7 @@ public class SqlFilterOperator extends SqlBinaryOperator {
validateOperands(validator, scope, call);
// Assume the first operand is an aggregate call and derive its type.
- SqlNode agg = call.operand(0);
-
- if (!(agg instanceof SqlCall)) {
- throw new IllegalStateException("Argument to SqlOverOperator"
- + " should be SqlCall, got " + agg.getClass() + ": " + agg);
- }
-
- final SqlCall aggCall = (SqlCall) agg;
+ final SqlCall aggCall = getAggCall(call);
// Pretend that group-count is 0. This tells the aggregate function that it
// might be invoked with 0 rows in a group. Most aggregate functions will
@@ -114,9 +112,36 @@ public class SqlFilterOperator extends SqlBinaryOperator {
// Copied from validateOperands
((SqlValidatorImpl) validator).setValidatedNodeType(call, ret);
- ((SqlValidatorImpl) validator).setValidatedNodeType(agg, ret);
+ ((SqlValidatorImpl) validator).setValidatedNodeType(aggCall, ret);
+ if (hasWithinGroupCall(call)) {
+ ((SqlValidatorImpl) validator).setValidatedNodeType(getWithinGroupCall(call), ret);
+ }
return ret;
}
+
+ private static SqlCall getAggCall(SqlCall call) {
+ assert call.getOperator().getKind() == SqlKind.FILTER;
+ call = call.operand(0);
+ if (call.getOperator().getKind() == SqlKind.WITHIN_GROUP) {
+ call = call.operand(0);
+ }
+ return call;
+ }
+
+ private static SqlCall getWithinGroupCall(SqlCall call) {
+ assert call.getOperator().getKind() == SqlKind.FILTER;
+ call = call.operand(0);
+ if (call.getOperator().getKind() == SqlKind.WITHIN_GROUP) {
+ return call;
+ }
+ throw new AssertionError();
+ }
+
+ private static boolean hasWithinGroupCall(SqlCall call) {
+ assert call.getOperator().getKind() == SqlKind.FILTER;
+ call = call.operand(0);
+ return call.getOperator().getKind() == SqlKind.WITHIN_GROUP;
+ }
}
// End SqlFilterOperator.java
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/sql/SqlKind.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlKind.java b/core/src/main/java/org/apache/calcite/sql/SqlKind.java
index e77b4be..225d3b4 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlKind.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlKind.java
@@ -213,6 +213,11 @@ public enum SqlKind {
FILTER,
/**
+ * WITHIN_GROUP operator
+ */
+ WITHIN_GROUP,
+
+ /**
* Window specification
*/
WINDOW,
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/sql/SqlRankFunction.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlRankFunction.java b/core/src/main/java/org/apache/calcite/sql/SqlRankFunction.java
index f330463..0b01d91 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlRankFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlRankFunction.java
@@ -19,6 +19,7 @@ package org.apache.calcite.sql;
import org.apache.calcite.sql.type.OperandTypes;
import org.apache.calcite.sql.type.ReturnTypes;
import org.apache.calcite.sql.type.SqlReturnTypeInference;
+import org.apache.calcite.util.Optionality;
/**
* Operator which aggregates sets of values into a result.
@@ -35,7 +36,7 @@ public class SqlRankFunction extends SqlAggFunction {
boolean requiresOrder) {
super(kind.name(), null, kind, returnTypes, null,
OperandTypes.NILADIC, SqlFunctionCategory.NUMERIC, requiresOrder,
- true);
+ true, Optionality.FORBIDDEN);
}
//~ Methods ----------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/sql/SqlSplittableAggFunction.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlSplittableAggFunction.java b/core/src/main/java/org/apache/calcite/sql/SqlSplittableAggFunction.java
index 83dad3a..652886f 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlSplittableAggFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlSplittableAggFunction.java
@@ -16,6 +16,7 @@
*/
package org.apache.calcite.sql;
+import org.apache.calcite.rel.RelCollations;
import org.apache.calcite.rel.core.AggregateCall;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeFactory;
@@ -118,7 +119,7 @@ public interface SqlSplittableAggFunction {
public AggregateCall other(RelDataTypeFactory typeFactory, AggregateCall e) {
return AggregateCall.create(SqlStdOperatorTable.COUNT, false, false,
- ImmutableIntList.of(), -1,
+ ImmutableIntList.of(), -1, RelCollations.EMPTY,
typeFactory.createSqlType(SqlTypeName.BIGINT), null);
}
@@ -147,8 +148,8 @@ public interface SqlSplittableAggFunction {
}
int ordinal = extra.register(node);
return AggregateCall.create(SqlStdOperatorTable.SUM0, false, false,
- ImmutableList.of(ordinal), -1, aggregateCall.type,
- aggregateCall.name);
+ ImmutableList.of(ordinal), -1, aggregateCall.collation,
+ aggregateCall.type, aggregateCall.name);
}
/**
@@ -207,8 +208,10 @@ public interface SqlSplittableAggFunction {
Registry<RexNode> extra, int offset, RelDataType inputRowType,
AggregateCall aggregateCall, int leftSubTotal, int rightSubTotal) {
assert (leftSubTotal >= 0) != (rightSubTotal >= 0);
+ assert aggregateCall.collation.getFieldCollations().isEmpty();
final int arg = leftSubTotal >= 0 ? leftSubTotal : rightSubTotal;
- return aggregateCall.copy(ImmutableIntList.of(arg), -1);
+ return aggregateCall.copy(ImmutableIntList.of(arg), -1,
+ RelCollations.EMPTY);
}
}
@@ -230,6 +233,7 @@ public interface SqlSplittableAggFunction {
public AggregateCall other(RelDataTypeFactory typeFactory, AggregateCall e) {
return AggregateCall.create(SqlStdOperatorTable.COUNT, false, false,
ImmutableIntList.of(), -1,
+ RelCollations.EMPTY,
typeFactory.createSqlType(SqlTypeName.BIGINT), null);
}
@@ -260,8 +264,8 @@ public interface SqlSplittableAggFunction {
}
int ordinal = extra.register(node);
return AggregateCall.create(getMergeAggFunctionOfTopSplit(), false, false,
- ImmutableList.of(ordinal), -1, aggregateCall.type,
- aggregateCall.name);
+ ImmutableList.of(ordinal), -1, aggregateCall.collation,
+ aggregateCall.type, aggregateCall.name);
}
protected abstract SqlAggFunction getMergeAggFunctionOfTopSplit();
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/sql/SqlWithinGroupOperator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlWithinGroupOperator.java b/core/src/main/java/org/apache/calcite/sql/SqlWithinGroupOperator.java
new file mode 100644
index 0000000..3ec0ee8
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/sql/SqlWithinGroupOperator.java
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+package org.apache.calcite.sql;
+
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.sql.type.OperandTypes;
+import org.apache.calcite.sql.type.ReturnTypes;
+import org.apache.calcite.sql.validate.SqlValidator;
+import org.apache.calcite.sql.validate.SqlValidatorScope;
+
+import static org.apache.calcite.util.Static.RESOURCE;
+
+/**
+ * An operator that applies a sort operation before rows are included in an aggregate function.
+ *
+ * <p>Operands are as follows:</p>
+ *
+ * <ul>
+ * <li>0: a call to an aggregate function ({@link SqlCall})
+ * <li>1: order operation list
+ * </ul>
+ */
+public class SqlWithinGroupOperator extends SqlBinaryOperator {
+
+ public SqlWithinGroupOperator() {
+ super("WITHIN GROUP", SqlKind.WITHIN_GROUP, 100, true, ReturnTypes.ARG0,
+ null, OperandTypes.ANY_ANY);
+ }
+
+ @Override public void unparse(SqlWriter writer, SqlCall call, int leftPrec, int rightPrec) {
+ assert call.operandCount() == 2;
+ call.operand(0).unparse(writer, 0, 0);
+ writer.keyword("WITHIN GROUP");
+ final SqlWriter.Frame orderFrame =
+ writer.startList(SqlWriter.FrameTypeEnum.ORDER_BY_LIST, "(", ")");
+ writer.keyword("ORDER BY");
+ ((SqlNodeList) call.operand(1)).commaList(writer);
+ writer.endList(orderFrame);
+ }
+
+ public void validateCall(
+ SqlCall call,
+ SqlValidator validator,
+ SqlValidatorScope scope,
+ SqlValidatorScope operandScope) {
+ assert call.getOperator() == this;
+ assert call.operandCount() == 2;
+ SqlCall aggCall = call.operand(0);
+ if (!aggCall.getOperator().isAggregator()) {
+ throw validator.newValidationError(call,
+ RESOURCE.withinGroupNotAllowed(aggCall.getOperator().getName()));
+ }
+ final SqlNodeList orderList = call.operand(1);
+ for (SqlNode order : orderList) {
+ RelDataType nodeType =
+ validator.deriveType(scope, order);
+ assert nodeType != null;
+ }
+ validator.validateAggregateParams(aggCall, null, orderList, scope);
+ }
+
+ public RelDataType deriveType(
+ SqlValidator validator,
+ SqlValidatorScope scope,
+ SqlCall call) {
+ // Validate type of the inner aggregate call
+ return validateOperands(validator, scope, call);
+ }
+}
+
+// End SqlWithinGroupOperator.java
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/sql/fun/SqlAbstractGroupFunction.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlAbstractGroupFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlAbstractGroupFunction.java
index d1001dd..132c8cd 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlAbstractGroupFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlAbstractGroupFunction.java
@@ -31,6 +31,7 @@ import org.apache.calcite.sql.validate.SelectScope;
import org.apache.calcite.sql.validate.SqlValidator;
import org.apache.calcite.sql.validate.SqlValidatorScope;
import org.apache.calcite.sql.validate.SqlValidatorUtil;
+import org.apache.calcite.util.Optionality;
import org.apache.calcite.util.Static;
/**
@@ -55,7 +56,7 @@ public class SqlAbstractGroupFunction extends SqlAggFunction {
SqlOperandTypeChecker operandTypeChecker,
SqlFunctionCategory category) {
super(name, null, kind, returnTypeInference, operandTypeInference,
- operandTypeChecker, category, false, false);
+ operandTypeChecker, category, false, false, Optionality.FORBIDDEN);
}
@Override public void validateCall(SqlCall call, SqlValidator validator,
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/sql/fun/SqlAnyValueAggFunction.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlAnyValueAggFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlAnyValueAggFunction.java
index 49208b9..32e9792 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlAnyValueAggFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlAnyValueAggFunction.java
@@ -21,6 +21,7 @@ import org.apache.calcite.sql.SqlFunctionCategory;
import org.apache.calcite.sql.SqlKind;
import org.apache.calcite.sql.type.OperandTypes;
import org.apache.calcite.sql.type.ReturnTypes;
+import org.apache.calcite.util.Optionality;
import com.google.common.base.Preconditions;
@@ -44,7 +45,8 @@ public class SqlAnyValueAggFunction extends SqlAggFunction {
OperandTypes.ANY,
SqlFunctionCategory.SYSTEM,
false,
- false);
+ false,
+ Optionality.FORBIDDEN);
Preconditions.checkArgument(kind == SqlKind.ANY_VALUE);
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/sql/fun/SqlAvgAggFunction.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlAvgAggFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlAvgAggFunction.java
index 6be1ce9..f1ffcf9 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlAvgAggFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlAvgAggFunction.java
@@ -22,6 +22,7 @@ import org.apache.calcite.sql.SqlFunctionCategory;
import org.apache.calcite.sql.SqlKind;
import org.apache.calcite.sql.type.OperandTypes;
import org.apache.calcite.sql.type.ReturnTypes;
+import org.apache.calcite.util.Optionality;
import com.google.common.base.Preconditions;
@@ -51,8 +52,10 @@ public class SqlAvgAggFunction extends SqlAggFunction {
OperandTypes.NUMERIC,
SqlFunctionCategory.NUMERIC,
false,
- false);
- Preconditions.checkArgument(SqlKind.AVG_AGG_FUNCTIONS.contains(kind), "unsupported sql kind");
+ false,
+ Optionality.FORBIDDEN);
+ Preconditions.checkArgument(SqlKind.AVG_AGG_FUNCTIONS.contains(kind),
+ "unsupported sql kind");
}
@Deprecated // to be removed before 2.0
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/sql/fun/SqlCountAggFunction.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlCountAggFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlCountAggFunction.java
index db54102..1678727 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlCountAggFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlCountAggFunction.java
@@ -30,6 +30,7 @@ import org.apache.calcite.sql.type.SqlOperandTypeChecker;
import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.sql.validate.SqlValidator;
import org.apache.calcite.sql.validate.SqlValidatorScope;
+import org.apache.calcite.util.Optionality;
import com.google.common.collect.ImmutableList;
@@ -49,9 +50,11 @@ public class SqlCountAggFunction extends SqlAggFunction {
this(name, SqlValidator.STRICT ? OperandTypes.ANY : OperandTypes.ONE_OR_MORE);
}
- public SqlCountAggFunction(String name, SqlOperandTypeChecker sqlOperandTypeChecker) {
+ public SqlCountAggFunction(String name,
+ SqlOperandTypeChecker sqlOperandTypeChecker) {
super(name, null, SqlKind.COUNT, ReturnTypes.BIGINT, null,
- sqlOperandTypeChecker, SqlFunctionCategory.NUMERIC, false, false);
+ sqlOperandTypeChecker, SqlFunctionCategory.NUMERIC, false, false,
+ Optionality.FORBIDDEN);
}
//~ Methods ----------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/sql/fun/SqlCovarAggFunction.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlCovarAggFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlCovarAggFunction.java
index 8591959..764339d 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlCovarAggFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlCovarAggFunction.java
@@ -22,6 +22,7 @@ import org.apache.calcite.sql.SqlFunctionCategory;
import org.apache.calcite.sql.SqlKind;
import org.apache.calcite.sql.type.OperandTypes;
import org.apache.calcite.sql.type.ReturnTypes;
+import org.apache.calcite.util.Optionality;
import com.google.common.base.Preconditions;
@@ -48,7 +49,8 @@ public class SqlCovarAggFunction extends SqlAggFunction {
OperandTypes.NUMERIC_NUMERIC,
SqlFunctionCategory.NUMERIC,
false,
- false);
+ false,
+ Optionality.FORBIDDEN);
Preconditions.checkArgument(SqlKind.COVAR_AVG_AGG_FUNCTIONS.contains(kind),
"unsupported sql kind: " + kind);
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/sql/fun/SqlFirstLastValueAggFunction.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlFirstLastValueAggFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlFirstLastValueAggFunction.java
index 72dccd1..397acf8 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlFirstLastValueAggFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlFirstLastValueAggFunction.java
@@ -24,6 +24,7 @@ import org.apache.calcite.sql.SqlKind;
import org.apache.calcite.sql.type.OperandTypes;
import org.apache.calcite.sql.type.ReturnTypes;
import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.util.Optionality;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
@@ -48,7 +49,8 @@ public class SqlFirstLastValueAggFunction extends SqlAggFunction {
OperandTypes.ANY,
SqlFunctionCategory.NUMERIC,
false,
- true);
+ true,
+ Optionality.FORBIDDEN);
Preconditions.checkArgument(kind == SqlKind.FIRST_VALUE
|| kind == SqlKind.LAST_VALUE);
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/sql/fun/SqlHistogramAggFunction.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlHistogramAggFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlHistogramAggFunction.java
index a3fb5f2..864f8b4 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlHistogramAggFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlHistogramAggFunction.java
@@ -23,6 +23,7 @@ import org.apache.calcite.sql.SqlFunctionCategory;
import org.apache.calcite.sql.SqlKind;
import org.apache.calcite.sql.type.OperandTypes;
import org.apache.calcite.sql.type.ReturnTypes;
+import org.apache.calcite.util.Optionality;
import com.google.common.collect.ImmutableList;
@@ -53,7 +54,8 @@ public class SqlHistogramAggFunction extends SqlAggFunction {
OperandTypes.NUMERIC_OR_STRING,
SqlFunctionCategory.NUMERIC,
false,
- false);
+ false,
+ Optionality.FORBIDDEN);
this.type = type;
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/sql/fun/SqlLeadLagAggFunction.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlLeadLagAggFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlLeadLagAggFunction.java
index d52c82b..55dc0cf 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlLeadLagAggFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlLeadLagAggFunction.java
@@ -28,6 +28,7 @@ import org.apache.calcite.sql.type.SqlSingleOperandTypeChecker;
import org.apache.calcite.sql.type.SqlTypeFamily;
import org.apache.calcite.sql.type.SqlTypeTransform;
import org.apache.calcite.sql.type.SqlTypeTransforms;
+import org.apache.calcite.util.Optionality;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
@@ -78,7 +79,8 @@ public class SqlLeadLagAggFunction extends SqlAggFunction {
OPERAND_TYPES,
SqlFunctionCategory.NUMERIC,
false,
- true);
+ true,
+ Optionality.FORBIDDEN);
Preconditions.checkArgument(kind == SqlKind.LEAD
|| kind == SqlKind.LAG);
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/sql/fun/SqlMinMaxAggFunction.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlMinMaxAggFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlMinMaxAggFunction.java
index 438c7f1..edbc4b0 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlMinMaxAggFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlMinMaxAggFunction.java
@@ -24,6 +24,7 @@ import org.apache.calcite.sql.SqlKind;
import org.apache.calcite.sql.SqlSplittableAggFunction;
import org.apache.calcite.sql.type.OperandTypes;
import org.apache.calcite.sql.type.ReturnTypes;
+import org.apache.calcite.util.Optionality;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
@@ -75,7 +76,8 @@ public class SqlMinMaxAggFunction extends SqlAggFunction {
OperandTypes.COMPARABLE_ORDERED,
SqlFunctionCategory.SYSTEM,
false,
- false);
+ false,
+ Optionality.FORBIDDEN);
this.argTypes = ImmutableList.of();
this.minMaxKind = MINMAX_COMPARABLE;
Preconditions.checkArgument(kind == SqlKind.MIN
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/sql/fun/SqlNthValueAggFunction.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlNthValueAggFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlNthValueAggFunction.java
index b1a2a86..6e66653 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlNthValueAggFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlNthValueAggFunction.java
@@ -21,6 +21,7 @@ import org.apache.calcite.sql.SqlFunctionCategory;
import org.apache.calcite.sql.SqlKind;
import org.apache.calcite.sql.type.OperandTypes;
import org.apache.calcite.sql.type.ReturnTypes;
+import org.apache.calcite.util.Optionality;
/**
* <code>NTH_VALUE</code> windowed aggregate function
@@ -31,7 +32,7 @@ public class SqlNthValueAggFunction extends SqlAggFunction {
public SqlNthValueAggFunction(SqlKind kind) {
super(kind.name(), null, kind, ReturnTypes.ARG0_NULLABLE_IF_EMPTY,
null, OperandTypes.ANY_NUMERIC, SqlFunctionCategory.NUMERIC, false,
- true);
+ true, Optionality.FORBIDDEN);
}
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/sql/fun/SqlNtileAggFunction.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlNtileAggFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlNtileAggFunction.java
index 18677f5..d451c0b 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlNtileAggFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlNtileAggFunction.java
@@ -21,6 +21,7 @@ import org.apache.calcite.sql.SqlFunctionCategory;
import org.apache.calcite.sql.SqlKind;
import org.apache.calcite.sql.type.OperandTypes;
import org.apache.calcite.sql.type.ReturnTypes;
+import org.apache.calcite.util.Optionality;
/**
* <code>NTILE</code> aggregate function
@@ -37,7 +38,8 @@ public class SqlNtileAggFunction extends SqlAggFunction {
OperandTypes.POSITIVE_INTEGER_LITERAL,
SqlFunctionCategory.NUMERIC,
false,
- true);
+ true,
+ Optionality.FORBIDDEN);
}
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/sql/fun/SqlSingleValueAggFunction.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlSingleValueAggFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlSingleValueAggFunction.java
index 6e745df..6f4518e 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlSingleValueAggFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlSingleValueAggFunction.java
@@ -23,6 +23,7 @@ import org.apache.calcite.sql.SqlFunctionCategory;
import org.apache.calcite.sql.SqlKind;
import org.apache.calcite.sql.type.OperandTypes;
import org.apache.calcite.sql.type.ReturnTypes;
+import org.apache.calcite.util.Optionality;
import com.google.common.collect.ImmutableList;
@@ -51,7 +52,8 @@ public class SqlSingleValueAggFunction extends SqlAggFunction {
OperandTypes.ANY,
SqlFunctionCategory.SYSTEM,
false,
- false);
+ false,
+ Optionality.FORBIDDEN);
this.type = type;
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
index 827a516..ac09932 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
@@ -46,6 +46,7 @@ import org.apache.calcite.sql.SqlUnnestOperator;
import org.apache.calcite.sql.SqlUtil;
import org.apache.calcite.sql.SqlValuesOperator;
import org.apache.calcite.sql.SqlWindow;
+import org.apache.calcite.sql.SqlWithinGroupOperator;
import org.apache.calcite.sql.SqlWriter;
import org.apache.calcite.sql.type.InferTypes;
import org.apache.calcite.sql.type.OperandTypes;
@@ -57,6 +58,7 @@ import org.apache.calcite.sql.validate.SqlConformance;
import org.apache.calcite.sql.validate.SqlModality;
import org.apache.calcite.sql2rel.AuxiliaryConverter;
import org.apache.calcite.util.Litmus;
+import org.apache.calcite.util.Optionality;
import org.apache.calcite.util.Pair;
import com.google.common.collect.ImmutableList;
@@ -177,6 +179,9 @@ public class SqlStdOperatorTable extends ReflectiveSqlOperatorTable {
* aggregate function. */
public static final SqlFilterOperator FILTER = new SqlFilterOperator();
+ /** <code>WITHIN_GROUP</code> operator performs aggregations on ordered data input. */
+ public static final SqlWithinGroupOperator WITHIN_GROUP = new SqlWithinGroupOperator();
+
/** {@code CUBE} operator, occurs within {@code GROUP BY} clause
* or nested within a {@code GROUPING SETS}. */
public static final SqlInternalOperator CUBE =
@@ -1980,7 +1985,8 @@ public class SqlStdOperatorTable extends ReflectiveSqlOperatorTable {
ReturnTypes.TO_MULTISET,
null,
OperandTypes.ANY,
- SqlFunctionCategory.SYSTEM, false, false) {
+ SqlFunctionCategory.SYSTEM, false, false,
+ Optionality.OPTIONAL) {
};
/**
@@ -1992,7 +1998,8 @@ public class SqlStdOperatorTable extends ReflectiveSqlOperatorTable {
ReturnTypes.ARG0,
null,
OperandTypes.MULTISET,
- SqlFunctionCategory.SYSTEM, false, false) {
+ SqlFunctionCategory.SYSTEM, false, false,
+ Optionality.FORBIDDEN) {
};
/**
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/sql/fun/SqlSumAggFunction.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlSumAggFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlSumAggFunction.java
index eac5731..941a4e7 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlSumAggFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlSumAggFunction.java
@@ -24,6 +24,7 @@ import org.apache.calcite.sql.SqlKind;
import org.apache.calcite.sql.SqlSplittableAggFunction;
import org.apache.calcite.sql.type.OperandTypes;
import org.apache.calcite.sql.type.ReturnTypes;
+import org.apache.calcite.util.Optionality;
import com.google.common.collect.ImmutableList;
@@ -54,7 +55,8 @@ public class SqlSumAggFunction extends SqlAggFunction {
OperandTypes.NUMERIC,
SqlFunctionCategory.NUMERIC,
false,
- false);
+ false,
+ Optionality.FORBIDDEN);
this.type = type;
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/sql/fun/SqlSumEmptyIsZeroAggFunction.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlSumEmptyIsZeroAggFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlSumEmptyIsZeroAggFunction.java
index 1fd9bf1..b74e9f9 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlSumEmptyIsZeroAggFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlSumEmptyIsZeroAggFunction.java
@@ -25,6 +25,7 @@ import org.apache.calcite.sql.SqlSplittableAggFunction;
import org.apache.calcite.sql.type.OperandTypes;
import org.apache.calcite.sql.type.ReturnTypes;
import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.util.Optionality;
import com.google.common.collect.ImmutableList;
@@ -48,7 +49,8 @@ public class SqlSumEmptyIsZeroAggFunction extends SqlAggFunction {
OperandTypes.NUMERIC,
SqlFunctionCategory.NUMERIC,
false,
- false);
+ false,
+ Optionality.FORBIDDEN);
}
//~ Methods ----------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/sql/validate/AggChecker.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/AggChecker.java b/core/src/main/java/org/apache/calcite/sql/validate/AggChecker.java
index a00a523..2962459 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/AggChecker.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/AggChecker.java
@@ -156,6 +156,10 @@ class AggChecker extends SqlBasicVisitor<Void> {
call.operand(0).accept(this);
return null;
}
+ if (call.getKind() == SqlKind.WITHIN_GROUP) {
+ call.operand(0).accept(this);
+ return null;
+ }
// Visit the operand in window function
if (call.getKind() == SqlKind.OVER) {
for (SqlNode operand : call.<SqlCall>operand(0).getOperandList()) {
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedAggFunction.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedAggFunction.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedAggFunction.java
index 0cf8c0f..186daf5 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedAggFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedAggFunction.java
@@ -31,6 +31,7 @@ import org.apache.calcite.sql.type.SqlOperandTypeChecker;
import org.apache.calcite.sql.type.SqlOperandTypeInference;
import org.apache.calcite.sql.type.SqlReturnTypeInference;
import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.util.Optionality;
import org.apache.calcite.util.Util;
import com.google.common.collect.Lists;
@@ -57,10 +58,12 @@ public class SqlUserDefinedAggFunction extends SqlAggFunction {
SqlReturnTypeInference returnTypeInference,
SqlOperandTypeInference operandTypeInference,
SqlOperandTypeChecker operandTypeChecker, AggregateFunction function,
- boolean requiresOrder, boolean requiresOver, RelDataTypeFactory typeFactory) {
+ boolean requiresOrder, boolean requiresOver,
+ Optionality requiresGroupOrder, RelDataTypeFactory typeFactory) {
super(Util.last(opName.names), opName, SqlKind.OTHER_FUNCTION,
returnTypeInference, operandTypeInference, operandTypeChecker,
- SqlFunctionCategory.USER_DEFINED_FUNCTION, requiresOrder, requiresOver);
+ SqlFunctionCategory.USER_DEFINED_FUNCTION, requiresOrder, requiresOver,
+ requiresGroupOrder);
this.function = function;
this.typeFactory = typeFactory;
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/sql/validate/SqlValidator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidator.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidator.java
index a4b4297..2045eac 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidator.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidator.java
@@ -294,12 +294,14 @@ public interface SqlValidator {
/**
* Validates parameters for aggregate function.
*
- * @param aggCall Function containing COLUMN_LIST parameter
- * @param filter Filter, or null
+ * @param aggCall Call to aggregate function
+ * @param filter Filter ({@code FILTER (WHERE)} clause), or null
+ * @param orderList Ordering specification ({@code WITHING GROUP} clause),
+ * or null
* @param scope Syntactic scope
*/
void validateAggregateParams(SqlCall aggCall, SqlNode filter,
- SqlValidatorScope scope);
+ SqlNodeList orderList, SqlValidatorScope scope);
/**
* Validates a COLUMN_LIST parameter
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
index f7ce4d6..626399f 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
@@ -45,6 +45,7 @@ import org.apache.calcite.sql.JoinConditionType;
import org.apache.calcite.sql.JoinType;
import org.apache.calcite.sql.SqlAccessEnum;
import org.apache.calcite.sql.SqlAccessType;
+import org.apache.calcite.sql.SqlAggFunction;
import org.apache.calcite.sql.SqlBasicCall;
import org.apache.calcite.sql.SqlCall;
import org.apache.calcite.sql.SqlCallBinding;
@@ -4868,7 +4869,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
targetWindow.setWindowCall(null);
call.validate(this, scope);
- validateAggregateParams(call, null, scope);
+ validateAggregateParams(call, null, null, scope);
// Disable nested aggregates post validation
inWindow = false;
@@ -5141,7 +5142,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
}
public void validateAggregateParams(SqlCall aggCall, SqlNode filter,
- SqlValidatorScope scope) {
+ SqlNodeList orderList, SqlValidatorScope scope) {
// For "agg(expr)", expr cannot itself contain aggregate function
// invocations. For example, "SUM(2 * MAX(x))" is illegal; when
// we see it, we'll report the error for the SUM (not the MAX).
@@ -5175,6 +5176,40 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
throw newValidationError(filter, RESOURCE.aggregateInFilterIllegal());
}
}
+ if (orderList != null) {
+ for (SqlNode param : orderList) {
+ if (a.findAgg(param) != null) {
+ throw newValidationError(aggCall,
+ RESOURCE.aggregateInWithinGroupIllegal());
+ }
+ }
+ }
+
+ final SqlAggFunction op = (SqlAggFunction) aggCall.getOperator();
+ switch (op.requiresGroupOrder()) {
+ case MANDATORY:
+ if (orderList == null || orderList.size() == 0) {
+ throw newValidationError(aggCall,
+ RESOURCE.aggregateMissingWithinGroupClause(op.getName()));
+ }
+ break;
+ case OPTIONAL:
+ break;
+ case IGNORED:
+ // rewrite the order list to empty
+ if (orderList != null) {
+ orderList.getList().clear();
+ }
+ break;
+ case FORBIDDEN:
+ if (orderList != null && orderList.size() != 0) {
+ throw newValidationError(aggCall,
+ RESOURCE.withinGroupClauseIllegalInAggregate(op.getName()));
+ }
+ break;
+ default:
+ throw new AssertionError(op);
+ }
}
public void validateCall(
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java b/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java
index e9031ba..5d6fc16 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java
@@ -20,6 +20,7 @@ import org.apache.calcite.linq4j.Ord;
import org.apache.calcite.plan.RelOptCluster;
import org.apache.calcite.plan.RelOptUtil;
import org.apache.calcite.rel.RelCollation;
+import org.apache.calcite.rel.RelCollations;
import org.apache.calcite.rel.RelFieldCollation;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.core.Aggregate;
@@ -794,12 +795,11 @@ public class RelFieldTrimmer implements ReflectiveVisitor {
aggregate.getGroupSet().rebuild();
// 2. agg functions
for (AggregateCall aggCall : aggregate.getAggCallList()) {
- for (int i : aggCall.getArgList()) {
- inputFieldsUsed.set(i);
- }
+ inputFieldsUsed.addAll(aggCall.getArgList());
if (aggCall.filterArg >= 0) {
inputFieldsUsed.set(aggCall.filterArg);
}
+ inputFieldsUsed.addAll(RelCollations.ordinals(aggCall.collation));
}
// Create input with trimmed columns.
@@ -871,6 +871,7 @@ public class RelFieldTrimmer implements ReflectiveVisitor {
.distinct(aggCall.isDistinct())
.filter(filterArg)
.approximate(aggCall.isApproximate())
+ .sort(relBuilder.fields(aggCall.collation))
.as(aggCall.name);
mapping.set(j, groupCount + indicatorCount + newAggCallList.size());
newAggCallList.add(newAggCall);
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
index 9750664..9512312 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
@@ -199,6 +199,7 @@ import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.Supplier;
+import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import static org.apache.calcite.sql.SqlUtil.stripAs;
@@ -1124,9 +1125,10 @@ public class SqlToRelConverter {
LogicalAggregate.create(seek, ImmutableBitSet.of(), null,
ImmutableList.of(
AggregateCall.create(SqlStdOperatorTable.COUNT, false,
- false, ImmutableList.of(), -1, longType, null),
+ false, ImmutableList.of(), -1, RelCollations.EMPTY,
+ longType, null),
AggregateCall.create(SqlStdOperatorTable.COUNT, false,
- false, args, -1, longType, null)));
+ false, args, -1, RelCollations.EMPTY, longType, null)));
LogicalJoin join =
LogicalJoin.create(bb.root, aggregate, rexBuilder.makeLiteral(true),
ImmutableSet.of(), JoinRelType.INNER);
@@ -2727,6 +2729,10 @@ public class SqlToRelConverter {
replaceSubQueries(bb, aggregateFinder.filterList,
RelOptUtil.Logic.TRUE_FALSE_UNKNOWN);
+ // also replace sub-queries inside ordering spec in the aggregates
+ replaceSubQueries(bb, aggregateFinder.orderList,
+ RelOptUtil.Logic.TRUE_FALSE_UNKNOWN);
+
// If group-by clause is missing, pretend that it has zero elements.
if (groupList == null) {
groupList = SqlNodeList.EMPTY;
@@ -4667,7 +4673,9 @@ public class SqlToRelConverter {
if (agg != null) {
final SqlOperator op = call.getOperator();
if (window == null
- && (op.isAggregator() || op.getKind() == SqlKind.FILTER)) {
+ && (op.isAggregator()
+ || op.getKind() == SqlKind.FILTER
+ || op.getKind() == SqlKind.WITHIN_GROUP)) {
return agg.lookupAggregates(call);
}
}
@@ -4918,7 +4926,8 @@ public class SqlToRelConverter {
public Void visit(SqlCall call) {
switch (call.getKind()) {
case FILTER:
- translateAgg((SqlCall) call.operand(0), call.operand(1), call);
+ case WITHIN_GROUP:
+ translateAgg(call);
return null;
case SELECT:
// rchen 2006-10-17:
@@ -4952,7 +4961,7 @@ public class SqlToRelConverter {
inOver = false;
} else {
// We're beyond the one ignored level
- translateAgg(call, null, call);
+ translateAgg(call);
return null;
}
}
@@ -4967,8 +4976,24 @@ public class SqlToRelConverter {
return null;
}
- private void translateAgg(SqlCall call, SqlNode filter, SqlCall outerCall) {
+ private void translateAgg(SqlCall call) {
+ translateAgg(call, null, null, call);
+ }
+
+ private void translateAgg(SqlCall call, SqlNode filter,
+ SqlNodeList orderList, SqlCall outerCall) {
assert bb.agg == this;
+ assert outerCall != null;
+ switch (call.getKind()) {
+ case FILTER:
+ assert filter == null;
+ translateAgg(call.operand(0), call.operand(1), orderList, outerCall);
+ return;
+ case WITHIN_GROUP:
+ assert orderList == null;
+ translateAgg(call.operand(0), filter, call.operand(1), outerCall);
+ return;
+ }
final List<Integer> args = new ArrayList<>();
int filterArg = -1;
final List<RelDataType> argTypes =
@@ -5026,6 +5051,24 @@ public class SqlToRelConverter {
distinct = true;
approximate = true;
}
+ final RelCollation collation;
+ if (orderList == null || orderList.size() == 0) {
+ collation = RelCollations.EMPTY;
+ } else {
+ collation = RelCollations.of(
+ orderList.getList()
+ .stream()
+ .map(order ->
+ bb.convertSortExpression(order,
+ RelFieldCollation.Direction.ASCENDING,
+ RelFieldCollation.NullDirection.UNSPECIFIED))
+ .map(fieldCollation ->
+ new RelFieldCollation(
+ lookupOrCreateGroupExpr(fieldCollation.left),
+ fieldCollation.getDirection(),
+ fieldCollation.getNullDirection()))
+ .collect(Collectors.toList()));
+ }
final AggregateCall aggCall =
AggregateCall.create(
aggFunction,
@@ -5033,6 +5076,7 @@ public class SqlToRelConverter {
approximate,
args,
filterArg,
+ collation,
type,
nameMap.get(outerCall.toString()));
final AggregatingSelectScope.Resolved r =
@@ -5358,6 +5402,7 @@ public class SqlToRelConverter {
private static class AggregateFinder extends SqlBasicVisitor<Void> {
final SqlNodeList list = new SqlNodeList(SqlParserPos.ZERO);
final SqlNodeList filterList = new SqlNodeList(SqlParserPos.ZERO);
+ final SqlNodeList orderList = new SqlNodeList(SqlParserPos.ZERO);
@Override public Void visit(SqlCall call) {
// ignore window aggregates and ranking functions (associated with OVER operator)
@@ -5375,6 +5420,16 @@ public class SqlToRelConverter {
return null;
}
+ if (call.getOperator().getKind() == SqlKind.WITHIN_GROUP) {
+ // the WHERE in a WITHIN_GROUP must be tracked too so we can call replaceSubQueries on it.
+ // see https://issues.apache.org/jira/browse/CALCITE-1910
+ final SqlNode aggCall = call.getOperandList().get(0);
+ final SqlNodeList orderList = (SqlNodeList) call.getOperandList().get(1);
+ list.add(aggCall);
+ orderList.getList().forEach(this.orderList::add);
+ return null;
+ }
+
if (call.getOperator().isAggregator()) {
list.add(call);
return null;
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
index 323f65a..3b0be9c 100644
--- a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
+++ b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
@@ -94,6 +94,7 @@ import java.math.BigDecimal;
import java.util.AbstractList;
import java.util.ArrayDeque;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Deque;
import java.util.HashSet;
import java.util.LinkedList;
@@ -785,17 +786,18 @@ public class RelBuilder {
public AggCall aggregateCall(SqlAggFunction aggFunction, boolean distinct,
boolean approximate, RexNode filter, String alias,
Iterable<? extends RexNode> operands) {
- return aggregateCall(aggFunction, distinct, filter, alias, operands)
- .approximate(approximate);
+ return aggregateCall(aggFunction, distinct, approximate, filter,
+ ImmutableList.of(), alias, ImmutableList.copyOf(operands));
}
/** Creates a call to an aggregate function.
*
* <p>To add other operands, apply
- * {@link AggCall#filter(RexNode...)},
* {@link AggCall#distinct()},
- * {@link AggCall#as},
- * {@link AggCall#sort} to the result. */
+ * {@link AggCall#approximate(boolean)},
+ * {@link AggCall#filter(RexNode...)},
+ * {@link AggCall#sort},
+ * {@link AggCall#as} to the result. */
public AggCall aggregateCall(SqlAggFunction aggFunction,
Iterable<? extends RexNode> operands) {
return aggregateCall(aggFunction, false, false, null, ImmutableList.of(),
@@ -805,10 +807,11 @@ public class RelBuilder {
/** Creates a call to an aggregate function.
*
* <p>To add other operands, apply
- * {@link AggCall#filter(RexNode...)},
* {@link AggCall#distinct()},
- * {@link AggCall#as},
- * {@link AggCall#sort} to the result. */
+ * {@link AggCall#approximate(boolean)},
+ * {@link AggCall#filter(RexNode...)},
+ * {@link AggCall#sort},
+ * {@link AggCall#as} to the result. */
public AggCall aggregateCall(SqlAggFunction aggFunction,
RexNode... operands) {
return aggregateCall(aggFunction, false, false, null, ImmutableList.of(),
@@ -816,7 +819,7 @@ public class RelBuilder {
}
/** Creates a call to an aggregate function with all applicable operands. */
- AggCall aggregateCall(SqlAggFunction aggFunction, boolean distinct,
+ protected AggCall aggregateCall(SqlAggFunction aggFunction, boolean distinct,
boolean approximate, RexNode filter, ImmutableList<RexNode> orderKeys,
String alias, ImmutableList<RexNode> operands) {
return new AggCallImpl(aggFunction, distinct, approximate, filter, alias,
@@ -828,11 +831,24 @@ public class RelBuilder {
return count(false, null, operands);
}
+ /** Creates a call to the {@code COUNT} aggregate function. */
+ public AggCall count(Iterable<? extends RexNode> operands) {
+ return count(false, null, operands);
+ }
+
/** Creates a call to the {@code COUNT} aggregate function,
* optionally distinct and with an alias. */
public AggCall count(boolean distinct, String alias, RexNode... operands) {
- return aggregateCall(SqlStdOperatorTable.COUNT, distinct, null, alias,
- ImmutableList.copyOf(operands));
+ return aggregateCall(SqlStdOperatorTable.COUNT, distinct, false, null,
+ ImmutableList.of(), alias, ImmutableList.copyOf(operands));
+ }
+
+ /** Creates a call to the {@code COUNT} aggregate function,
+ * optionally distinct and with an alias. */
+ public AggCall count(boolean distinct, String alias,
+ Iterable<? extends RexNode> operands) {
+ return aggregateCall(SqlStdOperatorTable.COUNT, distinct, false, null,
+ ImmutableList.of(), alias, ImmutableList.copyOf(operands));
}
/** Creates a call to the {@code COUNT(*)} aggregate function. */
@@ -848,8 +864,8 @@ public class RelBuilder {
/** Creates a call to the {@code SUM} aggregate function,
* optionally distinct and with an alias. */
public AggCall sum(boolean distinct, String alias, RexNode operand) {
- return aggregateCall(SqlStdOperatorTable.SUM, distinct, null, alias,
- ImmutableList.of(operand));
+ return aggregateCall(SqlStdOperatorTable.SUM, distinct, false, null,
+ ImmutableList.of(), alias, ImmutableList.of(operand));
}
/** Creates a call to the {@code AVG} aggregate function. */
@@ -860,8 +876,8 @@ public class RelBuilder {
/** Creates a call to the {@code AVG} aggregate function,
* optionally distinct and with an alias. */
public AggCall avg(boolean distinct, String alias, RexNode operand) {
- return aggregateCall(SqlStdOperatorTable.AVG, distinct, null, alias,
- ImmutableList.of(operand));
+ return aggregateCall(SqlStdOperatorTable.AVG, distinct, false, null,
+ ImmutableList.of(), alias, ImmutableList.of(operand));
}
/** Creates a call to the {@code MIN} aggregate function. */
@@ -872,8 +888,8 @@ public class RelBuilder {
/** Creates a call to the {@code MIN} aggregate function,
* optionally with an alias. */
public AggCall min(String alias, RexNode operand) {
- return aggregateCall(SqlStdOperatorTable.MIN, false, null, alias,
- ImmutableList.of(operand));
+ return aggregateCall(SqlStdOperatorTable.MIN, false, false, null,
+ ImmutableList.of(), alias, ImmutableList.of(operand));
}
/** Creates a call to the {@code MAX} aggregate function,
@@ -884,8 +900,8 @@ public class RelBuilder {
/** Creates a call to the {@code MAX} aggregate function. */
public AggCall max(String alias, RexNode operand) {
- return aggregateCall(SqlStdOperatorTable.MAX, false, null, alias,
- ImmutableList.of(operand));
+ return aggregateCall(SqlStdOperatorTable.MAX, false, false, null,
+ ImmutableList.of(), alias, ImmutableList.of(operand));
}
// Methods for patterns
@@ -1447,10 +1463,17 @@ public class RelBuilder {
if (aggCall1.filter != null && !aggCall1.aggFunction.allowsFilter()) {
throw new IllegalArgumentException("FILTER not allowed");
}
+ RelCollation collation =
+ RelCollations.of(aggCall1.orderKeys
+ .stream()
+ .map(orderKey ->
+ collation(orderKey, RelFieldCollation.Direction.ASCENDING,
+ null, Collections.emptyList()))
+ .collect(Collectors.toList()));
aggregateCall =
AggregateCall.create(aggCall1.aggFunction, aggCall1.distinct,
- aggCall1.approximate, args,
- filterArg, groupSet.cardinality(), r, null, aggCall1.alias);
+ aggCall1.approximate, args, filterArg, collation,
+ groupSet.cardinality(), r, null, aggCall1.alias);
} else {
aggregateCall = ((AggCallImpl2) aggCall).aggregateCall;
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
index c7a3d22..8462d95 100644
--- a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
+++ b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
@@ -17,6 +17,10 @@
package org.apache.calcite.util;
import org.apache.calcite.DataContext;
+import org.apache.calcite.adapter.enumerable.AggregateLambdaFactory;
+import org.apache.calcite.adapter.enumerable.OrderedAggregateLambdaFactory;
+import org.apache.calcite.adapter.enumerable.SequencedAdderAggregateLambdaFactory;
+import org.apache.calcite.adapter.enumerable.SourceSorter;
import org.apache.calcite.adapter.java.ReflectiveSchema;
import org.apache.calcite.adapter.jdbc.JdbcSchema;
import org.apache.calcite.avatica.util.DateTimeUtils;
@@ -433,7 +437,21 @@ public enum BuiltInMethod {
CONTEXT_ROOT(Context.class, "root", true),
DATA_CONTEXT_GET_QUERY_PROVIDER(DataContext.class, "getQueryProvider"),
METADATA_REL(Metadata.class, "rel"),
- STRUCT_ACCESS(SqlFunctions.class, "structAccess", Object.class, int.class, String.class);
+ STRUCT_ACCESS(SqlFunctions.class, "structAccess", Object.class, int.class,
+ String.class),
+ SOURCE_SORTER(SourceSorter.class, Function2.class, Function1.class,
+ Comparator.class),
+ ORDERED_AGGREGATE_LAMBDA_FACTORY(OrderedAggregateLambdaFactory.class,
+ Function0.class, List.class),
+ SEQUENCED_ADDER_AGGREGATE_LAMBDA_FACTORY(SequencedAdderAggregateLambdaFactory.class,
+ Function0.class, List.class),
+ AGG_LAMBDA_FACTORY_ACC_INITIALIZER(AggregateLambdaFactory.class,
+ "accumulatorInitializer"),
+ AGG_LAMBDA_FACTORY_ACC_ADDER(AggregateLambdaFactory.class, "accumulatorAdder"),
+ AGG_LAMBDA_FACTORY_ACC_RESULT_SELECTOR(AggregateLambdaFactory.class,
+ "resultSelector", Function2.class),
+ AGG_LAMBDA_FACTORY_ACC_SINGLE_GROUP_RESULT_SELECTOR(AggregateLambdaFactory.class,
+ "singleGroupResultSelector", Function1.class);
public final Method method;
public final Constructor constructor;
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/util/Optionality.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/util/Optionality.java b/core/src/main/java/org/apache/calcite/util/Optionality.java
new file mode 100644
index 0000000..43da19d
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/util/Optionality.java
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+package org.apache.calcite.util;
+
+/**
+ * Four states that describe whether a particular behavior or
+ * property is allowed and/or not allowed.
+ */
+public enum Optionality {
+ /** A property is <em>mandatory</em> if an instance must possess it;
+ * it is an error if it does not. */
+ MANDATORY,
+
+ /** A property is <em>optional</em> if an instance may or may not possess it;
+ * neither state is an error. */
+ OPTIONAL,
+
+ /** A property is <em>ignored</em> if an instance may or may not possess it;
+ * if it possesses the property, the effect is as if it does not. */
+ IGNORED,
+
+ /** A property is <em>forbidden</em> if an instance must not possess it;
+ * it is an error if the instance has the property. */
+ FORBIDDEN
+}
+
+// End Optionality.java
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties
----------------------------------------------------------------------
diff --git a/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties b/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties
index faefd68..edb93b9 100644
--- a/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties
+++ b/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties
@@ -109,6 +109,9 @@ AggregateIllegalInClause=Aggregate expression is illegal in {0} clause
WindowedAggregateIllegalInClause=Windowed aggregate expression is illegal in {0} clause
NestedAggIllegal=Aggregate expressions cannot be nested
AggregateInFilterIllegal=FILTER must not contain aggregate expression
+AggregateInWithinGroupIllegal=WITHIN GROUP must not contain aggregate expression
+AggregateMissingWithinGroupClause=Aggregate expression ''{0}'' must contain a within group clause
+WithinGroupClauseIllegalInAggregate=Aggregate expression ''{0}'' must not contain a within group clause
AggregateIllegalInOrderBy=Aggregate expression is illegal in ORDER BY clause of non-aggregating SELECT
CondMustBeBoolean={0} clause must be a condition
HavingMustBeBoolean=HAVING clause must be a condition
@@ -146,6 +149,7 @@ OrderByOverlap=ORDER BY not allowed in both base and referenced windows
RefWindowWithFrame=Referenced window cannot have framing declarations
TypeNotSupported=Type ''{0}'' is not supported
FunctionQuantifierNotAllowed=DISTINCT/ALL not allowed with {0} function
+WithinGroupNotAllowed=WITHIN GROUP not allowed with {0} function
SomeButNotAllArgumentsAreNamed=Some but not all arguments are named
DuplicateArgumentName=Duplicate argument name ''{0}''
DefaultForOptionalParameter=DEFAULT is only allowed for optional parameters
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/test/java/org/apache/calcite/plan/RelWriterTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/plan/RelWriterTest.java b/core/src/test/java/org/apache/calcite/plan/RelWriterTest.java
index c411888..7b63497 100644
--- a/core/src/test/java/org/apache/calcite/plan/RelWriterTest.java
+++ b/core/src/test/java/org/apache/calcite/plan/RelWriterTest.java
@@ -17,6 +17,7 @@
package org.apache.calcite.plan;
import org.apache.calcite.adapter.java.ReflectiveSchema;
+import org.apache.calcite.rel.RelCollations;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.core.AggregateCall;
import org.apache.calcite.rel.externalize.RelJsonReader;
@@ -140,10 +141,10 @@ public class RelWriterTest {
ImmutableList.of(
AggregateCall.create(SqlStdOperatorTable.COUNT,
true, false, ImmutableList.of(1), -1,
- bigIntType, "c"),
+ RelCollations.EMPTY, bigIntType, "c"),
AggregateCall.create(SqlStdOperatorTable.COUNT,
false, false, ImmutableList.of(), -1,
- bigIntType, "d")));
+ RelCollations.EMPTY, bigIntType, "d")));
aggregate.explain(writer);
return writer.asString();
});
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/test/java/org/apache/calcite/plan/volcano/TraitPropagationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/plan/volcano/TraitPropagationTest.java b/core/src/test/java/org/apache/calcite/plan/volcano/TraitPropagationTest.java
index 2822c1c..e901868 100644
--- a/core/src/test/java/org/apache/calcite/plan/volcano/TraitPropagationTest.java
+++ b/core/src/test/java/org/apache/calcite/plan/volcano/TraitPropagationTest.java
@@ -165,7 +165,8 @@ public class TraitPropagationTest {
// aggregate on s, count
AggregateCall aggCall = AggregateCall.create(SqlStdOperatorTable.COUNT,
- false, false, Collections.singletonList(1), -1, sqlBigInt, "cnt");
+ false, false, Collections.singletonList(1), -1, RelCollations.EMPTY,
+ sqlBigInt, "cnt");
RelNode agg = new LogicalAggregate(cluster,
cluster.traitSetOf(Convention.NONE), project, false,
ImmutableBitSet.of(0), null, Collections.singletonList(aggCall));
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
index 94b98b7..7a5181e 100644
--- a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
+++ b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
@@ -274,8 +274,8 @@ public class RelToSqlConverterTest {
final RelNode root = builder
.scan("EMP")
.aggregate(builder.groupKey(),
- builder.aggregateCall(SqlStdOperatorTable.SUM0, false, null, "s",
- builder.field(3)))
+ builder.aggregateCall(SqlStdOperatorTable.SUM0, builder.field(3))
+ .as("s"))
.build();
final String expectedMysql = "SELECT COALESCE(SUM(`MGR`), 0) AS `s`\n"
+ "FROM `scott`.`EMP`";
@@ -2763,6 +2763,53 @@ public class RelToSqlConverterTest {
callsUnparseCallOnSqlSelect[0], is(true));
}
+ @Test public void testWithinGroup1() {
+ final String query = "select \"product_class_id\", collect(\"net_weight\") "
+ + "within group (order by \"net_weight\" desc) "
+ + "from \"product\" group by \"product_class_id\"";
+ final String expected = "SELECT \"product_class_id\", COLLECT(\"net_weight\") "
+ + "WITHIN GROUP (ORDER BY \"net_weight\" DESC)\n"
+ + "FROM \"foodmart\".\"product\"\n"
+ + "GROUP BY \"product_class_id\"";
+ sql(query).ok(expected);
+ }
+
+ @Test public void testWithinGroup2() {
+ final String query = "select \"product_class_id\", collect(\"net_weight\") "
+ + "within group (order by \"low_fat\", \"net_weight\" desc nulls last) "
+ + "from \"product\" group by \"product_class_id\"";
+ final String expected = "SELECT \"product_class_id\", COLLECT(\"net_weight\") "
+ + "WITHIN GROUP (ORDER BY \"low_fat\", \"net_weight\" DESC NULLS LAST)\n"
+ + "FROM \"foodmart\".\"product\"\n"
+ + "GROUP BY \"product_class_id\"";
+ sql(query).ok(expected);
+ }
+
+ @Test public void testWithinGroup3() {
+ final String query = "select \"product_class_id\", collect(\"net_weight\") "
+ + "within group (order by \"net_weight\" desc), "
+ + "min(\"low_fat\")"
+ + "from \"product\" group by \"product_class_id\"";
+ final String expected = "SELECT \"product_class_id\", COLLECT(\"net_weight\") "
+ + "WITHIN GROUP (ORDER BY \"net_weight\" DESC), MIN(\"low_fat\")\n"
+ + "FROM \"foodmart\".\"product\"\n"
+ + "GROUP BY \"product_class_id\"";
+ sql(query).ok(expected);
+ }
+
+ @Test public void testWithinGroup4() {
+ // filter in AggregateCall is not unparsed
+ final String query = "select \"product_class_id\", collect(\"net_weight\") "
+ + "within group (order by \"net_weight\" desc) filter (where \"net_weight\" > 0)"
+ + "from \"product\" group by \"product_class_id\"";
+ final String expected = "SELECT \"product_class_id\", COLLECT(\"net_weight\") "
+ + "WITHIN GROUP (ORDER BY \"net_weight\" DESC)\n"
+ + "FROM \"foodmart\".\"product\"\n"
+ + "GROUP BY \"product_class_id\"";
+ sql(query).ok(expected);
+ }
+
+
/** Fluid interface to run tests. */
static class Sql {
private final SchemaPlus schema;
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java b/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java
index 6ad2769..d8dddc3 100644
--- a/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java
+++ b/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java
@@ -8194,6 +8194,64 @@ public class SqlParserTest {
sql(sql).ok(expected);
}
+ @Test public void testWithinGroupClause1() {
+ final String sql = "select col1,\n"
+ + " collect(col2) within group (order by col3)\n"
+ + "from t\n"
+ + "order by col1 limit 10";
+ final String expected = "SELECT `COL1`,"
+ + " (COLLECT(`COL2`) WITHIN GROUP (ORDER BY `COL3`))\n"
+ + "FROM `T`\n"
+ + "ORDER BY `COL1`\n"
+ + "FETCH NEXT 10 ROWS ONLY";
+ sql(sql).ok(expected);
+ }
+
+ @Test public void testWithinGroupClause2() {
+ final String sql = "select collect(col2) within group (order by col3)\n"
+ + "from t\n"
+ + "order by col1 limit 10";
+ final String expected = "SELECT"
+ + " (COLLECT(`COL2`) WITHIN GROUP (ORDER BY `COL3`))\n"
+ + "FROM `T`\n"
+ + "ORDER BY `COL1`\n"
+ + "FETCH NEXT 10 ROWS ONLY";
+ sql(sql).ok(expected);
+ }
+
+ @Test public void testWithinGroupClause3() {
+ final String sql = "select collect(col2) within group (^)^ "
+ + "from t order by col1 limit 10";
+ sql(sql).fails("(?s).*Encountered \"\\)\" at line 1, column 36\\..*");
+ }
+
+ @Test public void testWithinGroupClause4() {
+ final String sql = "select col1,\n"
+ + " collect(col2) within group (order by col3, col4)\n"
+ + "from t\n"
+ + "order by col1 limit 10";
+ final String expected = "SELECT `COL1`,"
+ + " (COLLECT(`COL2`) WITHIN GROUP (ORDER BY `COL3`, `COL4`))\n"
+ + "FROM `T`\n"
+ + "ORDER BY `COL1`\n"
+ + "FETCH NEXT 10 ROWS ONLY";
+ sql(sql).ok(expected);
+ }
+
+ @Test public void testWithinGroupClause5() {
+ final String sql = "select col1,\n"
+ + " collect(col2) within group (\n"
+ + " order by col3 desc nulls first, col4 asc nulls last)\n"
+ + "from t\n"
+ + "order by col1 limit 10";
+ final String expected = "SELECT `COL1`, (COLLECT(`COL2`) "
+ + "WITHIN GROUP (ORDER BY `COL3` DESC NULLS FIRST, `COL4` NULLS LAST))\n"
+ + "FROM `T`\n"
+ + "ORDER BY `COL1`\n"
+ + "FETCH NEXT 10 ROWS ONLY";
+ sql(sql).ok(expected);
+ }
+
//~ Inner Interfaces -------------------------------------------------------
/**
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java b/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java
index 4484762..da7d099 100644
--- a/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java
+++ b/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java
@@ -5622,6 +5622,8 @@ public abstract class SqlOperatorBaseTest {
final String[] values = {"0", "CAST(null AS INTEGER)", "2", "2"};
tester.checkAgg("collect(x)", values,
Collections.singletonList("[0, 2, 2]"), (double) 0);
+ tester.checkAgg("collect(x) within group(order by x desc)", values,
+ Collections.singletonList("[2, 2, 0]"), (double) 0);
Object result1 = -3;
if (!enable) {
return;
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/test/java/org/apache/calcite/test/JdbcTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/JdbcTest.java b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
index cd87e5f..888ed6c 100644
--- a/core/src/test/java/org/apache/calcite/test/JdbcTest.java
+++ b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
@@ -6657,6 +6657,81 @@ public class JdbcTest {
connection.close();
}
+
+ /** Test case for
+ * <a href="https://issues.apache.org/jira/browse/CALCITE-2224">[CALCITE-2224]
+ * WITHIN GROUP clause for aggregate functions</a>. */
+ @Test public void testWithinGroupClause1() {
+ final String sql = "select X,\n"
+ + " collect(Y) within group (order by Y desc) as \"SET\"\n"
+ + "from (values (1, 'a'), (1, 'b'),\n"
+ + " (3, 'c'), (3, 'd')) AS t(X, Y)\n"
+ + "group by X\n"
+ + "limit 10";
+ CalciteAssert.that().query(sql)
+ .returnsUnordered("X=1; SET=[b, a]",
+ "X=3; SET=[d, c]");
+ }
+
+ @Test public void testWithinGroupClause2() {
+ final String sql = "select X,\n"
+ + " collect(Y) within group (order by Y desc) as SET_1,\n"
+ + " collect(Y) within group (order by Y asc) as SET_2\n"
+ + "from (values (1, 'a'), (1, 'b'), (3, 'c'), (3, 'd')) AS t(X, Y)\n"
+ + "group by X\n"
+ + "limit 10";
+ CalciteAssert
+ .that()
+ .query(sql)
+ .returnsUnordered("X=1; SET_1=[b, a]; SET_2=[a, b]",
+ "X=3; SET_1=[d, c]; SET_2=[c, d]");
+ }
+
+ @Test public void testWithinGroupClause3() {
+ final String sql = "select"
+ + " collect(Y) within group (order by Y desc) as SET_1,\n"
+ + " collect(Y) within group (order by Y asc) as SET_2\n"
+ + "from (values (1, 'a'), (1, 'b'), (3, 'c'), (3, 'd')) AS t(X, Y)\n"
+ + "limit 10";
+ CalciteAssert.that().query(sql)
+ .returns("SET_1=[d, c, b, a]; SET_2=[a, b, c, d]\n");
+ }
+
+ @Test public void testWithinGroupClause4() {
+ final String sql = "select"
+ + " collect(Y) within group (order by Y desc) as SET_1,\n"
+ + " collect(Y) within group (order by Y asc) as SET_2\n"
+ + "from (values (1, 'a'), (1, 'b'), (3, 'c'), (3, 'd')) AS t(X, Y)\n"
+ + "group by X\n"
+ + "limit 10";
+ CalciteAssert.that().query(sql)
+ .returnsUnordered("SET_1=[b, a]; SET_2=[a, b]",
+ "SET_1=[d, c]; SET_2=[c, d]");
+ }
+
+ @Test public void testWithinGroupClause5() {
+ CalciteAssert
+ .that()
+ .query("select collect(array[X, Y])\n"
+ + " within group (order by Y desc) as \"SET\"\n"
+ + "from (values ('b', 'a'), ('a', 'b'), ('a', 'c'),\n"
+ + " ('a', 'd')) AS t(X, Y)\n"
+ + "limit 10")
+ .returns("SET=[[a, d], [a, c], [a, b], [b, a]]\n");
+ }
+
+ @Test public void testWithinGroupClause6() {
+ final String sql = "select collect(\"commission\")"
+ + " within group (order by \"commission\")\n"
+ + "from \"hr\".\"emps\"";
+ CalciteAssert.that()
+ .with(CalciteAssert.Config.REGULAR)
+ .query(sql)
+ .explainContains("EnumerableAggregate(group=[{}], "
+ + "EXPR$0=[COLLECT($4) WITHIN GROUP ([4])])")
+ .returns("EXPR$0=[250, 500, 1000]\n");
+ }
+
private static String sums(int n, boolean c) {
final StringBuilder b = new StringBuilder();
for (int i = 0; i < n; i++) {
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java b/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java
index ec94d94..0c216b3 100644
--- a/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java
@@ -1350,7 +1350,7 @@ public class RelMetadataTest extends SqlToRelTestBase {
ImmutableList.of(
AggregateCall.create(SqlStdOperatorTable.COUNT,
false, false, ImmutableIntList.of(),
- -1, 2, join, null, null)));
+ -1, RelCollations.EMPTY, 2, join, null, null)));
rowSize = mq.getAverageRowSize(aggregate);
columnSizes = mq.getAverageColumnSizes(aggregate);
assertThat(columnSizes.size(), equalTo(3));
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java b/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
index 92f98a8..94707fe 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
@@ -2862,6 +2862,34 @@ public class SqlToRelConverterTest extends SqlToRelTestBase {
sql(sql).with(tester).ok();
}
+ @Test public void testWithinGroup1() {
+ final String sql = "select deptno,\n"
+ + " collect(empno) within group (order by deptno, hiredate desc)\n"
+ + "from emp\n"
+ + "group by deptno";
+ sql(sql).ok();
+ }
+
+ @Test public void testWithinGroup2() {
+ final String sql = "select dept.deptno,\n"
+ + " collect(sal) within group (order by sal desc) as s,\n"
+ + " collect(sal) within group (order by 1)as s1,\n"
+ + " collect(sal) within group (order by sal)\n"
+ + " filter (where sal > 2000) as s2\n"
+ + "from emp\n"
+ + "join dept using (deptno)\n"
+ + "group by dept.deptno";
+ sql(sql).ok();
+ }
+
+ @Test public void testWithinGroup3() {
+ final String sql = "select deptno,\n"
+ + " collect(empno) within group (order by empno not in (1, 2)), count(*)\n"
+ + "from emp\n"
+ + "group by deptno";
+ sql(sql).ok();
+ }
+
/**
* Visitor that checks that every {@link RelNode} in a tree is valid.
*
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
index abfdc57..a1d4233 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
@@ -7116,6 +7116,40 @@ public class SqlValidatorTest extends SqlValidatorTestCase {
.fails("FILTER must not contain aggregate expression");
}
+ @Test public void testWithinGroup() {
+ sql("select deptno,\n"
+ + " collect(empno) within group(order by 1)\n"
+ + "from emp\n"
+ + "group by deptno").ok();
+ sql("select collect(empno) within group(order by 1)\n"
+ + "from emp\n"
+ + "group by ()").ok();
+ sql("select deptno,\n"
+ + " collect(empno) within group(order by deptno)\n"
+ + "from emp\n"
+ + "group by deptno").ok();
+ sql("select deptno,\n"
+ + " collect(empno) within group(order by deptno, hiredate desc)\n"
+ + "from emp\n"
+ + "group by deptno").ok();
+ sql("select deptno,\n"
+ + " collect(empno) within group(\n"
+ + " order by cast(deptno as varchar), hiredate desc)\n"
+ + "from emp\n"
+ + "group by deptno").ok();
+ sql("select collect(empno) within group(order by 1)\n"
+ + "from emp\n"
+ + "group by deptno").ok();
+ sql("select collect(empno) within group(order by 1)\n"
+ + "from emp").ok();
+ sql("select ^power(deptno, 1) within group(order by 1)^ from emp")
+ .fails("(?s).*WITHIN GROUP not allowed with POWER function.*");
+ sql("select ^collect(empno)^ within group(order by count(*))\n"
+ + "from emp\n"
+ + "group by deptno")
+ .fails("WITHIN GROUP must not contain aggregate expression");
+ }
+
@Test public void testCorrelatingVariables() {
// reference to unqualified correlating column
check("select * from emp where exists (\n"
@@ -8645,6 +8679,7 @@ public class SqlValidatorTest extends SqlValidatorTestCase {
+ "NEXT_VALUE -\n"
+ "PATTERN_EXCLUDE -\n"
+ "PATTERN_PERMUTE -\n"
+ + "WITHIN GROUP left\n"
+ "\n"
+ "PATTERN_QUANTIFIER -\n"
+ "\n"
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/test/java/org/apache/calcite/tools/PlannerTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/tools/PlannerTest.java b/core/src/test/java/org/apache/calcite/tools/PlannerTest.java
index e0ed1c1..3b68d16 100644
--- a/core/src/test/java/org/apache/calcite/tools/PlannerTest.java
+++ b/core/src/test/java/org/apache/calcite/tools/PlannerTest.java
@@ -72,6 +72,7 @@ import org.apache.calcite.sql.util.ListSqlOperatorTable;
import org.apache.calcite.sql.validate.SqlValidator;
import org.apache.calcite.sql.validate.SqlValidatorScope;
import org.apache.calcite.test.CalciteAssert;
+import org.apache.calcite.util.Optionality;
import org.apache.calcite.util.Util;
import com.google.common.base.Throwables;
@@ -1060,7 +1061,8 @@ public class PlannerTest {
public static class MyCountAggFunction extends SqlAggFunction {
public MyCountAggFunction() {
super("MY_COUNT", null, SqlKind.OTHER_FUNCTION, ReturnTypes.BIGINT, null,
- OperandTypes.ANY, SqlFunctionCategory.NUMERIC, false, false);
+ OperandTypes.ANY, SqlFunctionCategory.NUMERIC, false, false,
+ Optionality.FORBIDDEN);
}
@SuppressWarnings("deprecation")
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
----------------------------------------------------------------------
diff --git a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
index 35d69e5..db6d2a9 100644
--- a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
@@ -5296,4 +5296,42 @@ LogicalProject(ANYEMPNO=[$1])
]]>
</Resource>
</TestCase>
+ <TestCase name="testWithinGroup1">
+ <Resource name="sql">
+ <![CDATA[select deptno, collect(empno) within group (order by deptno, hiredate desc) from emp group by deptno]]>
+ </Resource>
+ <Resource name="plan">
+ <![CDATA[
+LogicalAggregate(group=[{0}], EXPR$1=[COLLECT($1) WITHIN GROUP ([1, 2 DESC])])
+ LogicalProject(DEPTNO=[$7], EMPNO=[$0], HIREDATE=[$4])
+ LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+ </Resource>
+ </TestCase>
+ <TestCase name="testWithinGroup2">
+ <Resource name="sql">
+ <![CDATA[select dept.deptno, collect(sal) within group (order by sal desc) as s, collect(sal) within group (order by 1)as s1, collect(sal) within group (order by sal) filter (where sal > 2000) as s2 from emp join dept using (deptno) group by dept.deptn]]>
+ </Resource>
+ <Resource name="plan">
+ <![CDATA[
+LogicalAggregate(group=[{0}], S=[COLLECT($1) WITHIN GROUP ([1 DESC])], S1=[COLLECT($1) WITHIN GROUP ([2])], S2=[COLLECT($1) WITHIN GROUP ([1]) FILTER $3])
+ LogicalProject(DEPTNO=[$9], SAL=[$5], $f2=[1], $f3=[>($5, 2000)])
+ LogicalJoin(condition=[=($7, $9)], joinType=[inner])
+ LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+ LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
+]]>
+ </Resource>
+ </TestCase>
+ <TestCase name="testWithinGroup3">
+ <Resource name="sql">
+ <![CDATA[select deptno, collect(empno) filter (where empno not in (1, 2)), count(*) from emp group by deptno]]>
+ </Resource>
+ <Resource name="plan">
+ <![CDATA[
+LogicalAggregate(group=[{0}], EXPR$1=[COLLECT($1) WITHIN GROUP ([2])], EXPR$2=[COUNT()])
+ LogicalProject(DEPTNO=[$7], EMPNO=[$0], $f2=[AND(<>($0, 1), <>($0, 2))])
+ LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+ </Resource>
+ </TestCase>
</Root>
http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/test/resources/sql/agg.iq
----------------------------------------------------------------------
diff --git a/core/src/test/resources/sql/agg.iq b/core/src/test/resources/sql/agg.iq
index 24b75b3..d093a8a 100755
--- a/core/src/test/resources/sql/agg.iq
+++ b/core/src/test/resources/sql/agg.iq
@@ -2381,4 +2381,127 @@ group by MONTH(HIREDATE);
!ok
+# [CALCITE-2224] WITHIN GROUP clause for aggregate functions
+select deptno, collect(empno) within group (order by empno asc) as empnos
+from "scott".emp
+group by deptno;
+
++--------+--------------------------------------+
+| DEPTNO | EMPNOS |
++--------+--------------------------------------+
+| 10 | [7782, 7839, 7934] |
+| 20 | [7369, 7566, 7788, 7876, 7902] |
+| 30 | [7499, 7521, 7654, 7698, 7844, 7900] |
++--------+--------------------------------------+
+(3 rows)
+
+!ok
+EnumerableAggregate(group=[{7}], EMPNOS=[COLLECT($0) WITHIN GROUP ([0])])
+ EnumerableTableScan(table=[[scott, EMP]])
+!plan
+
+select deptno, collect(empno) within group (order by empno desc) as empnos
+from "scott".emp
+group by deptno;
+
++--------+--------------------------------------+
+| DEPTNO | EMPNOS |
++--------+--------------------------------------+
+| 10 | [7934, 7839, 7782] |
+| 20 | [7902, 7876, 7788, 7566, 7369] |
+| 30 | [7900, 7844, 7698, 7654, 7521, 7499] |
++--------+--------------------------------------+
+(3 rows)
+
+!ok
+EnumerableAggregate(group=[{7}], EMPNOS=[COLLECT($0) WITHIN GROUP ([0 DESC])])
+ EnumerableTableScan(table=[[scott, EMP]])
+!plan
+
+select deptno, collect(empno) within group (order by empno desc)
+filter (where empno > 7500) as empnos
+from "scott".emp
+group by deptno;
+
++--------+--------------------------------+
+| DEPTNO | EMPNOS |
++--------+--------------------------------+
+| 10 | [7934, 7839, 7782] |
+| 20 | [7902, 7876, 7788, 7566] |
+| 30 | [7900, 7844, 7698, 7654, 7521] |
++--------+--------------------------------+
+(3 rows)
+
+!ok
+EnumerableAggregate(group=[{0}], EMPNOS=[COLLECT($1) WITHIN GROUP ([1 DESC]) FILTER $2])
+ EnumerableCalc(expr#0..7=[{inputs}], expr#8=[7500], expr#9=[>($t0, $t8)], DEPTNO=[$t7], EMPNO=[$t0], $f2=[$t9])
+ EnumerableTableScan(table=[[scott, EMP]])
+!plan
+
+select deptno, collect(empno) within group (order by empno desc) as empnos1,
+collect(empno) within group (order by empno asc) as empnos2
+from "scott".emp
+group by deptno;
+
++--------+--------------------------------------+--------------------------------------+
+| DEPTNO | EMPNOS1 | EMPNOS2 |
++--------+--------------------------------------+--------------------------------------+
+| 10 | [7934, 7839, 7782] | [7782, 7839, 7934] |
+| 20 | [7902, 7876, 7788, 7566, 7369] | [7369, 7566, 7788, 7876, 7902] |
+| 30 | [7900, 7844, 7698, 7654, 7521, 7499] | [7499, 7521, 7654, 7698, 7844, 7900] |
++--------+--------------------------------------+--------------------------------------+
+(3 rows)
+
+!ok
+EnumerableAggregate(group=[{7}], EMPNOS1=[COLLECT($0) WITHIN GROUP ([0 DESC])], EMPNOS2=[COLLECT($0) WITHIN GROUP ([0])])
+ EnumerableTableScan(table=[[scott, EMP]])
+!plan
+
+# Aggregate WITHIN GROUP with JOIN
+select dept.deptno,
+ collect(sal) within group (order by sal desc) as s,
+ collect(sal) within group (order by 1)as s1,
+ collect(sal) within group (order by sal) filter (where sal > 2000) as s2
+from "scott".emp
+join "scott".dept using (deptno)
+group by dept.deptno;
+
++--------+-------------------------------------------------------+-------------------------------------------------------+-----------------------------+
+| DEPTNO | S | S1 | S2 |
++--------+-------------------------------------------------------+-------------------------------------------------------+-----------------------------+
+| 10 | [5000.00, 2450.00, 1300.00] | [2450.00, 5000.00, 1300.00] | [2450.00, 5000.00] |
+| 20 | [3000.00, 3000.00, 2975.00, 1100.00, 800.00] | [800.00, 2975.00, 3000.00, 1100.00, 3000.00] | [2975.00, 3000.00, 3000.00] |
+| 30 | [2850.00, 1600.00, 1500.00, 1250.00, 1250.00, 950.00] | [1600.00, 1250.00, 1250.00, 2850.00, 1500.00, 950.00] | [2850.00] |
++--------+-------------------------------------------------------+-------------------------------------------------------+-----------------------------+
+(3 rows)
+
+!ok
+EnumerableAggregate(group=[{0}], S=[COLLECT($1) WITHIN GROUP ([1 DESC])], S1=[COLLECT($1) WITHIN GROUP ([2])], S2=[COLLECT($1) WITHIN GROUP ([1]) FILTER $3])
+ EnumerableCalc(expr#0..3=[{inputs}], expr#4=[1], expr#5=[2000], expr#6=[>($t2, $t5)], expr#7=[IS TRUE($t6)], DEPTNO=[$t0], SAL=[$t2], $f2=[$t4], $f3=[$t7])
+ EnumerableJoin(condition=[=($0, $3)], joinType=[inner])
+ EnumerableCalc(expr#0..2=[{inputs}], DEPTNO=[$t0])
+ EnumerableTableScan(table=[[scott, DEPT]])
+ EnumerableCalc(expr#0..7=[{inputs}], EMPNO=[$t0], SAL=[$t5], DEPTNO=[$t7])
+ EnumerableTableScan(table=[[scott, EMP]])
+!plan
+
+select deptno, collect(empno + 1) within group (order by 1) as empnos
+from "scott".emp
+group by deptno;
+
++--------+--------------------------------------+
+| DEPTNO | EMPNOS |
++--------+--------------------------------------+
+| 10 | [7783, 7840, 7935] |
+| 20 | [7370, 7567, 7789, 7877, 7903] |
+| 30 | [7500, 7522, 7655, 7699, 7845, 7901] |
++--------+--------------------------------------+
+(3 rows)
+
+!ok
+EnumerableAggregate(group=[{0}], EMPNOS=[COLLECT($1) WITHIN GROUP ([2])])
+ EnumerableCalc(expr#0..7=[{inputs}], expr#8=[1], expr#9=[+($t0, $t8)], DEPTNO=[$t7], $f1=[$t9], $f2=[$t8])
+ EnumerableTableScan(table=[[scott, EMP]])
+!plan
+
# End agg.iq