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 2015/10/07 21:15:26 UTC
[3/7] incubator-calcite git commit: [CALCITE-879] COLLECT aggregate
function
[CALCITE-879] COLLECT aggregate function
Allow multisets and maps to be instances of RexLiteral
Add RelBuilder.values
Project: http://git-wip-us.apache.org/repos/asf/incubator-calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-calcite/commit/8f2b32cd
Tree: http://git-wip-us.apache.org/repos/asf/incubator-calcite/tree/8f2b32cd
Diff: http://git-wip-us.apache.org/repos/asf/incubator-calcite/diff/8f2b32cd
Branch: refs/heads/master
Commit: 8f2b32cda8ae95e66490a83c8ef17411e66e1095
Parents: d618cd9
Author: Julian Hyde <jh...@apache.org>
Authored: Tue Oct 6 13:54:23 2015 -0700
Committer: Julian Hyde <jh...@apache.org>
Committed: Tue Oct 6 17:08:06 2015 -0700
----------------------------------------------------------------------
.../calcite/adapter/enumerable/RexImpTable.java | 24 +++++
.../java/org/apache/calcite/rex/RexBuilder.java | 33 ++++++-
.../java/org/apache/calcite/rex/RexLiteral.java | 18 ++++
.../calcite/sql/fun/SqlStdOperatorTable.java | 10 +--
.../apache/calcite/sql/type/ReturnTypes.java | 10 +++
.../calcite/sql/type/SqlTypeTransforms.java | 15 ++++
.../org/apache/calcite/tools/RelBuilder.java | 95 +++++++++++++++++---
.../calcite/sql/test/SqlOperatorBaseTest.java | 29 +++++-
.../apache/calcite/test/SqlValidatorTest.java | 5 +-
core/src/test/resources/sql/agg.oq | 48 ++++++++++
site/_docs/reference.md | 1 +
11 files changed, 262 insertions(+), 26 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/8f2b32cd/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
index 29c107f..5209fe3 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
@@ -96,6 +96,7 @@ import static org.apache.calcite.sql.fun.SqlStdOperatorTable.CAST;
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.CEIL;
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.CHARACTER_LENGTH;
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.CHAR_LENGTH;
+import static org.apache.calcite.sql.fun.SqlStdOperatorTable.COLLECT;
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.CONCAT;
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.COUNT;
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.CURRENT_DATE;
@@ -323,6 +324,7 @@ public class RexImpTable {
aggMap.put(MIN, minMax);
aggMap.put(MAX, minMax);
aggMap.put(SINGLE_VALUE, constructorSupplier(SingleValueImplementor.class));
+ aggMap.put(COLLECT, constructorSupplier(CollectImplementor.class));
winAggMap.put(RANK, constructorSupplier(RankImplementor.class));
winAggMap.put(DENSE_RANK, constructorSupplier(DenseRankImplementor.class));
winAggMap.put(ROW_NUMBER, constructorSupplier(RowNumberImplementor.class));
@@ -1071,6 +1073,28 @@ public class RexImpTable {
}
}
+ /** Implementor for the {@code COLLECT} aggregate function. */
+ static class CollectImplementor extends StrictAggImplementor {
+ @Override protected void implementNotNullReset(AggContext info,
+ AggResetContext reset) {
+ // acc[0] = new ArrayList();
+ reset.currentBlock().add(
+ Expressions.statement(
+ Expressions.assign(reset.accumulator().get(0),
+ Expressions.new_(ArrayList.class))));
+ }
+
+ @Override public void implementNotNullAdd(AggContext info,
+ AggAddContext add) {
+ // acc[0].add(arg);
+ add.currentBlock().add(
+ Expressions.statement(
+ Expressions.call(add.accumulator().get(0),
+ BuiltInMethod.COLLECTION_ADD.method,
+ add.arguments().get(0))));
+ }
+ }
+
/** Implementor for user-defined aggregate functions. */
public static class UserDefinedAggReflectiveImplementor
extends StrictAggImplementor {
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/8f2b32cd/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 36b27d6..b8c64e3 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexBuilder.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexBuilder.java
@@ -26,6 +26,7 @@ import org.apache.calcite.rel.core.AggregateCall;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeFactory;
import org.apache.calcite.rel.type.RelDataTypeField;
+import org.apache.calcite.runtime.FlatLists;
import org.apache.calcite.runtime.SqlFunctions;
import org.apache.calcite.sql.SqlAggFunction;
import org.apache.calcite.sql.SqlIntervalQualifier;
@@ -38,6 +39,7 @@ import org.apache.calcite.sql.fun.SqlStdOperatorTable;
import org.apache.calcite.sql.type.ArraySqlType;
import org.apache.calcite.sql.type.IntervalSqlType;
import org.apache.calcite.sql.type.MapSqlType;
+import org.apache.calcite.sql.type.MultisetSqlType;
import org.apache.calcite.sql.type.SqlTypeFamily;
import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.sql.type.SqlTypeUtil;
@@ -1249,7 +1251,7 @@ public class RexBuilder {
final MapSqlType mapType = (MapSqlType) type;
@SuppressWarnings("unchecked")
final Map<Object, Object> map = (Map) value;
- operands = new ArrayList<RexNode>();
+ operands = new ArrayList<>();
for (Map.Entry<Object, Object> entry : map.entrySet()) {
operands.add(
makeLiteral(entry.getKey(), mapType.getKeyType(), allowCast));
@@ -1261,12 +1263,39 @@ public class RexBuilder {
final ArraySqlType arrayType = (ArraySqlType) type;
@SuppressWarnings("unchecked")
final List<Object> listValue = (List) value;
- operands = new ArrayList<RexNode>();
+ operands = new ArrayList<>();
for (Object entry : listValue) {
operands.add(
makeLiteral(entry, arrayType.getComponentType(), allowCast));
}
return makeCall(SqlStdOperatorTable.ARRAY_VALUE_CONSTRUCTOR, operands);
+ case MULTISET:
+ final MultisetSqlType multisetType = (MultisetSqlType) type;
+ operands = new ArrayList<>();
+ for (Object entry : (List) value) {
+ final RexNode e = entry instanceof RexLiteral
+ ? (RexNode) entry
+ : makeLiteral(entry, multisetType.getComponentType(), allowCast);
+ operands.add(e);
+ }
+ if (allowCast) {
+ return makeCall(SqlStdOperatorTable.MULTISET_VALUE, operands);
+ } else {
+ return new RexLiteral((Comparable) FlatLists.of(operands), type,
+ type.getSqlTypeName());
+ }
+ case ROW:
+ operands = new ArrayList<>();
+ //noinspection unchecked
+ for (Pair<RelDataTypeField, Object> pair
+ : Pair.zip(type.getFieldList(), (List<Object>) value)) {
+ final RexNode e = pair.right instanceof RexLiteral
+ ? (RexNode) pair.right
+ : makeLiteral(pair.right, pair.left.getType(), allowCast);
+ operands.add(e);
+ }
+ return new RexLiteral((Comparable) FlatLists.of(operands), type,
+ type.getSqlTypeName());
case ANY:
return makeLiteral(value, guessType(value), allowCast);
default:
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/8f2b32cd/core/src/main/java/org/apache/calcite/rex/RexLiteral.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rex/RexLiteral.java b/core/src/main/java/org/apache/calcite/rex/RexLiteral.java
index 0f8dceb..bd07072 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexLiteral.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexLiteral.java
@@ -40,6 +40,7 @@ import java.io.StringWriter;
import java.math.BigDecimal;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
+import java.util.AbstractList;
import java.util.Calendar;
import java.util.List;
import java.util.Map;
@@ -236,6 +237,9 @@ public class RexLiteral extends RexNode {
&& (((NlsString) value).getCollation() != null);
case SYMBOL:
return value instanceof Enum;
+ case ROW:
+ case MULTISET:
+ return value instanceof List;
case ANY:
// Literal of type ANY is not legal. "CAST(2 AS ANY)" remains
// an integer literal surrounded by a cast function.
@@ -387,6 +391,20 @@ public class RexLiteral extends RexNode {
pw.print("null");
}
break;
+ case MULTISET:
+ case ROW:
+ @SuppressWarnings("unchecked") final List<RexLiteral> list = (List) value;
+ pw.print(
+ new AbstractList<String>() {
+ public String get(int index) {
+ return list.get(index).digest;
+ }
+
+ public int size() {
+ return list.size();
+ }
+ });
+ break;
default:
assert valueMatchesType(value, typeName, true);
throw Util.needToImplement(typeName);
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/8f2b32cd/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 fa5fda5..8aff020 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
@@ -1494,14 +1494,14 @@ public class SqlStdOperatorTable extends ReflectiveSqlOperatorTable {
/**
* The COLLECT operator. Multiset aggregator function.
*/
- public static final SqlFunction COLLECT =
- new SqlFunction(
- "COLLECT",
+ public static final SqlAggFunction COLLECT =
+ new SqlAggFunction("COLLECT",
SqlKind.OTHER_FUNCTION,
- ReturnTypes.ARG0,
+ ReturnTypes.TO_MULTISET,
null,
OperandTypes.ANY,
- SqlFunctionCategory.SYSTEM);
+ SqlFunctionCategory.SYSTEM) {
+ };
/**
* The FUSION operator. Multiset aggregator function.
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/8f2b32cd/core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java b/core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java
index 7095ac6..0a755c6 100644
--- a/core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java
+++ b/core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java
@@ -310,6 +310,16 @@ public abstract class ReturnTypes {
-1);
}
};
+
+ /**
+ * Returns a multiset type.
+ *
+ * <p>For example, given <code>INTEGER</code>, returns
+ * <code>INTEGER MULTISET</code>.
+ */
+ public static final SqlReturnTypeInference TO_MULTISET =
+ cascade(ARG0, SqlTypeTransforms.TO_MULTISET);
+
/**
* Returns the element type of a multiset
*/
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/8f2b32cd/core/src/main/java/org/apache/calcite/sql/type/SqlTypeTransforms.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeTransforms.java b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeTransforms.java
index 89ab33b..2f4668f 100644
--- a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeTransforms.java
+++ b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeTransforms.java
@@ -151,6 +151,21 @@ public abstract class SqlTypeTransforms {
};
/**
+ * Parameter type-inference transform strategy that wraps a given type
+ * in a multiset.
+ *
+ * @see org.apache.calcite.rel.type.RelDataTypeFactory#createMultisetType(RelDataType, long)
+ */
+ public static final SqlTypeTransform TO_MULTISET =
+ new SqlTypeTransform() {
+ public RelDataType transformType(SqlOperatorBinding opBinding,
+ RelDataType typeToTransform) {
+ return opBinding.getTypeFactory().createMultisetType(typeToTransform,
+ -1);
+ }
+ };
+
+ /**
* Parameter type-inference transform strategy where a derived type must be
* a struct type with precisely one field and the returned type is the type
* of that field.
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/8f2b32cd/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 ca74f31..a403c6a 100644
--- a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
+++ b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
@@ -73,6 +73,7 @@ import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
+import java.util.Objects;
import java.util.SortedSet;
import java.util.TreeSet;
@@ -170,6 +171,11 @@ public class RelBuilder {
return cluster.getTypeFactory();
}
+ /** Returns the builder for {@link RexNode} expressions. */
+ public RexBuilder getRexBuilder() {
+ return cluster.getRexBuilder();
+ }
+
/** Creates a {@link RelBuilderFactory}, a partially-created RelBuilder.
* Just add a {@link RelOptCluster} and a {@link RelOptSchema} */
public static RelBuilderFactory proto(final Context context) {
@@ -401,6 +407,18 @@ public class RelBuilder {
return fields(Mappings.asList(mapping));
}
+ /** Creates an access to a field by name. */
+ public RexNode dot(RexNode node, String fieldName) {
+ final RexBuilder builder = cluster.getRexBuilder();
+ return builder.makeFieldAccess(node, fieldName, true);
+ }
+
+ /** Creates an access to a field by ordinal. */
+ public RexNode dot(RexNode node, int fieldOrdinal) {
+ final RexBuilder builder = cluster.getRexBuilder();
+ return builder.makeFieldAccess(node, fieldOrdinal);
+ }
+
/** Creates a call to a scalar operator. */
public RexNode call(SqlOperator operator, RexNode... operands) {
final RexBuilder builder = cluster.getRexBuilder();
@@ -521,7 +539,7 @@ public class RelBuilder {
/** Creates a group key. */
public GroupKey groupKey(Iterable<? extends RexNode> nodes) {
- return new GroupKeyImpl(ImmutableList.copyOf(nodes), null);
+ return new GroupKeyImpl(ImmutableList.copyOf(nodes), null, null);
}
/** Creates a group key with grouping sets. */
@@ -532,7 +550,7 @@ public class RelBuilder {
for (Iterable<? extends RexNode> nodeList : nodeLists) {
builder.add(ImmutableList.copyOf(nodeList));
}
- return new GroupKeyImpl(ImmutableList.copyOf(nodes), builder.build());
+ return new GroupKeyImpl(ImmutableList.copyOf(nodes), builder.build(), null);
}
/** Creates a group key of fields identified by ordinal. */
@@ -1013,7 +1031,7 @@ public class RelBuilder {
*
* <p>If there are zero rows, or if all values of a any column are
* null, this method cannot deduce the type of columns. For these cases,
- * call {@link #values(RelDataType, Iterable)}.
+ * call {@link #values(Iterable, RelDataType)}.
*
* @param fieldNames Field names
* @param values Values
@@ -1053,7 +1071,7 @@ public class RelBuilder {
rowTypeBuilder.add(name, type);
}
final RelDataType rowType = rowTypeBuilder.build();
- return values(rowType, tupleList);
+ return values(tupleList, rowType);
}
private ImmutableList<ImmutableList<RexLiteral>> tupleList(int columnCount,
@@ -1106,13 +1124,13 @@ public class RelBuilder {
* cannot, such as all values of a column being null, or there being zero
* rows.
*
- * @param rowType Row type
* @param tupleList Tuple list
+ * @param rowType Row type
*/
- protected RelBuilder values(RelDataType rowType,
- Iterable<ImmutableList<RexLiteral>> tupleList) {
- RelNode values = valuesFactory.createValues(cluster, rowType,
- ImmutableList.copyOf(tupleList));
+ public RelBuilder values(Iterable<? extends List<RexLiteral>> tupleList,
+ RelDataType rowType) {
+ RelNode values =
+ valuesFactory.createValues(cluster, rowType, copy(tupleList));
push(values);
return this;
}
@@ -1123,7 +1141,30 @@ public class RelBuilder {
* @param rowType Row type
*/
public RelBuilder values(RelDataType rowType) {
- return values(rowType, ImmutableList.<ImmutableList<RexLiteral>>of());
+ return values(ImmutableList.<ImmutableList<RexLiteral>>of(), rowType);
+ }
+
+ /** Converts an iterable of lists into an immutable list of immutable lists
+ * with the same contents. Returns the same object if possible. */
+ private static <E> ImmutableList<ImmutableList<E>>
+ copy(Iterable<? extends List<E>> tupleList) {
+ final ImmutableList.Builder<ImmutableList<E>> builder =
+ ImmutableList.builder();
+ int changeCount = 0;
+ for (List<E> literals : tupleList) {
+ final ImmutableList<E> literals2 =
+ ImmutableList.copyOf(literals);
+ builder.add(literals2);
+ if (literals != literals2) {
+ ++changeCount;
+ }
+ }
+ if (changeCount == 0) {
+ // don't make a copy if we don't have to
+ //noinspection unchecked
+ return (ImmutableList<ImmutableList<E>>) tupleList;
+ }
+ return builder.build();
}
/** Creates a limit without a sort. */
@@ -1288,6 +1329,20 @@ public class RelBuilder {
}));
}
+ /** Clears the stack.
+ *
+ * <p>The builder's state is now the same as when it was created. */
+ public void clear() {
+ stack.clear();
+ }
+
+ protected String getAlias() {
+ final Frame frame = Stacks.peek(stack);
+ return frame.right.size() == 1
+ ? frame.right.get(0).left
+ : null;
+ }
+
/** Information necessary to create a call to an aggregate function.
*
* @see RelBuilder#aggregateCall */
@@ -1298,17 +1353,33 @@ public class RelBuilder {
*
* @see RelBuilder#groupKey */
public interface GroupKey {
+ /** Assigns an alias to this group key.
+ *
+ * <p>Used to assign field names in the {@code group} operation. */
+ GroupKey alias(String alias);
}
/** Implementation of {@link RelBuilder.GroupKey}. */
- private static class GroupKeyImpl implements GroupKey {
+ protected static class GroupKeyImpl implements GroupKey {
final ImmutableList<RexNode> nodes;
final ImmutableList<ImmutableList<RexNode>> nodeLists;
+ final String alias;
GroupKeyImpl(ImmutableList<RexNode> nodes,
- ImmutableList<ImmutableList<RexNode>> nodeLists) {
+ ImmutableList<ImmutableList<RexNode>> nodeLists, String alias) {
this.nodes = Preconditions.checkNotNull(nodes);
this.nodeLists = nodeLists;
+ this.alias = alias;
+ }
+
+ @Override public String toString() {
+ return alias == null ? nodes.toString() : nodes + " as " + alias;
+ }
+
+ public GroupKey alias(String alias) {
+ return Objects.equals(this.alias, alias)
+ ? this
+ : new GroupKeyImpl(nodes, nodeLists, alias);
}
}
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/8f2b32cd/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 82041cc..47161b3 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
@@ -4107,10 +4107,31 @@ public abstract class SqlOperatorBaseTest {
}
@Test public void testCollectFunc() {
- tester.setFor(
- SqlStdOperatorTable.COLLECT,
- VM_FENNEL,
- VM_JAVA);
+ tester.setFor(SqlStdOperatorTable.COLLECT, VM_FENNEL, VM_JAVA);
+ tester.checkFails("collect(^*^)", "Unknown identifier '\\*'", false);
+ checkAggType(tester, "collect(1)", "INTEGER NOT NULL MULTISET NOT NULL");
+ checkAggType(tester,
+ "collect(1.2)", "DECIMAL(2, 1) NOT NULL MULTISET NOT NULL");
+ checkAggType(tester,
+ "collect(DISTINCT 1.5)", "DECIMAL(2, 1) NOT NULL MULTISET NOT NULL");
+ tester.checkFails("^collect()^",
+ "Invalid number of arguments to function 'COLLECT'. Was expecting 1 arguments",
+ false);
+ tester.checkFails("^collect(1, 2)^",
+ "Invalid number of arguments to function 'COLLECT'. Was expecting 1 arguments",
+ false);
+ final String[] values = {"0", "CAST(null AS INTEGER)", "2", "2"};
+ tester.checkAgg("collect(x)", values, Arrays.asList("[0, 2, 2]"), (double) 0);
+ Object result1 = -3;
+ if (!enable) {
+ return;
+ }
+ tester.checkAgg("collect(CASE x WHEN 0 THEN NULL ELSE -1 END)", values,
+ result1, (double) 0);
+ Object result = -1;
+ tester.checkAgg("collect(DISTINCT CASE x WHEN 0 THEN NULL ELSE -1 END)",
+ values, result, (double) 0);
+ tester.checkAgg("collect(DISTINCT x)", values, 2, (double) 0);
}
@Test public void testFusionFunc() {
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/8f2b32cd/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 a319f43..026f41e 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
@@ -6532,9 +6532,8 @@ public class SqlValidatorTest extends SqlValidatorTestCase {
@Test public void testCollect() {
check("select collect(deptno) from emp");
check("select collect(multiset[3]) from emp");
- // todo. COLLECT is an aggregate function. test that validator only can
- // take set operators in its select list once aggregation support is
- // complete
+ sql("select collect(multiset[3]), ^deptno^ from emp")
+ .fails("Expression 'DEPTNO' is not being grouped");
}
@Test public void testFusion() {
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/8f2b32cd/core/src/test/resources/sql/agg.oq
----------------------------------------------------------------------
diff --git a/core/src/test/resources/sql/agg.oq b/core/src/test/resources/sql/agg.oq
index 17610b9..3787ac2 100644
--- a/core/src/test/resources/sql/agg.oq
+++ b/core/src/test/resources/sql/agg.oq
@@ -588,6 +588,54 @@ from (values cast(-86.4 as float), cast(-100 as float)) as t(v);
!ok
+# COLLECT
+select deptno, collect(empno) 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
+
+# COLLECT DISTINCT
+# Disabled in JDK 1.7 because order of values is different
+!if (jdk18) {
+select deptno, collect(distinct job) as jobs
+from "scott".emp
+group by deptno;
++--------+-----------------------------+
+| DEPTNO | JOBS |
++--------+-----------------------------+
+| 10 | [MANAGER, CLERK, PRESIDENT] |
+| 20 | [CLERK, ANALYST, MANAGER] |
+| 30 | [SALESMAN, MANAGER, CLERK] |
++--------+-----------------------------+
+(3 rows)
+
+!ok
+!}
+
+# COLLECT ... FILTER
+select deptno, collect(empno) filter (where empno < 7550) as empnos
+from "scott".emp
+group by deptno;
++--------+--------------+
+| DEPTNO | EMPNOS |
++--------+--------------+
+| 10 | [] |
+| 20 | [7369] |
+| 30 | [7499, 7521] |
++--------+--------------+
+(3 rows)
+
+!ok
+
# Aggregate FILTER
select deptno,
sum(sal) filter (where job = 'CLERK') c_sal,
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/8f2b32cd/site/_docs/reference.md
----------------------------------------------------------------------
diff --git a/site/_docs/reference.md b/site/_docs/reference.md
index 5d2e51f..7df7926 100644
--- a/site/_docs/reference.md
+++ b/site/_docs/reference.md
@@ -533,6 +533,7 @@ passed to the aggregate function.
| Operator syntax | Description
|:---------------------------------- |:-----------
+| COLLECT( [ DISTINCT ] value) | Returns a multiset of the values
| COUNT( [ DISTINCT ] value [, value]* ) | Returns the number of input rows for which *value* is not null (wholly not null if *value* is composite)
| COUNT(*) | Returns the number of input rows
| AVG( [ DISTINCT ] numeric) | Returns the average (arithmetic mean) of *numeric* across all input values