[NO ISSUE][COMP] Support GROUPING SETS, ROLLUP, CUBE
- user model changes: no
- storage format changes: no
- interface changes: no
Details:
- Implement support for GROUPING SETS, ROLLUP, CUBE in
GROUP BY clause, including GROUPING() operation
- Modify OptimizerTest to account for different variable id bases
when comparing actual query plan with expected one
- Add RQG testsuite for grouping sets and regular testcases
Change-Id: I540ae172b9904e869f89f501e192dc83f3ea2550
Reviewed-on: https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/5426
Contrib: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Integration-Tests: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Tested-by: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Reviewed-by: Ali Alsuliman <ali.al.solaiman@gmail.com>
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/LangExpressionToPlanTranslator.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/LangExpressionToPlanTranslator.java
index 6550bb4..2706ac3 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/LangExpressionToPlanTranslator.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/LangExpressionToPlanTranslator.java
@@ -967,14 +967,26 @@
topOp = new MutableObject<>(groupRecordVarAssignOp);
}
+ boolean propagateHashHint = true;
+
GroupByOperator gOp = new GroupByOperator();
- for (GbyVariableExpressionPair ve : gc.getGbyPairList()) {
- VariableExpr vexpr = ve.getVar();
- LogicalVariable v = vexpr == null ? context.newVar() : context.newVarFromExpression(vexpr);
- Pair<ILogicalExpression, Mutable<ILogicalOperator>> eo = langExprToAlgExpression(ve.getExpr(), topOp);
- gOp.addGbyExpression(v, eo.first);
- topOp = eo.second;
+ if (!gc.isGroupAll()) {
+ List<GbyVariableExpressionPair> groupingSet = getSingleGroupingSet(gc);
+ if (groupingSet.isEmpty()) {
+ gOp.addGbyExpression(context.newVar(), ConstantExpression.TRUE);
+ propagateHashHint = false;
+ } else {
+ for (GbyVariableExpressionPair ve : groupingSet) {
+ VariableExpr vexpr = ve.getVar();
+ LogicalVariable v = vexpr == null ? context.newVar() : context.newVarFromExpression(vexpr);
+ Pair<ILogicalExpression, Mutable<ILogicalOperator>> eo =
+ langExprToAlgExpression(ve.getExpr(), topOp);
+ gOp.addGbyExpression(v, eo.first);
+ topOp = eo.second;
+ }
+ }
}
+
if (gc.hasDecorList()) {
for (GbyVariableExpressionPair ve : gc.getDecorPairList()) {
VariableExpr vexpr = ve.getVar();
@@ -1017,11 +1029,23 @@
}
gOp.setGroupAll(gc.isGroupAll());
- gOp.getAnnotations().put(OperatorAnnotations.USE_HASH_GROUP_BY, gc.hasHashGroupByHint());
+ if (propagateHashHint) {
+ gOp.getAnnotations().put(OperatorAnnotations.USE_HASH_GROUP_BY, gc.hasHashGroupByHint());
+ }
gOp.setSourceLocation(sourceLoc);
return new Pair<>(gOp, null);
}
+ protected List<GbyVariableExpressionPair> getSingleGroupingSet(GroupbyClause gby) throws CompilationException {
+ List<List<GbyVariableExpressionPair>> groupingSetList = gby.getGbyPairList();
+ if (groupingSetList.size() != 1) {
+ // should've been rewritten by SqlppGroupingSetsVisitor
+ throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, gby.getSourceLocation(),
+ String.valueOf(groupingSetList.size()));
+ }
+ return groupingSetList.get(0);
+ }
+
protected AbstractFunctionCallExpression createRecordConstructor(List<Pair<Expression, Identifier>> fieldList,
Mutable<ILogicalOperator> inputOp, SourceLocation sourceLoc) throws CompilationException {
List<Mutable<ILogicalExpression>> args = new ArrayList<>();
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/SqlppExpressionToPlanTranslator.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/SqlppExpressionToPlanTranslator.java
index 82dc344..59642cb 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/SqlppExpressionToPlanTranslator.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/SqlppExpressionToPlanTranslator.java
@@ -849,8 +849,13 @@
// Generates all field bindings according to the from clause.
private void getGroupBindings(GroupbyClause groupbyClause, List<FieldBinding> outFieldBindings,
Set<String> outFieldNames) throws CompilationException {
- for (GbyVariableExpressionPair pair : groupbyClause.getGbyPairList()) {
- outFieldBindings.add(getFieldBinding(pair.getVar(), outFieldNames));
+ Set<VariableExpr> gbyKeyVars = new HashSet<>();
+ List<GbyVariableExpressionPair> groupingSet = getSingleGroupingSet(groupbyClause);
+ for (GbyVariableExpressionPair pair : groupingSet) {
+ VariableExpr var = pair.getVar();
+ if (gbyKeyVars.add(var)) {
+ outFieldBindings.add(getFieldBinding(var, outFieldNames));
+ }
}
if (groupbyClause.hasGroupVar()) {
outFieldBindings.add(getFieldBinding(groupbyClause.getGroupVar(), outFieldNames));
diff --git a/asterixdb/asterix-app/pom.xml b/asterixdb/asterix-app/pom.xml
index e39bb1f..392dbd8 100644
--- a/asterixdb/asterix-app/pom.xml
+++ b/asterixdb/asterix-app/pom.xml
@@ -158,6 +158,7 @@
<ignoredUnusedDeclaredDependencies>
<ignoredUnusedDeclaredDependency>org.apache.asterix:asterix-external-data:zip:*</ignoredUnusedDeclaredDependency>
<ignoredUnusedDeclaredDependency>org.apache.asterix:asterix-external-data:test-jar:*</ignoredUnusedDeclaredDependency>
+ <ignoredUnusedDeclaredDependency>org.postgresql:postgresql:jar:*</ignoredUnusedDeclaredDependency>
</ignoredUnusedDeclaredDependencies>
</configuration>
</plugin>
@@ -326,6 +327,14 @@
</properties>
</profile>
<profile>
+ <id>asterix-gerrit-asterix-app-sql-rqg</id>
+ <properties>
+ <test.excludes>**/*.java</test.excludes>
+ <itest.includes>**/SqlppRQG*IT.java</itest.includes>
+ <failIfNoTests>false</failIfNoTests>
+ </properties>
+ </profile>
+ <profile>
<id>asterix-gerrit-ssl-compression</id>
<properties>
<test.includes>**/*Compression*Test.java,**/*Ssl*Test.java</test.includes>
@@ -337,7 +346,7 @@
<id>asterix-gerrit-verify-asterix-app</id>
<properties>
<test.includes>**/AqlExecutionTest.java</test.includes>
- <itest.excludes>**/SqlppExecution*IT.java,**/RebalanceWithCancellationIT.java</itest.excludes>
+ <itest.excludes>**/SqlppExecution*IT.java,**/SqlppRQG*IT.java,**/RebalanceWithCancellationIT.java</itest.excludes>
<failIfNoTests>false</failIfNoTests>
</properties>
</profile>
@@ -677,6 +686,16 @@
<artifactId>hyracks-storage-am-lsm-invertedindex</artifactId>
</dependency>
<dependency>
+ <groupId>org.testcontainers</groupId>
+ <artifactId>postgresql</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.postgresql</groupId>
+ <artifactId>postgresql</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>com.teradata.tpcds</groupId>
<artifactId>tpcds</artifactId>
<version>1.2</version>
diff --git a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/ResultExtractor.java b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/ResultExtractor.java
index 8dab64f..eb91675 100644
--- a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/ResultExtractor.java
+++ b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/ResultExtractor.java
@@ -35,6 +35,7 @@
import org.apache.logging.log4j.Logger;
import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.PrettyPrinter;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -220,26 +221,25 @@
} else if (fieldValue.isArray()) {
JsonNode oneElement = fieldValue.get(0);
if (oneElement.isTextual()) {
- resultBuilder.append(
- isJsonFormat ? PP_WRITER.writeValueAsString(oneElement) : oneElement.asText());
+ resultBuilder.append(isJsonFormat ? prettyPrint(oneElement) : oneElement.asText());
} else {
- resultBuilder.append(PP_WRITER.writeValueAsString(oneElement));
+ resultBuilder.append(prettyPrint(oneElement));
}
} else {
- resultBuilder.append(PP_WRITER.writeValueAsString(fieldValue));
+ resultBuilder.append(prettyPrint(fieldValue));
}
} else {
JsonNode[] fields = Iterators.toArray(fieldValue.elements(), JsonNode.class);
if (isJsonFormat) {
for (JsonNode f : fields) {
- resultBuilder.append(PP_WRITER.writeValueAsString(f)).append('\n');
+ resultBuilder.append(prettyPrint(f)).append('\n');
}
} else {
for (JsonNode f : fields) {
if (f.isValueNode()) {
resultBuilder.append(f.asText());
} else {
- resultBuilder.append(PP_WRITER.writeValueAsString(f)).append('\n');
+ resultBuilder.append(prettyPrint(f)).append('\n');
}
}
}
@@ -275,6 +275,10 @@
return extractedResult;
}
+ public static String prettyPrint(JsonNode node) throws JsonProcessingException {
+ return PP_WRITER.writeValueAsString(node);
+ }
+
private static void checkForErrors(ObjectNode result) throws Exception {
final JsonNode errorsField = result.get(ResultField.ERRORS.getFieldName());
if (errorsField != null) {
diff --git a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/optimizer/OptimizerTest.java b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/optimizer/OptimizerTest.java
index 6e0413c..c3dc821 100644
--- a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/optimizer/OptimizerTest.java
+++ b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/optimizer/OptimizerTest.java
@@ -18,17 +18,19 @@
*/
package org.apache.asterix.test.optimizer;
-import java.io.BufferedReader;
import java.io.File;
-import java.io.FileInputStream;
-import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StringReader;
import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import org.apache.asterix.api.common.AsterixHyracksIntegrationUtil;
import org.apache.asterix.api.java.AsterixJavaClient;
@@ -93,6 +95,9 @@
protected static AsterixHyracksIntegrationUtil integrationUtil = new AsterixHyracksIntegrationUtil();
+ private static final String PATTERN_VAR_ID_PREFIX = "\\$\\$";
+ private static final Pattern PATTERN_VAR_ID = Pattern.compile(PATTERN_VAR_ID_PREFIX + "(\\d+)");
+
@BeforeClass
public static void setUp() throws Exception {
final File outdir = new File(PATH_ACTUAL);
@@ -205,37 +210,36 @@
throw new Exception("Compile ERROR for " + queryFile + ": " + e.getMessage(), e);
}
- BufferedReader readerExpected =
- new BufferedReader(new InputStreamReader(new FileInputStream(expectedFile), "UTF-8"));
- BufferedReader readerActual =
- new BufferedReader(new InputStreamReader(new FileInputStream(actualFile), "UTF-8"));
+ List<String> linesExpected = Files.readAllLines(expectedFile.toPath(), StandardCharsets.UTF_8);
+ List<String> linesActual = Files.readAllLines(actualFile.toPath(), StandardCharsets.UTF_8);
+ int varBaseExpected = findBaseVarId(linesExpected);
+ int varBaseActual = findBaseVarId(linesActual);
+
+ Iterator<String> readerExpected = linesExpected.iterator();
+ Iterator<String> readerActual = linesActual.iterator();
String lineExpected, lineActual;
int num = 1;
- try {
- while ((lineExpected = readerExpected.readLine()) != null) {
- lineActual = readerActual.readLine();
- if (lineActual == null) {
- throw new Exception("Result for " + queryFile + " changed at line " + num + ":\n< "
- + lineExpected + "\n> ");
- }
- if (!lineExpected.equals(lineActual)) {
- throw new Exception("Result for " + queryFile + " changed at line " + num + ":\n< "
- + lineExpected + "\n> " + lineActual);
- }
- ++num;
- }
- lineActual = readerActual.readLine();
- if (lineActual != null) {
+ while (readerExpected.hasNext()) {
+ lineExpected = readerExpected.next();
+ if (!readerActual.hasNext()) {
throw new Exception(
- "Result for " + queryFile + " changed at line " + num + ":\n< \n> " + lineActual);
+ "Result for " + queryFile + " changed at line " + num + ":\n< " + lineExpected + "\n> ");
}
- LOGGER.info("Test \"" + queryFile.getPath() + "\" PASSED!");
- actualFile.delete();
- } finally {
- readerExpected.close();
- readerActual.close();
+ lineActual = readerActual.next();
+
+ if (!planLineEquals(lineExpected, varBaseExpected, lineActual, varBaseActual)) {
+ throw new Exception("Result for " + queryFile + " changed at line " + num + ":\n< " + lineExpected
+ + "\n> " + lineActual);
+ }
+ ++num;
}
+ if (readerActual.hasNext()) {
+ throw new Exception(
+ "Result for " + queryFile + " changed at line " + num + ":\n< \n> " + readerActual.next());
+ }
+ LOGGER.info("Test \"" + queryFile.getPath() + "\" PASSED!");
+ actualFile.delete();
} catch (Exception e) {
if (!(e instanceof AssumptionViolatedException)) {
LOGGER.error("Test \"" + queryFile.getPath() + "\" FAILED!");
@@ -245,4 +249,40 @@
}
}
}
+
+ private boolean planLineEquals(String lineExpected, int varIdBaseExpected, String lineActual, int varIdBaseActual) {
+ String lineExpectedNorm = normalizePlanLine(lineExpected, varIdBaseExpected);
+ String lineActualNorm = normalizePlanLine(lineActual, varIdBaseActual);
+ return lineExpectedNorm.equals(lineActualNorm);
+ }
+
+ // rewrite variable ids in given plan line: $$varId -> $$(varId-varIdBase)
+ private String normalizePlanLine(String line, int varIdBase) {
+ if (varIdBase == Integer.MAX_VALUE) {
+ // plan did not contain any variables -> no rewriting necessary
+ return line;
+ }
+ Matcher m = PATTERN_VAR_ID.matcher(line);
+ StringBuffer sb = new StringBuffer(line.length());
+ while (m.find()) {
+ int varId = Integer.parseInt(m.group(1));
+ int newVarId = varId - varIdBase;
+ m.appendReplacement(sb, PATTERN_VAR_ID_PREFIX + newVarId);
+ }
+ m.appendTail(sb);
+ return sb.toString();
+ }
+
+ private int findBaseVarId(Collection<String> plan) {
+ int varIdBase = Integer.MAX_VALUE;
+ Matcher m = PATTERN_VAR_ID.matcher("");
+ for (String line : plan) {
+ m.reset(line);
+ while (m.find()) {
+ int varId = Integer.parseInt(m.group(1));
+ varIdBase = Math.min(varIdBase, varId);
+ }
+ }
+ return varIdBase;
+ }
}
diff --git a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/runtime/SqlppRQGGroupingSetsIT.java b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/runtime/SqlppRQGGroupingSetsIT.java
new file mode 100644
index 0000000..838d980
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/runtime/SqlppRQGGroupingSetsIT.java
@@ -0,0 +1,613 @@
+/*
+ * 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.asterix.test.runtime;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.JDBCType;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.Types;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.apache.asterix.common.utils.Servlets;
+import org.apache.asterix.om.types.BuiltinType;
+import org.apache.asterix.om.types.IAType;
+import org.apache.asterix.test.common.ExtractedResult;
+import org.apache.asterix.test.common.ResultExtractor;
+import org.apache.asterix.test.common.TestExecutor;
+import org.apache.asterix.test.common.TestHelper;
+import org.apache.asterix.testframework.context.TestCaseContext;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.testcontainers.containers.PostgreSQLContainer;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.ObjectReader;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+// Prerequisite:
+// setenv TESTCONTAINERS_RYUK_DISABLED true
+
+@RunWith(Parameterized.class)
+public class SqlppRQGGroupingSetsIT {
+
+ private static final String CONF_PROPERTY_SEED = getConfigurationPropertyName("seed");
+
+ private static final long CONF_PROPERTY_SEED_DEFAULT = System.currentTimeMillis();
+
+ private static final String CONF_PROPERTY_LIMIT = getConfigurationPropertyName("limit");
+
+ private static final int CONF_PROPERTY_LIMIT_DEFAULT = 100;
+
+ private static final String TESTCONTAINERS_RYUK_DISABLED = "TESTCONTAINERS_RYUK_DISABLED";
+
+ private static final String TEST_CONFIG_FILE_NAME = "src/main/resources/cc.conf";
+
+ private static final String POSTGRES_IMAGE = "postgres:12.2";
+
+ private static final String TABLE_NAME = "tenk";
+
+ private static final Path TABLE_FILE = Paths.get("data", "tenk.tbl");
+
+ private static final char TABLE_FILE_COLUMN_SEPARATOR = '|';
+
+ private static final Path RESULT_OUTPUT_DIR = Paths.get("target", SqlppRQGGroupingSetsIT.class.getSimpleName());
+
+ private static final int ROLLUP_ELEMENT_LIMIT = 2;
+ private static final int CUBE_ELEMENT_LIMIT = 1;
+ private static final int GROUPING_SETS_ELEMENT_LIMIT = 2;
+ private static final int MULTI_ELEMENT_LIMIT = 2;
+
+ private static final String UNIQUE_1 = "unique1";
+ private static final String UNIQUE_2 = "unique2";
+ private static final String TWO = "two";
+ private static final String FOUR = "four";
+ private static final String TEN = "ten";
+ private static final String TWENTY = "twenty";
+ private static final String HUNDRED = "hundred";
+ private static final String THOUSAND = "thousand";
+ private static final String TWOTHOUSAND = "twothousand";
+ private static final String FIVETHOUS = "fivethous";
+ private static final String TENTHOUS = "tenthous";
+ private static final String ODD100 = "odd100";
+ private static final String EVEN100 = "even100";
+ private static final String STRINGU1 = "stringu1";
+ private static final String STRINGU2 = "stringu2";
+ private static final String STRING4 = "string4";
+
+ private static final List<String> GROUPBY_COLUMNS = Arrays.asList(TWO, FOUR, TEN, TWENTY, HUNDRED, ODD100, EVEN100);
+
+ private static final LinkedHashMap<String, JDBCType> TABLE_SCHEMA = createTableSchema();
+
+ private static final ObjectReader JSON_NODE_READER = new ObjectMapper().readerFor(JsonNode.class);
+
+ private static final Logger LOGGER = LogManager.getLogger(SqlppRQGGroupingSetsIT.class);
+
+ private static TestExecutor testExecutor;
+
+ private static PostgreSQLContainer<?> postgres;
+
+ private static Connection conn;
+
+ private static Statement stmt;
+
+ private final int testcaseId;
+
+ private final String sqlQuery;
+
+ private final String sqlppQuery;
+
+ private final String groupByClause;
+
+ @Parameters(name = "SqlppRQGGroupingSetsIT {index}: {3}")
+ public static Collection<Object[]> tests() {
+ List<Object[]> testCases = new ArrayList<>();
+
+ long seed = getLongConfigurationProperty(CONF_PROPERTY_SEED, CONF_PROPERTY_SEED_DEFAULT);
+ int limit = (int) getLongConfigurationProperty(CONF_PROPERTY_LIMIT, CONF_PROPERTY_LIMIT_DEFAULT);
+
+ LOGGER.info(String.format("Testsuite configuration: -D%s=%d -D%s=%d", CONF_PROPERTY_SEED, seed,
+ CONF_PROPERTY_LIMIT, limit));
+
+ Random random = new Random(seed);
+ for (int i = 0; i < limit; i++) {
+ TestQuery query = generateQuery(i, random);
+ testCases.add(new Object[] { i, query.sqlQuery, query.sqlppQuery, query.groupbyClause });
+ }
+
+ return testCases;
+ }
+
+ public SqlppRQGGroupingSetsIT(int testcaseId, String sqlQuery, String sqlppQuery, String groupByClause) {
+ this.testcaseId = testcaseId;
+ this.sqlQuery = sqlQuery;
+ this.sqlppQuery = sqlppQuery;
+ this.groupByClause = groupByClause;
+ }
+
+ @Test
+ public void test() throws Exception {
+ LOGGER.info(String.format("Starting testcase #%d: %s", testcaseId, groupByClause));
+
+ LOGGER.info("Running SQL");
+ LOGGER.info(sqlQuery);
+ stmt.execute(sqlQuery);
+ ArrayNode sqlResult;
+ try (ResultSet rs = stmt.getResultSet()) {
+ sqlResult = asJson(rs);
+ }
+
+ LOGGER.info("Running SQL++");
+ LOGGER.info(sqlppQuery);
+ ArrayNode sqlppResult;
+ try (InputStream resultStream = testExecutor.executeQueryService(sqlppQuery,
+ testExecutor.getEndpoint(Servlets.QUERY_SERVICE), TestCaseContext.OutputFormat.ADM)) {
+ sqlppResult = asJson(
+ ResultExtractor.extract(resultStream, StandardCharsets.UTF_8, TestCaseContext.OutputFormat.ADM));
+ }
+
+ boolean eq = TestHelper.equalJson(sqlResult, sqlppResult, false);
+ if (!eq) {
+ File sqlResultFile = writeResult(sqlResult, "sql");
+ File sqlppResultFile = writeResult(sqlppResult, "sqlpp");
+
+ Assert.fail(String.format("Results do not match.\n%s\n%s", sqlResultFile.getCanonicalPath(),
+ sqlppResultFile.getCanonicalPath()));
+ }
+ }
+
+ private static TestQuery generateQuery(int testcaseId, Random random) {
+ Set<String> allColumns = new LinkedHashSet<>();
+ List<String> groupingElements = new ArrayList<>();
+ int nElements = 1 + random.nextInt(3);
+
+ int rollupCount = 0, cubeCount = 0, groupingSetsCount = 0;
+ for (int i = 0; i < nElements; i++) {
+ String prefix;
+ int minItems, maxItems;
+ boolean allowSimpleSubgroup = false, allowComplexSubgroup = false;
+
+ switch (random.nextInt(6)) {
+ case 0:
+ case 1:
+ prefix = "";
+ minItems = 1;
+ maxItems = 4;
+ break;
+ case 2:
+ case 3:
+ if (isSingleElementLimitReached(rollupCount, ROLLUP_ELEMENT_LIMIT)
+ || isMultiElementLimitReached(rollupCount, cubeCount, groupingSetsCount)) {
+ // skip this element
+ nElements++;
+ continue;
+ }
+ prefix = "ROLLUP";
+ minItems = 1;
+ maxItems = 4;
+ allowSimpleSubgroup = true;
+ rollupCount++;
+ break;
+ case 4:
+ if (isSingleElementLimitReached(cubeCount, CUBE_ELEMENT_LIMIT)
+ || isMultiElementLimitReached(rollupCount, cubeCount, groupingSetsCount)) {
+ // skip this element
+ nElements++;
+ continue;
+ }
+ prefix = "CUBE";
+ minItems = 2;
+ maxItems = 2;
+ allowSimpleSubgroup = true; // allowed, not actually used, because we set maxItems to 2
+ cubeCount++;
+ break;
+ case 5:
+ if (isSingleElementLimitReached(groupingSetsCount, GROUPING_SETS_ELEMENT_LIMIT)
+ || isMultiElementLimitReached(rollupCount, cubeCount, groupingSetsCount)) {
+ // skip this element
+ nElements++;
+ continue;
+ }
+ prefix = "GROUPING SETS";
+ minItems = 0;
+ maxItems = 3;
+ allowSimpleSubgroup = allowComplexSubgroup = true;
+ groupingSetsCount++;
+ break;
+ default:
+ throw new IllegalStateException();
+ }
+ int nItems = minItems + random.nextInt(maxItems - minItems + 1);
+ List<String> elementItems =
+ nItems == 0 ? Collections.emptyList() : randomize(GROUPBY_COLUMNS, random).subList(0, nItems);
+ allColumns.addAll(elementItems);
+ if (allowSimpleSubgroup && nItems >= 3 && random.nextInt(2) == 0) {
+ makeSubgroup(elementItems, random, allowComplexSubgroup);
+ }
+ String elementItemsText = elementItems.isEmpty() ? "()" : String.join(",", elementItems);
+ String element = String.format("%s(%s)", prefix, elementItemsText);
+ groupingElements.add(element);
+ }
+
+ StringBuilder selectClause = new StringBuilder();
+ for (String col : allColumns) {
+ selectClause.append(col).append(',');
+ }
+ for (String col : allColumns) {
+ selectClause.append(String.format("GROUPING(%s) AS grp_%s", col, col)).append(',');
+ }
+ if (allColumns.size() > 1) {
+ selectClause.append(String.format("GROUPING(%s) AS grp", String.join(",", randomize(allColumns, random))))
+ .append(',');
+ }
+ selectClause.append(String.format("SUM(%s) AS agg_sum", UNIQUE_1));
+
+ String groupingElementText = groupingElements.isEmpty() ? "()" : String.join(",", groupingElements);
+ String groupbyClause = String.format("GROUP BY %s", groupingElementText);
+
+ String orderbyClauseSql = generateOrderBy(allColumns, true);
+ String orderbyClauseSqlpp = generateOrderBy(allColumns, false);
+
+ String queryTemplate = "SELECT %s FROM %s %s %s";
+ String sqlQuery = String.format(queryTemplate, selectClause, TABLE_NAME, groupbyClause, orderbyClauseSql);
+ String sqlppQuery = String.format(queryTemplate, selectClause, TABLE_NAME, groupbyClause, orderbyClauseSqlpp);
+
+ LOGGER.info(String.format("Testcase #%d: %s", testcaseId, groupbyClause));
+
+ return new TestQuery(sqlQuery, sqlppQuery, groupbyClause);
+ }
+
+ private static boolean isSingleElementLimitReached(int elementCount, int limit) {
+ return elementCount >= limit;
+ }
+
+ private static boolean isMultiElementLimitReached(int elementCount1, int elementCount2, int elementCount3) {
+ return elementCount1 + elementCount2 + elementCount3 >= MULTI_ELEMENT_LIMIT;
+ }
+
+ private static String generateOrderBy(Set<String> allColumns, boolean insertNullsFirst) {
+ if (allColumns.isEmpty()) {
+ return "";
+ }
+ return "ORDER BY " + allColumns.stream().map(c -> c + (insertNullsFirst ? " NULLS FIRST" : ""))
+ .collect(Collectors.joining(", "));
+ }
+
+ private static void makeSubgroup(List<String> elementColumns, Random random, boolean allowComplexSubgroup) {
+ // rewrite (a, b, c, ... ) into (a,(b,c), ...) or (a,ROLLUP(b,c), ...)
+ String subgroupSpecifier = "";
+ if (allowComplexSubgroup && random.nextInt(2) == 0) {
+ subgroupSpecifier = "ROLLUP";
+ }
+ int start = random.nextInt(elementColumns.size() - 1);
+ List<String> sublist = elementColumns.subList(start, start + 2);
+ String s = String.format("%s(%s)", subgroupSpecifier, String.join(",", sublist));
+ sublist.clear();
+ sublist.add(s);
+ }
+
+ private ArrayNode asJson(ExtractedResult aresult) throws IOException {
+ ArrayNode result = (ArrayNode) JSON_NODE_READER.createArrayNode();
+ try (BufferedReader reader =
+ new BufferedReader(new InputStreamReader(aresult.getResult(), StandardCharsets.UTF_8))) {
+ reader.lines().forEachOrdered(l -> {
+ try {
+ result.add(JSON_NODE_READER.readTree(l));
+ } catch (JsonProcessingException e) {
+ throw new RuntimeException(e);
+ }
+ });
+ }
+ return result;
+ }
+
+ private ArrayNode asJson(ResultSet rs) throws SQLException {
+ ResultSetMetaData rsmd = rs.getMetaData();
+ int rsColumnCount = rsmd.getColumnCount();
+ ArrayNode result = (ArrayNode) JSON_NODE_READER.createArrayNode();
+ while (rs.next()) {
+ ObjectNode row = (ObjectNode) JSON_NODE_READER.createObjectNode();
+ for (int i = 0; i < rsColumnCount; i++) {
+ int jdbcColumnIdx = i + 1;
+ String columnName = rsmd.getColumnName(jdbcColumnIdx);
+ switch (rsmd.getColumnType(jdbcColumnIdx)) {
+ case Types.INTEGER:
+ int intValue = rs.getInt(jdbcColumnIdx);
+ if (rs.wasNull()) {
+ row.putNull(columnName);
+ } else {
+ row.put(columnName, intValue);
+ }
+ break;
+ case Types.BIGINT:
+ long longValue = rs.getLong(jdbcColumnIdx);
+ if (rs.wasNull()) {
+ row.putNull(columnName);
+ } else {
+ row.put(columnName, longValue);
+ }
+ break;
+ case Types.VARCHAR:
+ String stringValue = rs.getString(jdbcColumnIdx);
+ if (rs.wasNull()) {
+ row.putNull(columnName);
+ } else {
+ row.put(columnName, stringValue);
+ }
+ break;
+ default:
+ throw new UnsupportedOperationException();
+ }
+ }
+ result.add(row);
+ }
+ return result;
+ }
+
+ private static void loadAsterixData() throws Exception {
+ String tableTypeName = TABLE_NAME + "Type";
+ String createTypeStmtText =
+ String.format("CREATE TYPE %s AS CLOSED { %s }", tableTypeName,
+ TABLE_SCHEMA.entrySet().stream()
+ .map(e -> e.getKey() + ':' + getAsterixType(e.getValue()).getTypeName())
+ .collect(Collectors.joining(",")));
+
+ LOGGER.debug(createTypeStmtText);
+ testExecutor.executeSqlppUpdateOrDdl(createTypeStmtText, TestCaseContext.OutputFormat.ADM);
+
+ String createDatasetStmtText =
+ String.format("CREATE DATASET %s(%s) PRIMARY KEY %s", TABLE_NAME, tableTypeName, UNIQUE_2);
+ LOGGER.debug(createDatasetStmtText);
+ testExecutor.executeSqlppUpdateOrDdl(createDatasetStmtText, TestCaseContext.OutputFormat.ADM);
+
+ String loadStmtText =
+ String.format("LOAD DATASET %s USING localfs ((`path`=`%s`),(`format`=`%s`),(`delimiter`=`%s`))",
+ TABLE_NAME, "asterix_nc1://" + TABLE_FILE, "delimited-text", "|");
+ LOGGER.debug(loadStmtText);
+ testExecutor.executeSqlppUpdateOrDdl(loadStmtText, TestCaseContext.OutputFormat.ADM);
+ }
+
+ private static void loadSQLData() throws SQLException, IOException {
+ String createTableStmtText = String.format("CREATE TEMPORARY TABLE %s (%s)", TABLE_NAME, TABLE_SCHEMA.entrySet()
+ .stream().map(e -> e.getKey() + ' ' + getSQLType(e.getValue())).collect(Collectors.joining(",")));
+
+ stmt.execute(createTableStmtText);
+
+ String insertStmtText = String.format("INSERT INTO %s VALUES (%s)", TABLE_NAME,
+ StringUtils.repeat("?", ",", TABLE_SCHEMA.size()));
+
+ try (PreparedStatement insertStmt = conn.prepareStatement(insertStmtText)) {
+ Files.lines(TABLE_FILE).forEachOrdered(line -> {
+ String[] values = StringUtils.split(line, TABLE_FILE_COLUMN_SEPARATOR);
+ try {
+ insertStmt.clearParameters();
+ int i = 0;
+ for (JDBCType type : TABLE_SCHEMA.values()) {
+ setColumnValue(insertStmt, i + 1, type, values[i]);
+ i++;
+ }
+ insertStmt.addBatch();
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ });
+ insertStmt.executeBatch();
+ }
+ }
+
+ private static LinkedHashMap<String, JDBCType> createTableSchema() {
+ LinkedHashMap<String, JDBCType> schema = new LinkedHashMap<>();
+ schema.put(UNIQUE_1, JDBCType.INTEGER);
+ schema.put(UNIQUE_2, JDBCType.INTEGER);
+ schema.put(TWO, JDBCType.INTEGER);
+ schema.put(FOUR, JDBCType.INTEGER);
+ schema.put(TEN, JDBCType.INTEGER);
+ schema.put(TWENTY, JDBCType.INTEGER);
+ schema.put(HUNDRED, JDBCType.INTEGER);
+ schema.put(THOUSAND, JDBCType.INTEGER);
+ schema.put(TWOTHOUSAND, JDBCType.INTEGER);
+ schema.put(FIVETHOUS, JDBCType.INTEGER);
+ schema.put(TENTHOUS, JDBCType.INTEGER);
+ schema.put(ODD100, JDBCType.INTEGER);
+ schema.put(EVEN100, JDBCType.INTEGER);
+ schema.put(STRINGU1, JDBCType.VARCHAR);
+ schema.put(STRINGU2, JDBCType.VARCHAR);
+ schema.put(STRING4, JDBCType.VARCHAR);
+ return schema;
+ }
+
+ private static String getSQLType(JDBCType type) {
+ String suffix = "";
+ if (type == JDBCType.VARCHAR) {
+ suffix = "(256)";
+ }
+ return type.getName() + suffix;
+ }
+
+ private static IAType getAsterixType(JDBCType type) {
+ switch (type) {
+ case INTEGER:
+ return BuiltinType.AINT32;
+ case VARCHAR:
+ return BuiltinType.ASTRING;
+ default:
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ private static void setColumnValue(PreparedStatement stmt, int jdbcParamIdx, JDBCType type, String value)
+ throws SQLException {
+ switch (type) {
+ case INTEGER:
+ stmt.setInt(jdbcParamIdx, Integer.parseInt(value));
+ break;
+ case VARCHAR:
+ stmt.setString(jdbcParamIdx, value);
+ break;
+ default:
+ throw new UnsupportedOperationException(type.getName());
+ }
+ }
+
+ private static <T> List<T> randomize(Collection<T> input, Random random) {
+ List<T> output = new ArrayList<>(input);
+ Collections.shuffle(output, random);
+ return output;
+ }
+
+ private static String getConfigurationPropertyName(String propertyName) {
+ return String.format("%s.%s", SqlppRQGGroupingSetsIT.class.getSimpleName(), propertyName);
+ }
+
+ private static long getLongConfigurationProperty(String propertyName, long defValue) {
+ String textValue = System.getProperty(propertyName);
+ if (textValue == null) {
+ return defValue;
+ }
+ try {
+ return Long.parseLong(textValue);
+ } catch (NumberFormatException e) {
+ LOGGER.warn(String.format("Cannot parse configuration property: %s. Will use default value: %d",
+ propertyName, defValue));
+ return defValue;
+ }
+ }
+
+ private File writeResult(ArrayNode result, String resultKind) throws IOException {
+ String outFileName = String.format("%d.%s.txt", testcaseId, resultKind);
+ File outFile = new File(RESULT_OUTPUT_DIR.toFile(), outFileName);
+ try (PrintWriter pw = new PrintWriter(outFile, StandardCharsets.UTF_8.name())) {
+ pw.print("---");
+ pw.println(groupByClause);
+ for (int i = 0, ln = result.size(); i < ln; i++) {
+ pw.println(ResultExtractor.prettyPrint(result.get(i)));
+ }
+ }
+ return outFile;
+ }
+
+ @BeforeClass
+ public static void setUp() throws Exception {
+ startAsterix();
+ startPostgres();
+ FileUtils.forceMkdir(RESULT_OUTPUT_DIR.toFile());
+ }
+
+ @AfterClass
+ public static void tearDown() throws Exception {
+ stopPostgres();
+ stopAsterix();
+ }
+
+ private static void startAsterix() throws Exception {
+ testExecutor = new TestExecutor();
+ LangExecutionUtil.setUp(TEST_CONFIG_FILE_NAME, testExecutor);
+ loadAsterixData();
+ }
+
+ private static void stopAsterix() throws Exception {
+ LangExecutionUtil.tearDown();
+ }
+
+ private static void startPostgres() throws SQLException, IOException {
+ if (!Boolean.parseBoolean(System.getenv(TESTCONTAINERS_RYUK_DISABLED))) {
+ throw new IllegalStateException(
+ String.format("Set environment variable %s=%s", TESTCONTAINERS_RYUK_DISABLED, true));
+ }
+ LOGGER.info("Starting Postgres");
+ postgres = new PostgreSQLContainer<>(POSTGRES_IMAGE);
+ postgres.start();
+ conn = DriverManager.getConnection(postgres.getJdbcUrl(), postgres.getUsername(), postgres.getPassword());
+ stmt = conn.createStatement();
+ loadSQLData();
+ }
+
+ private static void stopPostgres() {
+ LOGGER.info("Stopping Postgres");
+ if (stmt != null) {
+ try {
+ stmt.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ if (conn != null) {
+ try {
+ conn.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ if (postgres != null) {
+ try {
+ postgres.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private static class TestQuery {
+ final String sqlQuery;
+ final String sqlppQuery;
+ final String groupbyClause;
+
+ TestQuery(String sqlQuery, String sqlppQuery, String groupbyClause) {
+ this.sqlQuery = sqlQuery;
+ this.sqlppQuery = sqlppQuery;
+ this.groupbyClause = groupbyClause;
+ }
+ }
+}
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/queries/group-by/grouping-sets-1.1.sqlpp b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/group-by/grouping-sets-1.1.sqlpp
new file mode 100644
index 0000000..9410948
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/group-by/grouping-sets-1.1.sqlpp
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+/*
+ * Test various combinations of grouping sets
+ */
+
+drop dataverse test if exists;
+create dataverse test;
+
+use test;
+
+create type tenkType as closed {
+ unique1 : integer,
+ unique2 : integer,
+ two : integer,
+ four : integer,
+ ten : integer,
+ twenty : integer,
+ hundred : integer,
+ thousand : integer,
+ twothousand : integer,
+ fivethous : integer,
+ tenthous : integer,
+ odd100 : integer,
+ even100 : integer,
+ stringu1 : string,
+ stringu2 : string,
+ string4 : string
+};
+
+create dataset tenk(tenkType) primary key unique2;
+
+select two, four, ten,
+ grouping(two, four, ten) as grp,
+ sum(twenty) as agg_sum
+from tenk
+group by grouping sets((two), (four), (two, four), (ten))
+order by two, four, ten;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/queries/group-by/grouping-sets-1.2.sqlpp b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/group-by/grouping-sets-1.2.sqlpp
new file mode 100644
index 0000000..1073678
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/group-by/grouping-sets-1.2.sqlpp
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+/*
+ * Test various combinations of grouping sets
+ */
+
+drop dataverse test if exists;
+create dataverse test;
+
+use test;
+
+create type tenkType as closed {
+ unique1 : integer,
+ unique2 : integer,
+ two : integer,
+ four : integer,
+ ten : integer,
+ twenty : integer,
+ hundred : integer,
+ thousand : integer,
+ twothousand : integer,
+ fivethous : integer,
+ tenthous : integer,
+ odd100 : integer,
+ even100 : integer,
+ stringu1 : string,
+ stringu2 : string,
+ string4 : string
+};
+
+create dataset tenk(tenkType) primary key unique2;
+
+select two, four, ten, twenty,
+ grouping(two, four, ten, twenty) as grp,
+ sum(hundred) as agg_sum
+from tenk
+group by rollup(two, four), cube(ten, twenty)
+order by two, four, ten, twenty;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results/group-by/grouping-sets-1.1.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/group-by/grouping-sets-1.1.plan
new file mode 100644
index 0000000..72d2bb1
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/group-by/grouping-sets-1.1.plan
@@ -0,0 +1,131 @@
+-- DISTRIBUTE_RESULT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- SORT_MERGE_EXCHANGE [$$245(ASC), $$246(ASC), $$247(ASC) ] |PARTITIONED|
+ -- STABLE_SORT [$$245(ASC), $$246(ASC), $$247(ASC)] |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- UNION_ALL |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- UNION_ALL |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- UNION_ALL |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- SORT_GROUP_BY[$$253] |PARTITIONED|
+ {
+ -- AGGREGATE |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- HASH_PARTITION_EXCHANGE [$$253] |PARTITIONED|
+ -- SORT_GROUP_BY[$$232] |PARTITIONED|
+ {
+ -- AGGREGATE |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- REPLICATE |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- DATASOURCE_SCAN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- SORT_GROUP_BY[$$255] |PARTITIONED|
+ {
+ -- AGGREGATE |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- HASH_PARTITION_EXCHANGE [$$255] |PARTITIONED|
+ -- SORT_GROUP_BY[$$233] |PARTITIONED|
+ {
+ -- AGGREGATE |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- REPLICATE |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- DATASOURCE_SCAN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- SORT_GROUP_BY[$$257, $$258] |PARTITIONED|
+ {
+ -- AGGREGATE |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- HASH_PARTITION_EXCHANGE [$$257, $$258] |PARTITIONED|
+ -- SORT_GROUP_BY[$$234, $$235] |PARTITIONED|
+ {
+ -- AGGREGATE |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- REPLICATE |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- DATASOURCE_SCAN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- SORT_GROUP_BY[$$260] |PARTITIONED|
+ {
+ -- AGGREGATE |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- HASH_PARTITION_EXCHANGE [$$260] |PARTITIONED|
+ -- SORT_GROUP_BY[$$236] |PARTITIONED|
+ {
+ -- AGGREGATE |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- REPLICATE |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- DATASOURCE_SCAN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results/group-by/grouping-sets-1.2.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/group-by/grouping-sets-1.2.plan
new file mode 100644
index 0000000..bea5ef6
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/group-by/grouping-sets-1.2.plan
@@ -0,0 +1,375 @@
+-- DISTRIBUTE_RESULT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- SORT_MERGE_EXCHANGE [$$759(ASC), $$760(ASC), $$761(ASC), $$762(ASC) ] |PARTITIONED|
+ -- STABLE_SORT [$$759(ASC), $$760(ASC), $$761(ASC), $$762(ASC)] |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- UNION_ALL |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- UNION_ALL |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- UNION_ALL |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- UNION_ALL |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- UNION_ALL |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- UNION_ALL |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- UNION_ALL |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- UNION_ALL |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- UNION_ALL |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- UNION_ALL |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- UNION_ALL |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- SORT_GROUP_BY[$$776, $$777, $$778, $$779] |PARTITIONED|
+ {
+ -- AGGREGATE |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- HASH_PARTITION_EXCHANGE [$$776, $$777, $$778, $$779] |PARTITIONED|
+ -- SORT_GROUP_BY[$$710, $$711, $$712, $$713] |PARTITIONED|
+ {
+ -- AGGREGATE |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- REPLICATE |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- DATASOURCE_SCAN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- SORT_GROUP_BY[$$781, $$782, $$783] |PARTITIONED|
+ {
+ -- AGGREGATE |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- HASH_PARTITION_EXCHANGE [$$781, $$782, $$783] |PARTITIONED|
+ -- SORT_GROUP_BY[$$714, $$715, $$716] |PARTITIONED|
+ {
+ -- AGGREGATE |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- REPLICATE |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- DATASOURCE_SCAN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- SORT_GROUP_BY[$$785, $$786, $$787] |PARTITIONED|
+ {
+ -- AGGREGATE |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- HASH_PARTITION_EXCHANGE [$$785, $$786, $$787] |PARTITIONED|
+ -- SORT_GROUP_BY[$$717, $$718, $$719] |PARTITIONED|
+ {
+ -- AGGREGATE |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- REPLICATE |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- DATASOURCE_SCAN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- SORT_GROUP_BY[$$789, $$790] |PARTITIONED|
+ {
+ -- AGGREGATE |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- HASH_PARTITION_EXCHANGE [$$789, $$790] |PARTITIONED|
+ -- SORT_GROUP_BY[$$720, $$721] |PARTITIONED|
+ {
+ -- AGGREGATE |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- REPLICATE |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- DATASOURCE_SCAN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- SORT_GROUP_BY[$$792, $$793, $$794] |PARTITIONED|
+ {
+ -- AGGREGATE |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- HASH_PARTITION_EXCHANGE [$$792, $$793, $$794] |PARTITIONED|
+ -- SORT_GROUP_BY[$$722, $$723, $$724] |PARTITIONED|
+ {
+ -- AGGREGATE |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- REPLICATE |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- DATASOURCE_SCAN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- SORT_GROUP_BY[$$796, $$797] |PARTITIONED|
+ {
+ -- AGGREGATE |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- HASH_PARTITION_EXCHANGE [$$796, $$797] |PARTITIONED|
+ -- SORT_GROUP_BY[$$725, $$726] |PARTITIONED|
+ {
+ -- AGGREGATE |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- REPLICATE |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- DATASOURCE_SCAN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- SORT_GROUP_BY[$$799, $$800] |PARTITIONED|
+ {
+ -- AGGREGATE |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- HASH_PARTITION_EXCHANGE [$$799, $$800] |PARTITIONED|
+ -- SORT_GROUP_BY[$$727, $$728] |PARTITIONED|
+ {
+ -- AGGREGATE |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- REPLICATE |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- DATASOURCE_SCAN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- SORT_GROUP_BY[$$802] |PARTITIONED|
+ {
+ -- AGGREGATE |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- HASH_PARTITION_EXCHANGE [$$802] |PARTITIONED|
+ -- SORT_GROUP_BY[$$729] |PARTITIONED|
+ {
+ -- AGGREGATE |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- REPLICATE |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- DATASOURCE_SCAN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- SORT_GROUP_BY[$$804, $$805] |PARTITIONED|
+ {
+ -- AGGREGATE |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- HASH_PARTITION_EXCHANGE [$$804, $$805] |PARTITIONED|
+ -- SORT_GROUP_BY[$$730, $$731] |PARTITIONED|
+ {
+ -- AGGREGATE |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- REPLICATE |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- DATASOURCE_SCAN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- SORT_GROUP_BY[$$807] |PARTITIONED|
+ {
+ -- AGGREGATE |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- HASH_PARTITION_EXCHANGE [$$807] |PARTITIONED|
+ -- SORT_GROUP_BY[$$732] |PARTITIONED|
+ {
+ -- AGGREGATE |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- REPLICATE |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- DATASOURCE_SCAN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- SORT_GROUP_BY[$$809] |PARTITIONED|
+ {
+ -- AGGREGATE |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- HASH_PARTITION_EXCHANGE [$$809] |PARTITIONED|
+ -- SORT_GROUP_BY[$$733] |PARTITIONED|
+ {
+ -- AGGREGATE |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- REPLICATE |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- DATASOURCE_SCAN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- SORT_GROUP_BY[$$811] |PARTITIONED|
+ {
+ -- AGGREGATE |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- HASH_PARTITION_EXCHANGE [$$811] |PARTITIONED|
+ -- SORT_GROUP_BY[$$734] |PARTITIONED|
+ {
+ -- AGGREGATE |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- REPLICATE |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- DATASOURCE_SCAN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-1/grouping-sets-1.1.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-1/grouping-sets-1.1.ddl.sqlpp
new file mode 100644
index 0000000..e93b929
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-1/grouping-sets-1.1.ddl.sqlpp
@@ -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.
+ */
+
+/*
+ * Test various combinations of grouping sets
+ */
+
+drop dataverse test if exists;
+create dataverse test;
+
+use test;
+
+create type tenkType as closed {
+ unique1 : integer,
+ unique2 : integer,
+ two : integer,
+ four : integer,
+ ten : integer,
+ twenty : integer,
+ hundred : integer,
+ thousand : integer,
+ twothousand : integer,
+ fivethous : integer,
+ tenthous : integer,
+ odd100 : integer,
+ even100 : integer,
+ stringu1 : string,
+ stringu2 : string,
+ string4 : string
+};
+
+create dataset tenk(tenkType) primary key unique2;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-1/grouping-sets-1.10.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-1/grouping-sets-1.10.query.sqlpp
new file mode 100644
index 0000000..9fca153
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-1/grouping-sets-1.10.query.sqlpp
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+use test;
+
+select two, four, ten,
+ grouping(two, four, ten) as grp,
+ sum(twenty) as agg_sum
+from tenk
+group by grouping sets((two), (four), (two, four), (ten))
+order by two, four, ten;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-1/grouping-sets-1.11.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-1/grouping-sets-1.11.query.sqlpp
new file mode 100644
index 0000000..6fafaa8
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-1/grouping-sets-1.11.query.sqlpp
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+use test;
+
+select two, four, ten,
+ grouping(two, four, ten) as grp,
+ sum(twenty) as agg_sum
+from tenk
+group by rollup(two, (four, ten))
+order by two, four, ten;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-1/grouping-sets-1.12.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-1/grouping-sets-1.12.query.sqlpp
new file mode 100644
index 0000000..c401353
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-1/grouping-sets-1.12.query.sqlpp
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+use test;
+
+select two, grouping(two) as grp, sum(ten) as agg_sum
+from tenk
+group by grouping sets(grouping sets(grouping sets(grouping sets((two)))))
+order by two;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-1/grouping-sets-1.13.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-1/grouping-sets-1.13.query.sqlpp
new file mode 100644
index 0000000..85b3b54
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-1/grouping-sets-1.13.query.sqlpp
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+use test;
+
+select two, four, ten, twenty,
+ grouping(two, four, ten, twenty) as grp,
+ sum(hundred) as agg_sum
+from tenk
+group by rollup(two, four), cube(ten, twenty)
+order by two, four, ten, twenty;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-1/grouping-sets-1.14.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-1/grouping-sets-1.14.query.sqlpp
new file mode 100644
index 0000000..e7fab5a
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-1/grouping-sets-1.14.query.sqlpp
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+use test;
+
+select two, four, ten, twenty,
+ grouping(two, four, ten, twenty) as grp,
+ sum(hundred) as agg_sum
+from tenk
+group by grouping sets(rollup(two, four)), grouping sets(cube(ten, twenty))
+order by two, four, ten, twenty;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-1/grouping-sets-1.15.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-1/grouping-sets-1.15.query.sqlpp
new file mode 100644
index 0000000..7a870a7
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-1/grouping-sets-1.15.query.sqlpp
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+use test;
+
+select two, four, ten, twenty,
+ grouping(two, four, ten, twenty) as grp,
+ sum(hundred) as agg_sum
+from tenk
+group by grouping sets(rollup(two, four), cube(ten, twenty))
+order by two, four, ten, twenty;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-1/grouping-sets-1.2.update.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-1/grouping-sets-1.2.update.sqlpp
new file mode 100644
index 0000000..2d7e768
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-1/grouping-sets-1.2.update.sqlpp
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+use test;
+
+load dataset tenk using localfs ((`path`=`asterix_nc1://data/tenk.tbl`),(`format`=`delimited-text`),(`delimiter`=`|`));
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-1/grouping-sets-1.3.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-1/grouping-sets-1.3.query.sqlpp
new file mode 100644
index 0000000..79f203f
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-1/grouping-sets-1.3.query.sqlpp
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+use test;
+
+select sum(ten) as agg_sum
+from tenk
+group by ();
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-1/grouping-sets-1.4.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-1/grouping-sets-1.4.query.sqlpp
new file mode 100644
index 0000000..15d7250
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-1/grouping-sets-1.4.query.sqlpp
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+use test;
+
+select sum(ten) as agg_sum
+from tenk
+group by grouping sets(());
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-1/grouping-sets-1.5.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-1/grouping-sets-1.5.query.sqlpp
new file mode 100644
index 0000000..43cc448
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-1/grouping-sets-1.5.query.sqlpp
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+use test;
+
+select sum(ten) as agg_sum
+from tenk
+group by grouping sets((), ());
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-1/grouping-sets-1.6.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-1/grouping-sets-1.6.query.sqlpp
new file mode 100644
index 0000000..b3a8aac
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-1/grouping-sets-1.6.query.sqlpp
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+use test;
+
+select sum(ten) as agg_sum
+from tenk
+group by grouping sets(()), grouping sets(());
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-1/grouping-sets-1.7.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-1/grouping-sets-1.7.query.sqlpp
new file mode 100644
index 0000000..baad394
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-1/grouping-sets-1.7.query.sqlpp
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+use test;
+
+select two, four, grouping(two, four) as grp, sum(ten) as agg_sum
+from tenk
+group by rollup(two,four)
+order by two, four;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-1/grouping-sets-1.8.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-1/grouping-sets-1.8.query.sqlpp
new file mode 100644
index 0000000..0d8fd3e
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-1/grouping-sets-1.8.query.sqlpp
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+use test;
+
+select two, four, grouping(two, four) as grp, sum(ten) as agg_sum
+from tenk
+group by cube(two,four)
+order by two, four;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-1/grouping-sets-1.9.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-1/grouping-sets-1.9.query.sqlpp
new file mode 100644
index 0000000..4e435da
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-1/grouping-sets-1.9.query.sqlpp
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+use test;
+
+select two, four, ten, grouping(two, four, ten) as grp, sum(twenty) as agg_sum
+from tenk
+group by two, rollup(four, ten)
+order by two, four, ten;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-2/grouping-sets-2.1.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-2/grouping-sets-2.1.ddl.sqlpp
new file mode 100644
index 0000000..39bde64
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-2/grouping-sets-2.1.ddl.sqlpp
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+/*
+ * Test miscellaneous grouping set features
+ */
+
+drop dataverse test if exists;
+create dataverse test;
+
+use test;
+
+create type tenkType as closed {
+ unique1 : integer,
+ unique2 : integer,
+ two : integer,
+ four : integer,
+ ten : integer,
+ twenty : integer,
+ hundred : integer,
+ thousand : integer,
+ twothousand : integer,
+ fivethous : integer,
+ tenthous : integer,
+ odd100 : integer,
+ even100 : integer,
+ stringu1 : string,
+ stringu2 : string,
+ string4 : string
+};
+
+create dataset tenk(tenkType) primary key unique2;
+
+create function gs1() {
+ select two, four, grouping(two, four) as grp, sum(ten) as agg_sum
+ from tenk
+ group by rollup(two,four)
+ order by two, four
+};
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-2/grouping-sets-2.2.update.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-2/grouping-sets-2.2.update.sqlpp
new file mode 100644
index 0000000..2d7e768
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-2/grouping-sets-2.2.update.sqlpp
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+use test;
+
+load dataset tenk using localfs ((`path`=`asterix_nc1://data/tenk.tbl`),(`format`=`delimited-text`),(`delimiter`=`|`));
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-2/grouping-sets-2.3.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-2/grouping-sets-2.3.query.sqlpp
new file mode 100644
index 0000000..fd5a82f
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-2/grouping-sets-2.3.query.sqlpp
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+/*
+ * Test grouping sets in UDF
+ */
+
+use test;
+
+gs1();
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-2/grouping-sets-2.4.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-2/grouping-sets-2.4.query.sqlpp
new file mode 100644
index 0000000..6fcac65
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-2/grouping-sets-2.4.query.sqlpp
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+/*
+ * Test aliases in grouping sets
+ */
+
+use test;
+
+select v2, v4, grouping(v2, v4) as grp, sum(ten) as agg_sum
+ from tenk
+ group by rollup(two as v2, four as v4)
+ order by v2, v4;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-2/grouping-sets-2.5.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-2/grouping-sets-2.5.query.sqlpp
new file mode 100644
index 0000000..38a80f1
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-2/grouping-sets-2.5.query.sqlpp
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+/*
+ * Test aliases in grouping sets.
+ * An element is first used with an alias then without an alias.
+ */
+
+use test;
+
+select v2, v4, grouping(v2, v4) as grp, sum(ten) as agg_sum
+ from tenk
+ group by two as v2, rollup(two, four as v4)
+ order by v2, v4;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-3-negative/grouping-sets-3-negative.1.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-3-negative/grouping-sets-3-negative.1.query.sqlpp
new file mode 100644
index 0000000..b823e5e
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-3-negative/grouping-sets-3-negative.1.query.sqlpp
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+/*
+ * Unexpected alias after the same element without an alias
+ */
+
+with hundred as (
+ select r % 2 as two, r % 4 as four, r % 10 as ten
+ from range(1, 100) r
+)
+
+select two, four, grouping(two, four) as grp, sum(ten) as agg_sum
+from hundred
+group by two, rollup(two as v21, four);
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-3-negative/grouping-sets-3-negative.2.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-3-negative/grouping-sets-3-negative.2.query.sqlpp
new file mode 100644
index 0000000..7370e11
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-3-negative/grouping-sets-3-negative.2.query.sqlpp
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+/*
+ * Unexpected alias after the same element with a different alias
+ * in another grouping set
+ */
+
+with hundred as (
+ select r % 2 as two, r % 4 as four, r % 10 as ten
+ from range(1, 100) r
+)
+
+select two, four, grouping(two, four) as grp, sum(ten) as agg_sum
+from hundred
+group by rollup(two as v2, four), cube(two as v22, four);
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-3-negative/grouping-sets-3-negative.3.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-3-negative/grouping-sets-3-negative.3.query.sqlpp
new file mode 100644
index 0000000..b481dbf
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-3-negative/grouping-sets-3-negative.3.query.sqlpp
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+/*
+ * Unexpected alias after the same element with a different alias
+ * in the same grouping set
+ */
+
+with hundred as (
+ select r % 2 as two, r % 4 as four, r % 10 as ten
+ from range(1, 100) r
+)
+
+select two, four, grouping(two, four) as grp, sum(ten) as agg_sum
+from hundred
+group by grouping sets( (two as v2, two as v23, four) );
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-3-negative/grouping-sets-3-negative.4.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-3-negative/grouping-sets-3-negative.4.query.sqlpp
new file mode 100644
index 0000000..58a789e
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-3-negative/grouping-sets-3-negative.4.query.sqlpp
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+/*
+ * GROUPING() with no arguments
+ */
+
+with hundred as (
+ select r % 2 as two, r % 4 as four, r % 10 as ten
+ from range(1, 100) r
+)
+
+select two, four, grouping() as grp, sum(ten) as agg_sum
+from hundred
+group by two, four;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-3-negative/grouping-sets-3-negative.5.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-3-negative/grouping-sets-3-negative.5.query.sqlpp
new file mode 100644
index 0000000..36f11d1
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-3-negative/grouping-sets-3-negative.5.query.sqlpp
@@ -0,0 +1,31 @@
+
+/*
+ * 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.
+ */
+/*
+ * GROUPING() on a non-grouping element
+ */
+
+with hundred as (
+ select r % 2 as two, r % 4 as four, r % 10 as ten
+ from range(1, 100) r
+)
+
+select two, four, grouping(ten) as grp, sum(ten) as agg_sum
+from hundred
+group by two, four;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-3-negative/grouping-sets-3-negative.6.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-3-negative/grouping-sets-3-negative.6.query.sqlpp
new file mode 100644
index 0000000..0876c44
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-3-negative/grouping-sets-3-negative.6.query.sqlpp
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+/*
+ * GROUPING() with f(grouping element)
+ */
+
+with hundred as (
+ select r % 2 as two, r % 4 as four, r % 10 as ten
+ from range(1, 100) r
+)
+
+select two, four, grouping(two+four) as grp, sum(ten) as agg_sum
+from hundred
+group by two, four;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-3-negative/grouping-sets-3-negative.7.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-3-negative/grouping-sets-3-negative.7.query.sqlpp
new file mode 100644
index 0000000..2cb8f36
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-3-negative/grouping-sets-3-negative.7.query.sqlpp
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+/*
+ * GROUPING() with GROUPING() argument
+ */
+
+with hundred as (
+ select r % 2 as two, r % 4 as four, r % 10 as ten
+ from range(1, 100) r
+)
+
+select two, four, grouping(grouping(two)) as grp, sum(ten) as agg_sum
+from hundred
+group by two, four;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-3-negative/grouping-sets-3-negative.8.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-3-negative/grouping-sets-3-negative.8.query.sqlpp
new file mode 100644
index 0000000..4fec641
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/grouping-sets-3-negative/grouping-sets-3-negative.8.query.sqlpp
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+/*
+ * Too many grouping sets
+ */
+
+with hundred as (
+ select r % 2 as two, r %3 as three, r % 4 as four,
+ r % 5 as five, r % 6 as six, r % 7 as seven,
+ t % 8 as eight, r % 9 as nine, r % 10 as ten
+ from range(1, 100) r
+)
+
+select count(*) as agg_sum
+from hundred
+group by cube(two,three,four,five,six,seven,eight,nine,ten);
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/grouping-sets-1/grouping-sets-1.10.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/grouping-sets-1/grouping-sets-1.10.adm
new file mode 100644
index 0000000..9c3921a4
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/grouping-sets-1/grouping-sets-1.10.adm
@@ -0,0 +1,20 @@
+{ "two": null, "four": null, "ten": 0, "grp": 6, "agg_sum": 5000 }
+{ "two": null, "four": null, "ten": 1, "grp": 6, "agg_sum": 6000 }
+{ "two": null, "four": null, "ten": 2, "grp": 6, "agg_sum": 7000 }
+{ "two": null, "four": null, "ten": 3, "grp": 6, "agg_sum": 8000 }
+{ "two": null, "four": null, "ten": 4, "grp": 6, "agg_sum": 9000 }
+{ "two": null, "four": null, "ten": 5, "grp": 6, "agg_sum": 10000 }
+{ "two": null, "four": null, "ten": 6, "grp": 6, "agg_sum": 11000 }
+{ "two": null, "four": null, "ten": 7, "grp": 6, "agg_sum": 12000 }
+{ "two": null, "four": null, "ten": 8, "grp": 6, "agg_sum": 13000 }
+{ "two": null, "four": null, "ten": 9, "grp": 6, "agg_sum": 14000 }
+{ "two": null, "four": 0, "ten": null, "grp": 5, "agg_sum": 20000 }
+{ "two": null, "four": 1, "ten": null, "grp": 5, "agg_sum": 22500 }
+{ "two": null, "four": 2, "ten": null, "grp": 5, "agg_sum": 25000 }
+{ "two": null, "four": 3, "ten": null, "grp": 5, "agg_sum": 27500 }
+{ "two": 0, "four": null, "ten": null, "grp": 3, "agg_sum": 45000 }
+{ "two": 0, "four": 0, "ten": null, "grp": 1, "agg_sum": 20000 }
+{ "two": 0, "four": 2, "ten": null, "grp": 1, "agg_sum": 25000 }
+{ "two": 1, "four": null, "ten": null, "grp": 3, "agg_sum": 50000 }
+{ "two": 1, "four": 1, "ten": null, "grp": 1, "agg_sum": 22500 }
+{ "two": 1, "four": 3, "ten": null, "grp": 1, "agg_sum": 27500 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/grouping-sets-1/grouping-sets-1.11.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/grouping-sets-1/grouping-sets-1.11.adm
new file mode 100644
index 0000000..dca3762
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/grouping-sets-1/grouping-sets-1.11.adm
@@ -0,0 +1,23 @@
+{ "two": null, "four": null, "ten": null, "grp": 7, "agg_sum": 95000 }
+{ "two": 0, "four": null, "ten": null, "grp": 3, "agg_sum": 45000 }
+{ "two": 0, "four": 0, "ten": 0, "grp": 0, "agg_sum": 0 }
+{ "two": 0, "four": 0, "ten": 2, "grp": 0, "agg_sum": 6000 }
+{ "two": 0, "four": 0, "ten": 4, "grp": 0, "agg_sum": 2000 }
+{ "two": 0, "four": 0, "ten": 6, "grp": 0, "agg_sum": 8000 }
+{ "two": 0, "four": 0, "ten": 8, "grp": 0, "agg_sum": 4000 }
+{ "two": 0, "four": 2, "ten": 0, "grp": 0, "agg_sum": 5000 }
+{ "two": 0, "four": 2, "ten": 2, "grp": 0, "agg_sum": 1000 }
+{ "two": 0, "four": 2, "ten": 4, "grp": 0, "agg_sum": 7000 }
+{ "two": 0, "four": 2, "ten": 6, "grp": 0, "agg_sum": 3000 }
+{ "two": 0, "four": 2, "ten": 8, "grp": 0, "agg_sum": 9000 }
+{ "two": 1, "four": null, "ten": null, "grp": 3, "agg_sum": 50000 }
+{ "two": 1, "four": 1, "ten": 1, "grp": 0, "agg_sum": 500 }
+{ "two": 1, "four": 1, "ten": 3, "grp": 0, "agg_sum": 6500 }
+{ "two": 1, "four": 1, "ten": 5, "grp": 0, "agg_sum": 2500 }
+{ "two": 1, "four": 1, "ten": 7, "grp": 0, "agg_sum": 8500 }
+{ "two": 1, "four": 1, "ten": 9, "grp": 0, "agg_sum": 4500 }
+{ "two": 1, "four": 3, "ten": 1, "grp": 0, "agg_sum": 5500 }
+{ "two": 1, "four": 3, "ten": 3, "grp": 0, "agg_sum": 1500 }
+{ "two": 1, "four": 3, "ten": 5, "grp": 0, "agg_sum": 7500 }
+{ "two": 1, "four": 3, "ten": 7, "grp": 0, "agg_sum": 3500 }
+{ "two": 1, "four": 3, "ten": 9, "grp": 0, "agg_sum": 9500 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/grouping-sets-1/grouping-sets-1.12.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/grouping-sets-1/grouping-sets-1.12.adm
new file mode 100644
index 0000000..a398052
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/grouping-sets-1/grouping-sets-1.12.adm
@@ -0,0 +1,2 @@
+{ "two": 0, "grp": 0, "agg_sum": 20000 }
+{ "two": 1, "grp": 0, "agg_sum": 25000 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/grouping-sets-1/grouping-sets-1.13.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/grouping-sets-1/grouping-sets-1.13.adm
new file mode 100644
index 0000000..b05632b
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/grouping-sets-1/grouping-sets-1.13.adm
@@ -0,0 +1,167 @@
+{ "two": null, "four": null, "ten": null, "twenty": null, "grp": 15, "agg_sum": 495000 }
+{ "two": null, "four": null, "ten": null, "twenty": 0, "grp": 14, "agg_sum": 20000 }
+{ "two": null, "four": null, "ten": null, "twenty": 1, "grp": 14, "agg_sum": 20500 }
+{ "two": null, "four": null, "ten": null, "twenty": 2, "grp": 14, "agg_sum": 21000 }
+{ "two": null, "four": null, "ten": null, "twenty": 3, "grp": 14, "agg_sum": 21500 }
+{ "two": null, "four": null, "ten": null, "twenty": 4, "grp": 14, "agg_sum": 22000 }
+{ "two": null, "four": null, "ten": null, "twenty": 5, "grp": 14, "agg_sum": 22500 }
+{ "two": null, "four": null, "ten": null, "twenty": 6, "grp": 14, "agg_sum": 23000 }
+{ "two": null, "four": null, "ten": null, "twenty": 7, "grp": 14, "agg_sum": 23500 }
+{ "two": null, "four": null, "ten": null, "twenty": 8, "grp": 14, "agg_sum": 24000 }
+{ "two": null, "four": null, "ten": null, "twenty": 9, "grp": 14, "agg_sum": 24500 }
+{ "two": null, "four": null, "ten": null, "twenty": 10, "grp": 14, "agg_sum": 25000 }
+{ "two": null, "four": null, "ten": null, "twenty": 11, "grp": 14, "agg_sum": 25500 }
+{ "two": null, "four": null, "ten": null, "twenty": 12, "grp": 14, "agg_sum": 26000 }
+{ "two": null, "four": null, "ten": null, "twenty": 13, "grp": 14, "agg_sum": 26500 }
+{ "two": null, "four": null, "ten": null, "twenty": 14, "grp": 14, "agg_sum": 27000 }
+{ "two": null, "four": null, "ten": null, "twenty": 15, "grp": 14, "agg_sum": 27500 }
+{ "two": null, "four": null, "ten": null, "twenty": 16, "grp": 14, "agg_sum": 28000 }
+{ "two": null, "four": null, "ten": null, "twenty": 17, "grp": 14, "agg_sum": 28500 }
+{ "two": null, "four": null, "ten": null, "twenty": 18, "grp": 14, "agg_sum": 29000 }
+{ "two": null, "four": null, "ten": null, "twenty": 19, "grp": 14, "agg_sum": 29500 }
+{ "two": null, "four": null, "ten": 0, "twenty": null, "grp": 13, "agg_sum": 45000 }
+{ "two": null, "four": null, "ten": 0, "twenty": 0, "grp": 12, "agg_sum": 20000 }
+{ "two": null, "four": null, "ten": 0, "twenty": 10, "grp": 12, "agg_sum": 25000 }
+{ "two": null, "four": null, "ten": 1, "twenty": null, "grp": 13, "agg_sum": 46000 }
+{ "two": null, "four": null, "ten": 1, "twenty": 1, "grp": 12, "agg_sum": 20500 }
+{ "two": null, "four": null, "ten": 1, "twenty": 11, "grp": 12, "agg_sum": 25500 }
+{ "two": null, "four": null, "ten": 2, "twenty": null, "grp": 13, "agg_sum": 47000 }
+{ "two": null, "four": null, "ten": 2, "twenty": 2, "grp": 12, "agg_sum": 21000 }
+{ "two": null, "four": null, "ten": 2, "twenty": 12, "grp": 12, "agg_sum": 26000 }
+{ "two": null, "four": null, "ten": 3, "twenty": null, "grp": 13, "agg_sum": 48000 }
+{ "two": null, "four": null, "ten": 3, "twenty": 3, "grp": 12, "agg_sum": 21500 }
+{ "two": null, "four": null, "ten": 3, "twenty": 13, "grp": 12, "agg_sum": 26500 }
+{ "two": null, "four": null, "ten": 4, "twenty": null, "grp": 13, "agg_sum": 49000 }
+{ "two": null, "four": null, "ten": 4, "twenty": 4, "grp": 12, "agg_sum": 22000 }
+{ "two": null, "four": null, "ten": 4, "twenty": 14, "grp": 12, "agg_sum": 27000 }
+{ "two": null, "four": null, "ten": 5, "twenty": null, "grp": 13, "agg_sum": 50000 }
+{ "two": null, "four": null, "ten": 5, "twenty": 5, "grp": 12, "agg_sum": 22500 }
+{ "two": null, "four": null, "ten": 5, "twenty": 15, "grp": 12, "agg_sum": 27500 }
+{ "two": null, "four": null, "ten": 6, "twenty": null, "grp": 13, "agg_sum": 51000 }
+{ "two": null, "four": null, "ten": 6, "twenty": 6, "grp": 12, "agg_sum": 23000 }
+{ "two": null, "four": null, "ten": 6, "twenty": 16, "grp": 12, "agg_sum": 28000 }
+{ "two": null, "four": null, "ten": 7, "twenty": null, "grp": 13, "agg_sum": 52000 }
+{ "two": null, "four": null, "ten": 7, "twenty": 7, "grp": 12, "agg_sum": 23500 }
+{ "two": null, "four": null, "ten": 7, "twenty": 17, "grp": 12, "agg_sum": 28500 }
+{ "two": null, "four": null, "ten": 8, "twenty": null, "grp": 13, "agg_sum": 53000 }
+{ "two": null, "four": null, "ten": 8, "twenty": 8, "grp": 12, "agg_sum": 24000 }
+{ "two": null, "four": null, "ten": 8, "twenty": 18, "grp": 12, "agg_sum": 29000 }
+{ "two": null, "four": null, "ten": 9, "twenty": null, "grp": 13, "agg_sum": 54000 }
+{ "two": null, "four": null, "ten": 9, "twenty": 9, "grp": 12, "agg_sum": 24500 }
+{ "two": null, "four": null, "ten": 9, "twenty": 19, "grp": 12, "agg_sum": 29500 }
+{ "two": 0, "four": null, "ten": null, "twenty": null, "grp": 7, "agg_sum": 245000 }
+{ "two": 0, "four": null, "ten": null, "twenty": 0, "grp": 6, "agg_sum": 20000 }
+{ "two": 0, "four": null, "ten": null, "twenty": 2, "grp": 6, "agg_sum": 21000 }
+{ "two": 0, "four": null, "ten": null, "twenty": 4, "grp": 6, "agg_sum": 22000 }
+{ "two": 0, "four": null, "ten": null, "twenty": 6, "grp": 6, "agg_sum": 23000 }
+{ "two": 0, "four": null, "ten": null, "twenty": 8, "grp": 6, "agg_sum": 24000 }
+{ "two": 0, "four": null, "ten": null, "twenty": 10, "grp": 6, "agg_sum": 25000 }
+{ "two": 0, "four": null, "ten": null, "twenty": 12, "grp": 6, "agg_sum": 26000 }
+{ "two": 0, "four": null, "ten": null, "twenty": 14, "grp": 6, "agg_sum": 27000 }
+{ "two": 0, "four": null, "ten": null, "twenty": 16, "grp": 6, "agg_sum": 28000 }
+{ "two": 0, "four": null, "ten": null, "twenty": 18, "grp": 6, "agg_sum": 29000 }
+{ "two": 0, "four": null, "ten": 0, "twenty": null, "grp": 5, "agg_sum": 45000 }
+{ "two": 0, "four": null, "ten": 0, "twenty": 0, "grp": 4, "agg_sum": 20000 }
+{ "two": 0, "four": null, "ten": 0, "twenty": 10, "grp": 4, "agg_sum": 25000 }
+{ "two": 0, "four": null, "ten": 2, "twenty": null, "grp": 5, "agg_sum": 47000 }
+{ "two": 0, "four": null, "ten": 2, "twenty": 2, "grp": 4, "agg_sum": 21000 }
+{ "two": 0, "four": null, "ten": 2, "twenty": 12, "grp": 4, "agg_sum": 26000 }
+{ "two": 0, "four": null, "ten": 4, "twenty": null, "grp": 5, "agg_sum": 49000 }
+{ "two": 0, "four": null, "ten": 4, "twenty": 4, "grp": 4, "agg_sum": 22000 }
+{ "two": 0, "four": null, "ten": 4, "twenty": 14, "grp": 4, "agg_sum": 27000 }
+{ "two": 0, "four": null, "ten": 6, "twenty": null, "grp": 5, "agg_sum": 51000 }
+{ "two": 0, "four": null, "ten": 6, "twenty": 6, "grp": 4, "agg_sum": 23000 }
+{ "two": 0, "four": null, "ten": 6, "twenty": 16, "grp": 4, "agg_sum": 28000 }
+{ "two": 0, "four": null, "ten": 8, "twenty": null, "grp": 5, "agg_sum": 53000 }
+{ "two": 0, "four": null, "ten": 8, "twenty": 8, "grp": 4, "agg_sum": 24000 }
+{ "two": 0, "four": null, "ten": 8, "twenty": 18, "grp": 4, "agg_sum": 29000 }
+{ "two": 0, "four": 0, "ten": null, "twenty": null, "grp": 3, "agg_sum": 120000 }
+{ "two": 0, "four": 0, "ten": null, "twenty": 0, "grp": 2, "agg_sum": 20000 }
+{ "two": 0, "four": 0, "ten": null, "twenty": 4, "grp": 2, "agg_sum": 22000 }
+{ "two": 0, "four": 0, "ten": null, "twenty": 8, "grp": 2, "agg_sum": 24000 }
+{ "two": 0, "four": 0, "ten": null, "twenty": 12, "grp": 2, "agg_sum": 26000 }
+{ "two": 0, "four": 0, "ten": null, "twenty": 16, "grp": 2, "agg_sum": 28000 }
+{ "two": 0, "four": 0, "ten": 0, "twenty": null, "grp": 1, "agg_sum": 20000 }
+{ "two": 0, "four": 0, "ten": 0, "twenty": 0, "grp": 0, "agg_sum": 20000 }
+{ "two": 0, "four": 0, "ten": 2, "twenty": null, "grp": 1, "agg_sum": 26000 }
+{ "two": 0, "four": 0, "ten": 2, "twenty": 12, "grp": 0, "agg_sum": 26000 }
+{ "two": 0, "four": 0, "ten": 4, "twenty": null, "grp": 1, "agg_sum": 22000 }
+{ "two": 0, "four": 0, "ten": 4, "twenty": 4, "grp": 0, "agg_sum": 22000 }
+{ "two": 0, "four": 0, "ten": 6, "twenty": null, "grp": 1, "agg_sum": 28000 }
+{ "two": 0, "four": 0, "ten": 6, "twenty": 16, "grp": 0, "agg_sum": 28000 }
+{ "two": 0, "four": 0, "ten": 8, "twenty": null, "grp": 1, "agg_sum": 24000 }
+{ "two": 0, "four": 0, "ten": 8, "twenty": 8, "grp": 0, "agg_sum": 24000 }
+{ "two": 0, "four": 2, "ten": null, "twenty": null, "grp": 3, "agg_sum": 125000 }
+{ "two": 0, "four": 2, "ten": null, "twenty": 2, "grp": 2, "agg_sum": 21000 }
+{ "two": 0, "four": 2, "ten": null, "twenty": 6, "grp": 2, "agg_sum": 23000 }
+{ "two": 0, "four": 2, "ten": null, "twenty": 10, "grp": 2, "agg_sum": 25000 }
+{ "two": 0, "four": 2, "ten": null, "twenty": 14, "grp": 2, "agg_sum": 27000 }
+{ "two": 0, "four": 2, "ten": null, "twenty": 18, "grp": 2, "agg_sum": 29000 }
+{ "two": 0, "four": 2, "ten": 0, "twenty": null, "grp": 1, "agg_sum": 25000 }
+{ "two": 0, "four": 2, "ten": 0, "twenty": 10, "grp": 0, "agg_sum": 25000 }
+{ "two": 0, "four": 2, "ten": 2, "twenty": null, "grp": 1, "agg_sum": 21000 }
+{ "two": 0, "four": 2, "ten": 2, "twenty": 2, "grp": 0, "agg_sum": 21000 }
+{ "two": 0, "four": 2, "ten": 4, "twenty": null, "grp": 1, "agg_sum": 27000 }
+{ "two": 0, "four": 2, "ten": 4, "twenty": 14, "grp": 0, "agg_sum": 27000 }
+{ "two": 0, "four": 2, "ten": 6, "twenty": null, "grp": 1, "agg_sum": 23000 }
+{ "two": 0, "four": 2, "ten": 6, "twenty": 6, "grp": 0, "agg_sum": 23000 }
+{ "two": 0, "four": 2, "ten": 8, "twenty": null, "grp": 1, "agg_sum": 29000 }
+{ "two": 0, "four": 2, "ten": 8, "twenty": 18, "grp": 0, "agg_sum": 29000 }
+{ "two": 1, "four": null, "ten": null, "twenty": null, "grp": 7, "agg_sum": 250000 }
+{ "two": 1, "four": null, "ten": null, "twenty": 1, "grp": 6, "agg_sum": 20500 }
+{ "two": 1, "four": null, "ten": null, "twenty": 3, "grp": 6, "agg_sum": 21500 }
+{ "two": 1, "four": null, "ten": null, "twenty": 5, "grp": 6, "agg_sum": 22500 }
+{ "two": 1, "four": null, "ten": null, "twenty": 7, "grp": 6, "agg_sum": 23500 }
+{ "two": 1, "four": null, "ten": null, "twenty": 9, "grp": 6, "agg_sum": 24500 }
+{ "two": 1, "four": null, "ten": null, "twenty": 11, "grp": 6, "agg_sum": 25500 }
+{ "two": 1, "four": null, "ten": null, "twenty": 13, "grp": 6, "agg_sum": 26500 }
+{ "two": 1, "four": null, "ten": null, "twenty": 15, "grp": 6, "agg_sum": 27500 }
+{ "two": 1, "four": null, "ten": null, "twenty": 17, "grp": 6, "agg_sum": 28500 }
+{ "two": 1, "four": null, "ten": null, "twenty": 19, "grp": 6, "agg_sum": 29500 }
+{ "two": 1, "four": null, "ten": 1, "twenty": null, "grp": 5, "agg_sum": 46000 }
+{ "two": 1, "four": null, "ten": 1, "twenty": 1, "grp": 4, "agg_sum": 20500 }
+{ "two": 1, "four": null, "ten": 1, "twenty": 11, "grp": 4, "agg_sum": 25500 }
+{ "two": 1, "four": null, "ten": 3, "twenty": null, "grp": 5, "agg_sum": 48000 }
+{ "two": 1, "four": null, "ten": 3, "twenty": 3, "grp": 4, "agg_sum": 21500 }
+{ "two": 1, "four": null, "ten": 3, "twenty": 13, "grp": 4, "agg_sum": 26500 }
+{ "two": 1, "four": null, "ten": 5, "twenty": null, "grp": 5, "agg_sum": 50000 }
+{ "two": 1, "four": null, "ten": 5, "twenty": 5, "grp": 4, "agg_sum": 22500 }
+{ "two": 1, "four": null, "ten": 5, "twenty": 15, "grp": 4, "agg_sum": 27500 }
+{ "two": 1, "four": null, "ten": 7, "twenty": null, "grp": 5, "agg_sum": 52000 }
+{ "two": 1, "four": null, "ten": 7, "twenty": 7, "grp": 4, "agg_sum": 23500 }
+{ "two": 1, "four": null, "ten": 7, "twenty": 17, "grp": 4, "agg_sum": 28500 }
+{ "two": 1, "four": null, "ten": 9, "twenty": null, "grp": 5, "agg_sum": 54000 }
+{ "two": 1, "four": null, "ten": 9, "twenty": 9, "grp": 4, "agg_sum": 24500 }
+{ "two": 1, "four": null, "ten": 9, "twenty": 19, "grp": 4, "agg_sum": 29500 }
+{ "two": 1, "four": 1, "ten": null, "twenty": null, "grp": 3, "agg_sum": 122500 }
+{ "two": 1, "four": 1, "ten": null, "twenty": 1, "grp": 2, "agg_sum": 20500 }
+{ "two": 1, "four": 1, "ten": null, "twenty": 5, "grp": 2, "agg_sum": 22500 }
+{ "two": 1, "four": 1, "ten": null, "twenty": 9, "grp": 2, "agg_sum": 24500 }
+{ "two": 1, "four": 1, "ten": null, "twenty": 13, "grp": 2, "agg_sum": 26500 }
+{ "two": 1, "four": 1, "ten": null, "twenty": 17, "grp": 2, "agg_sum": 28500 }
+{ "two": 1, "four": 1, "ten": 1, "twenty": null, "grp": 1, "agg_sum": 20500 }
+{ "two": 1, "four": 1, "ten": 1, "twenty": 1, "grp": 0, "agg_sum": 20500 }
+{ "two": 1, "four": 1, "ten": 3, "twenty": null, "grp": 1, "agg_sum": 26500 }
+{ "two": 1, "four": 1, "ten": 3, "twenty": 13, "grp": 0, "agg_sum": 26500 }
+{ "two": 1, "four": 1, "ten": 5, "twenty": null, "grp": 1, "agg_sum": 22500 }
+{ "two": 1, "four": 1, "ten": 5, "twenty": 5, "grp": 0, "agg_sum": 22500 }
+{ "two": 1, "four": 1, "ten": 7, "twenty": null, "grp": 1, "agg_sum": 28500 }
+{ "two": 1, "four": 1, "ten": 7, "twenty": 17, "grp": 0, "agg_sum": 28500 }
+{ "two": 1, "four": 1, "ten": 9, "twenty": null, "grp": 1, "agg_sum": 24500 }
+{ "two": 1, "four": 1, "ten": 9, "twenty": 9, "grp": 0, "agg_sum": 24500 }
+{ "two": 1, "four": 3, "ten": null, "twenty": null, "grp": 3, "agg_sum": 127500 }
+{ "two": 1, "four": 3, "ten": null, "twenty": 3, "grp": 2, "agg_sum": 21500 }
+{ "two": 1, "four": 3, "ten": null, "twenty": 7, "grp": 2, "agg_sum": 23500 }
+{ "two": 1, "four": 3, "ten": null, "twenty": 11, "grp": 2, "agg_sum": 25500 }
+{ "two": 1, "four": 3, "ten": null, "twenty": 15, "grp": 2, "agg_sum": 27500 }
+{ "two": 1, "four": 3, "ten": null, "twenty": 19, "grp": 2, "agg_sum": 29500 }
+{ "two": 1, "four": 3, "ten": 1, "twenty": null, "grp": 1, "agg_sum": 25500 }
+{ "two": 1, "four": 3, "ten": 1, "twenty": 11, "grp": 0, "agg_sum": 25500 }
+{ "two": 1, "four": 3, "ten": 3, "twenty": null, "grp": 1, "agg_sum": 21500 }
+{ "two": 1, "four": 3, "ten": 3, "twenty": 3, "grp": 0, "agg_sum": 21500 }
+{ "two": 1, "four": 3, "ten": 5, "twenty": null, "grp": 1, "agg_sum": 27500 }
+{ "two": 1, "four": 3, "ten": 5, "twenty": 15, "grp": 0, "agg_sum": 27500 }
+{ "two": 1, "four": 3, "ten": 7, "twenty": null, "grp": 1, "agg_sum": 23500 }
+{ "two": 1, "four": 3, "ten": 7, "twenty": 7, "grp": 0, "agg_sum": 23500 }
+{ "two": 1, "four": 3, "ten": 9, "twenty": null, "grp": 1, "agg_sum": 29500 }
+{ "two": 1, "four": 3, "ten": 9, "twenty": 19, "grp": 0, "agg_sum": 29500 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/grouping-sets-1/grouping-sets-1.14.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/grouping-sets-1/grouping-sets-1.14.adm
new file mode 100644
index 0000000..e93a964
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/grouping-sets-1/grouping-sets-1.14.adm
@@ -0,0 +1,167 @@
+{ "two": null, "four": null, "ten": null, "twenty": null, "grp": 15, "agg_sum": 495000 }
+{ "two": null, "four": null, "ten": null, "twenty": 0, "grp": 14, "agg_sum": 20000 }
+{ "two": null, "four": null, "ten": null, "twenty": 1, "grp": 14, "agg_sum": 20500 }
+{ "two": null, "four": null, "ten": null, "twenty": 2, "grp": 14, "agg_sum": 21000 }
+{ "two": null, "four": null, "ten": null, "twenty": 3, "grp": 14, "agg_sum": 21500 }
+{ "two": null, "four": null, "ten": null, "twenty": 4, "grp": 14, "agg_sum": 22000 }
+{ "two": null, "four": null, "ten": null, "twenty": 5, "grp": 14, "agg_sum": 22500 }
+{ "two": null, "four": null, "ten": null, "twenty": 6, "grp": 14, "agg_sum": 23000 }
+{ "two": null, "four": null, "ten": null, "twenty": 7, "grp": 14, "agg_sum": 23500 }
+{ "two": null, "four": null, "ten": null, "twenty": 8, "grp": 14, "agg_sum": 24000 }
+{ "two": null, "four": null, "ten": null, "twenty": 9, "grp": 14, "agg_sum": 24500 }
+{ "two": null, "four": null, "ten": null, "twenty": 10, "grp": 14, "agg_sum": 25000 }
+{ "two": null, "four": null, "ten": null, "twenty": 11, "grp": 14, "agg_sum": 25500 }
+{ "two": null, "four": null, "ten": null, "twenty": 12, "grp": 14, "agg_sum": 26000 }
+{ "two": null, "four": null, "ten": null, "twenty": 13, "grp": 14, "agg_sum": 26500 }
+{ "two": null, "four": null, "ten": null, "twenty": 14, "grp": 14, "agg_sum": 27000 }
+{ "two": null, "four": null, "ten": null, "twenty": 15, "grp": 14, "agg_sum": 27500 }
+{ "two": null, "four": null, "ten": null, "twenty": 16, "grp": 14, "agg_sum": 28000 }
+{ "two": null, "four": null, "ten": null, "twenty": 17, "grp": 14, "agg_sum": 28500 }
+{ "two": null, "four": null, "ten": null, "twenty": 18, "grp": 14, "agg_sum": 29000 }
+{ "two": null, "four": null, "ten": null, "twenty": 19, "grp": 14, "agg_sum": 29500 }
+{ "two": null, "four": null, "ten": 0, "twenty": null, "grp": 13, "agg_sum": 45000 }
+{ "two": null, "four": null, "ten": 0, "twenty": 0, "grp": 12, "agg_sum": 20000 }
+{ "two": null, "four": null, "ten": 0, "twenty": 10, "grp": 12, "agg_sum": 25000 }
+{ "two": null, "four": null, "ten": 1, "twenty": null, "grp": 13, "agg_sum": 46000 }
+{ "two": null, "four": null, "ten": 1, "twenty": 1, "grp": 12, "agg_sum": 20500 }
+{ "two": null, "four": null, "ten": 1, "twenty": 11, "grp": 12, "agg_sum": 25500 }
+{ "two": null, "four": null, "ten": 2, "twenty": null, "grp": 13, "agg_sum": 47000 }
+{ "two": null, "four": null, "ten": 2, "twenty": 2, "grp": 12, "agg_sum": 21000 }
+{ "two": null, "four": null, "ten": 2, "twenty": 12, "grp": 12, "agg_sum": 26000 }
+{ "two": null, "four": null, "ten": 3, "twenty": null, "grp": 13, "agg_sum": 48000 }
+{ "two": null, "four": null, "ten": 3, "twenty": 3, "grp": 12, "agg_sum": 21500 }
+{ "two": null, "four": null, "ten": 3, "twenty": 13, "grp": 12, "agg_sum": 26500 }
+{ "two": null, "four": null, "ten": 4, "twenty": null, "grp": 13, "agg_sum": 49000 }
+{ "two": null, "four": null, "ten": 4, "twenty": 4, "grp": 12, "agg_sum": 22000 }
+{ "two": null, "four": null, "ten": 4, "twenty": 14, "grp": 12, "agg_sum": 27000 }
+{ "two": null, "four": null, "ten": 5, "twenty": null, "grp": 13, "agg_sum": 50000 }
+{ "two": null, "four": null, "ten": 5, "twenty": 5, "grp": 12, "agg_sum": 22500 }
+{ "two": null, "four": null, "ten": 5, "twenty": 15, "grp": 12, "agg_sum": 27500 }
+{ "two": null, "four": null, "ten": 6, "twenty": null, "grp": 13, "agg_sum": 51000 }
+{ "two": null, "four": null, "ten": 6, "twenty": 6, "grp": 12, "agg_sum": 23000 }
+{ "two": null, "four": null, "ten": 6, "twenty": 16, "grp": 12, "agg_sum": 28000 }
+{ "two": null, "four": null, "ten": 7, "twenty": null, "grp": 13, "agg_sum": 52000 }
+{ "two": null, "four": null, "ten": 7, "twenty": 7, "grp": 12, "agg_sum": 23500 }
+{ "two": null, "four": null, "ten": 7, "twenty": 17, "grp": 12, "agg_sum": 28500 }
+{ "two": null, "four": null, "ten": 8, "twenty": null, "grp": 13, "agg_sum": 53000 }
+{ "two": null, "four": null, "ten": 8, "twenty": 8, "grp": 12, "agg_sum": 24000 }
+{ "two": null, "four": null, "ten": 8, "twenty": 18, "grp": 12, "agg_sum": 29000 }
+{ "two": null, "four": null, "ten": 9, "twenty": null, "grp": 13, "agg_sum": 54000 }
+{ "two": null, "four": null, "ten": 9, "twenty": 9, "grp": 12, "agg_sum": 24500 }
+{ "two": null, "four": null, "ten": 9, "twenty": 19, "grp": 12, "agg_sum": 29500 }
+{ "two": 0, "four": null, "ten": null, "twenty": null, "grp": 7, "agg_sum": 245000 }
+{ "two": 0, "four": null, "ten": null, "twenty": 0, "grp": 6, "agg_sum": 20000 }
+{ "two": 0, "four": null, "ten": null, "twenty": 2, "grp": 6, "agg_sum": 21000 }
+{ "two": 0, "four": null, "ten": null, "twenty": 4, "grp": 6, "agg_sum": 22000 }
+{ "two": 0, "four": null, "ten": null, "twenty": 6, "grp": 6, "agg_sum": 23000 }
+{ "two": 0, "four": null, "ten": null, "twenty": 8, "grp": 6, "agg_sum": 24000 }
+{ "two": 0, "four": null, "ten": null, "twenty": 10, "grp": 6, "agg_sum": 25000 }
+{ "two": 0, "four": null, "ten": null, "twenty": 12, "grp": 6, "agg_sum": 26000 }
+{ "two": 0, "four": null, "ten": null, "twenty": 14, "grp": 6, "agg_sum": 27000 }
+{ "two": 0, "four": null, "ten": null, "twenty": 16, "grp": 6, "agg_sum": 28000 }
+{ "two": 0, "four": null, "ten": null, "twenty": 18, "grp": 6, "agg_sum": 29000 }
+{ "two": 0, "four": null, "ten": 0, "twenty": null, "grp": 5, "agg_sum": 45000 }
+{ "two": 0, "four": null, "ten": 0, "twenty": 0, "grp": 4, "agg_sum": 20000 }
+{ "two": 0, "four": null, "ten": 0, "twenty": 10, "grp": 4, "agg_sum": 25000 }
+{ "two": 0, "four": null, "ten": 2, "twenty": null, "grp": 5, "agg_sum": 47000 }
+{ "two": 0, "four": null, "ten": 2, "twenty": 2, "grp": 4, "agg_sum": 21000 }
+{ "two": 0, "four": null, "ten": 2, "twenty": 12, "grp": 4, "agg_sum": 26000 }
+{ "two": 0, "four": null, "ten": 4, "twenty": null, "grp": 5, "agg_sum": 49000 }
+{ "two": 0, "four": null, "ten": 4, "twenty": 4, "grp": 4, "agg_sum": 22000 }
+{ "two": 0, "four": null, "ten": 4, "twenty": 14, "grp": 4, "agg_sum": 27000 }
+{ "two": 0, "four": null, "ten": 6, "twenty": null, "grp": 5, "agg_sum": 51000 }
+{ "two": 0, "four": null, "ten": 6, "twenty": 6, "grp": 4, "agg_sum": 23000 }
+{ "two": 0, "four": null, "ten": 6, "twenty": 16, "grp": 4, "agg_sum": 28000 }
+{ "two": 0, "four": null, "ten": 8, "twenty": null, "grp": 5, "agg_sum": 53000 }
+{ "two": 0, "four": null, "ten": 8, "twenty": 8, "grp": 4, "agg_sum": 24000 }
+{ "two": 0, "four": null, "ten": 8, "twenty": 18, "grp": 4, "agg_sum": 29000 }
+{ "two": 0, "four": 0, "ten": null, "twenty": null, "grp": 3, "agg_sum": 120000 }
+{ "two": 0, "four": 0, "ten": null, "twenty": 0, "grp": 2, "agg_sum": 20000 }
+{ "two": 0, "four": 0, "ten": null, "twenty": 4, "grp": 2, "agg_sum": 22000 }
+{ "two": 0, "four": 0, "ten": null, "twenty": 8, "grp": 2, "agg_sum": 24000 }
+{ "two": 0, "four": 0, "ten": null, "twenty": 12, "grp": 2, "agg_sum": 26000 }
+{ "two": 0, "four": 0, "ten": null, "twenty": 16, "grp": 2, "agg_sum": 28000 }
+{ "two": 0, "four": 0, "ten": 0, "twenty": null, "grp": 1, "agg_sum": 20000 }
+{ "two": 0, "four": 0, "ten": 0, "twenty": 0, "grp": 0, "agg_sum": 20000 }
+{ "two": 0, "four": 0, "ten": 2, "twenty": null, "grp": 1, "agg_sum": 26000 }
+{ "two": 0, "four": 0, "ten": 2, "twenty": 12, "grp": 0, "agg_sum": 26000 }
+{ "two": 0, "four": 0, "ten": 4, "twenty": null, "grp": 1, "agg_sum": 22000 }
+{ "two": 0, "four": 0, "ten": 4, "twenty": 4, "grp": 0, "agg_sum": 22000 }
+{ "two": 0, "four": 0, "ten": 6, "twenty": null, "grp": 1, "agg_sum": 28000 }
+{ "two": 0, "four": 0, "ten": 6, "twenty": 16, "grp": 0, "agg_sum": 28000 }
+{ "two": 0, "four": 0, "ten": 8, "twenty": null, "grp": 1, "agg_sum": 24000 }
+{ "two": 0, "four": 0, "ten": 8, "twenty": 8, "grp": 0, "agg_sum": 24000 }
+{ "two": 0, "four": 2, "ten": null, "twenty": null, "grp": 3, "agg_sum": 125000 }
+{ "two": 0, "four": 2, "ten": null, "twenty": 2, "grp": 2, "agg_sum": 21000 }
+{ "two": 0, "four": 2, "ten": null, "twenty": 6, "grp": 2, "agg_sum": 23000 }
+{ "two": 0, "four": 2, "ten": null, "twenty": 10, "grp": 2, "agg_sum": 25000 }
+{ "two": 0, "four": 2, "ten": null, "twenty": 14, "grp": 2, "agg_sum": 27000 }
+{ "two": 0, "four": 2, "ten": null, "twenty": 18, "grp": 2, "agg_sum": 29000 }
+{ "two": 0, "four": 2, "ten": 0, "twenty": null, "grp": 1, "agg_sum": 25000 }
+{ "two": 0, "four": 2, "ten": 0, "twenty": 10, "grp": 0, "agg_sum": 25000 }
+{ "two": 0, "four": 2, "ten": 2, "twenty": null, "grp": 1, "agg_sum": 21000 }
+{ "two": 0, "four": 2, "ten": 2, "twenty": 2, "grp": 0, "agg_sum": 21000 }
+{ "two": 0, "four": 2, "ten": 4, "twenty": null, "grp": 1, "agg_sum": 27000 }
+{ "two": 0, "four": 2, "ten": 4, "twenty": 14, "grp": 0, "agg_sum": 27000 }
+{ "two": 0, "four": 2, "ten": 6, "twenty": null, "grp": 1, "agg_sum": 23000 }
+{ "two": 0, "four": 2, "ten": 6, "twenty": 6, "grp": 0, "agg_sum": 23000 }
+{ "two": 0, "four": 2, "ten": 8, "twenty": null, "grp": 1, "agg_sum": 29000 }
+{ "two": 0, "four": 2, "ten": 8, "twenty": 18, "grp": 0, "agg_sum": 29000 }
+{ "two": 1, "four": null, "ten": null, "twenty": null, "grp": 7, "agg_sum": 250000 }
+{ "two": 1, "four": null, "ten": null, "twenty": 1, "grp": 6, "agg_sum": 20500 }
+{ "two": 1, "four": null, "ten": null, "twenty": 3, "grp": 6, "agg_sum": 21500 }
+{ "two": 1, "four": null, "ten": null, "twenty": 5, "grp": 6, "agg_sum": 22500 }
+{ "two": 1, "four": null, "ten": null, "twenty": 7, "grp": 6, "agg_sum": 23500 }
+{ "two": 1, "four": null, "ten": null, "twenty": 9, "grp": 6, "agg_sum": 24500 }
+{ "two": 1, "four": null, "ten": null, "twenty": 11, "grp": 6, "agg_sum": 25500 }
+{ "two": 1, "four": null, "ten": null, "twenty": 13, "grp": 6, "agg_sum": 26500 }
+{ "two": 1, "four": null, "ten": null, "twenty": 15, "grp": 6, "agg_sum": 27500 }
+{ "two": 1, "four": null, "ten": null, "twenty": 17, "grp": 6, "agg_sum": 28500 }
+{ "two": 1, "four": null, "ten": null, "twenty": 19, "grp": 6, "agg_sum": 29500 }
+{ "two": 1, "four": null, "ten": 1, "twenty": null, "grp": 5, "agg_sum": 46000 }
+{ "two": 1, "four": null, "ten": 1, "twenty": 1, "grp": 4, "agg_sum": 20500 }
+{ "two": 1, "four": null, "ten": 1, "twenty": 11, "grp": 4, "agg_sum": 25500 }
+{ "two": 1, "four": null, "ten": 3, "twenty": null, "grp": 5, "agg_sum": 48000 }
+{ "two": 1, "four": null, "ten": 3, "twenty": 3, "grp": 4, "agg_sum": 21500 }
+{ "two": 1, "four": null, "ten": 3, "twenty": 13, "grp": 4, "agg_sum": 26500 }
+{ "two": 1, "four": null, "ten": 5, "twenty": null, "grp": 5, "agg_sum": 50000 }
+{ "two": 1, "four": null, "ten": 5, "twenty": 5, "grp": 4, "agg_sum": 22500 }
+{ "two": 1, "four": null, "ten": 5, "twenty": 15, "grp": 4, "agg_sum": 27500 }
+{ "two": 1, "four": null, "ten": 7, "twenty": null, "grp": 5, "agg_sum": 52000 }
+{ "two": 1, "four": null, "ten": 7, "twenty": 7, "grp": 4, "agg_sum": 23500 }
+{ "two": 1, "four": null, "ten": 7, "twenty": 17, "grp": 4, "agg_sum": 28500 }
+{ "two": 1, "four": null, "ten": 9, "twenty": null, "grp": 5, "agg_sum": 54000 }
+{ "two": 1, "four": null, "ten": 9, "twenty": 9, "grp": 4, "agg_sum": 24500 }
+{ "two": 1, "four": null, "ten": 9, "twenty": 19, "grp": 4, "agg_sum": 29500 }
+{ "two": 1, "four": 1, "ten": null, "twenty": null, "grp": 3, "agg_sum": 122500 }
+{ "two": 1, "four": 1, "ten": null, "twenty": 1, "grp": 2, "agg_sum": 20500 }
+{ "two": 1, "four": 1, "ten": null, "twenty": 5, "grp": 2, "agg_sum": 22500 }
+{ "two": 1, "four": 1, "ten": null, "twenty": 9, "grp": 2, "agg_sum": 24500 }
+{ "two": 1, "four": 1, "ten": null, "twenty": 13, "grp": 2, "agg_sum": 26500 }
+{ "two": 1, "four": 1, "ten": null, "twenty": 17, "grp": 2, "agg_sum": 28500 }
+{ "two": 1, "four": 1, "ten": 1, "twenty": null, "grp": 1, "agg_sum": 20500 }
+{ "two": 1, "four": 1, "ten": 1, "twenty": 1, "grp": 0, "agg_sum": 20500 }
+{ "two": 1, "four": 1, "ten": 3, "twenty": null, "grp": 1, "agg_sum": 26500 }
+{ "two": 1, "four": 1, "ten": 3, "twenty": 13, "grp": 0, "agg_sum": 26500 }
+{ "two": 1, "four": 1, "ten": 5, "twenty": null, "grp": 1, "agg_sum": 22500 }
+{ "two": 1, "four": 1, "ten": 5, "twenty": 5, "grp": 0, "agg_sum": 22500 }
+{ "two": 1, "four": 1, "ten": 7, "twenty": null, "grp": 1, "agg_sum": 28500 }
+{ "two": 1, "four": 1, "ten": 7, "twenty": 17, "grp": 0, "agg_sum": 28500 }
+{ "two": 1, "four": 1, "ten": 9, "twenty": null, "grp": 1, "agg_sum": 24500 }
+{ "two": 1, "four": 1, "ten": 9, "twenty": 9, "grp": 0, "agg_sum": 24500 }
+{ "two": 1, "four": 3, "ten": null, "twenty": null, "grp": 3, "agg_sum": 127500 }
+{ "two": 1, "four": 3, "ten": null, "twenty": 3, "grp": 2, "agg_sum": 21500 }
+{ "two": 1, "four": 3, "ten": null, "twenty": 7, "grp": 2, "agg_sum": 23500 }
+{ "two": 1, "four": 3, "ten": null, "twenty": 11, "grp": 2, "agg_sum": 25500 }
+{ "two": 1, "four": 3, "ten": null, "twenty": 15, "grp": 2, "agg_sum": 27500 }
+{ "two": 1, "four": 3, "ten": null, "twenty": 19, "grp": 2, "agg_sum": 29500 }
+{ "two": 1, "four": 3, "ten": 1, "twenty": null, "grp": 1, "agg_sum": 25500 }
+{ "two": 1, "four": 3, "ten": 1, "twenty": 11, "grp": 0, "agg_sum": 25500 }
+{ "two": 1, "four": 3, "ten": 3, "twenty": null, "grp": 1, "agg_sum": 21500 }
+{ "two": 1, "four": 3, "ten": 3, "twenty": 3, "grp": 0, "agg_sum": 21500 }
+{ "two": 1, "four": 3, "ten": 5, "twenty": null, "grp": 1, "agg_sum": 27500 }
+{ "two": 1, "four": 3, "ten": 5, "twenty": 15, "grp": 0, "agg_sum": 27500 }
+{ "two": 1, "four": 3, "ten": 7, "twenty": null, "grp": 1, "agg_sum": 23500 }
+{ "two": 1, "four": 3, "ten": 7, "twenty": 7, "grp": 0, "agg_sum": 23500 }
+{ "two": 1, "four": 3, "ten": 9, "twenty": null, "grp": 1, "agg_sum": 29500 }
+{ "two": 1, "four": 3, "ten": 9, "twenty": 19, "grp": 0, "agg_sum": 29500 }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/grouping-sets-1/grouping-sets-1.15.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/grouping-sets-1/grouping-sets-1.15.adm
new file mode 100644
index 0000000..f2c77b2
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/grouping-sets-1/grouping-sets-1.15.adm
@@ -0,0 +1,58 @@
+{ "two": null, "four": null, "ten": null, "twenty": null, "grp": 15, "agg_sum": 495000 }
+{ "two": null, "four": null, "ten": null, "twenty": null, "grp": 15, "agg_sum": 495000 }
+{ "two": null, "four": null, "ten": null, "twenty": 0, "grp": 14, "agg_sum": 20000 }
+{ "two": null, "four": null, "ten": null, "twenty": 1, "grp": 14, "agg_sum": 20500 }
+{ "two": null, "four": null, "ten": null, "twenty": 2, "grp": 14, "agg_sum": 21000 }
+{ "two": null, "four": null, "ten": null, "twenty": 3, "grp": 14, "agg_sum": 21500 }
+{ "two": null, "four": null, "ten": null, "twenty": 4, "grp": 14, "agg_sum": 22000 }
+{ "two": null, "four": null, "ten": null, "twenty": 5, "grp": 14, "agg_sum": 22500 }
+{ "two": null, "four": null, "ten": null, "twenty": 6, "grp": 14, "agg_sum": 23000 }
+{ "two": null, "four": null, "ten": null, "twenty": 7, "grp": 14, "agg_sum": 23500 }
+{ "two": null, "four": null, "ten": null, "twenty": 8, "grp": 14, "agg_sum": 24000 }
+{ "two": null, "four": null, "ten": null, "twenty": 9, "grp": 14, "agg_sum": 24500 }
+{ "two": null, "four": null, "ten": null, "twenty": 10, "grp": 14, "agg_sum": 25000 }
+{ "two": null, "four": null, "ten": null, "twenty": 11, "grp": 14, "agg_sum": 25500 }
+{ "two": null, "four": null, "ten": null, "twenty": 12, "grp": 14, "agg_sum": 26000 }
+{ "two": null, "four": null, "ten": null, "twenty": 13, "grp": 14, "agg_sum": 26500 }
+{ "two": null, "four": null, "ten": null, "twenty": 14, "grp": 14, "agg_sum": 27000 }
+{ "two": null, "four": null, "ten": null, "twenty": 15, "grp": 14, "agg_sum": 27500 }
+{ "two": null, "four": null, "ten": null, "twenty": 16, "grp": 14, "agg_sum": 28000 }
+{ "two": null, "four": null, "ten": null, "twenty": 17, "grp": 14, "agg_sum": 28500 }
+{ "two": null, "four": null, "ten": null, "twenty": 18, "grp": 14, "agg_sum": 29000 }
+{ "two": null, "four": null, "ten": null, "twenty": 19, "grp": 14, "agg_sum": 29500 }
+{ "two": null, "four": null, "ten": 0, "twenty": null, "grp": 13, "agg_sum": 45000 }
+{ "two": null, "four": null, "ten": 0, "twenty": 0, "grp": 12, "agg_sum": 20000 }
+{ "two": null, "four": null, "ten": 0, "twenty": 10, "grp": 12, "agg_sum": 25000 }
+{ "two": null, "four": null, "ten": 1, "twenty": null, "grp": 13, "agg_sum": 46000 }
+{ "two": null, "four": null, "ten": 1, "twenty": 1, "grp": 12, "agg_sum": 20500 }
+{ "two": null, "four": null, "ten": 1, "twenty": 11, "grp": 12, "agg_sum": 25500 }
+{ "two": null, "four": null, "ten": 2, "twenty": null, "grp": 13, "agg_sum": 47000 }
+{ "two": null, "four": null, "ten": 2, "twenty": 2, "grp": 12, "agg_sum": 21000 }
+{ "two": null, "four": null, "ten": 2, "twenty": 12, "grp": 12, "agg_sum": 26000 }
+{ "two": null, "four": null, "ten": 3, "twenty": null, "grp": 13, "agg_sum": 48000 }
+{ "two": null, "four": null, "ten": 3, "twenty": 3, "grp": 12, "agg_sum": 21500 }
+{ "two": null, "four": null, "ten": 3, "twenty": 13, "grp": 12, "agg_sum": 26500 }
+{ "two": null, "four": null, "ten": 4, "twenty": null, "grp": 13, "agg_sum": 49000 }
+{ "two": null, "four": null, "ten": 4, "twenty": 4, "grp": 12, "agg_sum": 22000 }
+{ "two": null, "four": null, "ten": 4, "twenty": 14, "grp": 12, "agg_sum": 27000 }
+{ "two": null, "four": null, "ten": 5, "twenty": null, "grp": 13, "agg_sum": 50000 }
+{ "two": null, "four": null, "ten": 5, "twenty": 5, "grp": 12, "agg_sum": 22500 }
+{ "two": null, "four": null, "ten": 5, "twenty": 15, "grp": 12, "agg_sum": 27500 }
+{ "two": null, "four": null, "ten": 6, "twenty": null, "grp": 13, "agg_sum": 51000 }
+{ "two": null, "four": null, "ten": 6, "twenty": 6, "grp": 12, "agg_sum": 23000 }
+{ "two": null, "four": null, "ten": 6, "twenty": 16, "grp": 12, "agg_sum": 28000 }
+{ "two": null, "four": null, "ten": 7, "twenty": null, "grp": 13, "agg_sum": 52000 }
+{ "two": null, "four": null, "ten": 7, "twenty": 7, "grp": 12, "agg_sum": 23500 }
+{ "two": null, "four": null, "ten": 7, "twenty": 17, "grp": 12, "agg_sum": 28500 }
+{ "two": null, "four": null, "ten": 8, "twenty": null, "grp": 13, "agg_sum": 53000 }
+{ "two": null, "four": null, "ten": 8, "twenty": 8, "grp": 12, "agg_sum": 24000 }
+{ "two": null, "four": null, "ten": 8, "twenty": 18, "grp": 12, "agg_sum": 29000 }
+{ "two": null, "four": null, "ten": 9, "twenty": null, "grp": 13, "agg_sum": 54000 }
+{ "two": null, "four": null, "ten": 9, "twenty": 9, "grp": 12, "agg_sum": 24500 }
+{ "two": null, "four": null, "ten": 9, "twenty": 19, "grp": 12, "agg_sum": 29500 }
+{ "two": 0, "four": null, "ten": null, "twenty": null, "grp": 7, "agg_sum": 245000 }
+{ "two": 0, "four": 0, "ten": null, "twenty": null, "grp": 3, "agg_sum": 120000 }
+{ "two": 0, "four": 2, "ten": null, "twenty": null, "grp": 3, "agg_sum": 125000 }
+{ "two": 1, "four": null, "ten": null, "twenty": null, "grp": 7, "agg_sum": 250000 }
+{ "two": 1, "four": 1, "ten": null, "twenty": null, "grp": 3, "agg_sum": 122500 }
+{ "two": 1, "four": 3, "ten": null, "twenty": null, "grp": 3, "agg_sum": 127500 }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/grouping-sets-1/grouping-sets-1.3.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/grouping-sets-1/grouping-sets-1.3.adm
new file mode 100644
index 0000000..66820e4
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/grouping-sets-1/grouping-sets-1.3.adm
@@ -0,0 +1 @@
+{ "agg_sum": 45000 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/grouping-sets-1/grouping-sets-1.4.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/grouping-sets-1/grouping-sets-1.4.adm
new file mode 100644
index 0000000..66820e4
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/grouping-sets-1/grouping-sets-1.4.adm
@@ -0,0 +1 @@
+{ "agg_sum": 45000 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/grouping-sets-1/grouping-sets-1.5.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/grouping-sets-1/grouping-sets-1.5.adm
new file mode 100644
index 0000000..4939818
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/grouping-sets-1/grouping-sets-1.5.adm
@@ -0,0 +1,2 @@
+{ "agg_sum": 45000 }
+{ "agg_sum": 45000 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/grouping-sets-1/grouping-sets-1.6.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/grouping-sets-1/grouping-sets-1.6.adm
new file mode 100644
index 0000000..66820e4
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/grouping-sets-1/grouping-sets-1.6.adm
@@ -0,0 +1 @@
+{ "agg_sum": 45000 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/grouping-sets-1/grouping-sets-1.7.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/grouping-sets-1/grouping-sets-1.7.adm
new file mode 100644
index 0000000..7bbace1
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/grouping-sets-1/grouping-sets-1.7.adm
@@ -0,0 +1,7 @@
+{ "two": null, "four": null, "grp": 3, "agg_sum": 45000 }
+{ "two": 0, "four": null, "grp": 1, "agg_sum": 20000 }
+{ "two": 0, "four": 0, "grp": 0, "agg_sum": 10000 }
+{ "two": 0, "four": 2, "grp": 0, "agg_sum": 10000 }
+{ "two": 1, "four": null, "grp": 1, "agg_sum": 25000 }
+{ "two": 1, "four": 1, "grp": 0, "agg_sum": 12500 }
+{ "two": 1, "four": 3, "grp": 0, "agg_sum": 12500 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/grouping-sets-1/grouping-sets-1.8.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/grouping-sets-1/grouping-sets-1.8.adm
new file mode 100644
index 0000000..0db7590
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/grouping-sets-1/grouping-sets-1.8.adm
@@ -0,0 +1,11 @@
+{ "two": null, "four": null, "grp": 3, "agg_sum": 45000 }
+{ "two": null, "four": 0, "grp": 2, "agg_sum": 10000 }
+{ "two": null, "four": 1, "grp": 2, "agg_sum": 12500 }
+{ "two": null, "four": 2, "grp": 2, "agg_sum": 10000 }
+{ "two": null, "four": 3, "grp": 2, "agg_sum": 12500 }
+{ "two": 0, "four": null, "grp": 1, "agg_sum": 20000 }
+{ "two": 0, "four": 0, "grp": 0, "agg_sum": 10000 }
+{ "two": 0, "four": 2, "grp": 0, "agg_sum": 10000 }
+{ "two": 1, "four": null, "grp": 1, "agg_sum": 25000 }
+{ "two": 1, "four": 1, "grp": 0, "agg_sum": 12500 }
+{ "two": 1, "four": 3, "grp": 0, "agg_sum": 12500 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/grouping-sets-1/grouping-sets-1.9.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/grouping-sets-1/grouping-sets-1.9.adm
new file mode 100644
index 0000000..134743a
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/grouping-sets-1/grouping-sets-1.9.adm
@@ -0,0 +1,26 @@
+{ "two": 0, "four": null, "ten": null, "grp": 3, "agg_sum": 45000 }
+{ "two": 0, "four": 0, "ten": null, "grp": 1, "agg_sum": 20000 }
+{ "two": 0, "four": 0, "ten": 0, "grp": 0, "agg_sum": 0 }
+{ "two": 0, "four": 0, "ten": 2, "grp": 0, "agg_sum": 6000 }
+{ "two": 0, "four": 0, "ten": 4, "grp": 0, "agg_sum": 2000 }
+{ "two": 0, "four": 0, "ten": 6, "grp": 0, "agg_sum": 8000 }
+{ "two": 0, "four": 0, "ten": 8, "grp": 0, "agg_sum": 4000 }
+{ "two": 0, "four": 2, "ten": null, "grp": 1, "agg_sum": 25000 }
+{ "two": 0, "four": 2, "ten": 0, "grp": 0, "agg_sum": 5000 }
+{ "two": 0, "four": 2, "ten": 2, "grp": 0, "agg_sum": 1000 }
+{ "two": 0, "four": 2, "ten": 4, "grp": 0, "agg_sum": 7000 }
+{ "two": 0, "four": 2, "ten": 6, "grp": 0, "agg_sum": 3000 }
+{ "two": 0, "four": 2, "ten": 8, "grp": 0, "agg_sum": 9000 }
+{ "two": 1, "four": null, "ten": null, "grp": 3, "agg_sum": 50000 }
+{ "two": 1, "four": 1, "ten": null, "grp": 1, "agg_sum": 22500 }
+{ "two": 1, "four": 1, "ten": 1, "grp": 0, "agg_sum": 500 }
+{ "two": 1, "four": 1, "ten": 3, "grp": 0, "agg_sum": 6500 }
+{ "two": 1, "four": 1, "ten": 5, "grp": 0, "agg_sum": 2500 }
+{ "two": 1, "four": 1, "ten": 7, "grp": 0, "agg_sum": 8500 }
+{ "two": 1, "four": 1, "ten": 9, "grp": 0, "agg_sum": 4500 }
+{ "two": 1, "four": 3, "ten": null, "grp": 1, "agg_sum": 27500 }
+{ "two": 1, "four": 3, "ten": 1, "grp": 0, "agg_sum": 5500 }
+{ "two": 1, "four": 3, "ten": 3, "grp": 0, "agg_sum": 1500 }
+{ "two": 1, "four": 3, "ten": 5, "grp": 0, "agg_sum": 7500 }
+{ "two": 1, "four": 3, "ten": 7, "grp": 0, "agg_sum": 3500 }
+{ "two": 1, "four": 3, "ten": 9, "grp": 0, "agg_sum": 9500 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/grouping-sets-2/grouping-sets-2.3.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/grouping-sets-2/grouping-sets-2.3.adm
new file mode 100644
index 0000000..7bbace1
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/grouping-sets-2/grouping-sets-2.3.adm
@@ -0,0 +1,7 @@
+{ "two": null, "four": null, "grp": 3, "agg_sum": 45000 }
+{ "two": 0, "four": null, "grp": 1, "agg_sum": 20000 }
+{ "two": 0, "four": 0, "grp": 0, "agg_sum": 10000 }
+{ "two": 0, "four": 2, "grp": 0, "agg_sum": 10000 }
+{ "two": 1, "four": null, "grp": 1, "agg_sum": 25000 }
+{ "two": 1, "four": 1, "grp": 0, "agg_sum": 12500 }
+{ "two": 1, "four": 3, "grp": 0, "agg_sum": 12500 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/grouping-sets-2/grouping-sets-2.4.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/grouping-sets-2/grouping-sets-2.4.adm
new file mode 100644
index 0000000..d633a62
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/grouping-sets-2/grouping-sets-2.4.adm
@@ -0,0 +1,7 @@
+{ "v2": null, "v4": null, "grp": 3, "agg_sum": 45000 }
+{ "v2": 0, "v4": null, "grp": 1, "agg_sum": 20000 }
+{ "v2": 0, "v4": 0, "grp": 0, "agg_sum": 10000 }
+{ "v2": 0, "v4": 2, "grp": 0, "agg_sum": 10000 }
+{ "v2": 1, "v4": null, "grp": 1, "agg_sum": 25000 }
+{ "v2": 1, "v4": 1, "grp": 0, "agg_sum": 12500 }
+{ "v2": 1, "v4": 3, "grp": 0, "agg_sum": 12500 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/grouping-sets-2/grouping-sets-2.5.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/grouping-sets-2/grouping-sets-2.5.adm
new file mode 100644
index 0000000..d5e2489
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/grouping-sets-2/grouping-sets-2.5.adm
@@ -0,0 +1,8 @@
+{ "v2": 0, "v4": null, "grp": 1, "agg_sum": 20000 }
+{ "v2": 0, "v4": null, "grp": 1, "agg_sum": 20000 }
+{ "v2": 0, "v4": 0, "grp": 0, "agg_sum": 10000 }
+{ "v2": 0, "v4": 2, "grp": 0, "agg_sum": 10000 }
+{ "v2": 1, "v4": null, "grp": 1, "agg_sum": 25000 }
+{ "v2": 1, "v4": null, "grp": 1, "agg_sum": 25000 }
+{ "v2": 1, "v4": 1, "grp": 0, "agg_sum": 12500 }
+{ "v2": 1, "v4": 3, "grp": 0, "agg_sum": 12500 }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/push-limit-to-primary-lookup-select/push-limit-to-primary-lookup-select.3.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/push-limit-to-primary-lookup-select/push-limit-to-primary-lookup-select.3.adm
index 7a8668b..1f1e3a0 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/push-limit-to-primary-lookup-select/push-limit-to-primary-lookup-select.3.adm
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/push-limit-to-primary-lookup-select/push-limit-to-primary-lookup-select.3.adm
@@ -16,7 +16,7 @@
-- BTREE_SEARCH |PARTITIONED|
exchange
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- order (ASC, $$22) (ASC, $$23)
+ order (ASC, $$22) (ASC, $$23)
-- STABLE_SORT [$$22(ASC), $$23(ASC)] |PARTITIONED|
exchange
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/push-limit-to-primary-lookup-select/push-limit-to-primary-lookup-select.5.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/push-limit-to-primary-lookup-select/push-limit-to-primary-lookup-select.5.adm
index 45e0602..a0e070b 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/push-limit-to-primary-lookup-select/push-limit-to-primary-lookup-select.5.adm
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/push-limit-to-primary-lookup-select/push-limit-to-primary-lookup-select.5.adm
@@ -22,7 +22,7 @@
-- BTREE_SEARCH |PARTITIONED|
exchange
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- order (ASC, $$25) (ASC, $$26)
+ order (ASC, $$25) (ASC, $$26)
-- STABLE_SORT [$$25(ASC), $$26(ASC)] |PARTITIONED|
exchange
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/push-limit-to-primary-lookup/push-limit-to-primary-lookup.3.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/push-limit-to-primary-lookup/push-limit-to-primary-lookup.3.adm
index e741aec..4aba1a7 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/push-limit-to-primary-lookup/push-limit-to-primary-lookup.3.adm
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/push-limit-to-primary-lookup/push-limit-to-primary-lookup.3.adm
@@ -16,7 +16,7 @@
-- BTREE_SEARCH |PARTITIONED|
exchange
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- order (ASC, $$18) (ASC, $$19)
+ order (ASC, $$18) (ASC, $$19)
-- STABLE_SORT [$$18(ASC), $$19(ASC)] |PARTITIONED|
exchange
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/push-limit-to-primary-lookup/push-limit-to-primary-lookup.5.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/push-limit-to-primary-lookup/push-limit-to-primary-lookup.5.adm
index b64b9c5..f611c2b 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/push-limit-to-primary-lookup/push-limit-to-primary-lookup.5.adm
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/push-limit-to-primary-lookup/push-limit-to-primary-lookup.5.adm
@@ -16,7 +16,7 @@
-- BTREE_SEARCH |PARTITIONED|
exchange
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- order (ASC, $$20) (ASC, $$21)
+ order (ASC, $$20) (ASC, $$21)
-- STABLE_SORT [$$20(ASC), $$21(ASC)] |PARTITIONED|
exchange
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-1/grouping-sets-1.1.ast b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-1/grouping-sets-1.1.ast
new file mode 100644
index 0000000..8e03d54
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-1/grouping-sets-1.1.ast
@@ -0,0 +1,22 @@
+DataverseUse test
+TypeDecl tenkType [
+ closed RecordType {
+ unique1 : integer,
+ unique2 : integer,
+ two : integer,
+ four : integer,
+ ten : integer,
+ twenty : integer,
+ hundred : integer,
+ thousand : integer,
+ twothousand : integer,
+ fivethous : integer,
+ tenthous : integer,
+ odd100 : integer,
+ even100 : integer,
+ stringu1 : string,
+ stringu2 : string,
+ string4 : string
+ }
+]
+DatasetDecl tenk(tenkType) partitioned by [[unique2]]
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-1/grouping-sets-1.10.ast b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-1/grouping-sets-1.10.ast
new file mode 100644
index 0000000..9c6df5d
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-1/grouping-sets-1.10.ast
@@ -0,0 +1,93 @@
+DataverseUse test
+Query:
+SELECT [
+Variable [ Name=$two ]
+two
+Variable [ Name=$four ]
+four
+Variable [ Name=$ten ]
+ten
+Variable [ Name=#2 ]
+grp
+FunctionCall asterix.sql-sum@1[
+ (
+ SELECT ELEMENT [
+ FieldAccessor [
+ FieldAccessor [
+ Variable [ Name=#3 ]
+ Field=tenk
+ ]
+ Field=twenty
+ ]
+ ]
+ FROM [ Variable [ Name=#1 ]
+ AS Variable [ Name=#3 ]
+ ]
+ )
+]
+agg_sum
+]
+FROM [ FunctionCall asterix.dataset@1[
+ LiteralExpr [STRING] [test.tenk]
+ ]
+ AS Variable [ Name=$tenk ]
+]
+Groupby
+ GROUPING SET (
+ Variable [ Name=$two ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=two
+ ]
+ )
+ GROUPING SET (
+ Variable [ Name=$four ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=four
+ ]
+ ),
+ GROUPING SET (
+ Variable [ Name=$two ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=two
+ ]
+ Variable [ Name=$four ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=four
+ ]
+ ),
+ GROUPING SET (
+ Variable [ Name=$ten ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=ten
+ ]
+ ),
+ GROUP AS Variable [ Name=#1 ]
+ (
+ tenk:=Variable [ Name=$tenk ]
+ )
+
+Let Variable [ Name=#2 ]
+ :=
+ FunctionCall test.grouping@3[
+ Variable [ Name=$two ]
+ Variable [ Name=$four ]
+ Variable [ Name=$ten ]
+ ]
+Orderby
+ Variable [ Name=$two ]
+ ASC
+ Variable [ Name=$four ]
+ ASC
+ Variable [ Name=$ten ]
+ ASC
+
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-1/grouping-sets-1.11.ast b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-1/grouping-sets-1.11.ast
new file mode 100644
index 0000000..462e1e4
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-1/grouping-sets-1.11.ast
@@ -0,0 +1,85 @@
+DataverseUse test
+Query:
+SELECT [
+Variable [ Name=$two ]
+two
+Variable [ Name=$four ]
+four
+Variable [ Name=$ten ]
+ten
+Variable [ Name=#2 ]
+grp
+FunctionCall asterix.sql-sum@1[
+ (
+ SELECT ELEMENT [
+ FieldAccessor [
+ FieldAccessor [
+ Variable [ Name=#3 ]
+ Field=tenk
+ ]
+ Field=twenty
+ ]
+ ]
+ FROM [ Variable [ Name=#1 ]
+ AS Variable [ Name=#3 ]
+ ]
+ )
+]
+agg_sum
+]
+FROM [ FunctionCall asterix.dataset@1[
+ LiteralExpr [STRING] [test.tenk]
+ ]
+ AS Variable [ Name=$tenk ]
+]
+Groupby
+ GROUPING SET (
+ Variable [ Name=$two ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=two
+ ]
+ Variable [ Name=$four ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=four
+ ]
+ Variable [ Name=$ten ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=ten
+ ]
+ )
+ GROUPING SET (
+ Variable [ Name=$two ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=two
+ ]
+ ),
+ GROUPING SET (
+ ),
+ GROUP AS Variable [ Name=#1 ]
+ (
+ tenk:=Variable [ Name=$tenk ]
+ )
+
+Let Variable [ Name=#2 ]
+ :=
+ FunctionCall test.grouping@3[
+ Variable [ Name=$two ]
+ Variable [ Name=$four ]
+ Variable [ Name=$ten ]
+ ]
+Orderby
+ Variable [ Name=$two ]
+ ASC
+ Variable [ Name=$four ]
+ ASC
+ Variable [ Name=$ten ]
+ ASC
+
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-1/grouping-sets-1.12.ast b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-1/grouping-sets-1.12.ast
new file mode 100644
index 0000000..f89980c
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-1/grouping-sets-1.12.ast
@@ -0,0 +1,51 @@
+DataverseUse test
+Query:
+SELECT [
+Variable [ Name=$two ]
+two
+Variable [ Name=#2 ]
+grp
+FunctionCall asterix.sql-sum@1[
+ (
+ SELECT ELEMENT [
+ FieldAccessor [
+ FieldAccessor [
+ Variable [ Name=#3 ]
+ Field=tenk
+ ]
+ Field=ten
+ ]
+ ]
+ FROM [ Variable [ Name=#1 ]
+ AS Variable [ Name=#3 ]
+ ]
+ )
+]
+agg_sum
+]
+FROM [ FunctionCall asterix.dataset@1[
+ LiteralExpr [STRING] [test.tenk]
+ ]
+ AS Variable [ Name=$tenk ]
+]
+Groupby
+ Variable [ Name=$two ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=two
+ ]
+ GROUP AS Variable [ Name=#1 ]
+ (
+ tenk:=Variable [ Name=$tenk ]
+ )
+
+Let Variable [ Name=#2 ]
+ :=
+ FunctionCall test.grouping@1[
+ Variable [ Name=$two ]
+ ]
+Orderby
+ Variable [ Name=$two ]
+ ASC
+
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-1/grouping-sets-1.13.ast b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-1/grouping-sets-1.13.ast
new file mode 100644
index 0000000..5fa0a46
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-1/grouping-sets-1.13.ast
@@ -0,0 +1,228 @@
+DataverseUse test
+Query:
+SELECT [
+Variable [ Name=$two ]
+two
+Variable [ Name=$four ]
+four
+Variable [ Name=$ten ]
+ten
+Variable [ Name=$twenty ]
+twenty
+Variable [ Name=#2 ]
+grp
+FunctionCall asterix.sql-sum@1[
+ (
+ SELECT ELEMENT [
+ FieldAccessor [
+ FieldAccessor [
+ Variable [ Name=#3 ]
+ Field=tenk
+ ]
+ Field=hundred
+ ]
+ ]
+ FROM [ Variable [ Name=#1 ]
+ AS Variable [ Name=#3 ]
+ ]
+ )
+]
+agg_sum
+]
+FROM [ FunctionCall asterix.dataset@1[
+ LiteralExpr [STRING] [test.tenk]
+ ]
+ AS Variable [ Name=$tenk ]
+]
+Groupby
+ GROUPING SET (
+ Variable [ Name=$two ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=two
+ ]
+ Variable [ Name=$four ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=four
+ ]
+ Variable [ Name=$ten ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=ten
+ ]
+ Variable [ Name=$twenty ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=twenty
+ ]
+ )
+ GROUPING SET (
+ Variable [ Name=$two ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=two
+ ]
+ Variable [ Name=$four ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=four
+ ]
+ Variable [ Name=$ten ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=ten
+ ]
+ ),
+ GROUPING SET (
+ Variable [ Name=$two ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=two
+ ]
+ Variable [ Name=$four ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=four
+ ]
+ Variable [ Name=$twenty ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=twenty
+ ]
+ ),
+ GROUPING SET (
+ Variable [ Name=$two ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=two
+ ]
+ Variable [ Name=$four ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=four
+ ]
+ ),
+ GROUPING SET (
+ Variable [ Name=$two ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=two
+ ]
+ Variable [ Name=$ten ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=ten
+ ]
+ Variable [ Name=$twenty ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=twenty
+ ]
+ ),
+ GROUPING SET (
+ Variable [ Name=$two ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=two
+ ]
+ Variable [ Name=$ten ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=ten
+ ]
+ ),
+ GROUPING SET (
+ Variable [ Name=$two ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=two
+ ]
+ Variable [ Name=$twenty ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=twenty
+ ]
+ ),
+ GROUPING SET (
+ Variable [ Name=$two ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=two
+ ]
+ ),
+ GROUPING SET (
+ Variable [ Name=$ten ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=ten
+ ]
+ Variable [ Name=$twenty ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=twenty
+ ]
+ ),
+ GROUPING SET (
+ Variable [ Name=$ten ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=ten
+ ]
+ ),
+ GROUPING SET (
+ Variable [ Name=$twenty ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=twenty
+ ]
+ ),
+ GROUPING SET (
+ ),
+ GROUP AS Variable [ Name=#1 ]
+ (
+ tenk:=Variable [ Name=$tenk ]
+ )
+
+Let Variable [ Name=#2 ]
+ :=
+ FunctionCall test.grouping@4[
+ Variable [ Name=$two ]
+ Variable [ Name=$four ]
+ Variable [ Name=$ten ]
+ Variable [ Name=$twenty ]
+ ]
+Orderby
+ Variable [ Name=$two ]
+ ASC
+ Variable [ Name=$four ]
+ ASC
+ Variable [ Name=$ten ]
+ ASC
+ Variable [ Name=$twenty ]
+ ASC
+
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-1/grouping-sets-1.14.ast b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-1/grouping-sets-1.14.ast
new file mode 100644
index 0000000..5fa0a46
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-1/grouping-sets-1.14.ast
@@ -0,0 +1,228 @@
+DataverseUse test
+Query:
+SELECT [
+Variable [ Name=$two ]
+two
+Variable [ Name=$four ]
+four
+Variable [ Name=$ten ]
+ten
+Variable [ Name=$twenty ]
+twenty
+Variable [ Name=#2 ]
+grp
+FunctionCall asterix.sql-sum@1[
+ (
+ SELECT ELEMENT [
+ FieldAccessor [
+ FieldAccessor [
+ Variable [ Name=#3 ]
+ Field=tenk
+ ]
+ Field=hundred
+ ]
+ ]
+ FROM [ Variable [ Name=#1 ]
+ AS Variable [ Name=#3 ]
+ ]
+ )
+]
+agg_sum
+]
+FROM [ FunctionCall asterix.dataset@1[
+ LiteralExpr [STRING] [test.tenk]
+ ]
+ AS Variable [ Name=$tenk ]
+]
+Groupby
+ GROUPING SET (
+ Variable [ Name=$two ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=two
+ ]
+ Variable [ Name=$four ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=four
+ ]
+ Variable [ Name=$ten ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=ten
+ ]
+ Variable [ Name=$twenty ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=twenty
+ ]
+ )
+ GROUPING SET (
+ Variable [ Name=$two ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=two
+ ]
+ Variable [ Name=$four ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=four
+ ]
+ Variable [ Name=$ten ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=ten
+ ]
+ ),
+ GROUPING SET (
+ Variable [ Name=$two ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=two
+ ]
+ Variable [ Name=$four ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=four
+ ]
+ Variable [ Name=$twenty ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=twenty
+ ]
+ ),
+ GROUPING SET (
+ Variable [ Name=$two ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=two
+ ]
+ Variable [ Name=$four ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=four
+ ]
+ ),
+ GROUPING SET (
+ Variable [ Name=$two ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=two
+ ]
+ Variable [ Name=$ten ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=ten
+ ]
+ Variable [ Name=$twenty ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=twenty
+ ]
+ ),
+ GROUPING SET (
+ Variable [ Name=$two ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=two
+ ]
+ Variable [ Name=$ten ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=ten
+ ]
+ ),
+ GROUPING SET (
+ Variable [ Name=$two ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=two
+ ]
+ Variable [ Name=$twenty ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=twenty
+ ]
+ ),
+ GROUPING SET (
+ Variable [ Name=$two ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=two
+ ]
+ ),
+ GROUPING SET (
+ Variable [ Name=$ten ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=ten
+ ]
+ Variable [ Name=$twenty ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=twenty
+ ]
+ ),
+ GROUPING SET (
+ Variable [ Name=$ten ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=ten
+ ]
+ ),
+ GROUPING SET (
+ Variable [ Name=$twenty ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=twenty
+ ]
+ ),
+ GROUPING SET (
+ ),
+ GROUP AS Variable [ Name=#1 ]
+ (
+ tenk:=Variable [ Name=$tenk ]
+ )
+
+Let Variable [ Name=#2 ]
+ :=
+ FunctionCall test.grouping@4[
+ Variable [ Name=$two ]
+ Variable [ Name=$four ]
+ Variable [ Name=$ten ]
+ Variable [ Name=$twenty ]
+ ]
+Orderby
+ Variable [ Name=$two ]
+ ASC
+ Variable [ Name=$four ]
+ ASC
+ Variable [ Name=$ten ]
+ ASC
+ Variable [ Name=$twenty ]
+ ASC
+
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-1/grouping-sets-1.15.ast b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-1/grouping-sets-1.15.ast
new file mode 100644
index 0000000..767e94f
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-1/grouping-sets-1.15.ast
@@ -0,0 +1,116 @@
+DataverseUse test
+Query:
+SELECT [
+Variable [ Name=$two ]
+two
+Variable [ Name=$four ]
+four
+Variable [ Name=$ten ]
+ten
+Variable [ Name=$twenty ]
+twenty
+Variable [ Name=#2 ]
+grp
+FunctionCall asterix.sql-sum@1[
+ (
+ SELECT ELEMENT [
+ FieldAccessor [
+ FieldAccessor [
+ Variable [ Name=#3 ]
+ Field=tenk
+ ]
+ Field=hundred
+ ]
+ ]
+ FROM [ Variable [ Name=#1 ]
+ AS Variable [ Name=#3 ]
+ ]
+ )
+]
+agg_sum
+]
+FROM [ FunctionCall asterix.dataset@1[
+ LiteralExpr [STRING] [test.tenk]
+ ]
+ AS Variable [ Name=$tenk ]
+]
+Groupby
+ GROUPING SET (
+ Variable [ Name=$two ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=two
+ ]
+ Variable [ Name=$four ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=four
+ ]
+ )
+ GROUPING SET (
+ Variable [ Name=$two ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=two
+ ]
+ ),
+ GROUPING SET (
+ ),
+ GROUPING SET (
+ Variable [ Name=$ten ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=ten
+ ]
+ Variable [ Name=$twenty ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=twenty
+ ]
+ ),
+ GROUPING SET (
+ Variable [ Name=$ten ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=ten
+ ]
+ ),
+ GROUPING SET (
+ Variable [ Name=$twenty ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=twenty
+ ]
+ ),
+ GROUPING SET (
+ ),
+ GROUP AS Variable [ Name=#1 ]
+ (
+ tenk:=Variable [ Name=$tenk ]
+ )
+
+Let Variable [ Name=#2 ]
+ :=
+ FunctionCall test.grouping@4[
+ Variable [ Name=$two ]
+ Variable [ Name=$four ]
+ Variable [ Name=$ten ]
+ Variable [ Name=$twenty ]
+ ]
+Orderby
+ Variable [ Name=$two ]
+ ASC
+ Variable [ Name=$four ]
+ ASC
+ Variable [ Name=$ten ]
+ ASC
+ Variable [ Name=$twenty ]
+ ASC
+
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-1/grouping-sets-1.2.ast b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-1/grouping-sets-1.2.ast
new file mode 100644
index 0000000..916a59e
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-1/grouping-sets-1.2.ast
@@ -0,0 +1 @@
+DataverseUse test
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-1/grouping-sets-1.3.ast b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-1/grouping-sets-1.3.ast
new file mode 100644
index 0000000..fae9857
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-1/grouping-sets-1.3.ast
@@ -0,0 +1,34 @@
+DataverseUse test
+Query:
+SELECT [
+FunctionCall asterix.sql-sum@1[
+ (
+ SELECT ELEMENT [
+ FieldAccessor [
+ FieldAccessor [
+ Variable [ Name=#2 ]
+ Field=tenk
+ ]
+ Field=ten
+ ]
+ ]
+ FROM [ Variable [ Name=#1 ]
+ AS Variable [ Name=#2 ]
+ ]
+ )
+]
+agg_sum
+]
+FROM [ FunctionCall asterix.dataset@1[
+ LiteralExpr [STRING] [test.tenk]
+ ]
+ AS Variable [ Name=$tenk ]
+]
+Groupby
+ GROUPING SET (
+ )
+ GROUP AS Variable [ Name=#1 ]
+ (
+ tenk:=Variable [ Name=$tenk ]
+ )
+
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-1/grouping-sets-1.4.ast b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-1/grouping-sets-1.4.ast
new file mode 100644
index 0000000..fae9857
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-1/grouping-sets-1.4.ast
@@ -0,0 +1,34 @@
+DataverseUse test
+Query:
+SELECT [
+FunctionCall asterix.sql-sum@1[
+ (
+ SELECT ELEMENT [
+ FieldAccessor [
+ FieldAccessor [
+ Variable [ Name=#2 ]
+ Field=tenk
+ ]
+ Field=ten
+ ]
+ ]
+ FROM [ Variable [ Name=#1 ]
+ AS Variable [ Name=#2 ]
+ ]
+ )
+]
+agg_sum
+]
+FROM [ FunctionCall asterix.dataset@1[
+ LiteralExpr [STRING] [test.tenk]
+ ]
+ AS Variable [ Name=$tenk ]
+]
+Groupby
+ GROUPING SET (
+ )
+ GROUP AS Variable [ Name=#1 ]
+ (
+ tenk:=Variable [ Name=$tenk ]
+ )
+
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-1/grouping-sets-1.5.ast b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-1/grouping-sets-1.5.ast
new file mode 100644
index 0000000..f9f3410
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-1/grouping-sets-1.5.ast
@@ -0,0 +1,36 @@
+DataverseUse test
+Query:
+SELECT [
+FunctionCall asterix.sql-sum@1[
+ (
+ SELECT ELEMENT [
+ FieldAccessor [
+ FieldAccessor [
+ Variable [ Name=#2 ]
+ Field=tenk
+ ]
+ Field=ten
+ ]
+ ]
+ FROM [ Variable [ Name=#1 ]
+ AS Variable [ Name=#2 ]
+ ]
+ )
+]
+agg_sum
+]
+FROM [ FunctionCall asterix.dataset@1[
+ LiteralExpr [STRING] [test.tenk]
+ ]
+ AS Variable [ Name=$tenk ]
+]
+Groupby
+ GROUPING SET (
+ )
+ GROUPING SET (
+ ),
+ GROUP AS Variable [ Name=#1 ]
+ (
+ tenk:=Variable [ Name=$tenk ]
+ )
+
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-1/grouping-sets-1.6.ast b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-1/grouping-sets-1.6.ast
new file mode 100644
index 0000000..fae9857
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-1/grouping-sets-1.6.ast
@@ -0,0 +1,34 @@
+DataverseUse test
+Query:
+SELECT [
+FunctionCall asterix.sql-sum@1[
+ (
+ SELECT ELEMENT [
+ FieldAccessor [
+ FieldAccessor [
+ Variable [ Name=#2 ]
+ Field=tenk
+ ]
+ Field=ten
+ ]
+ ]
+ FROM [ Variable [ Name=#1 ]
+ AS Variable [ Name=#2 ]
+ ]
+ )
+]
+agg_sum
+]
+FROM [ FunctionCall asterix.dataset@1[
+ LiteralExpr [STRING] [test.tenk]
+ ]
+ AS Variable [ Name=$tenk ]
+]
+Groupby
+ GROUPING SET (
+ )
+ GROUP AS Variable [ Name=#1 ]
+ (
+ tenk:=Variable [ Name=$tenk ]
+ )
+
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-1/grouping-sets-1.7.ast b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-1/grouping-sets-1.7.ast
new file mode 100644
index 0000000..b2898d1
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-1/grouping-sets-1.7.ast
@@ -0,0 +1,74 @@
+DataverseUse test
+Query:
+SELECT [
+Variable [ Name=$two ]
+two
+Variable [ Name=$four ]
+four
+Variable [ Name=#2 ]
+grp
+FunctionCall asterix.sql-sum@1[
+ (
+ SELECT ELEMENT [
+ FieldAccessor [
+ FieldAccessor [
+ Variable [ Name=#3 ]
+ Field=tenk
+ ]
+ Field=ten
+ ]
+ ]
+ FROM [ Variable [ Name=#1 ]
+ AS Variable [ Name=#3 ]
+ ]
+ )
+]
+agg_sum
+]
+FROM [ FunctionCall asterix.dataset@1[
+ LiteralExpr [STRING] [test.tenk]
+ ]
+ AS Variable [ Name=$tenk ]
+]
+Groupby
+ GROUPING SET (
+ Variable [ Name=$two ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=two
+ ]
+ Variable [ Name=$four ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=four
+ ]
+ )
+ GROUPING SET (
+ Variable [ Name=$two ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=two
+ ]
+ ),
+ GROUPING SET (
+ ),
+ GROUP AS Variable [ Name=#1 ]
+ (
+ tenk:=Variable [ Name=$tenk ]
+ )
+
+Let Variable [ Name=#2 ]
+ :=
+ FunctionCall test.grouping@2[
+ Variable [ Name=$two ]
+ Variable [ Name=$four ]
+ ]
+Orderby
+ Variable [ Name=$two ]
+ ASC
+ Variable [ Name=$four ]
+ ASC
+
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-1/grouping-sets-1.8.ast b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-1/grouping-sets-1.8.ast
new file mode 100644
index 0000000..11c6281
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-1/grouping-sets-1.8.ast
@@ -0,0 +1,82 @@
+DataverseUse test
+Query:
+SELECT [
+Variable [ Name=$two ]
+two
+Variable [ Name=$four ]
+four
+Variable [ Name=#2 ]
+grp
+FunctionCall asterix.sql-sum@1[
+ (
+ SELECT ELEMENT [
+ FieldAccessor [
+ FieldAccessor [
+ Variable [ Name=#3 ]
+ Field=tenk
+ ]
+ Field=ten
+ ]
+ ]
+ FROM [ Variable [ Name=#1 ]
+ AS Variable [ Name=#3 ]
+ ]
+ )
+]
+agg_sum
+]
+FROM [ FunctionCall asterix.dataset@1[
+ LiteralExpr [STRING] [test.tenk]
+ ]
+ AS Variable [ Name=$tenk ]
+]
+Groupby
+ GROUPING SET (
+ Variable [ Name=$two ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=two
+ ]
+ Variable [ Name=$four ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=four
+ ]
+ )
+ GROUPING SET (
+ Variable [ Name=$two ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=two
+ ]
+ ),
+ GROUPING SET (
+ Variable [ Name=$four ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=four
+ ]
+ ),
+ GROUPING SET (
+ ),
+ GROUP AS Variable [ Name=#1 ]
+ (
+ tenk:=Variable [ Name=$tenk ]
+ )
+
+Let Variable [ Name=#2 ]
+ :=
+ FunctionCall test.grouping@2[
+ Variable [ Name=$two ]
+ Variable [ Name=$four ]
+ ]
+Orderby
+ Variable [ Name=$two ]
+ ASC
+ Variable [ Name=$four ]
+ ASC
+
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-1/grouping-sets-1.9.ast b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-1/grouping-sets-1.9.ast
new file mode 100644
index 0000000..775f28e
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-1/grouping-sets-1.9.ast
@@ -0,0 +1,97 @@
+DataverseUse test
+Query:
+SELECT [
+Variable [ Name=$two ]
+two
+Variable [ Name=$four ]
+four
+Variable [ Name=$ten ]
+ten
+Variable [ Name=#2 ]
+grp
+FunctionCall asterix.sql-sum@1[
+ (
+ SELECT ELEMENT [
+ FieldAccessor [
+ FieldAccessor [
+ Variable [ Name=#3 ]
+ Field=tenk
+ ]
+ Field=twenty
+ ]
+ ]
+ FROM [ Variable [ Name=#1 ]
+ AS Variable [ Name=#3 ]
+ ]
+ )
+]
+agg_sum
+]
+FROM [ FunctionCall asterix.dataset@1[
+ LiteralExpr [STRING] [test.tenk]
+ ]
+ AS Variable [ Name=$tenk ]
+]
+Groupby
+ GROUPING SET (
+ Variable [ Name=$two ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=two
+ ]
+ Variable [ Name=$four ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=four
+ ]
+ Variable [ Name=$ten ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=ten
+ ]
+ )
+ GROUPING SET (
+ Variable [ Name=$two ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=two
+ ]
+ Variable [ Name=$four ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=four
+ ]
+ ),
+ GROUPING SET (
+ Variable [ Name=$two ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=two
+ ]
+ ),
+ GROUP AS Variable [ Name=#1 ]
+ (
+ tenk:=Variable [ Name=$tenk ]
+ )
+
+Let Variable [ Name=#2 ]
+ :=
+ FunctionCall test.grouping@3[
+ Variable [ Name=$two ]
+ Variable [ Name=$four ]
+ Variable [ Name=$ten ]
+ ]
+Orderby
+ Variable [ Name=$two ]
+ ASC
+ Variable [ Name=$four ]
+ ASC
+ Variable [ Name=$ten ]
+ ASC
+
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-2/grouping-sets-2.1.ast b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-2/grouping-sets-2.1.ast
new file mode 100644
index 0000000..8e03d54
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-2/grouping-sets-2.1.ast
@@ -0,0 +1,22 @@
+DataverseUse test
+TypeDecl tenkType [
+ closed RecordType {
+ unique1 : integer,
+ unique2 : integer,
+ two : integer,
+ four : integer,
+ ten : integer,
+ twenty : integer,
+ hundred : integer,
+ thousand : integer,
+ twothousand : integer,
+ fivethous : integer,
+ tenthous : integer,
+ odd100 : integer,
+ even100 : integer,
+ stringu1 : string,
+ stringu2 : string,
+ string4 : string
+ }
+]
+DatasetDecl tenk(tenkType) partitioned by [[unique2]]
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-2/grouping-sets-2.2.ast b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-2/grouping-sets-2.2.ast
new file mode 100644
index 0000000..916a59e
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-2/grouping-sets-2.2.ast
@@ -0,0 +1 @@
+DataverseUse test
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-2/grouping-sets-2.3.ast b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-2/grouping-sets-2.3.ast
new file mode 100644
index 0000000..6022629
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-2/grouping-sets-2.3.ast
@@ -0,0 +1,4 @@
+DataverseUse test
+Query:
+FunctionCall test.gs1@0[
+]
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-2/grouping-sets-2.4.ast b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-2/grouping-sets-2.4.ast
new file mode 100644
index 0000000..04c8aec
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-2/grouping-sets-2.4.ast
@@ -0,0 +1,74 @@
+DataverseUse test
+Query:
+SELECT [
+Variable [ Name=$v2 ]
+v2
+Variable [ Name=$v4 ]
+v4
+Variable [ Name=#2 ]
+grp
+FunctionCall asterix.sql-sum@1[
+ (
+ SELECT ELEMENT [
+ FieldAccessor [
+ FieldAccessor [
+ Variable [ Name=#3 ]
+ Field=tenk
+ ]
+ Field=ten
+ ]
+ ]
+ FROM [ Variable [ Name=#1 ]
+ AS Variable [ Name=#3 ]
+ ]
+ )
+]
+agg_sum
+]
+FROM [ FunctionCall asterix.dataset@1[
+ LiteralExpr [STRING] [test.tenk]
+ ]
+ AS Variable [ Name=$tenk ]
+]
+Groupby
+ GROUPING SET (
+ Variable [ Name=$v2 ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=two
+ ]
+ Variable [ Name=$v4 ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=four
+ ]
+ )
+ GROUPING SET (
+ Variable [ Name=$v2 ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=two
+ ]
+ ),
+ GROUPING SET (
+ ),
+ GROUP AS Variable [ Name=#1 ]
+ (
+ tenk:=Variable [ Name=$tenk ]
+ )
+
+Let Variable [ Name=#2 ]
+ :=
+ FunctionCall test.grouping@2[
+ Variable [ Name=$v2 ]
+ Variable [ Name=$v4 ]
+ ]
+Orderby
+ Variable [ Name=$v2 ]
+ ASC
+ Variable [ Name=$v4 ]
+ ASC
+
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-2/grouping-sets-2.5.ast b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-2/grouping-sets-2.5.ast
new file mode 100644
index 0000000..af33220
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/group-by/grouping-sets-2/grouping-sets-2.5.ast
@@ -0,0 +1,80 @@
+DataverseUse test
+Query:
+SELECT [
+Variable [ Name=$v2 ]
+v2
+Variable [ Name=$v4 ]
+v4
+Variable [ Name=#2 ]
+grp
+FunctionCall asterix.sql-sum@1[
+ (
+ SELECT ELEMENT [
+ FieldAccessor [
+ FieldAccessor [
+ Variable [ Name=#3 ]
+ Field=tenk
+ ]
+ Field=ten
+ ]
+ ]
+ FROM [ Variable [ Name=#1 ]
+ AS Variable [ Name=#3 ]
+ ]
+ )
+]
+agg_sum
+]
+FROM [ FunctionCall asterix.dataset@1[
+ LiteralExpr [STRING] [test.tenk]
+ ]
+ AS Variable [ Name=$tenk ]
+]
+Groupby
+ GROUPING SET (
+ Variable [ Name=$v2 ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=two
+ ]
+ Variable [ Name=$v4 ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=four
+ ]
+ )
+ GROUPING SET (
+ Variable [ Name=$v2 ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=two
+ ]
+ ),
+ GROUPING SET (
+ Variable [ Name=$v2 ]
+ :=
+ FieldAccessor [
+ Variable [ Name=$tenk ]
+ Field=two
+ ]
+ ),
+ GROUP AS Variable [ Name=#1 ]
+ (
+ tenk:=Variable [ Name=$tenk ]
+ )
+
+Let Variable [ Name=#2 ]
+ :=
+ FunctionCall test.grouping@2[
+ Variable [ Name=$v2 ]
+ Variable [ Name=$v4 ]
+ ]
+Orderby
+ Variable [ Name=$v2 ]
+ ASC
+ Variable [ Name=$v4 ]
+ ASC
+
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
index 281c97e..fc7372e 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
@@ -5382,6 +5382,29 @@
</compilation-unit>
</test-case>
<test-case FilePath="group-by">
+ <compilation-unit name="grouping-sets-1">
+ <output-dir compare="Text">grouping-sets-1</output-dir>
+ </compilation-unit>
+ </test-case>
+ <test-case FilePath="group-by">
+ <compilation-unit name="grouping-sets-2">
+ <output-dir compare="Text">grouping-sets-2</output-dir>
+ </compilation-unit>
+ </test-case>
+ <test-case FilePath="group-by">
+ <compilation-unit name="grouping-sets-3-negative">
+ <output-dir compare="Text">grouping-sets-2</output-dir>
+ <expected-error>ASX1113: Unexpected alias: v21</expected-error>
+ <expected-error>ASX1113: Unexpected alias: v22</expected-error>
+ <expected-error>ASX1113: Unexpected alias: v23</expected-error>
+ <expected-error>ASX1087: Invalid number of arguments for function grouping</expected-error>
+ <expected-error>ASX1112: Invalid argument to grouping() function</expected-error>
+ <expected-error>ASX1112: Invalid argument to grouping() function</expected-error>
+ <expected-error>ASX1112: Invalid argument to grouping() function</expected-error>
+ <expected-error>ASX1111: Too many grouping sets in group by clause: 512. Maximum allowed: 128.</expected-error>
+ </compilation-unit>
+ </test-case>
+ <test-case FilePath="group-by">
<compilation-unit name="having">
<output-dir compare="Text">core-02</output-dir>
</compilation-unit>
@@ -11266,107 +11289,6 @@
</compilation-unit>
</test-case>
</test-group>
- <test-group name="window">
- <test-case FilePath="window">
- <compilation-unit name="cume_dist_01">
- <output-dir compare="Text">cume_dist_01</output-dir>
- </compilation-unit>
- </test-case>
- <test-case FilePath="window">
- <compilation-unit name="dense_rank_01">
- <output-dir compare="Text">dense_rank_01</output-dir>
- </compilation-unit>
- </test-case>
- <test-case FilePath="window">
- <compilation-unit name="first_value_01">
- <output-dir compare="Text">first_value_01</output-dir>
- </compilation-unit>
- </test-case>
- <test-case FilePath="window">
- <compilation-unit name="lag_01">
- <output-dir compare="Text">lag_01</output-dir>
- </compilation-unit>
- </test-case>
- <test-case FilePath="window">
- <compilation-unit name="last_value_01">
- <output-dir compare="Text">last_value_01</output-dir>
- </compilation-unit>
- </test-case>
- <test-case FilePath="window">
- <compilation-unit name="lead_01">
- <output-dir compare="Text">lead_01</output-dir>
- </compilation-unit>
- </test-case>
- <test-case FilePath="window">
- <compilation-unit name="misc_01">
- <output-dir compare="Text">misc_01</output-dir>
- </compilation-unit>
- </test-case>
- <test-case FilePath="window">
- <compilation-unit name="nth_value_01">
- <output-dir compare="Text">nth_value_01</output-dir>
- </compilation-unit>
- </test-case>
- <test-case FilePath="window">
- <compilation-unit name="ntile_01">
- <output-dir compare="Text">ntile_01</output-dir>
- </compilation-unit>
- </test-case>
- <test-case FilePath="window">
- <compilation-unit name="percent_rank_01">
- <output-dir compare="Text">percent_rank_01</output-dir>
- </compilation-unit>
- </test-case>
- <test-case FilePath="window">
- <compilation-unit name="pg_win">
- <output-dir compare="Text">pg_win</output-dir>
- </compilation-unit>
- </test-case>
- <test-case FilePath="window">
- <compilation-unit name="rank_01">
- <output-dir compare="Text">rank_01</output-dir>
- </compilation-unit>
- </test-case>
- <test-case FilePath="window">
- <compilation-unit name="ratio_to_report_01">
- <output-dir compare="Text">ratio_to_report_01</output-dir>
- </compilation-unit>
- </test-case>
- <test-case FilePath="window">
- <compilation-unit name="row_number_01">
- <output-dir compare="Text">row_number_01</output-dir>
- </compilation-unit>
- </test-case>
- <test-case FilePath="window">
- <compilation-unit name="win_negative">
- <output-dir compare="Text">misc_01</output-dir>
- <expected-error>ASX0002: Type mismatch</expected-error>
- <expected-error>ASX1104: Invalid modifier FROM FIRST/LAST for function</expected-error>
- <expected-error>ASX1037: Invalid query parameter compiler.windowmemory</expected-error>
- <expected-error>ASX1102: Expected window or aggregate function, got: unknown_func</expected-error>
- <expected-error>ASX1079: Compilation error: count is a SQL-92 aggregate function</expected-error>
- <expected-error>ASX1104: Invalid modifier RESPECT/IGNORE NULLS for function</expected-error>
- <expected-error>ASX1104: Invalid modifier RESPECT/IGNORE NULLS for function</expected-error>
- <expected-error>ASX1104: Invalid modifier FROM FIRST/LAST for function</expected-error>
- <source-location>false</source-location>
- </compilation-unit>
- </test-case>
- <test-case FilePath="window">
- <compilation-unit name="win_null_missing">
- <output-dir compare="Text">win_null_missing</output-dir>
- </compilation-unit>
- </test-case>
- <test-case FilePath="window">
- <compilation-unit name="win_opt_01">
- <output-dir compare="Text">win_opt_01</output-dir>
- </compilation-unit>
- </test-case>
- <test-case FilePath="window">
- <compilation-unit name="win_opt_02">
- <output-dir compare="Text">win_opt_02</output-dir>
- </compilation-unit>
- </test-case>
- </test-group>
<test-group name="writers">
<test-case FilePath="writers">
<compilation-unit name="print_01">
@@ -14002,4 +13924,105 @@
</compilation-unit>
</test-case>
</test-group>
+ <test-group name="window">
+ <test-case FilePath="window">
+ <compilation-unit name="cume_dist_01">
+ <output-dir compare="Text">cume_dist_01</output-dir>
+ </compilation-unit>
+ </test-case>
+ <test-case FilePath="window">
+ <compilation-unit name="dense_rank_01">
+ <output-dir compare="Text">dense_rank_01</output-dir>
+ </compilation-unit>
+ </test-case>
+ <test-case FilePath="window">
+ <compilation-unit name="first_value_01">
+ <output-dir compare="Text">first_value_01</output-dir>
+ </compilation-unit>
+ </test-case>
+ <test-case FilePath="window">
+ <compilation-unit name="lag_01">
+ <output-dir compare="Text">lag_01</output-dir>
+ </compilation-unit>
+ </test-case>
+ <test-case FilePath="window">
+ <compilation-unit name="last_value_01">
+ <output-dir compare="Text">last_value_01</output-dir>
+ </compilation-unit>
+ </test-case>
+ <test-case FilePath="window">
+ <compilation-unit name="lead_01">
+ <output-dir compare="Text">lead_01</output-dir>
+ </compilation-unit>
+ </test-case>
+ <test-case FilePath="window">
+ <compilation-unit name="misc_01">
+ <output-dir compare="Text">misc_01</output-dir>
+ </compilation-unit>
+ </test-case>
+ <test-case FilePath="window">
+ <compilation-unit name="nth_value_01">
+ <output-dir compare="Text">nth_value_01</output-dir>
+ </compilation-unit>
+ </test-case>
+ <test-case FilePath="window">
+ <compilation-unit name="ntile_01">
+ <output-dir compare="Text">ntile_01</output-dir>
+ </compilation-unit>
+ </test-case>
+ <test-case FilePath="window">
+ <compilation-unit name="percent_rank_01">
+ <output-dir compare="Text">percent_rank_01</output-dir>
+ </compilation-unit>
+ </test-case>
+ <test-case FilePath="window">
+ <compilation-unit name="pg_win">
+ <output-dir compare="Text">pg_win</output-dir>
+ </compilation-unit>
+ </test-case>
+ <test-case FilePath="window">
+ <compilation-unit name="rank_01">
+ <output-dir compare="Text">rank_01</output-dir>
+ </compilation-unit>
+ </test-case>
+ <test-case FilePath="window">
+ <compilation-unit name="ratio_to_report_01">
+ <output-dir compare="Text">ratio_to_report_01</output-dir>
+ </compilation-unit>
+ </test-case>
+ <test-case FilePath="window">
+ <compilation-unit name="row_number_01">
+ <output-dir compare="Text">row_number_01</output-dir>
+ </compilation-unit>
+ </test-case>
+ <test-case FilePath="window">
+ <compilation-unit name="win_negative">
+ <output-dir compare="Text">misc_01</output-dir>
+ <expected-error>ASX0002: Type mismatch</expected-error>
+ <expected-error>ASX1104: Invalid modifier FROM FIRST/LAST for function</expected-error>
+ <expected-error>ASX1037: Invalid query parameter compiler.windowmemory</expected-error>
+ <expected-error>ASX1102: Expected window or aggregate function, got: unknown_func</expected-error>
+ <expected-error>ASX1079: Compilation error: count is a SQL-92 aggregate function</expected-error>
+ <expected-error>ASX1104: Invalid modifier RESPECT/IGNORE NULLS for function</expected-error>
+ <expected-error>ASX1104: Invalid modifier RESPECT/IGNORE NULLS for function</expected-error>
+ <expected-error>ASX1104: Invalid modifier FROM FIRST/LAST for function</expected-error>
+ <source-location>false</source-location>
+ </compilation-unit>
+ </test-case>
+ <test-case FilePath="window">
+ <compilation-unit name="win_null_missing">
+ <output-dir compare="Text">win_null_missing</output-dir>
+ </compilation-unit>
+ </test-case>
+ <test-case FilePath="window">
+ <compilation-unit name="win_opt_01">
+ <output-dir compare="Text">win_opt_01</output-dir>
+ </compilation-unit>
+ </test-case>
+ <test-case FilePath="window">
+ <compilation-unit name="win_opt_02">
+ <output-dir compare="Text">win_opt_02</output-dir>
+ </compilation-unit>
+ </test-case>
+ </test-group>
</test-suite>
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp_parser.xml b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp_parser.xml
index ffde5ff..e9b1de0 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp_parser.xml
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp_parser.xml
@@ -6713,4 +6713,16 @@
</compilation-unit>
</test-case>
</test-group>
+ <test-group name="group-by">
+ <test-case FilePath="group-by">
+ <compilation-unit name="grouping-sets-1">
+ <output-dir compare="AST">grouping-sets-1</output-dir>
+ </compilation-unit>
+ </test-case>
+ <test-case FilePath="group-by">
+ <compilation-unit name="grouping-sets-2">
+ <output-dir compare="AST">grouping-sets-2</output-dir>
+ </compilation-unit>
+ </test-case>
+ </test-group>
</test-suite>
diff --git a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/exceptions/ErrorCode.java b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/exceptions/ErrorCode.java
index a30119a..3d30360 100644
--- a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/exceptions/ErrorCode.java
+++ b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/exceptions/ErrorCode.java
@@ -198,6 +198,9 @@
public static final int SYNONYM_EXISTS = 1108;
public static final int UNKNOWN_SYNONYM = 1109;
public static final int UNKNOWN_LIBRARY = 1110;
+ public static final int COMPILATION_GROUPING_SETS_OVERFLOW = 1111;
+ public static final int COMPILATION_GROUPING_OPERATION_INVALID_ARG = 1112;
+ public static final int COMPILATION_UNEXPECTED_ALIAS = 1113;
// Feed errors
public static final int DATAFLOW_ILLEGAL_STATE = 3001;
diff --git a/asterixdb/asterix-common/src/main/resources/asx_errormsg/en.properties b/asterixdb/asterix-common/src/main/resources/asx_errormsg/en.properties
index 5dcc8ec..fc356e9 100644
--- a/asterixdb/asterix-common/src/main/resources/asx_errormsg/en.properties
+++ b/asterixdb/asterix-common/src/main/resources/asx_errormsg/en.properties
@@ -193,6 +193,9 @@
1108 = A synonym with this name %1$s already exists
1109 = Cannot find synonym with name %1$s
1110 = Unknown library %1$s
+1111 = Too many grouping sets in group by clause: %1$s. Maximum allowed: %2$s.
+1112 = Invalid argument to grouping() function
+1113 = Unexpected alias: %1$s
# Feed Errors
3001 = Illegal state.
diff --git a/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/rewrites/AqlQueryRewriter.java b/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/rewrites/AqlQueryRewriter.java
index 142a183..369d4c2 100644
--- a/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/rewrites/AqlQueryRewriter.java
+++ b/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/rewrites/AqlQueryRewriter.java
@@ -187,8 +187,10 @@
@Override
public Void visit(GroupbyClause gc, Void arg) throws CompilationException {
- for (GbyVariableExpressionPair p : gc.getGbyPairList()) {
- p.getExpr().accept(this, arg);
+ for (List<GbyVariableExpressionPair> gbyPairList : gc.getGbyPairList()) {
+ for (GbyVariableExpressionPair p : gbyPairList) {
+ p.getExpr().accept(this, arg);
+ }
}
if (gc.hasDecorList()) {
for (GbyVariableExpressionPair p : gc.getDecorPairList()) {
diff --git a/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/visitor/AQLAstPrintVisitor.java b/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/visitor/AQLAstPrintVisitor.java
index d8614ac..d78640c 100644
--- a/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/visitor/AQLAstPrintVisitor.java
+++ b/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/visitor/AQLAstPrintVisitor.java
@@ -19,8 +19,11 @@
package org.apache.asterix.lang.aql.visitor;
import java.io.PrintWriter;
+import java.util.List;
+import java.util.Map;
import org.apache.asterix.common.exceptions.CompilationException;
+import org.apache.asterix.common.exceptions.ErrorCode;
import org.apache.asterix.lang.aql.clause.DistinctClause;
import org.apache.asterix.lang.aql.clause.ForClause;
import org.apache.asterix.lang.aql.expression.FLWOGRExpression;
@@ -28,7 +31,10 @@
import org.apache.asterix.lang.aql.visitor.base.IAQLVisitor;
import org.apache.asterix.lang.common.base.Clause;
import org.apache.asterix.lang.common.base.Expression;
+import org.apache.asterix.lang.common.clause.GroupbyClause;
+import org.apache.asterix.lang.common.expression.GbyVariableExpressionPair;
import org.apache.asterix.lang.common.expression.ListSliceExpression;
+import org.apache.asterix.lang.common.expression.VariableExpr;
import org.apache.asterix.lang.common.visitor.QueryPrintVisitor;
class AQLAstPrintVisitor extends QueryPrintVisitor implements IAQLVisitor<Void, Integer> {
@@ -83,4 +89,45 @@
return null;
}
+ @Override
+ public Void visit(GroupbyClause gc, Integer step) throws CompilationException {
+ out.println(skip(step) + "Groupby");
+ List<List<GbyVariableExpressionPair>> gbyList = gc.getGbyPairList();
+ if (gbyList.size() == 1) {
+ for (GbyVariableExpressionPair pair : gbyList.get(0)) {
+ if (pair.getVar() != null) {
+ pair.getVar().accept(this, step + 1);
+ out.println(skip(step + 1) + ":=");
+ }
+ pair.getExpr().accept(this, step + 1);
+ }
+ } else {
+ // AQL does not support grouping sets
+ throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, gc.getSourceLocation(), "");
+ }
+ if (gc.hasDecorList()) {
+ out.println(skip(step + 1) + "Decor");
+ for (GbyVariableExpressionPair pair : gc.getDecorPairList()) {
+ if (pair.getVar() != null) {
+ pair.getVar().accept(this, step + 1);
+ out.println(skip(step + 1) + ":=");
+ }
+ pair.getExpr().accept(this, step + 1);
+ }
+ }
+ if (gc.hasWithMap()) {
+ out.println(skip(step + 1) + "With");
+ for (Map.Entry<Expression, VariableExpr> entry : gc.getWithVarMap().entrySet()) {
+ Expression key = entry.getKey();
+ VariableExpr value = entry.getValue();
+ key.accept(this, step + 1);
+ if (!key.equals(value)) {
+ out.println(skip(step + 1) + "AS");
+ value.accept(this, step + 1);
+ }
+ }
+ }
+ out.println();
+ return null;
+ }
}
diff --git a/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/visitor/AQLFormatPrintVisitor.java b/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/visitor/AQLFormatPrintVisitor.java
index eea398f..3edbcce 100644
--- a/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/visitor/AQLFormatPrintVisitor.java
+++ b/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/visitor/AQLFormatPrintVisitor.java
@@ -19,14 +19,21 @@
package org.apache.asterix.lang.aql.visitor;
import java.io.PrintWriter;
+import java.util.List;
+import java.util.Map;
import org.apache.asterix.common.exceptions.CompilationException;
+import org.apache.asterix.common.exceptions.ErrorCode;
import org.apache.asterix.lang.aql.clause.DistinctClause;
import org.apache.asterix.lang.aql.clause.ForClause;
import org.apache.asterix.lang.aql.expression.FLWOGRExpression;
import org.apache.asterix.lang.aql.expression.UnionExpr;
import org.apache.asterix.lang.aql.visitor.base.IAQLVisitor;
import org.apache.asterix.lang.common.base.Clause;
+import org.apache.asterix.lang.common.base.Expression;
+import org.apache.asterix.lang.common.clause.GroupbyClause;
+import org.apache.asterix.lang.common.expression.GbyVariableExpressionPair;
+import org.apache.asterix.lang.common.expression.VariableExpr;
import org.apache.asterix.lang.common.visitor.FormatPrintVisitor;
public class AQLFormatPrintVisitor extends FormatPrintVisitor implements IAQLVisitor<Void, Integer> {
@@ -72,4 +79,43 @@
out.println();
return null;
}
+
+ @Override
+ public Void visit(GroupbyClause gc, Integer step) throws CompilationException {
+ if (gc.hasHashGroupByHint()) {
+ out.println(skip(step) + "/* +hash */");
+ }
+ out.print(skip(step) + "group by ");
+ List<List<GbyVariableExpressionPair>> gbyList = gc.getGbyPairList();
+ if (gbyList.size() == 1) {
+ printDelimitedGbyExpressions(gbyList.get(0), step + 2);
+ } else {
+ // AQL does not support grouping sets
+ throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, gc.getSourceLocation(), "");
+ }
+ if (gc.hasDecorList()) {
+ out.print(" decor ");
+ printDelimitedGbyExpressions(gc.getDecorPairList(), step + 2);
+ }
+ if (gc.hasWithMap()) {
+ out.print(" with ");
+ Map<Expression, VariableExpr> withVarMap = gc.getWithVarMap();
+ int index = 0;
+ int size = withVarMap.size();
+ for (Map.Entry<Expression, VariableExpr> entry : withVarMap.entrySet()) {
+ Expression key = entry.getKey();
+ VariableExpr value = entry.getValue();
+ key.accept(this, step + 2);
+ if (!key.equals(value)) {
+ out.print(" as ");
+ value.accept(this, step + 2);
+ }
+ if (++index < size) {
+ out.print(COMMA);
+ }
+ }
+ }
+ out.println();
+ return null;
+ }
}
diff --git a/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/visitor/AQLToSQLPPPrintVisitor.java b/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/visitor/AQLToSQLPPPrintVisitor.java
index 35a3b45..d0d88c8 100644
--- a/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/visitor/AQLToSQLPPPrintVisitor.java
+++ b/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/visitor/AQLToSQLPPPrintVisitor.java
@@ -30,6 +30,7 @@
import java.util.Set;
import org.apache.asterix.common.exceptions.CompilationException;
+import org.apache.asterix.common.exceptions.ErrorCode;
import org.apache.asterix.common.metadata.DataverseName;
import org.apache.asterix.lang.aql.clause.DistinctClause;
import org.apache.asterix.lang.aql.clause.ForClause;
@@ -327,7 +328,13 @@
out.println(skip(step) + "/* +hash */");
}
out.print(skip(step) + "group by ");
- printDelimitedGbyExpressions(gc.getGbyPairList(), step + 2);
+ List<List<GbyVariableExpressionPair>> gbyList = gc.getGbyPairList();
+ if (gbyList.size() == 1) {
+ printDelimitedGbyExpressions(gbyList.get(0), step + 2);
+ } else {
+ // AQL does not support grouping sets
+ throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, gc.getSourceLocation(), "");
+ }
out.println();
return null;
}
@@ -404,9 +411,11 @@
// Collects produced variables from group-by.
private List<VariableExpr> collectProducedVariablesFromGroupby(GroupbyClause gbyClause) {
- List<VariableExpr> producedVars = new ArrayList<VariableExpr>();
- for (GbyVariableExpressionPair keyPair : gbyClause.getGbyPairList()) {
- producedVars.add(keyPair.getVar());
+ List<VariableExpr> producedVars = new ArrayList<>();
+ for (List<GbyVariableExpressionPair> gbyPairList : gbyClause.getGbyPairList()) {
+ for (GbyVariableExpressionPair keyPair : gbyPairList) {
+ producedVars.add(keyPair.getVar());
+ }
}
if (gbyClause.hasDecorList()) {
for (GbyVariableExpressionPair keyPair : gbyClause.getDecorPairList()) {
diff --git a/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/visitor/base/AbstractAqlSimpleExpressionVisitor.java b/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/visitor/base/AbstractAqlSimpleExpressionVisitor.java
index 9fb0840..16bf19e 100644
--- a/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/visitor/base/AbstractAqlSimpleExpressionVisitor.java
+++ b/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/visitor/base/AbstractAqlSimpleExpressionVisitor.java
@@ -108,8 +108,10 @@
@Override
public Expression visit(GroupbyClause gc, ILangExpression arg) throws CompilationException {
- for (GbyVariableExpressionPair gbyVarExpr : gc.getGbyPairList()) {
- gbyVarExpr.setExpr(visit(gbyVarExpr.getExpr(), gc));
+ for (List<GbyVariableExpressionPair> gbyPairList : gc.getGbyPairList()) {
+ for (GbyVariableExpressionPair gbyVarExpr : gbyPairList) {
+ gbyVarExpr.setExpr(visit(gbyVarExpr.getExpr(), gc));
+ }
}
return null;
}
diff --git a/asterixdb/asterix-lang-aql/src/main/javacc/AQL.jj b/asterixdb/asterix-lang-aql/src/main/javacc/AQL.jj
index a5ffe03..88017db 100644
--- a/asterixdb/asterix-lang-aql/src/main/javacc/AQL.jj
+++ b/asterixdb/asterix-lang-aql/src/main/javacc/AQL.jj
@@ -40,6 +40,7 @@
import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
@@ -2589,7 +2590,7 @@
newScope.addNewVarSymbolToScope(withVar.getVar());
})*
{
- gbc.setGbyPairList(vePairList);
+ gbc.setGbyPairList(Collections.singletonList(vePairList));
gbc.setDecorPairList(decorPairList);
gbc.setWithVarMap(withVarMap);
replaceCurrentScope(newScope);
diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/clause/GroupbyClause.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/clause/GroupbyClause.java
index 20dba52..425abd6 100644
--- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/clause/GroupbyClause.java
+++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/clause/GroupbyClause.java
@@ -34,7 +34,7 @@
public class GroupbyClause extends AbstractClause {
- private List<GbyVariableExpressionPair> gbyPairList;
+ private List<List<GbyVariableExpressionPair>> gbyPairList;
private List<GbyVariableExpressionPair> decorPairList;
private Map<Expression, VariableExpr> withVarMap;
private VariableExpr groupVar;
@@ -46,15 +46,16 @@
// Default constructor.
}
- public GroupbyClause(List<GbyVariableExpressionPair> gbyPairList, List<GbyVariableExpressionPair> decorPairList,
- Map<Expression, VariableExpr> withVarList, VariableExpr groupVarExpr,
- List<Pair<Expression, Identifier>> groupFieldList, boolean hashGroupByHint) {
+ public GroupbyClause(List<List<GbyVariableExpressionPair>> gbyPairList,
+ List<GbyVariableExpressionPair> decorPairList, Map<Expression, VariableExpr> withVarList,
+ VariableExpr groupVarExpr, List<Pair<Expression, Identifier>> groupFieldList, boolean hashGroupByHint) {
this(gbyPairList, decorPairList, withVarList, groupVarExpr, groupFieldList, hashGroupByHint, false);
}
- public GroupbyClause(List<GbyVariableExpressionPair> gbyPairList, List<GbyVariableExpressionPair> decorPairList,
- Map<Expression, VariableExpr> withVarList, VariableExpr groupVarExpr,
- List<Pair<Expression, Identifier>> groupFieldList, boolean hashGroupByHint, boolean groupAll) {
+ public GroupbyClause(List<List<GbyVariableExpressionPair>> gbyPairList,
+ List<GbyVariableExpressionPair> decorPairList, Map<Expression, VariableExpr> withVarList,
+ VariableExpr groupVarExpr, List<Pair<Expression, Identifier>> groupFieldList, boolean hashGroupByHint,
+ boolean groupAll) {
this.gbyPairList = gbyPairList;
this.decorPairList = decorPairList;
this.withVarMap = withVarList;
@@ -66,11 +67,11 @@
this.groupAll = groupAll;
}
- public List<GbyVariableExpressionPair> getGbyPairList() {
+ public List<List<GbyVariableExpressionPair>> getGbyPairList() {
return gbyPairList;
}
- public void setGbyPairList(List<GbyVariableExpressionPair> vePairList) {
+ public void setGbyPairList(List<List<GbyVariableExpressionPair>> vePairList) {
this.gbyPairList = vePairList;
}
diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/AbstractInlineUdfsVisitor.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/AbstractInlineUdfsVisitor.java
index f934d60..282be2f 100644
--- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/AbstractInlineUdfsVisitor.java
+++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/AbstractInlineUdfsVisitor.java
@@ -208,9 +208,15 @@
@Override
public Boolean visit(GroupbyClause gc, List<FunctionDecl> arg) throws CompilationException {
- Pair<Boolean, List<GbyVariableExpressionPair>> p1 = inlineUdfsInGbyPairList(gc.getGbyPairList(), arg);
- gc.setGbyPairList(p1.second);
- boolean changed = p1.first;
+ boolean changed = false;
+ List<List<GbyVariableExpressionPair>> gbyList = gc.getGbyPairList();
+ List<List<GbyVariableExpressionPair>> newGbyList = new ArrayList<>(gbyList.size());
+ for (List<GbyVariableExpressionPair> gbyPairList : gbyList) {
+ Pair<Boolean, List<GbyVariableExpressionPair>> p1 = inlineUdfsInGbyPairList(gbyPairList, arg);
+ newGbyList.add(p1.second);
+ changed |= p1.first;
+ }
+ gc.setGbyPairList(newGbyList);
if (gc.hasDecorList()) {
Pair<Boolean, List<GbyVariableExpressionPair>> p2 = inlineUdfsInGbyPairList(gc.getDecorPairList(), arg);
gc.setDecorPairList(p2.second);
diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/CloneAndSubstituteVariablesVisitor.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/CloneAndSubstituteVariablesVisitor.java
index 6efb2ce..8d37e41 100644
--- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/CloneAndSubstituteVariablesVisitor.java
+++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/CloneAndSubstituteVariablesVisitor.java
@@ -83,8 +83,11 @@
public Pair<ILangExpression, VariableSubstitutionEnvironment> visit(GroupbyClause gc,
VariableSubstitutionEnvironment env) throws CompilationException {
VariableSubstitutionEnvironment newSubs = env;
- List<GbyVariableExpressionPair> newGbyList =
- VariableCloneAndSubstitutionUtil.substInVarExprPair(context, gc.getGbyPairList(), newSubs, this);
+ List<List<GbyVariableExpressionPair>> gbyList = gc.getGbyPairList();
+ List<List<GbyVariableExpressionPair>> newGbyList = new ArrayList<>(gbyList.size());
+ for (List<GbyVariableExpressionPair> gbyPairList : gbyList) {
+ newGbyList.add(VariableCloneAndSubstitutionUtil.substInVarExprPair(context, gbyPairList, newSubs, this));
+ }
List<GbyVariableExpressionPair> newDecorList = gc.hasDecorList()
? VariableCloneAndSubstitutionUtil.substInVarExprPair(context, gc.getDecorPairList(), newSubs, this)
: new ArrayList<>();
diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/FormatPrintVisitor.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/FormatPrintVisitor.java
index 9c65c2a..4903e4b 100644
--- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/FormatPrintVisitor.java
+++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/FormatPrintVisitor.java
@@ -37,7 +37,6 @@
import org.apache.asterix.external.dataset.adapter.AdapterIdentifier;
import org.apache.asterix.lang.common.base.Expression;
import org.apache.asterix.lang.common.base.Literal;
-import org.apache.asterix.lang.common.clause.GroupbyClause;
import org.apache.asterix.lang.common.clause.LetClause;
import org.apache.asterix.lang.common.clause.LimitClause;
import org.apache.asterix.lang.common.clause.OrderbyClause;
@@ -110,7 +109,7 @@
import org.apache.hyracks.algebricks.common.utils.Pair;
import org.apache.hyracks.algebricks.core.algebra.expressions.IExpressionAnnotation;
-public class FormatPrintVisitor implements ILangVisitor<Void, Integer> {
+public abstract class FormatPrintVisitor implements ILangVisitor<Void, Integer> {
protected final static String COMMA = ",";
protected final static String SEMICOLON = ";";
@@ -311,39 +310,6 @@
}
@Override
- public Void visit(GroupbyClause gc, Integer step) throws CompilationException {
- if (gc.hasHashGroupByHint()) {
- out.println(skip(step) + "/* +hash */");
- }
- out.print(skip(step) + "group by ");
- printDelimitedGbyExpressions(gc.getGbyPairList(), step + 2);
- if (gc.hasDecorList()) {
- out.print(" decor ");
- printDelimitedGbyExpressions(gc.getDecorPairList(), step + 2);
- }
- if (gc.hasWithMap()) {
- out.print(" with ");
- Map<Expression, VariableExpr> withVarMap = gc.getWithVarMap();
- int index = 0;
- int size = withVarMap.size();
- for (Entry<Expression, VariableExpr> entry : withVarMap.entrySet()) {
- Expression key = entry.getKey();
- VariableExpr value = entry.getValue();
- key.accept(this, step + 2);
- if (!key.equals(value)) {
- out.print(" as ");
- value.accept(this, step + 2);
- }
- if (++index < size) {
- out.print(COMMA);
- }
- }
- }
- out.println();
- return null;
- }
-
- @Override
public Void visit(LimitClause lc, Integer step) throws CompilationException {
out.print(skip(step) + "limit ");
lc.getLimitExpr().accept(this, step + 1);
diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/GatherFunctionCallsVisitor.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/GatherFunctionCallsVisitor.java
index 680e15f..73f489c 100644
--- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/GatherFunctionCallsVisitor.java
+++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/GatherFunctionCallsVisitor.java
@@ -20,6 +20,7 @@
package org.apache.asterix.lang.common.visitor;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -75,8 +76,10 @@
@Override
public Void visit(GroupbyClause gc, Void arg) throws CompilationException {
- for (GbyVariableExpressionPair p : gc.getGbyPairList()) {
- p.getExpr().accept(this, arg);
+ for (List<GbyVariableExpressionPair> gbyPairList : gc.getGbyPairList()) {
+ for (GbyVariableExpressionPair p : gbyPairList) {
+ p.getExpr().accept(this, arg);
+ }
}
if (gc.hasDecorList()) {
for (GbyVariableExpressionPair p : gc.getDecorPairList()) {
diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/QueryPrintVisitor.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/QueryPrintVisitor.java
index 6afe4e0..a734918 100644
--- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/QueryPrintVisitor.java
+++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/QueryPrintVisitor.java
@@ -21,14 +21,12 @@
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.List;
-import java.util.Map.Entry;
import org.apache.asterix.common.config.DatasetConfig.DatasetType;
import org.apache.asterix.common.exceptions.CompilationException;
import org.apache.asterix.common.functions.FunctionSignature;
import org.apache.asterix.lang.common.base.Expression;
import org.apache.asterix.lang.common.base.Literal;
-import org.apache.asterix.lang.common.clause.GroupbyClause;
import org.apache.asterix.lang.common.clause.LetClause;
import org.apache.asterix.lang.common.clause.LimitClause;
import org.apache.asterix.lang.common.clause.OrderbyClause;
@@ -37,7 +35,6 @@
import org.apache.asterix.lang.common.expression.CallExpr;
import org.apache.asterix.lang.common.expression.FieldAccessor;
import org.apache.asterix.lang.common.expression.FieldBinding;
-import org.apache.asterix.lang.common.expression.GbyVariableExpressionPair;
import org.apache.asterix.lang.common.expression.IfExpr;
import org.apache.asterix.lang.common.expression.IndexAccessor;
import org.apache.asterix.lang.common.expression.ListConstructor;
@@ -247,42 +244,6 @@
}
@Override
- public Void visit(GroupbyClause gc, Integer step) throws CompilationException {
- out.println(skip(step) + "Groupby");
- for (GbyVariableExpressionPair pair : gc.getGbyPairList()) {
- if (pair.getVar() != null) {
- pair.getVar().accept(this, step + 1);
- out.println(skip(step + 1) + ":=");
- }
- pair.getExpr().accept(this, step + 1);
- }
- if (gc.hasDecorList()) {
- out.println(skip(step + 1) + "Decor");
- for (GbyVariableExpressionPair pair : gc.getDecorPairList()) {
- if (pair.getVar() != null) {
- pair.getVar().accept(this, step + 1);
- out.println(skip(step + 1) + ":=");
- }
- pair.getExpr().accept(this, step + 1);
- }
- }
- if (gc.hasWithMap()) {
- out.println(skip(step + 1) + "With");
- for (Entry<Expression, VariableExpr> entry : gc.getWithVarMap().entrySet()) {
- Expression key = entry.getKey();
- VariableExpr value = entry.getValue();
- key.accept(this, step + 1);
- if (!key.equals(value)) {
- out.println(skip(step + 1) + "AS");
- value.accept(this, step + 1);
- }
- }
- }
- out.println();
- return null;
- }
-
- @Override
public Void visit(LimitClause lc, Integer step) throws CompilationException {
out.println(skip(step) + "Limit");
lc.getLimitExpr().accept(this, step + 1);
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/parser/SqlppGroupingSetsParser.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/parser/SqlppGroupingSetsParser.java
new file mode 100644
index 0000000..f9c63de
--- /dev/null
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/parser/SqlppGroupingSetsParser.java
@@ -0,0 +1,391 @@
+/*
+ * 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.asterix.lang.sqlpp.parser;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import org.apache.asterix.common.exceptions.CompilationException;
+import org.apache.asterix.common.exceptions.ErrorCode;
+import org.apache.asterix.lang.common.base.Expression;
+import org.apache.asterix.lang.common.expression.GbyVariableExpressionPair;
+import org.apache.asterix.lang.common.expression.VariableExpr;
+import org.apache.asterix.lang.common.struct.VarIdentifier;
+import org.apache.asterix.lang.sqlpp.util.SqlppVariableUtil;
+import org.apache.hyracks.api.exceptions.SourceLocation;
+
+/**
+ * Parses GROUP BY's grouping elements into GROUPING SETS:
+ * <p>
+ * GROUP BY {0},{1},... => GROUP BY GROUPING SETS (...)
+ */
+public final class SqlppGroupingSetsParser {
+
+ // max number of grouping sets in a single group by clause
+ static final int GROUPING_SETS_LIMIT = 128;
+
+ private SourceLocation sourceLoc;
+
+ private List<GroupingElement> workList;
+
+ private List<GbyVariableExpressionPair> gbyPairWorkList;
+
+ private LinkedHashMap<Expression, GbyVariableExpressionPair> gbyPairWorkMap;
+
+ private List<List<List<GbyVariableExpressionPair>>> crossProductWorkLists;
+
+ public List<List<GbyVariableExpressionPair>> parse(List<GroupingElement> inList, SourceLocation sourceLoc)
+ throws CompilationException {
+ this.sourceLoc = sourceLoc;
+ List<GbyVariableExpressionPair> primitiveList = transformSimpleToPrimitive(inList);
+ if (primitiveList != null) {
+ return Collections.singletonList(primitiveList);
+ } else {
+ if (workList == null) {
+ workList = new ArrayList<>();
+ } else {
+ workList.clear();
+ }
+ eliminateComplexGroupingSets(inList, workList, false);
+ return crossProductGroupingSets(workList);
+ }
+ }
+
+ private List<GbyVariableExpressionPair> transformSimpleToPrimitive(List<GroupingElement> inList)
+ throws CompilationException {
+ if (findComplexElement(inList) != null) {
+ return null;
+ }
+ if (gbyPairWorkList == null) {
+ gbyPairWorkList = new ArrayList<>();
+ } else {
+ gbyPairWorkList.clear();
+ }
+ concatOrdinary(inList, inList.size(), gbyPairWorkList);
+ return removeDuplicates(gbyPairWorkList);
+ }
+
+ private void eliminateComplexGroupingSets(List<? extends GroupingElement> inList, List<GroupingElement> outList,
+ boolean isInsideGroupingSets) throws CompilationException {
+ for (GroupingElement element : inList) {
+ switch (element.getKind()) {
+ case GROUPING_SET:
+ outList.add(element);
+ break;
+ case ROLLUP_CUBE:
+ RollupCube rollupCube = (RollupCube) element;
+ List<GroupingSet> rollupCubeTransform = expandRollupCube(rollupCube);
+ if (isInsideGroupingSets) {
+ outList.addAll(rollupCubeTransform);
+ } else { // top level
+ outList.add(new GroupingSets(rollupCubeTransform));
+ }
+ break;
+ case GROUPING_SETS:
+ GroupingSets groupingSets = (GroupingSets) element;
+ List<? extends GroupingElement> groupingSetsItems = groupingSets.getItems();
+ List<GroupingElement> groupingSetsTransform = new ArrayList<>(groupingSetsItems.size());
+ eliminateComplexGroupingSets(groupingSetsItems, groupingSetsTransform, true);
+ if (isInsideGroupingSets) {
+ outList.addAll(groupingSetsTransform);
+ } else { // top level
+ outList.add(new GroupingSets(groupingSetsTransform));
+ }
+ break;
+ default:
+ throw new IllegalStateException(String.valueOf(element.getKind()));
+ }
+ }
+ }
+
+ private List<GroupingSet> expandRollupCube(RollupCube rollupCube) throws CompilationException {
+ List<GroupingSet> rollupCubeItems = rollupCube.getItems();
+ return rollupCube.isCube() ? expandCube(rollupCubeItems) : expandRollup(rollupCubeItems);
+ }
+
+ private List<GroupingSet> expandRollup(List<GroupingSet> items) throws CompilationException {
+ int n = items.size();
+ int rollupSize = n + 1;
+ checkGroupingSetsLimit(rollupSize);
+ List<GroupingSet> result = new ArrayList<>(rollupSize);
+ for (int i = n; i > 0; i--) {
+ List<GbyVariableExpressionPair> groupingSetItems = new ArrayList<>();
+ concatOrdinary(items, i, groupingSetItems);
+ result.add(new GroupingSet(groupingSetItems));
+ }
+ result.add(GroupingSet.EMPTY);
+ return result;
+ }
+
+ private List<GroupingSet> expandCube(List<GroupingSet> items) throws CompilationException {
+ int n = items.size();
+ int cubeSize = 1 << n;
+ checkGroupingSetsLimit(cubeSize);
+ List<GroupingSet> result = new ArrayList<>(cubeSize);
+ List<GroupingSet> permutation = new ArrayList<>(n);
+ for (long v = cubeSize - 1; v > 0; v--) {
+ permutation.clear();
+ for (int i = n - 1; i >= 0; i--) {
+ if ((v & (1L << i)) != 0) {
+ permutation.add(items.get(n - i - 1));
+ }
+ }
+ List<GbyVariableExpressionPair> groupingSetItems = new ArrayList<>();
+ concatOrdinary(permutation, permutation.size(), groupingSetItems);
+ result.add(new GroupingSet(groupingSetItems));
+ }
+ result.add(GroupingSet.EMPTY);
+ return result;
+ }
+
+ private List<List<GbyVariableExpressionPair>> crossProductGroupingSets(List<GroupingElement> inList)
+ throws CompilationException {
+ if (crossProductWorkLists == null) {
+ crossProductWorkLists = Arrays.asList(new ArrayList<>(), new ArrayList<>());
+ } else {
+ for (List<List<GbyVariableExpressionPair>> list : crossProductWorkLists) {
+ list.clear();
+ }
+ }
+
+ int workInListIdx = 0;
+ for (int inListPos = 0, inListSize = inList.size(); inListPos < inListSize; inListPos++) {
+ List<List<GbyVariableExpressionPair>> workInList = crossProductWorkLists.get(workInListIdx);
+ int workOutListIdx = 1 - workInListIdx;
+ List<List<GbyVariableExpressionPair>> workOutList = crossProductWorkLists.get(workOutListIdx);
+ workOutList.clear();
+
+ GroupingElement element = inList.get(inListPos);
+ GroupingSet groupingSet = null;
+ GroupingSets groupingSets = null;
+ switch (element.getKind()) {
+ case GROUPING_SET:
+ groupingSet = (GroupingSet) element;
+ break;
+ case GROUPING_SETS:
+ groupingSets = (GroupingSets) element;
+ break;
+ default:
+ throw new IllegalStateException(String.valueOf(element.getKind()));
+ }
+
+ if (inListPos == 0) {
+ if (groupingSet != null) {
+ workOutList.add(groupingSet.getItems());
+ } else {
+ for (GroupingElement item : groupingSets.getItems()) {
+ workOutList.add(((GroupingSet) item).getItems());
+ }
+ }
+ } else {
+ for (List<GbyVariableExpressionPair> workGroupingSet : workInList) {
+ if (groupingSet != null) {
+ workOutList.add(concatOrdinary(workGroupingSet, groupingSet.getItems()));
+ } else {
+ for (GroupingElement groupingElement : groupingSets.getItems()) {
+ workOutList
+ .add(concatOrdinary(workGroupingSet, ((GroupingSet) groupingElement).getItems()));
+ }
+ }
+ }
+ }
+
+ checkGroupingSetsLimit(workOutList.size());
+
+ workInListIdx = workOutListIdx;
+ }
+
+ List<List<GbyVariableExpressionPair>> crossProductList = crossProductWorkLists.get(workInListIdx);
+
+ // check for unexpected aliases
+ Map<Expression, GbyVariableExpressionPair> gbyPairMap = new HashMap<>();
+ List<List<GbyVariableExpressionPair>> result = new ArrayList<>(crossProductList.size());
+ for (List<GbyVariableExpressionPair> groupingSet : crossProductList) {
+ List<GbyVariableExpressionPair> gsNoDups = removeDuplicates(groupingSet);
+ for (int i = 0, n = gsNoDups.size(); i < n; i++) {
+ GbyVariableExpressionPair gbyPair = gsNoDups.get(i);
+ GbyVariableExpressionPair existingPair = gbyPairMap.get(gbyPair.getExpr());
+ if (existingPair == null) {
+ gbyPairMap.put(gbyPair.getExpr(), gbyPair);
+ } else if (!Objects.equals(existingPair.getVar(), gbyPair.getVar())) {
+ if (gbyPair.getVar() != null) {
+ // existing pair's alias was different or null
+ throw new CompilationException(ErrorCode.COMPILATION_UNEXPECTED_ALIAS,
+ gbyPair.getVar().getSourceLocation(),
+ SqlppVariableUtil.toUserDefinedName(gbyPair.getVar().getVar().getValue()));
+ } else {
+ // this pair's alias is null, but the existing one was not null -> use the existing one
+ VariableExpr newVar = new VariableExpr(new VarIdentifier(existingPair.getVar().getVar()));
+ newVar.setSourceLocation(existingPair.getVar().getSourceLocation());
+ gbyPair = new GbyVariableExpressionPair(newVar, gbyPair.getExpr());
+ gsNoDups.set(i, gbyPair);
+ }
+ }
+ }
+ result.add(gsNoDups);
+ }
+ return result;
+ }
+
+ private List<GbyVariableExpressionPair> removeDuplicates(List<GbyVariableExpressionPair> inList)
+ throws CompilationException {
+ if (gbyPairWorkMap == null) {
+ gbyPairWorkMap = new LinkedHashMap<>();
+ } else {
+ gbyPairWorkMap.clear();
+ }
+ for (GbyVariableExpressionPair gbyPair : inList) {
+ GbyVariableExpressionPair existingPair = gbyPairWorkMap.get(gbyPair.getExpr());
+ if (existingPair == null) {
+ gbyPairWorkMap.put(gbyPair.getExpr(), gbyPair);
+ } else if (!Objects.equals(existingPair.getVar(), gbyPair.getVar())) {
+ if (gbyPair.getVar() != null) {
+ // existing pair's alias was different or null
+ throw new CompilationException(ErrorCode.COMPILATION_UNEXPECTED_ALIAS,
+ gbyPair.getVar().getSourceLocation(),
+ SqlppVariableUtil.toUserDefinedName(gbyPair.getVar().getVar().getValue()));
+ }
+ // otherwise this pair's alias is null, but the existing one was not null -> use the existing one
+ }
+ }
+ return new ArrayList<>(gbyPairWorkMap.values());
+ }
+
+ private List<GbyVariableExpressionPair> concatOrdinary(List<GbyVariableExpressionPair> groupingSet1,
+ List<GbyVariableExpressionPair> groupingSet2) {
+ List<GbyVariableExpressionPair> outList = new ArrayList<>(groupingSet1.size() + groupingSet2.size());
+ outList.addAll(groupingSet1);
+ outList.addAll(groupingSet2);
+ return outList;
+ }
+
+ private void concatOrdinary(List<? extends GroupingElement> inList, int endIdx,
+ List<GbyVariableExpressionPair> outList) {
+ for (int i = 0; i < endIdx; i++) {
+ GroupingElement element = inList.get(i);
+ if (element.getKind() != GroupingElement.Kind.GROUPING_SET) {
+ throw new IllegalStateException(String.valueOf(element.getKind()));
+ }
+ outList.addAll(((GroupingSet) element).getItems());
+ }
+ }
+
+ private static GroupingElement findComplexElement(List<GroupingElement> inList) {
+ for (GroupingElement element : inList) {
+ switch (element.getKind()) {
+ case ROLLUP_CUBE:
+ case GROUPING_SETS:
+ return element;
+ case GROUPING_SET:
+ break;
+ default:
+ throw new IllegalStateException(String.valueOf(element.getKind()));
+ }
+ }
+ return null;
+ }
+
+ private void checkGroupingSetsLimit(int n) throws CompilationException {
+ if (n > GROUPING_SETS_LIMIT) {
+ throw new CompilationException(ErrorCode.COMPILATION_GROUPING_SETS_OVERFLOW, sourceLoc, String.valueOf(n),
+ String.valueOf(GROUPING_SETS_LIMIT));
+ }
+ }
+
+ public abstract static class GroupingElement {
+ public enum Kind {
+ GROUPING_SET,
+ GROUPING_SETS,
+ ROLLUP_CUBE,
+ }
+
+ public abstract Kind getKind();
+ }
+
+ // ordinary grouping set, empty grouping set
+ public static final class GroupingSet extends GroupingElement {
+
+ public static final GroupingSet EMPTY = new GroupingSet(Collections.emptyList());
+
+ private final List<GbyVariableExpressionPair> items;
+
+ public GroupingSet(List<GbyVariableExpressionPair> items) {
+ this.items = items;
+ }
+
+ @Override
+ public Kind getKind() {
+ return Kind.GROUPING_SET;
+ }
+
+ public List<GbyVariableExpressionPair> getItems() {
+ return items;
+ }
+ }
+
+ public static final class RollupCube extends GroupingElement {
+
+ private final List<GroupingSet> items;
+
+ private final boolean isCube;
+
+ public RollupCube(List<GroupingSet> items, boolean isCube) {
+ this.items = items;
+ this.isCube = isCube;
+ }
+
+ @Override
+ public Kind getKind() {
+ return Kind.ROLLUP_CUBE;
+ }
+
+ public List<GroupingSet> getItems() {
+ return items;
+ }
+
+ public boolean isCube() {
+ return isCube;
+ }
+ }
+
+ public static final class GroupingSets extends GroupingElement {
+
+ private final List<? extends GroupingElement> items;
+
+ public GroupingSets(List<? extends GroupingElement> items) {
+ this.items = items;
+ }
+
+ @Override
+ public Kind getKind() {
+ return Kind.GROUPING_SETS;
+ }
+
+ public List<? extends GroupingElement> getItems() {
+ return items;
+ }
+ }
+}
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppFunctionBodyRewriter.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppFunctionBodyRewriter.java
index 1e16b70..1085e03 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppFunctionBodyRewriter.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppFunctionBodyRewriter.java
@@ -51,15 +51,19 @@
// Group-by core rewrites
rewriteGroupBys();
- // Window expression core rewrites.
- rewriteWindowExpressions();
-
// Rewrites set operations.
rewriteSetOperations();
// Inlines column aliases.
inlineColumnAlias();
+ // Window expression core rewrites.
+ rewriteWindowExpressions();
+
+ // Rewrites Group-By clauses with multiple grouping sets into UNION ALL
+ // Must run after rewriteSetOperations() and before variableCheckAndRewrite()
+ rewriteGroupingSets();
+
// Generate ids for variables (considering scopes) and replace global variable access with the dataset function.
variableCheckAndRewrite();
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppQueryRewriter.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppQueryRewriter.java
index 72de614..e52d604 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppQueryRewriter.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppQueryRewriter.java
@@ -66,6 +66,7 @@
import org.apache.asterix.lang.sqlpp.rewrites.visitor.SqlppBuiltinFunctionRewriteVisitor;
import org.apache.asterix.lang.sqlpp.rewrites.visitor.SqlppGroupByAggregationSugarVisitor;
import org.apache.asterix.lang.sqlpp.rewrites.visitor.SqlppGroupByVisitor;
+import org.apache.asterix.lang.sqlpp.rewrites.visitor.SqlppGroupingSetsVisitor;
import org.apache.asterix.lang.sqlpp.rewrites.visitor.SqlppInlineUdfsVisitor;
import org.apache.asterix.lang.sqlpp.rewrites.visitor.SqlppListInputFunctionRewriteVisitor;
import org.apache.asterix.lang.sqlpp.rewrites.visitor.SqlppWindowAggregationSugarVisitor;
@@ -144,6 +145,10 @@
// Window expression core rewrites.
rewriteWindowExpressions();
+ // Rewrites Group-By clauses with multiple grouping sets into UNION ALL
+ // Must run after rewriteSetOperations() and before variableCheckAndRewrite()
+ rewriteGroupingSets();
+
// Generate ids for variables (considering scopes) and replace global variable access with the dataset function.
variableCheckAndRewrite();
@@ -242,6 +247,11 @@
rewriteTopExpr(groupByVisitor, null);
}
+ protected void rewriteGroupingSets() throws CompilationException {
+ SqlppGroupingSetsVisitor groupingSetsVisitor = new SqlppGroupingSetsVisitor(context);
+ rewriteTopExpr(groupingSetsVisitor, null);
+ }
+
protected void rewriteWindowExpressions() throws CompilationException {
// Create window variables and extract aggregation inputs into LET clauses
SqlppWindowRewriteVisitor windowVisitor = new SqlppWindowRewriteVisitor(context);
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/AbstractSqlppExpressionExtractionVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/AbstractSqlppExpressionExtractionVisitor.java
index 57bfad5..fc2e25b 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/AbstractSqlppExpressionExtractionVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/AbstractSqlppExpressionExtractionVisitor.java
@@ -39,13 +39,12 @@
/**
* Base class for visitors that extract expressions into LET clauses.
- * Subclasses should call {@link #extractExpressions(List, int)} to perform the extraction.
*/
abstract class AbstractSqlppExpressionExtractionVisitor extends AbstractSqlppSimpleExpressionVisitor {
protected final LangRewritingContext context;
- private final Deque<List<Pair<Expression, VarIdentifier>>> stack = new ArrayDeque<>();
+ protected final Deque<StackElement> stack = new ArrayDeque<>();
AbstractSqlppExpressionExtractionVisitor(LangRewritingContext context) {
this.context = context;
@@ -53,30 +52,31 @@
@Override
public Expression visit(SelectBlock selectBlock, ILangExpression arg) throws CompilationException {
- List<Pair<Expression, VarIdentifier>> extractionList = new ArrayList<>();
- stack.push(extractionList);
+ StackElement stackElement = new StackElement(selectBlock);
+ stack.push(stackElement);
if (selectBlock.hasFromClause()) {
FromClause clause = selectBlock.getFromClause();
clause.accept(this, arg);
- if (!extractionList.isEmpty()) {
- handleUnsupportedClause(clause, extractionList);
+ if (!stackElement.extractionList.isEmpty()) {
+ handleUnsupportedClause(clause);
}
}
List<AbstractClause> letWhereList = selectBlock.getLetWhereList();
if (!letWhereList.isEmpty()) {
- visitLetWhereClauses(letWhereList, extractionList, arg);
+ visitLetWhereClauses(letWhereList, stackElement.extractionList, arg);
}
if (selectBlock.hasGroupbyClause()) {
selectBlock.getGroupbyClause().accept(this, arg);
- introduceLetClauses(extractionList, letWhereList);
+ introduceLetClauses(stackElement.extractionList, letWhereList);
}
List<AbstractClause> letHavingListAfterGby = selectBlock.getLetHavingListAfterGroupby();
if (!letHavingListAfterGby.isEmpty()) {
- visitLetWhereClauses(letHavingListAfterGby, extractionList, arg);
+ visitLetWhereClauses(letHavingListAfterGby, stackElement.extractionList, arg);
}
selectBlock.getSelectClause().accept(this, arg);
- introduceLetClauses(extractionList, selectBlock.hasGroupbyClause() ? letHavingListAfterGby : letWhereList);
+ introduceLetClauses(stackElement.extractionList,
+ selectBlock.hasGroupbyClause() ? letHavingListAfterGby : letWhereList);
stack.pop();
return null;
@@ -108,32 +108,27 @@
fromBindingList.clear();
}
- List<Expression> extractExpressions(List<Expression> exprList, int limit) {
- List<Pair<Expression, VarIdentifier>> outLetList = stack.peek();
- if (outLetList == null) {
- return null;
+ abstract void handleUnsupportedClause(FromClause clause) throws CompilationException;
+
+ protected final class StackElement {
+
+ private final SelectBlock selectBlock;
+
+ private final List<Pair<Expression, VarIdentifier>> extractionList;
+
+ private StackElement(SelectBlock selectBlock) {
+ this.selectBlock = selectBlock;
+ this.extractionList = new ArrayList<>();
}
- int n = exprList.size();
- List<Expression> newExprList = new ArrayList<>(n);
- for (int i = 0; i < n; i++) {
- Expression expr = exprList.get(i);
- Expression newExpr;
- if (i < limit && isExtractableExpression(expr)) {
- VarIdentifier v = context.newVariable();
- VariableExpr vExpr = new VariableExpr(v);
- vExpr.setSourceLocation(expr.getSourceLocation());
- outLetList.add(new Pair<>(expr, v));
- newExpr = vExpr;
- } else {
- newExpr = expr;
- }
- newExprList.add(newExpr);
+
+ public SelectBlock getSelectBlock() {
+ return selectBlock;
}
- return newExprList;
+
+ public VarIdentifier addPendingLetClause(Expression expression) {
+ VarIdentifier letVar = context.newVariable();
+ extractionList.add(new Pair<>(expression, letVar));
+ return letVar;
+ }
}
-
- abstract boolean isExtractableExpression(Expression expr);
-
- abstract void handleUnsupportedClause(FromClause clause, List<Pair<Expression, VarIdentifier>> extractionList)
- throws CompilationException;
}
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/GenerateColumnNameVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/GenerateColumnNameVisitor.java
index 6d2a0eb..3067641 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/GenerateColumnNameVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/GenerateColumnNameVisitor.java
@@ -19,7 +19,10 @@
package org.apache.asterix.lang.sqlpp.rewrites.visitor;
+import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
import java.util.Set;
import org.apache.asterix.common.exceptions.CompilationException;
@@ -47,6 +50,8 @@
*/
public class GenerateColumnNameVisitor extends AbstractSqlppExpressionScopingVisitor {
+ private final Map<Expression, VarIdentifier> gbyKeyExprMap = new HashMap<>();
+
private final Set<VarIdentifier> gbyKeyVars = new HashSet<>();
public GenerateColumnNameVisitor(LangRewritingContext context) {
@@ -70,24 +75,34 @@
@Override
public Expression visit(GroupbyClause groupbyClause, ILangExpression arg) throws CompilationException {
+ //TODO:FIXME:GBY:REVISIT
+ gbyKeyExprMap.clear();
gbyKeyVars.clear();
- for (GbyVariableExpressionPair gbyKeyPair : groupbyClause.getGbyPairList()) {
- if (gbyKeyPair.getVar() == null) {
- Expression gbyKeyExpr = gbyKeyPair.getExpr();
- SourceLocation sourceLoc = gbyKeyExpr.getSourceLocation();
- VariableExpr varExpr;
- try {
- varExpr = ExpressionToVariableUtil.getGeneratedVariable(gbyKeyExpr, false);
- } catch (ParseException e) {
- throw new CompilationException(ErrorCode.PARSE_ERROR, e, sourceLoc);
+ for (List<GbyVariableExpressionPair> gbyPairList : groupbyClause.getGbyPairList()) {
+ for (GbyVariableExpressionPair gbyKeyPair : gbyPairList) {
+ if (gbyKeyPair.getVar() == null) {
+ Expression gbyKeyExpr = gbyKeyPair.getExpr();
+ SourceLocation sourceLoc = gbyKeyExpr.getSourceLocation();
+ VariableExpr varExpr;
+ VarIdentifier varId = gbyKeyExprMap.get(gbyKeyExpr);
+ if (varId == null) {
+ try {
+ varExpr = ExpressionToVariableUtil.getGeneratedVariable(gbyKeyExpr, false);
+ } catch (ParseException e) {
+ throw new CompilationException(ErrorCode.PARSE_ERROR, e, sourceLoc);
+ }
+ if (varExpr == null || gbyKeyVars.contains(varExpr.getVar())) {
+ varExpr = new VariableExpr(context.newVariable());
+ }
+ gbyKeyExprMap.put(gbyKeyExpr, varExpr.getVar());
+ } else {
+ varExpr = new VariableExpr(varId);
+ }
+ varExpr.setSourceLocation(sourceLoc);
+ gbyKeyPair.setVar(varExpr);
}
- if (varExpr == null || gbyKeyVars.contains(varExpr.getVar())) {
- varExpr = new VariableExpr(context.newVariable());
- }
- varExpr.setSourceLocation(sourceLoc);
- gbyKeyPair.setVar(varExpr);
+ gbyKeyVars.add(gbyKeyPair.getVar().getVar());
}
- gbyKeyVars.add(gbyKeyPair.getVar().getVar());
}
return super.visit(groupbyClause, arg);
}
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SetOperationVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SetOperationVisitor.java
index dd02dbe..fe86cfc 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SetOperationVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SetOperationVisitor.java
@@ -87,16 +87,7 @@
// Wraps the set operation part with a subquery.
SelectExpression nestedSelectExpression = new SelectExpression(null, selectSetOperation, null, null, true);
nestedSelectExpression.setSourceLocation(sourceLoc);
- VariableExpr newBindingVar = new VariableExpr(context.newVariable()); // Binding variable for the subquery.
- newBindingVar.setSourceLocation(sourceLoc);
- FromTerm newFromTerm = new FromTerm(nestedSelectExpression, newBindingVar, null, null);
- newFromTerm.setSourceLocation(sourceLoc);
- FromClause newFromClause = new FromClause(new ArrayList<>(Collections.singletonList(newFromTerm)));
- newFromClause.setSourceLocation(sourceLoc);
- SelectClause selectClause = new SelectClause(new SelectElement(newBindingVar), null, false);
- selectClause.setSourceLocation(sourceLoc);
- SelectBlock selectBlock = new SelectBlock(selectClause, newFromClause, null, null, null);
- selectBlock.setSourceLocation(sourceLoc);
+ SelectBlock selectBlock = createSelectBlock(nestedSelectExpression, context);
SelectSetOperation newSelectSetOperation =
new SelectSetOperation(new SetOperationInput(selectBlock, null), null);
newSelectSetOperation.setSourceLocation(sourceLoc);
@@ -107,4 +98,18 @@
return super.visit(newSelectExpression, arg);
}
+ static SelectBlock createSelectBlock(Expression inputExpr, LangRewritingContext context) {
+ SourceLocation sourceLoc = inputExpr.getSourceLocation();
+ VariableExpr newBindingVar = new VariableExpr(context.newVariable()); // Binding variable for the subquery.
+ newBindingVar.setSourceLocation(sourceLoc);
+ FromTerm newFromTerm = new FromTerm(inputExpr, newBindingVar, null, null);
+ newFromTerm.setSourceLocation(sourceLoc);
+ FromClause newFromClause = new FromClause(new ArrayList<>(Collections.singletonList(newFromTerm)));
+ newFromClause.setSourceLocation(sourceLoc);
+ SelectClause selectClause = new SelectClause(new SelectElement(newBindingVar), null, false);
+ selectClause.setSourceLocation(sourceLoc);
+ SelectBlock selectBlock = new SelectBlock(selectClause, newFromClause, null, null, null);
+ selectBlock.setSourceLocation(sourceLoc);
+ return selectBlock;
+ }
}
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppGroupByVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppGroupByVisitor.java
index c21e989..d337de9 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppGroupByVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppGroupByVisitor.java
@@ -23,34 +23,38 @@
import java.util.List;
import org.apache.asterix.common.exceptions.CompilationException;
+import org.apache.asterix.common.exceptions.ErrorCode;
+import org.apache.asterix.common.functions.FunctionSignature;
import org.apache.asterix.lang.common.base.Expression;
import org.apache.asterix.lang.common.base.ILangExpression;
import org.apache.asterix.lang.common.clause.GroupbyClause;
+import org.apache.asterix.lang.common.expression.CallExpr;
import org.apache.asterix.lang.common.expression.GbyVariableExpressionPair;
import org.apache.asterix.lang.common.expression.VariableExpr;
import org.apache.asterix.lang.common.rewrites.LangRewritingContext;
import org.apache.asterix.lang.common.struct.Identifier;
+import org.apache.asterix.lang.common.struct.VarIdentifier;
+import org.apache.asterix.lang.sqlpp.clause.FromClause;
import org.apache.asterix.lang.sqlpp.clause.SelectBlock;
import org.apache.asterix.lang.sqlpp.clause.SelectClause;
import org.apache.asterix.lang.sqlpp.util.SqlppVariableUtil;
import org.apache.asterix.lang.sqlpp.visitor.CheckSql92AggregateVisitor;
-import org.apache.asterix.lang.sqlpp.visitor.base.AbstractSqlppSimpleExpressionVisitor;
+import org.apache.asterix.om.functions.BuiltinFunctions;
import org.apache.hyracks.algebricks.common.utils.Pair;
/**
* A pre-processor that
* <ul>
- * <li>adds the group variable as well as its group field
- * list into the AST. e.g., GROUP AS eis(e AS e, i AS i, s AS s)</li>
- * <li>adds group by clause if select block contains SQL-92 agregate function but there's no group by clause</li>
+ * <li>adds the group variable as well as its group field
+ * list into the AST. e.g., GROUP AS eis(e AS e, i AS i, s AS s)</li>
+ * <li>adds group by clause if select block contains SQL-92 aggregate function but there's no group by clause</li>
+ * <li>extracts GROUPING(...) operations into LET clauses</li>
* </ul>
*/
-public class SqlppGroupByVisitor extends AbstractSqlppSimpleExpressionVisitor {
-
- private final LangRewritingContext context;
+public class SqlppGroupByVisitor extends AbstractSqlppExpressionExtractionVisitor {
public SqlppGroupByVisitor(LangRewritingContext context) {
- this.context = context;
+ super(context);
}
@Override
@@ -92,7 +96,7 @@
private void rewriteSelectWithoutGroupBy(SelectBlock selectBlock) throws CompilationException {
if (hasSql92Aggregate(selectBlock)) {
// Adds an implicit group-by clause for SQL-92 global aggregate.
- List<GbyVariableExpressionPair> gbyPairList = new ArrayList<>();
+ List<List<GbyVariableExpressionPair>> gbyPairList = new ArrayList<>();
List<GbyVariableExpressionPair> decorPairList = new ArrayList<>();
VariableExpr groupVar = new VariableExpr(context.newVariable());
groupVar.setSourceLocation(selectBlock.getSourceLocation());
@@ -132,4 +136,37 @@
SqlppVariableUtil.addToFieldVariableList(varExpr, outFieldList);
}
}
+
+ // AbstractSqlppExpressionExtractionVisitor
+
+ @Override
+ public Expression visit(CallExpr callExpr, ILangExpression arg) throws CompilationException {
+ Expression resultExpr = super.visit(callExpr, arg);
+ if (isGroupingOperation(resultExpr)) {
+ StackElement stackElement = stack.peek();
+ if (stackElement != null && stackElement.getSelectBlock().hasGroupbyClause()) {
+ VarIdentifier v = stackElement.addPendingLetClause(resultExpr);
+ VariableExpr vExpr = new VariableExpr(v);
+ vExpr.setSourceLocation(callExpr.getSourceLocation());
+ resultExpr = vExpr;
+ }
+ }
+ return resultExpr;
+ }
+
+ static boolean isGroupingOperation(Expression expr) {
+ if (expr.getKind() == Expression.Kind.CALL_EXPRESSION) {
+ CallExpr callExpr = (CallExpr) expr;
+ FunctionSignature fs = callExpr.getFunctionSignature();
+ return BuiltinFunctions.GROUPING.getName().equalsIgnoreCase(fs.getName());
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ void handleUnsupportedClause(FromClause clause) throws CompilationException {
+ throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_USE_OF_IDENTIFIER, clause.getSourceLocation(),
+ BuiltinFunctions.GROUPING.getName());
+ }
}
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppGroupingSetsVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppGroupingSetsVisitor.java
new file mode 100644
index 0000000..4e517da
--- /dev/null
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppGroupingSetsVisitor.java
@@ -0,0 +1,258 @@
+/*
+ * 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.asterix.lang.sqlpp.rewrites.visitor;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.asterix.common.exceptions.CompilationException;
+import org.apache.asterix.common.exceptions.ErrorCode;
+import org.apache.asterix.lang.common.base.Clause;
+import org.apache.asterix.lang.common.base.Expression;
+import org.apache.asterix.lang.common.base.ILangExpression;
+import org.apache.asterix.lang.common.clause.GroupbyClause;
+import org.apache.asterix.lang.common.clause.LetClause;
+import org.apache.asterix.lang.common.expression.CallExpr;
+import org.apache.asterix.lang.common.expression.GbyVariableExpressionPair;
+import org.apache.asterix.lang.common.expression.LiteralExpr;
+import org.apache.asterix.lang.common.expression.VariableExpr;
+import org.apache.asterix.lang.common.literal.LongIntegerLiteral;
+import org.apache.asterix.lang.common.literal.NullLiteral;
+import org.apache.asterix.lang.common.rewrites.LangRewritingContext;
+import org.apache.asterix.lang.sqlpp.clause.SelectBlock;
+import org.apache.asterix.lang.sqlpp.clause.SelectSetOperation;
+import org.apache.asterix.lang.sqlpp.expression.SelectExpression;
+import org.apache.asterix.lang.sqlpp.optype.SetOpType;
+import org.apache.asterix.lang.sqlpp.struct.SetOperationInput;
+import org.apache.asterix.lang.sqlpp.struct.SetOperationRight;
+import org.apache.asterix.lang.sqlpp.util.SqlppRewriteUtil;
+import org.apache.asterix.lang.sqlpp.visitor.base.AbstractSqlppSimpleExpressionVisitor;
+import org.apache.asterix.om.functions.BuiltinFunctions;
+
+/**
+ * Rewrites GROUP BY clauses with multiple grouping sets into UNION ALL.
+ * Also rewrites valid GROUPING(...) operations into constants.
+ */
+public final class SqlppGroupingSetsVisitor extends AbstractSqlppSimpleExpressionVisitor {
+
+ private final LangRewritingContext context;
+
+ private final List<List<GbyVariableExpressionPair>> tmpGroupingSets = new ArrayList<>(1);
+
+ private final List<GbyVariableExpressionPair> tmpDecorPairList = new ArrayList<>();
+
+ private final Set<VariableExpr> tmpAllGroupingSetsVars = new LinkedHashSet<>();
+
+ private final Set<VariableExpr> tmpCurrentGroupingSetVars = new LinkedHashSet<>();
+
+ public SqlppGroupingSetsVisitor(LangRewritingContext context) {
+ this.context = context;
+ }
+
+ @Override
+ public Expression visit(SelectSetOperation setOp, ILangExpression arg) throws CompilationException {
+ super.visit(setOp, arg);
+
+ SetOperationInput setOpInputLeft = setOp.getLeftInput();
+ SelectBlock selectBlockLeft = setOpInputLeft.getSelectBlock();
+ if (selectBlockLeft != null && selectBlockLeft.hasGroupbyClause()) {
+ setOpInputLeft.setSelectBlock(rewriteSelectBlock(selectBlockLeft));
+ }
+ if (setOp.hasRightInputs()) {
+ for (SetOperationRight setOpRight : setOp.getRightInputs()) {
+ SetOperationInput setOpInputRight = setOpRight.getSetOperationRightInput();
+ SelectBlock selectBlockRight = setOpInputRight.getSelectBlock();
+ if (selectBlockRight != null && selectBlockRight.hasGroupbyClause()) {
+ setOpInputRight.setSelectBlock(rewriteSelectBlock(selectBlockRight));
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private SelectBlock rewriteSelectBlock(SelectBlock selectBlock) throws CompilationException {
+ return selectBlock.getGroupbyClause().getGbyPairList().size() <= 1 ? rewriteZeroOrOneGroupingSet(selectBlock)
+ : rewriteMultipleGroupingSets(selectBlock);
+ }
+
+ private SelectBlock rewriteZeroOrOneGroupingSet(SelectBlock selectBlock) throws CompilationException {
+ // no UNION ALL, we only need to rewrite GROUPING(..) operations
+ GroupbyClause gby = selectBlock.getGroupbyClause();
+ List<List<GbyVariableExpressionPair>> groupingSets = gby.getGbyPairList();
+ if (groupingSets.size() > 1) {
+ throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, gby.getSourceLocation(), "");
+ }
+ tmpAllGroupingSetsVars.clear();
+ getAllGroupingSetsVars(groupingSets, tmpAllGroupingSetsVars);
+ rewriteGroupingOperations(selectBlock, tmpAllGroupingSetsVars, tmpAllGroupingSetsVars);
+ return selectBlock;
+ }
+
+ private SelectBlock rewriteMultipleGroupingSets(SelectBlock selectBlock) throws CompilationException {
+ GroupbyClause gby = selectBlock.getGroupbyClause();
+ List<List<GbyVariableExpressionPair>> groupingSets = gby.getGbyPairList();
+ if (groupingSets.size() <= 1 || !gby.getDecorPairList().isEmpty()) {
+ throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, gby.getSourceLocation(), "");
+ }
+
+ tmpAllGroupingSetsVars.clear();
+ getAllGroupingSetsVars(groupingSets, tmpAllGroupingSetsVars);
+
+ int nGroupingSets = groupingSets.size();
+ List<SetOperationRight> newSetOpRightInputs = new ArrayList<>(nGroupingSets - 1);
+
+ // process 2nd and following grouping sets
+
+ for (int i = 1; i < nGroupingSets; i++) {
+ List<GbyVariableExpressionPair> groupingSet = groupingSets.get(i);
+
+ tmpCurrentGroupingSetVars.clear();
+ getGroupingSetVars(groupingSet, tmpCurrentGroupingSetVars);
+
+ tmpGroupingSets.clear();
+ tmpGroupingSets.add(groupingSet);
+ gby.setGbyPairList(tmpGroupingSets); // will be cloned by deepCopy() below
+
+ tmpDecorPairList.clear();
+ computeDecorVars(tmpAllGroupingSetsVars, tmpCurrentGroupingSetVars, tmpDecorPairList);
+ gby.setDecorPairList(tmpDecorPairList); // will be cloned by deepCopy() below
+
+ SelectBlock newSelectBlock = (SelectBlock) SqlppRewriteUtil.deepCopy(selectBlock);
+
+ rewriteGroupingOperations(newSelectBlock, tmpAllGroupingSetsVars, tmpCurrentGroupingSetVars);
+
+ SetOperationRight newSetOpRight =
+ new SetOperationRight(SetOpType.UNION, false, new SetOperationInput(newSelectBlock, null));
+ newSetOpRightInputs.add(newSetOpRight);
+ }
+
+ // process 1st grouping set
+
+ List<GbyVariableExpressionPair> groupingSet = groupingSets.get(0);
+ gby.setGbyPairList(Collections.singletonList(groupingSet));
+
+ tmpCurrentGroupingSetVars.clear();
+ getGroupingSetVars(groupingSet, tmpCurrentGroupingSetVars);
+
+ List<GbyVariableExpressionPair> newDecorPairList = new ArrayList<>();
+ computeDecorVars(tmpAllGroupingSetsVars, tmpCurrentGroupingSetVars, newDecorPairList);
+ gby.setDecorPairList(newDecorPairList);
+
+ rewriteGroupingOperations(selectBlock, tmpAllGroupingSetsVars, tmpCurrentGroupingSetVars);
+
+ SetOperationInput newSetOpInput = new SetOperationInput(selectBlock, null);
+
+ SelectSetOperation newSetOp = new SelectSetOperation(newSetOpInput, newSetOpRightInputs);
+ newSetOp.setSourceLocation(selectBlock.getSourceLocation());
+
+ SelectExpression newSelectExpr = new SelectExpression(null, newSetOp, null, null, true);
+ newSelectExpr.setSourceLocation(selectBlock.getSourceLocation());
+
+ return SetOperationVisitor.createSelectBlock(newSelectExpr, context);
+ }
+
+ /**
+ * Valid GROUPING() operations can only be in LET clauses after GROUP BY.
+ * This is guaranteed by {@link SqlppGroupByVisitor}.
+ * These operations a rewritten into constants by this method.
+ * The remaining GROUPING() operations are invalid and will lead to a compile-time failure later
+ * because there's no runtime for GROUPING() function.
+ */
+ private void rewriteGroupingOperations(SelectBlock selectBlock, Set<VariableExpr> allGroupingSetsVars,
+ Set<VariableExpr> currentGroupingSetVars) throws CompilationException {
+ if (selectBlock.hasLetHavingClausesAfterGroupby()) {
+ for (Clause clause : selectBlock.getLetHavingListAfterGroupby()) {
+ if (clause.getClauseType() == Clause.ClauseType.LET_CLAUSE) {
+ LetClause letClause = (LetClause) clause;
+ Expression letExpr = letClause.getBindingExpr();
+ if (SqlppGroupByVisitor.isGroupingOperation(letExpr)) {
+ Expression newLetExpr = rewriteGroupingOperation((CallExpr) letExpr, allGroupingSetsVars,
+ currentGroupingSetVars);
+ letClause.setBindingExpr(newLetExpr);
+ }
+ }
+ }
+ }
+ }
+
+ private Expression rewriteGroupingOperation(CallExpr callExpr, Set<VariableExpr> allGroupingSetsVars,
+ Set<VariableExpr> currentGroupingSetVars) throws CompilationException {
+ List<Expression> argList = callExpr.getExprList();
+ if (argList.isEmpty()) {
+ throw new CompilationException(ErrorCode.COMPILATION_INVALID_NUM_OF_ARGS, callExpr.getSourceLocation(),
+ BuiltinFunctions.GROUPING.getName());
+ }
+ long result = 0;
+ for (Expression argExpr : argList) {
+ int v;
+ if (argExpr.getKind() == Expression.Kind.VARIABLE_EXPRESSION) {
+ VariableExpr varExpr = (VariableExpr) argExpr;
+ if (currentGroupingSetVars.contains(varExpr)) {
+ v = 0;
+ } else if (allGroupingSetsVars.contains(varExpr)) {
+ v = 1;
+ } else {
+ throw new CompilationException(ErrorCode.COMPILATION_GROUPING_OPERATION_INVALID_ARG,
+ argExpr.getSourceLocation());
+ }
+ } else {
+ throw new CompilationException(ErrorCode.COMPILATION_GROUPING_OPERATION_INVALID_ARG,
+ argExpr.getSourceLocation());
+ }
+ result = (result << 1) + v;
+ }
+
+ LiteralExpr resultExpr = new LiteralExpr(new LongIntegerLiteral(result));
+ resultExpr.setSourceLocation(callExpr.getSourceLocation());
+ return resultExpr;
+ }
+
+ private static void getAllGroupingSetsVars(List<List<GbyVariableExpressionPair>> gbyList,
+ Set<VariableExpr> outVars) {
+ for (List<GbyVariableExpressionPair> gbyPairList : gbyList) {
+ getGroupingSetVars(gbyPairList, outVars);
+ }
+ }
+
+ private static void getGroupingSetVars(List<GbyVariableExpressionPair> groupingSet,
+ Collection<VariableExpr> outVars) {
+ for (GbyVariableExpressionPair gbyPair : groupingSet) {
+ outVars.add(gbyPair.getVar());
+ }
+ }
+
+ private static void computeDecorVars(Set<VariableExpr> allGroupingSetsVars,
+ Set<VariableExpr> currentGroupingSetVars, List<GbyVariableExpressionPair> outDecorPairList) {
+ for (VariableExpr var : allGroupingSetsVars) {
+ if (!currentGroupingSetVars.contains(var)) {
+ LiteralExpr nullExpr = new LiteralExpr(NullLiteral.INSTANCE);
+ nullExpr.setSourceLocation(var.getSourceLocation());
+ VariableExpr newDecorVarExpr = new VariableExpr(var.getVar());
+ newDecorVarExpr.setSourceLocation(var.getSourceLocation());
+ outDecorPairList.add(new GbyVariableExpressionPair(newDecorVarExpr, nullExpr));
+ }
+ }
+ }
+}
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppWindowRewriteVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppWindowRewriteVisitor.java
index ba1e7f9..5c20bec 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppWindowRewriteVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppWindowRewriteVisitor.java
@@ -19,6 +19,7 @@
package org.apache.asterix.lang.sqlpp.rewrites.visitor;
+import java.util.ArrayList;
import java.util.List;
import org.apache.asterix.common.exceptions.CompilationException;
@@ -34,7 +35,6 @@
import org.apache.asterix.lang.sqlpp.util.FunctionMapUtil;
import org.apache.asterix.lang.sqlpp.util.SqlppRewriteUtil;
import org.apache.asterix.om.functions.BuiltinFunctions;
-import org.apache.hyracks.algebricks.common.utils.Pair;
import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
/**
@@ -88,8 +88,30 @@
return winExpr;
}
- @Override
- protected boolean isExtractableExpression(Expression expr) {
+ private List<Expression> extractExpressions(List<Expression> exprList, int limit) {
+ StackElement stackElement = stack.peek();
+ if (stackElement == null) {
+ return null;
+ }
+ int n = exprList.size();
+ List<Expression> newExprList = new ArrayList<>(n);
+ for (int i = 0; i < n; i++) {
+ Expression expr = exprList.get(i);
+ Expression newExpr;
+ if (i < limit && isExtractableExpression(expr)) {
+ VarIdentifier v = stackElement.addPendingLetClause(expr);
+ VariableExpr vExpr = new VariableExpr(v);
+ vExpr.setSourceLocation(expr.getSourceLocation());
+ newExpr = vExpr;
+ } else {
+ newExpr = expr;
+ }
+ newExprList.add(newExpr);
+ }
+ return newExprList;
+ }
+
+ private boolean isExtractableExpression(Expression expr) {
switch (expr.getKind()) {
case LITERAL_EXPRESSION:
case VARIABLE_EXPRESSION:
@@ -100,8 +122,7 @@
}
@Override
- void handleUnsupportedClause(FromClause clause, List<Pair<Expression, VarIdentifier>> extractionList)
- throws CompilationException {
+ void handleUnsupportedClause(FromClause clause) throws CompilationException {
throw new CompilationException(ErrorCode.COMPILATION_UNEXPECTED_WINDOW_EXPRESSION, clause.getSourceLocation());
}
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SubstituteGroupbyExpressionWithVariableVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SubstituteGroupbyExpressionWithVariableVisitor.java
index 13f1128..69e28aa 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SubstituteGroupbyExpressionWithVariableVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SubstituteGroupbyExpressionWithVariableVisitor.java
@@ -20,6 +20,7 @@
package org.apache.asterix.lang.sqlpp.rewrites.visitor;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import org.apache.asterix.common.exceptions.CompilationException;
@@ -54,10 +55,12 @@
public Expression visit(SelectBlock selectBlock, ILangExpression arg) throws CompilationException {
if (selectBlock.hasGroupbyClause()) {
Map<Expression, Expression> map = new HashMap<>();
- for (GbyVariableExpressionPair gbyKeyPair : selectBlock.getGroupbyClause().getGbyPairList()) {
- Expression gbyKeyExpr = gbyKeyPair.getExpr();
- if (gbyKeyExpr.getKind() != Kind.VARIABLE_EXPRESSION) {
- map.put(gbyKeyExpr, gbyKeyPair.getVar());
+ for (List<GbyVariableExpressionPair> gbyPairList : selectBlock.getGroupbyClause().getGbyPairList()) {
+ for (GbyVariableExpressionPair gbyKeyPair : gbyPairList) {
+ Expression gbyKeyExpr = gbyKeyPair.getExpr();
+ if (gbyKeyExpr.getKind() != Kind.VARIABLE_EXPRESSION) {
+ map.putIfAbsent(gbyKeyExpr, gbyKeyPair.getVar());
+ }
}
}
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/struct/SetOperationInput.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/struct/SetOperationInput.java
index a369010..894aff3 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/struct/SetOperationInput.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/struct/SetOperationInput.java
@@ -40,10 +40,18 @@
return selectBlock;
}
+ public void setSelectBlock(SelectBlock selectBlock) {
+ this.selectBlock = selectBlock;
+ }
+
public SelectExpression getSubquery() {
return subquery;
}
+ public void setSubquery(SelectExpression subquery) {
+ this.subquery = subquery;
+ }
+
public boolean selectBlock() {
return selectBlock != null;
}
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppVariableUtil.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppVariableUtil.java
index 361bcf6..26f2a35 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppVariableUtil.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppVariableUtil.java
@@ -161,10 +161,13 @@
if (gbyClause == null) {
return bindingVars;
}
- for (GbyVariableExpressionPair gbyKey : gbyClause.getGbyPairList()) {
- VariableExpr var = gbyKey.getVar();
- if (var != null) {
- bindingVars.add(var);
+ Set<VariableExpr> gbyKeyVars = new HashSet<>();
+ for (List<GbyVariableExpressionPair> gbyPairList : gbyClause.getGbyPairList()) {
+ for (GbyVariableExpressionPair gbyKey : gbyPairList) {
+ VariableExpr var = gbyKey.getVar();
+ if (var != null && gbyKeyVars.add(var)) {
+ bindingVars.add(var);
+ }
}
}
if (gbyClause.hasDecorList()) {
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/CheckSubqueryVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/CheckSubqueryVisitor.java
index f918b41..18c2b35 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/CheckSubqueryVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/CheckSubqueryVisitor.java
@@ -20,6 +20,7 @@
package org.apache.asterix.lang.sqlpp.visitor;
import java.util.Collection;
+import java.util.List;
import org.apache.asterix.common.exceptions.CompilationException;
import org.apache.asterix.lang.common.base.ILangExpression;
@@ -264,9 +265,11 @@
@Override
public Boolean visit(GroupbyClause gc, ILangExpression arg) throws CompilationException {
- for (GbyVariableExpressionPair key : gc.getGbyPairList()) {
- if (visit(key.getExpr(), arg)) {
- return true;
+ for (List<GbyVariableExpressionPair> gbyPairList : gc.getGbyPairList()) {
+ for (GbyVariableExpressionPair key : gbyPairList) {
+ if (visit(key.getExpr(), arg)) {
+ return true;
+ }
}
}
if (gc.hasDecorList()) {
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/DeepCopyVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/DeepCopyVisitor.java
index 56f160b..e661ac1 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/DeepCopyVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/DeepCopyVisitor.java
@@ -283,11 +283,17 @@
@Override
public GroupbyClause visit(GroupbyClause gc, Void arg) throws CompilationException {
- List<GbyVariableExpressionPair> gbyPairList = new ArrayList<>();
- for (GbyVariableExpressionPair gbyVarExpr : gc.getGbyPairList()) {
- VariableExpr var = gbyVarExpr.getVar();
- gbyPairList.add(new GbyVariableExpressionPair(var == null ? null : (VariableExpr) var.accept(this, arg),
- (Expression) gbyVarExpr.getExpr().accept(this, arg)));
+ List<List<GbyVariableExpressionPair>> gbyList = gc.getGbyPairList();
+ List<List<GbyVariableExpressionPair>> newGbyList = new ArrayList<>(gbyList.size());
+ for (List<GbyVariableExpressionPair> gbyPairList : gbyList) {
+ List<GbyVariableExpressionPair> newGbyPairList = new ArrayList<>(gbyPairList.size());
+ for (GbyVariableExpressionPair gbyVarExpr : gbyPairList) {
+ VariableExpr var = gbyVarExpr.getVar();
+ newGbyPairList
+ .add(new GbyVariableExpressionPair(var == null ? null : (VariableExpr) var.accept(this, arg),
+ (Expression) gbyVarExpr.getExpr().accept(this, arg)));
+ }
+ newGbyList.add(newGbyPairList);
}
List<GbyVariableExpressionPair> decorPairList = new ArrayList<>();
if (gc.hasDecorList()) {
@@ -310,7 +316,7 @@
groupVarExpr = (VariableExpr) gc.getGroupVar().accept(this, arg);
}
List<Pair<Expression, Identifier>> groupFieldList = copyFieldList(gc.getGroupFieldList(), arg);
- GroupbyClause copy = new GroupbyClause(gbyPairList, decorPairList, withVarMap, groupVarExpr, groupFieldList,
+ GroupbyClause copy = new GroupbyClause(newGbyList, decorPairList, withVarMap, groupVarExpr, groupFieldList,
gc.hasHashGroupByHint(), gc.isGroupAll());
copy.setSourceLocation(gc.getSourceLocation());
return copy;
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/FreeVariableVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/FreeVariableVisitor.java
index 7a4febd..e1ff4a7 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/FreeVariableVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/FreeVariableVisitor.java
@@ -274,8 +274,10 @@
@Override
public Void visit(GroupbyClause gc, Collection<VariableExpr> freeVars) throws CompilationException {
// Puts all group-by variables into the symbol set of the new scope.
- for (GbyVariableExpressionPair gbyVarExpr : gc.getGbyPairList()) {
- gbyVarExpr.getExpr().accept(this, freeVars);
+ for (List<GbyVariableExpressionPair> gbyPairList : gc.getGbyPairList()) {
+ for (GbyVariableExpressionPair gbyVarExpr : gbyPairList) {
+ gbyVarExpr.getExpr().accept(this, freeVars);
+ }
}
if (gc.hasDecorList()) {
for (GbyVariableExpressionPair decorVarExpr : gc.getDecorPairList()) {
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppAstPrintVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppAstPrintVisitor.java
index 33446de..d7ad1cb 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppAstPrintVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppAstPrintVisitor.java
@@ -275,23 +275,23 @@
out.println(skip(step) + "Group All");
} else {
out.println(skip(step) + "Groupby");
- for (GbyVariableExpressionPair pair : gc.getGbyPairList()) {
- if (pair.getVar() != null) {
- pair.getVar().accept(this, step + 1);
- out.println(skip(step + 1) + ":=");
+ List<List<GbyVariableExpressionPair>> gbyList = gc.getGbyPairList();
+ if (gbyList.size() == 1 && !gbyList.get(0).isEmpty()) {
+ // simplified format for the most common case
+ printGroupByPairList(gbyList.get(0), step + 1);
+ } else {
+ String sep = "";
+ for (List<GbyVariableExpressionPair> groupingSet : gbyList) {
+ out.println(skip(step + 1) + "GROUPING SET (");
+ printGroupByPairList(groupingSet, step + 2);
+ out.println(skip(step + 1) + ")" + sep);
+ sep = ",";
}
- pair.getExpr().accept(this, step + 1);
}
}
if (gc.hasDecorList()) {
out.println(skip(step + 1) + "DECOR");
- for (GbyVariableExpressionPair pair : gc.getDecorPairList()) {
- if (pair.getVar() != null) {
- pair.getVar().accept(this, step + 1);
- out.println(skip(step + 1) + ":=");
- }
- pair.getExpr().accept(this, step + 1);
- }
+ printGroupByPairList(gc.getDecorPairList(), step + 1);
}
if (gc.hasGroupVar()) {
out.print(skip(step + 1) + "GROUP AS ");
@@ -312,6 +312,17 @@
return null;
}
+ private void printGroupByPairList(List<GbyVariableExpressionPair> gbyPairList, Integer step)
+ throws CompilationException {
+ for (GbyVariableExpressionPair pair : gbyPairList) {
+ if (pair.getVar() != null) {
+ pair.getVar().accept(this, step);
+ out.println(skip(step) + ":=");
+ }
+ pair.getExpr().accept(this, step);
+ }
+ }
+
@Override
public Void visit(CaseExpression caseExpr, Integer step) throws CompilationException {
out.print(skip(step) + "CASE");
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppFormatPrintVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppFormatPrintVisitor.java
index 9901c8c..bf71c05 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppFormatPrintVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppFormatPrintVisitor.java
@@ -258,7 +258,21 @@
out.println(skip(step) + "/* +hash */");
}
out.print(skip(step) + "group by ");
- printDelimitedGbyExpressions(gc.getGbyPairList(), step + 2);
+ List<List<GbyVariableExpressionPair>> gbyList = gc.getGbyPairList();
+ if (gbyList.size() == 1) {
+ printDelimitedGbyExpressions(gbyList.get(0), step + 2);
+ } else {
+ out.print("grouping sets (");
+ for (int i = 0, n = gbyList.size(); i < n; i++) {
+ if (i > 0) {
+ out.print(COMMA);
+ }
+ out.print("(");
+ printDelimitedGbyExpressions(gbyList.get(i), step + 2);
+ out.print(")");
+ }
+ out.print(")");
+ }
out.println();
return null;
}
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppContainsExpressionVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppContainsExpressionVisitor.java
index e3f65e6..6495742 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppContainsExpressionVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppContainsExpressionVisitor.java
@@ -20,6 +20,7 @@
package org.apache.asterix.lang.sqlpp.visitor.base;
import java.util.Collection;
+import java.util.List;
import org.apache.asterix.common.exceptions.CompilationException;
import org.apache.asterix.lang.common.base.ILangExpression;
@@ -256,9 +257,11 @@
@Override
public Boolean visit(GroupbyClause gc, T arg) throws CompilationException {
- for (GbyVariableExpressionPair key : gc.getGbyPairList()) {
- if (visit(key.getExpr(), arg)) {
- return true;
+ for (List<GbyVariableExpressionPair> gbyPairList : gc.getGbyPairList()) {
+ for (GbyVariableExpressionPair key : gbyPairList) {
+ if (visit(key.getExpr(), arg)) {
+ return true;
+ }
}
}
if (gc.hasDecorList()) {
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppExpressionScopingVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppExpressionScopingVisitor.java
index 68f18d6..db85d73 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppExpressionScopingVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppExpressionScopingVisitor.java
@@ -22,6 +22,8 @@
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
@@ -237,11 +239,14 @@
// or an outer scope query) should still be visible.
Scope newScope = new Scope(scopeChecker, scopeChecker.getPrecedingScope());
// Puts all group-by variables into the symbol set of the new scope.
- for (GbyVariableExpressionPair gbyKeyVarExpr : gc.getGbyPairList()) {
- gbyKeyVarExpr.setExpr(visit(gbyKeyVarExpr.getExpr(), gc));
- VariableExpr gbyKeyVar = gbyKeyVarExpr.getVar();
- if (gbyKeyVar != null) {
- addNewVarSymbolToScope(newScope, gbyKeyVar.getVar(), gbyKeyVar.getSourceLocation());
+ Set<VariableExpr> gbyKeyVars = new HashSet<>(); // bindings from prior grouping sets //TODO:FIXME:GBY:REVISIT
+ for (List<GbyVariableExpressionPair> gbyPairList : gc.getGbyPairList()) {
+ for (GbyVariableExpressionPair gbyKeyVarExpr : gbyPairList) {
+ gbyKeyVarExpr.setExpr(visit(gbyKeyVarExpr.getExpr(), gc));
+ VariableExpr gbyKeyVar = gbyKeyVarExpr.getVar();
+ if (gbyKeyVar != null && gbyKeyVars.add(gbyKeyVar)) {
+ addNewVarSymbolToScope(newScope, gbyKeyVar.getVar(), gbyKeyVar.getSourceLocation());
+ }
}
}
if (gc.hasGroupFieldList()) {
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppSimpleExpressionVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppSimpleExpressionVisitor.java
index 0428a73..3d39dc0 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppSimpleExpressionVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppSimpleExpressionVisitor.java
@@ -208,8 +208,10 @@
@Override
public Expression visit(GroupbyClause gc, ILangExpression arg) throws CompilationException {
- for (GbyVariableExpressionPair gbyVarExpr : gc.getGbyPairList()) {
- gbyVarExpr.setExpr(visit(gbyVarExpr.getExpr(), gc));
+ for (List<GbyVariableExpressionPair> gbyPairList : gc.getGbyPairList()) {
+ for (GbyVariableExpressionPair gbyVarExpr : gbyPairList) {
+ gbyVarExpr.setExpr(visit(gbyVarExpr.getExpr(), gc));
+ }
}
if (gc.hasDecorList()) {
for (GbyVariableExpressionPair decVarExpr : gc.getDecorPairList()) {
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj b/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
index 0b9c394..53faff9 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
+++ b/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
@@ -178,6 +178,11 @@
import org.apache.asterix.lang.sqlpp.expression.WindowExpression;
import org.apache.asterix.lang.sqlpp.optype.JoinType;
import org.apache.asterix.lang.sqlpp.optype.SetOpType;
+import org.apache.asterix.lang.sqlpp.parser.SqlppGroupingSetsParser;
+import org.apache.asterix.lang.sqlpp.parser.SqlppGroupingSetsParser.GroupingElement;
+import org.apache.asterix.lang.sqlpp.parser.SqlppGroupingSetsParser.GroupingSet;
+import org.apache.asterix.lang.sqlpp.parser.SqlppGroupingSetsParser.GroupingSets;
+import org.apache.asterix.lang.sqlpp.parser.SqlppGroupingSetsParser.RollupCube;
import org.apache.asterix.lang.sqlpp.parser.SqlppHint;
import org.apache.asterix.lang.sqlpp.struct.SetOperationInput;
import org.apache.asterix.lang.sqlpp.struct.SetOperationRight;
@@ -204,10 +209,12 @@
class SQLPPParser extends ScopeChecker implements IParser {
// tokens parsed as identifiers
+ private static final String CUBE = "CUBE";
private static final String CURRENT = "CURRENT";
private static final String EXCLUDE = "EXCLUDE";
private static final String FIRST = "FIRST";
private static final String FOLLOWING = "FOLLOWING";
+ private static final String GROUPING = "GROUPING";
private static final String GROUPS = "GROUPS";
private static final String IGNORE = "IGNORE";
private static final String LAST = "LAST";
@@ -218,8 +225,10 @@
private static final String PRECEDING = "PRECEDING";
private static final String RANGE = "RANGE";
private static final String RESPECT = "RESPECT";
+ private static final String ROLLUP = "ROLLUP";
private static final String ROW = "ROW";
private static final String ROWS = "ROWS";
+ private static final String SETS = "SETS";
private static final String TIES = "TIES";
private static final String UNBOUNDED = "UNBOUNDED";
private static final String ACTION = "ACTION";
@@ -239,6 +248,8 @@
private DataverseName defaultDataverse;
+ private SqlppGroupingSetsParser groupingSetsParser;
+
private final WarningCollector warningCollector = new WarningCollector();
private final Map<SourceLocation, String> hintCollector = new HashMap<SourceLocation, String>();
@@ -476,6 +487,11 @@
return new SqlppParseException(getSourceLocation(token), message);
}
+ private boolean laToken(int idx, int kind) {
+ Token t = getToken(idx);
+ return t.kind == kind;
+ }
+
private boolean laToken(int idx, int kind, String image) {
Token t = getToken(idx);
return t.kind == kind && t.image.equalsIgnoreCase(image);
@@ -4039,59 +4055,44 @@
{
Token startToken = null;
GroupbyClause gbc = new GroupbyClause();
- List<GbyVariableExpressionPair> vePairList = new ArrayList<GbyVariableExpressionPair>();
- VariableExpr var = null;
- Expression expr = null;
- VariableExpr decorVar = null;
- Expression decorExpr = null;
+ List<List<GbyVariableExpressionPair>> gbyList = null;
+ List<GroupingElement> groupingElementList = null;
Pair<VariableExpr, List<Pair<Expression, Identifier>>> groupVarWithFieldList = null;
VariableExpr groupVar = null;
List<Pair<Expression, Identifier>> groupFieldList = null;
}
{
- {
- Scope newScope = extendCurrentScopeNoPush(true);
- // extendCurrentScope(true);
- }
+ {
+ Scope newScope = extendCurrentScopeNoPush(true);
+ // extendCurrentScope(true);
+ }
<GROUP>
- {
- startToken = token;
- Token hintToken = fetchHint(token, SqlppHint.HASH_GROUP_BY_HINT);
- if (hintToken != null) {
- gbc.setHashGroupByHint(true);
- }
+ {
+ startToken = token;
+ Token hintToken = fetchHint(token, SqlppHint.HASH_GROUP_BY_HINT);
+ if (hintToken != null) {
+ gbc.setHashGroupByHint(true);
}
- <BY> (
- expr = Expression()
- (LOOKAHEAD(1) (<AS>)?
- var = Variable()
- )?
- {
- GbyVariableExpressionPair pair1 = new GbyVariableExpressionPair(var, expr);
- vePairList.add(pair1);
- }
- ( LOOKAHEAD(1) <COMMA>
- {
- var = null;
- }
- expr = Expression()
- (LOOKAHEAD(1) (<AS>)?
- var = Variable()
- )?
- {
- GbyVariableExpressionPair pair2 = new GbyVariableExpressionPair(var, expr);
- vePairList.add(pair2);
- }
- )*
- )
- (<GROUP> <AS> groupVarWithFieldList = VariableWithFieldMap()
+ }
+ <BY> groupingElementList = GroupingElementList()
+ (
+ <GROUP> <AS> groupVarWithFieldList = VariableWithFieldMap()
{
groupVar = groupVarWithFieldList.first;
groupFieldList = groupVarWithFieldList.second;
}
)?
{
- gbc.setGbyPairList(vePairList);
+ if (groupingSetsParser == null) {
+ groupingSetsParser = new SqlppGroupingSetsParser();
+ }
+ SourceLocation sourceLoc = getSourceLocation(startToken);
+ try {
+ gbyList = groupingSetsParser.parse(groupingElementList, sourceLoc);
+ } catch (CompilationException e) {
+ throw new SqlppParseException(sourceLoc, e.getMessage());
+ }
+ gbc.setGbyPairList(gbyList);
gbc.setDecorPairList(new ArrayList<GbyVariableExpressionPair>());
gbc.setWithVarMap(new HashMap<Expression, VariableExpr>());
gbc.setGroupVar(groupVar);
@@ -4101,6 +4102,114 @@
}
}
+List<GroupingElement> GroupingElementList() throws ParseException:
+{
+ List<GroupingElement> groupingElementList = new ArrayList<GroupingElement>();
+ GroupingElement groupingElement = null;
+}
+{
+ groupingElement = GroupingElement() { groupingElementList.add(groupingElement); }
+ ( LOOKAHEAD(1) <COMMA> groupingElement = GroupingElement() { groupingElementList.add(groupingElement); } )*
+ {
+ return groupingElementList;
+ }
+}
+
+GroupingElement GroupingElement() throws ParseException:
+{
+ GroupingElement groupingElement = null;
+ List<GroupingSet> groupingSets = null;
+ List<GroupingElement> groupingElements = null;
+}
+{
+ (
+ LOOKAHEAD(2)
+ <LEFTPAREN> <RIGHTPAREN>
+ {
+ groupingElement = GroupingSet.EMPTY;
+ }
+ |
+ LOOKAHEAD({ laIdentifier(ROLLUP) && laToken(2, LEFTPAREN) })
+ <IDENTIFIER> { expectToken(ROLLUP); }
+ <LEFTPAREN> groupingSets = OrdinaryGroupingSetList() <RIGHTPAREN>
+ {
+ groupingElement = new RollupCube(groupingSets, false);
+ }
+ |
+ LOOKAHEAD({ laIdentifier(CUBE) && laToken(2, LEFTPAREN) })
+ <IDENTIFIER> { expectToken(CUBE); }
+ <LEFTPAREN> groupingSets = OrdinaryGroupingSetList() <RIGHTPAREN>
+ {
+ groupingElement = new RollupCube(groupingSets, true);
+ }
+ |
+ LOOKAHEAD({ laIdentifier(GROUPING) && laIdentifier(2, SETS) && laToken(3, LEFTPAREN) })
+ <IDENTIFIER> { expectToken(GROUPING); } <IDENTIFIER> { expectToken(SETS); }
+ <LEFTPAREN> groupingElements = GroupingElementList() <RIGHTPAREN>
+ {
+ groupingElement = new GroupingSets(groupingElements);
+ }
+ |
+ groupingElement = OrdinaryGroupingSet()
+ )
+ {
+ return groupingElement;
+ }
+}
+
+GroupingSet OrdinaryGroupingSet() throws ParseException:
+{
+ GbyVariableExpressionPair gbyExprPair = null;
+ List<GbyVariableExpressionPair> items = null;
+}
+{
+ (
+ LOOKAHEAD(1) <LEFTPAREN> items = GbyVariableExpressionPairList() <RIGHTPAREN>
+ | gbyExprPair = GbyVariableExpressionPair() { items = Collections.singletonList(gbyExprPair); }
+ )
+ {
+ return new GroupingSet(items);
+ }
+}
+
+List<GroupingSet> OrdinaryGroupingSetList() throws ParseException:
+{
+ GroupingSet groupingSet = null;
+ List<GroupingSet> items = new ArrayList<GroupingSet>();
+}
+{
+ groupingSet = OrdinaryGroupingSet() { items.add(groupingSet); }
+ ( LOOKAHEAD(1) <COMMA> groupingSet = OrdinaryGroupingSet() { items.add(groupingSet); } )*
+ {
+ return items;
+ }
+}
+
+List<GbyVariableExpressionPair> GbyVariableExpressionPairList() throws ParseException:
+{
+ GbyVariableExpressionPair gbyExprPair = null;
+ List<GbyVariableExpressionPair> items = new ArrayList<GbyVariableExpressionPair>();
+}
+{
+ gbyExprPair = GbyVariableExpressionPair() { items.add(gbyExprPair); }
+ ( LOOKAHEAD(1) <COMMA> gbyExprPair = GbyVariableExpressionPair() { items.add(gbyExprPair); } )*
+ {
+ return items;
+ }
+}
+
+GbyVariableExpressionPair GbyVariableExpressionPair() throws ParseException:
+{
+ Expression expr = null;
+ VariableExpr var = null;
+}
+{
+ expr = Expression() ( (<AS>)? var = Variable() )?
+ {
+ return new GbyVariableExpressionPair(var, expr);
+ }
+}
+
HavingClause HavingClause() throws ParseException:
{
Token startToken = null;
diff --git a/asterixdb/asterix-lang-sqlpp/src/test/java/org/apache/asterix/lang/sqlpp/parser/SqlppGroupingSetsParserTest.java b/asterixdb/asterix-lang-sqlpp/src/test/java/org/apache/asterix/lang/sqlpp/parser/SqlppGroupingSetsParserTest.java
new file mode 100644
index 0000000..f597c6b
--- /dev/null
+++ b/asterixdb/asterix-lang-sqlpp/src/test/java/org/apache/asterix/lang/sqlpp/parser/SqlppGroupingSetsParserTest.java
@@ -0,0 +1,305 @@
+/*
+ * 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.asterix.lang.sqlpp.parser;
+
+import static org.apache.asterix.lang.sqlpp.parser.SqlppGroupingSetsParser.GROUPING_SETS_LIMIT;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.asterix.common.exceptions.CompilationException;
+import org.apache.asterix.common.exceptions.ErrorCode;
+import org.apache.asterix.lang.common.base.Expression;
+import org.apache.asterix.lang.common.base.ILangExpression;
+import org.apache.asterix.lang.common.base.IParser;
+import org.apache.asterix.lang.common.base.Statement;
+import org.apache.asterix.lang.common.clause.GroupbyClause;
+import org.apache.asterix.lang.common.expression.GbyVariableExpressionPair;
+import org.apache.asterix.lang.sqlpp.util.SqlppVariableUtil;
+import org.apache.asterix.lang.sqlpp.visitor.SqlppFormatPrintVisitor;
+import org.apache.asterix.lang.sqlpp.visitor.base.AbstractSqlppSimpleExpressionVisitor;
+import org.apache.hyracks.util.MathUtil;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/**
+ * Test rewriting from GROUP BY's grouping elements into grouping sets:
+ * <p>
+ * GROUP BY {0} => GROUP BY GROUPING SETS ({1})
+ */
+@RunWith(Parameterized.class)
+public class SqlppGroupingSetsParserTest {
+
+ private static final int GROUPING_SETS_LIMIT_LOG2 = MathUtil.log2Floor(GROUPING_SETS_LIMIT);
+
+ private static final int GROUPING_SETS_LIMIT_SQRT = (int) Math.ceil(Math.sqrt(GROUPING_SETS_LIMIT));
+
+ private static final String ERR_PREFIX = "ASX";
+
+ private static final String ERR_SYNTAX = ERR_PREFIX + ErrorCode.PARSE_ERROR;
+
+ private static final String ERR_OVERFLOW = ERR_PREFIX + ErrorCode.COMPILATION_GROUPING_SETS_OVERFLOW;
+
+ private static final String ERR_ALIAS = ERR_PREFIX + ErrorCode.COMPILATION_UNEXPECTED_ALIAS;
+
+ @Parameterized.Parameters(name = "{index}: GROUP BY {0}")
+ public static Collection<Object[]> data() {
+ return Arrays
+ .asList(new Object[][] {
+ // GROUP BY {0} => GROUP BY GROUPING SETS ({1})
+ //
+ // 1. Basic
+ //
+ { "()", "()" },
+ //
+ { "(), ()", "()" },
+ //
+ { "a", "(a)" },
+ //
+ { "a,b", "(a,b)" },
+ //
+ { "a,(b,c)", "(a,b,c)" },
+ //
+ { "(a,b),(c,d)", "(a,b,c,d)" },
+ //
+ // 2. Rollup
+ //
+ { "rollup(a,b,c)", "(a,b,c)(a,b)(a)()" },
+ //
+ { "Rollup(a,(b,c),d)", "(a,b,c,d)(a,b,c)(a)()" },
+ //
+ { "RollUP((a,b),(c,d))", "(a,b,c,d)(a,b)()" },
+ //
+ { "a,ROLLUP(a,b)", "(a,b)(a)(a)" },
+ //
+ { "a,ROLLUP(a,b),c", "(a,b,c)(a,c)(a,c)" },
+ //
+ { "a,b,ROLLUP(c,d)", "(a,b,c,d)(a,b,c)(a,b)" },
+ //
+ { "ROLLUP(a,b),ROLLUP(c,d)", "(a,b,c,d)(a,b,c)(a,b)(a,c,d)(a,c)(a)(c,d)(c)()" },
+ //
+ // 3. Cube
+ //
+ { "cube(a,b,c)", "(a,b,c)(a,b)(a,c)(a)(b,c)(b)(c)()" },
+ //
+ { "Cube(a,(b,c),d)", "(a,b,c,d)(a,b,c)(a,d)(a)(b,c,d)(b,c)(d)()" },
+ //
+ { "CubE((a,b),(c,d))", "(a,b,c,d)(a,b)(c,d)()" },
+ //
+ { "a,CUBE(a,b)", "(a,b)(a)(a,b)(a)" },
+ //
+ { "a,CUBE(a,b),c", "(a,b,c)(a,c)(a,b,c)(a,c)" },
+ //
+ { "a,b,CUBE(c,d)", "(a,b,c,d)(a,b,c)(a,b,d)(a,b)" },
+ //
+ { "CUBE(a,b),CUBE(c,d)",
+ "(a,b,c,d)(a,b,c)(a,b,d)(a,b)(a,c,d)(a,c)(a,d)(a)(b,c,d)(b,c)(b,d)(b)(c,d)(c)(d)()" },
+ //
+ // 4. Rollup + Cube
+ //
+ { "ROLLUP(a,b),CUBE(c,d)", "(a,b,c,d)(a,b,c)(a,b,d)(a,b)(a,c,d)(a,c)(a,d)(a)(c,d)(c)(d)()" },
+ //
+ { "CUBE(a,b),ROLLUP(c,d)", "(a,b,c,d)(a,b,c)(a,b)(a,c,d)(a,c)(a)(b,c,d)(b,c)(b)(c,d)(c)()" },
+ //
+ // 5. Grouping Sets
+ //
+ { "grouping sets(())", "()" },
+ //
+ { "Grouping Sets((), ())", "()()" },
+ //
+ { "Grouping setS(()), GROUPING SETS(())", "()" },
+ //
+ { "GROUPING SETS((a),(a,b))", "(a)(a,b)" },
+ //
+ { "GROUPING SETS((a,b),(a,b))", "(a,b)(a,b)" },
+ //
+ { "GROUPING SETS((a,b)),GROUPING SETS((a,b))", "(a,b)" },
+ //
+ { "GROUPING SETS((a,b),(c)),GROUPING SETS((d,e),())", "(a,b,d,e)(a,b)(c,d,e)(c)" },
+ //
+ { "GROUPING SETS(ROLLUP(a,b),ROLLUP(c,d))", "(a,b)(a)()(c,d)(c)()" },
+ //
+ { "GROUPING SETS(ROLLUP(a,b)), GROUPING SETS(ROLLUP(c,d))",
+ "(a,b,c,d)(a,b,c)(a,b)(a,c,d)(a,c)(a)(c,d)(c)()" },
+ //
+ { "GROUPING SETS((a, b), GROUPING SETS((c,d), (e,f)))", "(a,b)(c,d)(e,f)" },
+ //
+ { "GROUPING SETS(ROLLUP(a,b)),GROUPING SETS(ROLLUP(c,d))",
+ "(a,b,c,d)(a,b,c)(a,b)(a,c,d)(a,c)(a)(c,d)(c)()" },
+ //
+ // 6. Variable names (aliases)
+ //
+ { "a as x, b as y", "(a as x,b as y)" },
+ //
+ { "ROLLUP(a as x, b as y),ROLLUP(a, b)",
+ "(a as x,b as y)(a as x,b as y)(a as x,b as y)(a as x,b as y)"
+ + "(a as x)(a as x)(a as x,b as y)(a as x)()" },
+ //
+ { "CUBE(a as x, b as y),CUBE(a, b)",
+ "(a as x,b as y)(a as x,b as y)(a as x,b as y)(a as x,b as y)"
+ + "(a as x,b as y)(a as x)(a as x,b as y)(a as x)(b as y,a as x)(b as y,a as x)(b as y)(b as y)"
+ + "(a as x,b as y)(a as x)(b as y)()" },
+ //
+ { "GROUPING SETS((e1 as x, e2 as y)), GROUPING SETS((e1, e2))", "(e1 as x,e2 as y)" },
+ //
+ { "GROUPING SETS((e1 as x, e2 as y), (e1, e2))", "(e1 as x,e2 as y)(e1 as x,e2 as y)" },
+ //
+ // 7. Errors
+ //
+ // Syntax error
+ { "ROLLUP()", ERR_SYNTAX },
+ //
+ // Syntax error
+ { "CUBE()", ERR_SYNTAX },
+ //
+ // Too many grouping sets when expanding a rollup
+ { String.format("ROLLUP(%s)", generateSimpleGroupingSet("a", GROUPING_SETS_LIMIT)),
+ ERR_OVERFLOW },
+ //
+ // Too many grouping sets when expanding a cube
+ { String.format("CUBE(%s)", generateSimpleGroupingSet("a", GROUPING_SETS_LIMIT_LOG2 + 1)),
+ ERR_OVERFLOW },
+ //
+ // Too many grouping sets when doing a cross product of grouping sets
+ { String.format("GROUPING SETS(%s), GROUPING SETS(%s)",
+ generateSimpleGroupingSet("a", GROUPING_SETS_LIMIT_SQRT),
+ generateSimpleGroupingSet("b", GROUPING_SETS_LIMIT_SQRT)), ERR_OVERFLOW },
+ //
+ // Unexpected aliases
+ //
+ { "ROLLUP(a as x, b),ROLLUP(a, b as y)", ERR_ALIAS },
+ //
+ { "CUBE(a as x, b),CUBE(a, b as y)", ERR_ALIAS },
+ //
+ { "GROUPING SETS((e1 as x, e2), (e1, e2 as y))", ERR_ALIAS },
+ //
+ { "GROUPING SETS((e1 as x, e2)), GROUPING SETS((e1, e2 as y))", ERR_ALIAS },
+ //
+ { "GROUPING SETS((e1 as a, e2 as b)), GROUPING SETS((e1 as c, e2 as d))", ERR_ALIAS }, });
+ }
+
+ private final String groupbyInput;
+
+ private final String expectedGroupingSets;
+
+ private final String expectedErrorCode;
+
+ public SqlppGroupingSetsParserTest(String groupbyInput, String expectedResult) {
+ this.groupbyInput = groupbyInput;
+ if (expectedResult.startsWith(ERR_PREFIX)) {
+ this.expectedGroupingSets = null;
+ this.expectedErrorCode = expectedResult;
+ } else {
+ this.expectedGroupingSets = expectedResult;
+ this.expectedErrorCode = null;
+ }
+ }
+
+ @Test
+ public void test() throws Exception {
+ SqlppParserFactory parserFactory = new SqlppParserFactory();
+ String groupbyClause = "GROUP BY " + groupbyInput;
+ String query = "SELECT COUNT(*) FROM test " + groupbyClause + ";";
+ // parse 2 queries so we can test calling rewrite() multiple times on the same instance
+ IParser parser = parserFactory.createParser(query + query);
+ List<Statement> statements;
+ try {
+ statements = parser.parse();
+ } catch (CompilationException e) {
+ if (expectedErrorCode == null) {
+ throw e;
+ } else if (e.getMessage().contains(expectedErrorCode)) {
+ return; // Found expected error code. SUCCESS
+ } else {
+ Assert.fail(String.format("Unable to find expected error code %s in error message: %s",
+ expectedErrorCode, e.getMessage()));
+ throw new IllegalStateException();
+ }
+ }
+ Assert.assertEquals(2, statements.size());
+
+ for (Statement statement : statements) {
+ String groupingSets = extractGroupingSets(statement);
+ Assert.assertEquals(expectedGroupingSets, groupingSets);
+ }
+ }
+
+ private String extractGroupingSets(Statement stmt) throws Exception {
+ SqlppGroupingSetsRewriterTestVisitor visitor = new SqlppGroupingSetsRewriterTestVisitor();
+ stmt.accept(visitor, null);
+ return visitor.printGroupingSets();
+ }
+
+ private static class SqlppGroupingSetsRewriterTestVisitor extends AbstractSqlppSimpleExpressionVisitor {
+
+ private final List<List<GbyVariableExpressionPair>> groupingSets = new ArrayList<>();
+
+ private final StringWriter stringWriter = new StringWriter();
+
+ private final PrintWriter printWriter = new PrintWriter(stringWriter);
+
+ private final SqlppFormatPrintVisitor printVisitor = new SqlppFormatPrintVisitor(printWriter);
+
+ @Override
+ public Expression visit(GroupbyClause gc, ILangExpression arg) throws CompilationException {
+ groupingSets.addAll(gc.getGbyPairList());
+ return super.visit(gc, arg);
+ }
+
+ public String printGroupingSets() throws CompilationException {
+ StringBuffer sb = stringWriter.getBuffer();
+ sb.delete(0, sb.length());
+
+ for (List<GbyVariableExpressionPair> groupingSet : groupingSets) {
+ printWriter.append('(');
+ String sep = "";
+ for (GbyVariableExpressionPair pair : groupingSet) {
+ printWriter.append(sep);
+ sep = ",";
+ pair.getExpr().accept(printVisitor, 0);
+ if (pair.getVar() != null) {
+ String ident = SqlppVariableUtil.toUserDefinedName(pair.getVar().getVar().getValue());
+ printWriter.append(" as ").append(ident);
+ }
+ }
+ printWriter.append(')');
+ }
+ printWriter.flush();
+ return stringWriter.toString();
+ }
+ }
+
+ private static String generateSimpleGroupingSet(String prefix, int n) {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < n; i++) {
+ if (i > 0) {
+ sb.append(',');
+ }
+ sb.append(prefix).append(i);
+ }
+ return sb.toString();
+ }
+}
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java
index 51f5acd..3570aff 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java
@@ -198,6 +198,8 @@
FunctionConstants.ASTERIX_NS, "ordered-list-constructor", FunctionIdentifier.VARARGS);
public static final FunctionIdentifier UNORDERED_LIST_CONSTRUCTOR = new FunctionIdentifier(
FunctionConstants.ASTERIX_NS, "unordered-list-constructor", FunctionIdentifier.VARARGS);
+ public static final FunctionIdentifier GROUPING =
+ new FunctionIdentifier(FunctionConstants.ASTERIX_NS, "grouping", FunctionIdentifier.VARARGS);
public static final FunctionIdentifier DEEP_EQUAL =
new FunctionIdentifier(FunctionConstants.ASTERIX_NS, "deep-equal", 2);
@@ -1650,6 +1652,7 @@
addFunction(BOOLEAN_CONSTRUCTOR, ABooleanTypeComputer.INSTANCE, true);
addFunction(CIRCLE_CONSTRUCTOR, ACircleTypeComputer.INSTANCE, true);
addPrivateFunction(CONCAT_NON_NULL, ConcatNonNullTypeComputer.INSTANCE, true);
+ addFunction(GROUPING, AInt64TypeComputer.INSTANCE, true);
addPrivateFunction(COUNTHASHED_GRAM_TOKENS, OrderedListOfAInt32TypeComputer.INSTANCE, true);
addPrivateFunction(COUNTHASHED_WORD_TOKENS, OrderedListOfAInt32TypeComputer.INSTANCE, true);
diff --git a/asterixdb/pom.xml b/asterixdb/pom.xml
index 1538cbb..7cc4a3e 100644
--- a/asterixdb/pom.xml
+++ b/asterixdb/pom.xml
@@ -1348,7 +1348,17 @@
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-csv</artifactId>
- <version>1.8</version>
+ <version>1.8</version>
+ </dependency>
+ <dependency>
+ <groupId>org.testcontainers</groupId>
+ <artifactId>postgresql</artifactId>
+ <version>1.13.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.postgresql</groupId>
+ <artifactId>postgresql</artifactId>
+ <version>42.2.10</version>
</dependency>
</dependencies>
</dependencyManagement>
diff --git a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/UnionAllOperator.java b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/UnionAllOperator.java
index 9defb4f2..ba5ef5a 100644
--- a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/UnionAllOperator.java
+++ b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/UnionAllOperator.java
@@ -77,25 +77,66 @@
@Override
public void recomputeSchema() {
- schema = new ArrayList<LogicalVariable>();
- for (LogicalVariable v1 : inputs.get(0).getValue().getSchema()) {
- for (Triple<LogicalVariable, LogicalVariable, LogicalVariable> t : varMap) {
- if (t.first.equals(v1)) {
- schema.add(t.third);
- } else {
- schema.add(v1);
- }
+ // Assume input schemas are
+ // input0 = [a,b,c]
+ // input1 = [d,e,f,g,h]
+ // and
+ // UNION ALL mapping is
+ // [a,d -> X], [c,f -> Y]
+ //
+ // In order to compute the output schema we need to pick a larger input
+ // out of these two and replace variables there using UNION ALL mappings.
+ // Therefore in this example we'll pick input1 and the output schema will be
+ // [X,e,Y,g,h]
+ //
+ // Note that all input variables are out of scope after UNION ALL
+ // therefore it's ok return them in the output schema because
+ // no parent operator will refer to them.
+ // Also note the all UNION ALL operators in the final optimized plan
+ // will have input schemas that exactly match their mappings.
+ // This is guaranteed by InsertProjectBeforeUnionRule.
+ // In this example in the final optimized plan
+ // input0 schema will be [a,c]
+ // input1 schema will be [d,f]
+
+ List<LogicalVariable> inputSchema0 = inputs.get(0).getValue().getSchema();
+ List<LogicalVariable> inputSchema1 = inputs.get(1).getValue().getSchema();
+
+ List<LogicalVariable> inputSchema;
+ int inputSchemaIdx;
+ if (inputSchema0.size() >= inputSchema1.size()) {
+ inputSchema = inputSchema0;
+ inputSchemaIdx = 0;
+ } else {
+ inputSchema = inputSchema1;
+ inputSchemaIdx = 1;
+ }
+
+ schema = new ArrayList<>(inputSchema.size());
+ for (LogicalVariable inVar : inputSchema) {
+ LogicalVariable outVar = findOutputVar(inVar, inputSchemaIdx);
+ schema.add(outVar != null ? outVar : inVar);
+ }
+ }
+
+ private LogicalVariable findOutputVar(LogicalVariable inputVar, int inputIdx) {
+ for (Triple<LogicalVariable, LogicalVariable, LogicalVariable> t : varMap) {
+ LogicalVariable testVar;
+ switch (inputIdx) {
+ case 0:
+ testVar = t.first;
+ break;
+ case 1:
+ testVar = t.second;
+ break;
+ default:
+ throw new IllegalArgumentException(String.valueOf(inputIdx));
+ }
+ if (inputVar.equals(testVar)) {
+ return t.third;
}
}
- for (LogicalVariable v2 : inputs.get(1).getValue().getSchema()) {
- for (Triple<LogicalVariable, LogicalVariable, LogicalVariable> t : varMap) {
- if (t.second.equals(v2)) {
- schema.add(t.third);
- } else {
- schema.add(v2);
- }
- }
- }
+ return null;
}
@Override