Merge branch 'gerrit/cheshire-cat'

Change-Id: Ie6a4c591cf0581775f57979c62f69801c21edb9b
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/subplan/InlineAllNtsInSubplanVisitor.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/subplan/InlineAllNtsInSubplanVisitor.java
index 1b7b7d2..4ac44b4 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/subplan/InlineAllNtsInSubplanVisitor.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/subplan/InlineAllNtsInSubplanVisitor.java
@@ -743,7 +743,7 @@
         gbyOp.getNestedPlans().add(nestedPlan);
 
         OperatorManipulationUtil.computeTypeEnvironmentBottomUp(gbyOp, context);
-        return op;
+        return gbyOp;
     }
 
     private ILogicalOperator visitMultiInputOperator(ILogicalOperator op) throws AlgebricksException {
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/quantifiers/query-ASTERIXDB-2947/query-ASTERIXDB-2947.1.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/quantifiers/query-ASTERIXDB-2947/query-ASTERIXDB-2947.1.ddl.sqlpp
new file mode 100644
index 0000000..0ab290f
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/quantifiers/query-ASTERIXDB-2947/query-ASTERIXDB-2947.1.ddl.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.
+ */
+
+/*
+ * Description: This test case is to verify the fix for ASTERIXDB-2947
+ */
+
+drop dataverse test if exists;
+create dataverse test;
+
+use test;
+
+create dataset t1(id uuid not unknown) open type primary key id autogenerated;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/quantifiers/query-ASTERIXDB-2947/query-ASTERIXDB-2947.2.update.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/quantifiers/query-ASTERIXDB-2947/query-ASTERIXDB-2947.2.update.sqlpp
new file mode 100644
index 0000000..5dcf4b9
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/quantifiers/query-ASTERIXDB-2947/query-ASTERIXDB-2947.2.update.sqlpp
@@ -0,0 +1,35 @@
+/*
+ * 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;
+
+insert into t1
+([
+  {"x":1, "y":"A" },
+  {"x":1, "y":"B" },
+  {"x":1, "y":"C" },
+
+  {"x":2, "y":"D" },
+  {"x":2, "y":"E" },
+  {"x":2, "y":"F" },
+
+  {"x":3, "y":"G" },
+  {"x":3, "y":"H" },
+  {"x":3, "y":"I" }
+]);
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/quantifiers/query-ASTERIXDB-2947/query-ASTERIXDB-2947.3.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/quantifiers/query-ASTERIXDB-2947/query-ASTERIXDB-2947.3.query.sqlpp
new file mode 100644
index 0000000..712b8d1
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/quantifiers/query-ASTERIXDB-2947/query-ASTERIXDB-2947.3.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.
+ */
+
+/*
+ * Description: This test case is to verify the fix for ASTERIXDB-2947
+ */
+
+use test;
+
+select x, array_sort(array_agg({y})) yy
+from t1
+group by x
+having some r in array_agg({y}) satisfies r.y in ["B", "H"]
+order by x;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/quantifiers/query-ASTERIXDB-2947/query-ASTERIXDB-2947.3.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/quantifiers/query-ASTERIXDB-2947/query-ASTERIXDB-2947.3.adm
new file mode 100644
index 0000000..4f412c8
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/quantifiers/query-ASTERIXDB-2947/query-ASTERIXDB-2947.3.adm
@@ -0,0 +1,2 @@
+{ "yy": [ { "y": "A" }, { "y": "B" }, { "y": "C" } ], "x": 1 }
+{ "yy": [ { "y": "G" }, { "y": "H" }, { "y": "I" } ], "x": 3 }
\ No newline at end of file
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 2586447..4c821a8 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
@@ -9440,6 +9440,11 @@
         <output-dir compare="Text">query-ASTERIXDB-2696</output-dir>
       </compilation-unit>
     </test-case>
+    <test-case FilePath="quantifiers">
+      <compilation-unit name="query-ASTERIXDB-2947">
+        <output-dir compare="Text">query-ASTERIXDB-2947</output-dir>
+      </compilation-unit>
+    </test-case>
     <!--
         <test-case FilePath="quantifiers">
           <compilation-unit name="everysat_02">
diff --git a/asterixdb/asterix-doc/src/main/markdown/sqlpp/3_query.md b/asterixdb/asterix-doc/src/main/markdown/sqlpp/3_query.md
index fe9a3ad..04a65d1 100644
--- a/asterixdb/asterix-doc/src/main/markdown/sqlpp/3_query.md
+++ b/asterixdb/asterix-doc/src/main/markdown/sqlpp/3_query.md
@@ -742,7 +742,7 @@
     WHERE get_year(date(o.order_date)) = 2020
     GROUP BY get_month(date(o.order_date)) AS month
     SELECT month, COUNT(*) AS order_count
-    ORDER BY order_count desc
+    ORDER BY order_count DESC, month DESC
     LIMIT 3;
 
 Result:
@@ -794,6 +794,327 @@
         }
     ]
 
+#### <a id="Rollup">ROLLUP</a>
+
+The `ROLLUP` subclause is an aggregation feature that extends the functionality of the `GROUP BY` clause.
+It returns extra _super-aggregate_ items in the query results, giving subtotals and a grand total for the aggregate
+functions in the query.
+To illustrate, first consider the following query.
+
+##### Example
+
+(Q3.R1) List the number of orders, grouped by customer region and city.
+
+    SELECT customer_region AS Region,
+           customer_city AS City,
+           COUNT(o.orderno) AS `Order Count`
+    FROM customers AS c LEFT OUTER JOIN orders AS o ON c.custid = o.custid
+    LET address_line = SPLIT(c.address.city, ","),
+        customer_city = TRIM(address_line[0]),
+        customer_region = TRIM(address_line[1])
+    GROUP BY customer_region, customer_city
+    ORDER BY customer_region ASC, customer_city ASC, `Order Count` DESC;
+
+Result:
+
+    [
+      {
+        "Region": "Italy",
+        "City": "Rome",
+        "Order Count": 0
+      },
+      {
+        "Region": "MA",
+        "City": "Boston",
+        "Order Count": 2
+      },
+      {
+        "Region": "MA",
+        "City": "Hanover",
+        "Order Count": 0
+      },
+      {
+        "Region": "MO",
+        "City": "St. Louis",
+        "Order Count": 7
+      }
+    ]
+
+This query uses string functions to split each customer's address into city and region.
+The query then counts the total number of orders placed by each customer, and groups the results first by customer
+region, then by customer city.
+The aggregate results (labeled `Order Count`) are only shown by city, and there are no subtotals or grand total.
+We can add these using the `ROLLUP` subclause, as in the following example.
+
+##### Example
+
+(Q3.R2) List the number of orders by customer region and city, including subtotals and a grand total.
+
+    SELECT customer_region AS Region,
+           customer_city AS City,
+           COUNT(o.orderno) AS `Order Count`
+    FROM customers AS c LEFT OUTER JOIN orders AS o ON c.custid = o.custid
+    LET address_line = SPLIT(c.address.city, ","),
+        customer_city = TRIM(address_line[0]),
+        customer_region = TRIM(address_line[1])
+    GROUP BY ROLLUP(customer_region, customer_city)
+    ORDER BY customer_region ASC, customer_city ASC, `Order Count` DESC;
+
+Result:
+
+    [
+      {
+        "Region": null,
+        "City": null,
+        "Order Count": 9
+      },
+      {
+        "Region": "Italy",
+        "City": null,
+        "Order Count": 0
+      },
+      {
+        "Region": "Italy",
+        "City": "Rome",
+        "Order Count": 0
+      },
+      {
+        "Region": "MA",
+        "City": null,
+        "Order Count": 2
+      },
+      {
+        "Region": "MA",
+        "City": "Boston",
+        "Order Count": 2
+      },
+      {
+        "Region": "MA",
+        "City": "Hanover",
+        "Order Count": 0
+      },
+      {
+        "Region": "MO",
+        "City": null,
+        "Order Count": 7
+      },
+      {
+        "Region": "MO",
+        "City": "St. Louis",
+        "Order Count": 7
+      }
+    ]
+
+With the addition of the `ROLLUP` subclause, the results now include an extra item at the start of each region,
+giving the subtotal for that region.
+There is also another extra item at the very start of the results, giving the grand total for all regions.
+
+The order of the fields specified by the `ROLLUP` subclause determines the hierarchy of the super-aggregate items.
+The customer region is specified first, followed by the customer city; so the results are aggregated by region first,
+and then by city within each region.
+
+The grand total returns `null` as a value for the city and the region, and the subtotals return `null` as the
+value for the city, which may make the results hard to understand at first glance.
+A workaround for this is given in the next example.
+
+##### Example
+
+(Q3.R3) List the number of orders by customer region and city, with meaningful subtotals and grand total.
+
+    SELECT IFNULL(customer_region, "All regions") AS Region,
+           IFNULL(customer_city, "All cities") AS City,
+           COUNT(o.orderno) AS `Order Count`
+    FROM customers AS c LEFT OUTER JOIN orders AS o ON c.custid = o.custid
+    LET address_line = SPLIT(c.address.city, ","),
+        customer_city = TRIM(address_line[0]),
+        customer_region = TRIM(address_line[1])
+    GROUP BY ROLLUP(customer_region, customer_city)
+    ORDER BY customer_region ASC, customer_city ASC, `Order Count` DESC;
+
+Result:
+
+    [
+      {
+        "Region": "All regions",
+        "City": "All cities",
+        "Order Count": 9
+      },
+      {
+        "Region": "Italy",
+        "City": "All cities",
+        "Order Count": 0
+      },
+      {
+        "Region": "Italy",
+        "City": "Rome",
+        "Order Count": 0
+      },
+      {
+        "Region": "MA",
+        "City": "All cities",
+        "Order Count": 2
+      },
+      {
+        "Region": "MA",
+        "City": "Boston",
+        "Order Count": 2
+      },
+      {
+        "Region": "MA",
+        "City": "Hanover",
+        "Order Count": 0
+      },
+      {
+        "Region": "MO",
+        "City": "All cities",
+        "Order Count": 7
+      },
+      {
+        "Region": "MO",
+        "City": "St. Louis",
+        "Order Count": 7
+      }
+    ]
+
+This query uses the `IFNULL` function to populate the region and city fields with meaningful values for the
+super-aggregate items.
+This makes the results clearer and more readable.
+
+#### <a id="Cube">CUBE</a>
+
+The `CUBE` subclause is similar to the `ROLLUP` subclause, in that it returns extra super-aggregate items in the query
+results, giving subtotals and a grand total for the aggregate functions.
+Whereas `ROLLUP` returns a grand total and a hierarchy of subtotals based on the specified fields,
+the `CUBE` subclause returns a grand total and subtotals for every possible combination of the specified fields.
+
+The following example is a modification of Q3.R3 which illustrates the `CUBE` subclause.
+
+##### Example
+
+(Q3.C) List the number of orders by customer region and order date, with all possible subtotals and a grand total.
+
+    SELECT IFNULL(customer_region, "All regions") AS Region,
+           IFNULL(order_month, "All months") AS Month,
+           COUNT(o.orderno) AS `Order Count`
+    FROM customers AS c INNER JOIN orders AS o ON c.custid = o.custid
+    LET address_line = SPLIT(c.address.city, ","),
+        customer_region = TRIM(address_line[1]),
+        order_month = get_month(date(o.order_date))
+    GROUP BY CUBE(customer_region, order_month)
+    ORDER BY customer_region ASC, order_month ASC;
+
+Result:
+
+    [
+      {
+        "Region": "All regions",
+        "Order Count": 9,
+        "Month": "All months"
+      },
+      {
+        "Region": "All regions",
+        "Order Count": 1,
+        "Month": 4
+      },
+      {
+        "Region": "All regions",
+        "Order Count": 1,
+        "Month": 5
+      },
+      {
+        "Region": "All regions",
+        "Order Count": 1,
+        "Month": 6
+      },
+      {
+        "Region": "All regions",
+        "Order Count": 1,
+        "Month": 7
+      },
+      {
+        "Region": "All regions",
+        "Order Count": 1,
+        "Month": 8
+      },
+      {
+        "Region": "All regions",
+        "Order Count": 2,
+        "Month": 9
+      },
+      {
+        "Region": "All regions",
+        "Order Count": 2,
+        "Month": 10
+      },
+      {
+        "Region": "MA",
+        "Order Count": 2,
+        "Month": "All months"
+      },
+      {
+        "Region": "MA",
+        "Order Count": 1,
+        "Month": 7
+      },
+      {
+        "Region": "MA",
+        "Order Count": 1,
+        "Month": 8
+      },
+      {
+        "Region": "MO",
+        "Order Count": 7,
+        "Month": "All months"
+      },
+      {
+        "Region": "MO",
+        "Order Count": 1,
+        "Month": 4
+      },
+      {
+        "Region": "MO",
+        "Order Count": 1,
+        "Month": 5
+      },
+      {
+        "Region": "MO",
+        "Order Count": 1,
+        "Month": 6
+      },
+      {
+        "Region": "MO",
+        "Order Count": 2,
+        "Month": 9
+      },
+      {
+        "Region": "MO",
+        "Order Count": 2,
+        "Month": 10
+      }
+    ]
+
+To simplify the results, this query uses an inner join, so that customers who have not placed an order are not included
+in the totals.
+The query uses string functions to extract the region from each customer's address,
+and a temporal function to extract the year from the order date.
+
+The query uses the `CUBE` subclause with customer region and order month.
+This means that there are four possible aggregates to calculate:
+
+* All regions, all months
+* All regions, each month
+* Each region, all months
+* Each region, each month
+
+The results start with the grand total, showing the total number of orders across all regions for all months.
+This is followed by date subtotals, showing the number of orders across all regions for each month.
+Following that are the regional subtotals, showing the total number of orders for all months in each region;
+and the result items, giving the number of orders for each month in each region.
+
+The query also uses the `IFNULL` function to populate the region and date fields with meaningful values for the
+super-aggregate items.
+This makes the results clearer and more readable.
+
 ### <a id="Having_clauses">HAVING Clause</a>
 
 ##### HavingClause
diff --git a/asterixdb/asterix-server/pom.xml b/asterixdb/asterix-server/pom.xml
index 98e3e59..afaad49 100644
--- a/asterixdb/asterix-server/pom.xml
+++ b/asterixdb/asterix-server/pom.xml
@@ -343,6 +343,7 @@
                 <aliasUrl>http://www.apache.org/licenses/LICENSE-2.0</aliasUrl>
                 <aliasUrl>https://www.apache.org/licenses/LICENSE-2.0.txt</aliasUrl>
                 <aliasUrl>http://www.apache.org/licenses/LICENSE-2.0.html</aliasUrl>
+                <aliasUrl>https://www.apache.org/licenses/LICENSE-2.0.html</aliasUrl>
                 <aliasUrl>https://aws.amazon.com/apache2.0</aliasUrl>
                 <aliasUrl>https://raw.githubusercontent.com/jorabin/KeePassJava2/KeePassJava2-2.1.4/LICENSE</aliasUrl>
                 <aliasUrl>https://raw.githubusercontent.com/reactor/reactor-netty/v1.0.7/LICENSE</aliasUrl>
diff --git a/asterixdb/src/main/appended-resources/supplemental-models.xml b/asterixdb/src/main/appended-resources/supplemental-models.xml
index 9624ae5..5ed7c32 100644
--- a/asterixdb/src/main/appended-resources/supplemental-models.xml
+++ b/asterixdb/src/main/appended-resources/supplemental-models.xml
@@ -290,9 +290,10 @@
       <properties>
         <!-- snappy-java is ALv2, and does not contain any embedded LICENSE or NOTICE file -->
         <!-- license override not needed, ALv2 is specified in its pom.xml -->
-        <!-- see https://github.com/xerial/snappy-java/blob/1.1.7.1/LICENSE -->
-        <license.ignoreMissingEmbeddedLicense>1.1.7.1</license.ignoreMissingEmbeddedLicense>
-        <license.ignoreMissingEmbeddedNotice>1.1.7.1</license.ignoreMissingEmbeddedNotice>
+        <!-- see https://github.com/xerial/snappy-java/blob/1.1.8.4/LICENSE -->
+        <license.ignoreMissingEmbeddedLicense>1.1.7.1,1.1.8.4</license.ignoreMissingEmbeddedLicense>
+        <license.ignoreMissingEmbeddedNotice>1.1.7.1,1.1.8.4</license.ignoreMissingEmbeddedNotice>
+        <license.ignoreLicenseOverride>1.1.7.1</license.ignoreLicenseOverride>
       </properties>
     </project>
   </supplement>
diff --git a/hyracks-fullstack/hyracks/hyracks-storage-common/pom.xml b/hyracks-fullstack/hyracks/hyracks-storage-common/pom.xml
index 2e0ac6c..99056a4 100644
--- a/hyracks-fullstack/hyracks/hyracks-storage-common/pom.xml
+++ b/hyracks-fullstack/hyracks/hyracks-storage-common/pom.xml
@@ -69,7 +69,6 @@
     <dependency>
       <groupId>org.xerial.snappy</groupId>
       <artifactId>snappy-java</artifactId>
-      <version>1.1.7.1</version>
     </dependency>
     <dependency>
       <groupId>com.google.guava</groupId>
diff --git a/hyracks-fullstack/pom.xml b/hyracks-fullstack/pom.xml
index 051d4f5..15c1c15 100644
--- a/hyracks-fullstack/pom.xml
+++ b/hyracks-fullstack/pom.xml
@@ -73,6 +73,7 @@
     <hadoop.version>2.8.5</hadoop.version>
     <jacoco.version>0.7.6.201602180812</jacoco.version>
     <log4j.version>2.14.1</log4j.version>
+    <snappy.version>1.1.8.4</snappy.version>
 
     <implementation.title>Apache Hyracks and Algebricks - ${project.name}</implementation.title>
     <implementation.url>https://asterixdb.apache.org/</implementation.url>
@@ -331,6 +332,11 @@
         <artifactId>powermock-core</artifactId>
         <version>2.0.9</version>
       </dependency>
+      <dependency>
+        <groupId>org.xerial.snappy</groupId>
+        <artifactId>snappy-java</artifactId>
+        <version>${snappy.version}</version>
+      </dependency>
     </dependencies>
   </dependencyManagement>
   <build>