Merge branch 'gerrit/cheshire-cat'

Change-Id: Id09b95763801f6cd9d81cb35b42cd0a6541a21e2
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/translator/QueryTranslator.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/translator/QueryTranslator.java
index c348b14..ce6e10d 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/translator/QueryTranslator.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/translator/QueryTranslator.java
@@ -396,13 +396,13 @@
                         handleAdapterDropStatement(metadataProvider, stmt);
                         break;
                     case CREATE_FUNCTION:
-                        handleCreateFunctionStatement(metadataProvider, stmt, stmtRewriter);
+                        handleCreateFunctionStatement(metadataProvider, stmt, stmtRewriter, requestParameters);
                         break;
                     case FUNCTION_DROP:
                         handleFunctionDropStatement(metadataProvider, stmt);
                         break;
                     case CREATE_LIBRARY:
-                        handleCreateLibraryStatement(metadataProvider, stmt, hcc);
+                        handleCreateLibraryStatement(metadataProvider, stmt, hcc, requestParameters);
                         break;
                     case LIBRARY_DROP:
                         handleLibraryDropStatement(metadataProvider, stmt, hcc);
@@ -2406,7 +2406,7 @@
     }
 
     public void handleCreateFunctionStatement(MetadataProvider metadataProvider, Statement stmt,
-            IStatementRewriter stmtRewriter) throws Exception {
+            IStatementRewriter stmtRewriter, IRequestParameters requestParameters) throws Exception {
         CreateFunctionStatement cfs = (CreateFunctionStatement) stmt;
         FunctionSignature signature = cfs.getFunctionSignature();
         metadataProvider.validateDatabaseObjectName(signature.getDataverseName(), signature.getName(),
@@ -2425,7 +2425,7 @@
         lockUtil.createFunctionBegin(lockManager, metadataProvider.getLocks(), dataverseName, signature.getName(),
                 libraryDataverseName, libraryName);
         try {
-            doCreateFunction(metadataProvider, cfs, signature, stmtRewriter);
+            doCreateFunction(metadataProvider, cfs, signature, stmtRewriter, requestParameters);
         } finally {
             metadataProvider.getLocks().unlock();
             metadataProvider.setDefaultDataverse(activeDataverse);
@@ -2433,7 +2433,8 @@
     }
 
     protected void doCreateFunction(MetadataProvider metadataProvider, CreateFunctionStatement cfs,
-            FunctionSignature functionSignature, IStatementRewriter stmtRewriter) throws Exception {
+            FunctionSignature functionSignature, IStatementRewriter stmtRewriter, IRequestParameters requestParameters)
+            throws Exception {
         DataverseName dataverseName = functionSignature.getDataverseName();
         SourceLocation sourceLoc = cfs.getSourceLocation();
         MetadataTransactionContext mdTxnCtx = MetadataManager.INSTANCE.beginTransaction();
@@ -2826,21 +2827,22 @@
     }
 
     protected void handleCreateLibraryStatement(MetadataProvider metadataProvider, Statement stmt,
-            IHyracksClientConnection hcc) throws Exception {
+            IHyracksClientConnection hcc, IRequestParameters requestParameters) throws Exception {
         CreateLibraryStatement cls = (CreateLibraryStatement) stmt;
         DataverseName dataverseName = getActiveDataverseName(cls.getDataverseName());
         String libraryName = cls.getLibraryName();
         String libraryHash = cls.getHash();
         lockUtil.createLibraryBegin(lockManager, metadataProvider.getLocks(), dataverseName, libraryName);
         try {
-            doCreateLibrary(metadataProvider, dataverseName, libraryName, libraryHash, cls, hcc);
+            doCreateLibrary(metadataProvider, dataverseName, libraryName, libraryHash, cls, hcc, requestParameters);
         } finally {
             metadataProvider.getLocks().unlock();
         }
     }
 
-    private void doCreateLibrary(MetadataProvider metadataProvider, DataverseName dataverseName, String libraryName,
-            String libraryHash, CreateLibraryStatement cls, IHyracksClientConnection hcc) throws Exception {
+    protected void doCreateLibrary(MetadataProvider metadataProvider, DataverseName dataverseName, String libraryName,
+            String libraryHash, CreateLibraryStatement cls, IHyracksClientConnection hcc,
+            IRequestParameters requestParameters) throws Exception {
         JobUtils.ProgressState progress = ProgressState.NO_PROGRESS;
         boolean prepareJobSuccessful = false;
         JobSpecification abortJobSpec = null;
@@ -3068,6 +3070,10 @@
             if (synonym != null) {
                 if (css.getIfNotExists()) {
                     MetadataManager.INSTANCE.commitTransaction(mdTxnCtx);
+                    if (warningCollector.shouldWarn()) {
+                        warningCollector
+                                .warn(Warning.of(css.getSourceLocation(), ErrorCode.SYNONYM_EXISTS, synonymName));
+                    }
                     return;
                 }
                 throw new CompilationException(ErrorCode.SYNONYM_EXISTS, css.getSourceLocation(), synonymName);
diff --git a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/ComparisonException.java b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/ComparisonException.java
index da5c8f6..cb437f0 100644
--- a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/ComparisonException.java
+++ b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/ComparisonException.java
@@ -24,4 +24,8 @@
     public ComparisonException(String message) {
         super(message);
     }
+
+    public ComparisonException(String message, Throwable cause) {
+        super(message, cause);
+    }
 }
diff --git a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/TestExecutor.java b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/TestExecutor.java
index b8524c0..3cc6353 100644
--- a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/TestExecutor.java
+++ b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/TestExecutor.java
@@ -33,6 +33,7 @@
 import java.io.OutputStream;
 import java.io.PrintStream;
 import java.io.PrintWriter;
+import java.io.Reader;
 import java.io.StringWriter;
 import java.net.Inet4Address;
 import java.net.InetAddress;
@@ -163,6 +164,8 @@
     private static final Pattern HANDLE_VARIABLE_PATTERN = Pattern.compile("handlevariable=(\\w+)");
     private static final Pattern RESULT_VARIABLE_PATTERN = Pattern.compile("resultvariable=(\\w+)");
     private static final Pattern COMPARE_UNORDERED_ARRAY_PATTERN = Pattern.compile("compareunorderedarray=(\\w+)");
+    private static final Pattern IGNORE_EXTRA_FIELDS_PATTERN = Pattern.compile("ignoreextrafields=(\\w+)");
+    private static final Pattern PRETTIFY_JSON_PATTERN = Pattern.compile("prettifyjsonresult=(\\w+)");
     private static final Pattern BODY_REF_PATTERN = Pattern.compile("bodyref=(.*)", Pattern.MULTILINE);
     private static final Pattern MACRO_PARAM_PATTERN =
             Pattern.compile("macro (?<name>[\\w-$]+)=(?<value>.*)", Pattern.MULTILINE);
@@ -275,10 +278,12 @@
         if (expectedFile.getName().endsWith(".ignore")) {
             return; //skip the comparison
         }
+        boolean prettifyJsonResult = statement == null ? false : getPrettifyJsonResult(statement);
         try (BufferedReader readerExpected =
                 new BufferedReader(new InputStreamReader(new FileInputStream(expectedFile), UTF_8));
-                BufferedReader readerActual =
-                        new BufferedReader(new InputStreamReader(new FileInputStream(actualFile), actualEncoding))) {
+                Reader rawReaderActual = new InputStreamReader(new FileInputStream(actualFile), actualEncoding);
+                BufferedReader readerActual = new BufferedReader(
+                        prettifyJsonResult ? TestHelper.asPrettyJson(rawReaderActual) : rawReaderActual)) {
             if (ComparisonEnum.BINARY.equals(compare)) {
                 if (!IOUtils.contentEquals(new FileInputStream(actualFile), new FileInputStream(expectedFile))) {
                     throw new Exception("Result for " + scriptFile + ": actual file did not match expected result");
@@ -291,8 +296,7 @@
                 runScriptAndCompareWithResultRegexAdm(scriptFile, readerExpected, readerActual);
                 return;
             } else if (actualFile.toString().endsWith(".regexjson")) {
-                boolean compareUnorderedArray = statement != null && getCompareUnorderedArray(statement);
-                runScriptAndCompareWithResultRegexJson(scriptFile, readerExpected, readerActual, compareUnorderedArray);
+                runScriptAndCompareWithResultRegexJson(scriptFile, readerExpected, readerActual, statement);
                 return;
             } else if (actualFile.toString().endsWith(".unorderedtxt")) {
                 runScriptAndCompareWithResultUnorderedLinesText(scriptFile, readerExpected, readerActual);
@@ -546,24 +550,28 @@
     }
 
     private static void runScriptAndCompareWithResultRegexJson(File scriptFile, BufferedReader readerExpected,
-            BufferedReader readerActual, boolean compareUnorderedArray) throws ComparisonException, IOException {
+            BufferedReader readerActual, String statement) throws ComparisonException, IOException {
+
+        boolean compareUnorderedArray = statement != null && getCompareUnorderedArray(statement);
+        boolean ignoreExtraFields = statement != null && getIgnoreExtraFields(statement);
+
         JsonNode expectedJson, actualJson;
         try {
             expectedJson = SINGLE_JSON_NODE_READER.readTree(readerExpected);
         } catch (JsonProcessingException e) {
-            throw new ComparisonException("Invalid expected JSON for: " + scriptFile);
+            throw new ComparisonException("Invalid expected JSON for: " + scriptFile, e);
         }
         try {
             actualJson = SINGLE_JSON_NODE_READER.readTree(readerActual);
         } catch (JsonProcessingException e) {
-            throw new ComparisonException("Invalid actual JSON for: " + scriptFile);
+            throw new ComparisonException("Invalid actual JSON for: " + scriptFile, e);
         }
         if (expectedJson == null) {
             throw new ComparisonException("No expected result for: " + scriptFile);
         } else if (actualJson == null) {
             throw new ComparisonException("No actual result for: " + scriptFile);
         }
-        if (!TestHelper.equalJson(expectedJson, actualJson, compareUnorderedArray)) {
+        if (!TestHelper.equalJson(expectedJson, actualJson, compareUnorderedArray, ignoreExtraFields, false, null)) {
             throw new ComparisonException("Result for " + scriptFile + " didn't match the expected JSON"
                     + "\nexpected result:\n" + expectedJson + "\nactual result:\n" + actualJson);
         }
@@ -1756,6 +1764,16 @@
         return matcher.find() && Boolean.parseBoolean(matcher.group(1));
     }
 
+    protected static boolean getIgnoreExtraFields(String statement) {
+        final Matcher matcher = IGNORE_EXTRA_FIELDS_PATTERN.matcher(statement);
+        return matcher.find() && Boolean.parseBoolean(matcher.group(1));
+    }
+
+    protected static boolean getPrettifyJsonResult(String statement) {
+        final Matcher matcher = PRETTIFY_JSON_PATTERN.matcher(statement);
+        return matcher.find() && Boolean.parseBoolean(matcher.group(1));
+    }
+
     protected static String replaceVarRef(String statement, Map<String, Object> variableCtx) {
         String tmpStmt = statement;
         Matcher variableReferenceMatcher = VARIABLE_REF_PATTERN.matcher(tmpStmt);
diff --git a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/TestHelper.java b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/TestHelper.java
index 137efdb..24ca072 100644
--- a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/TestHelper.java
+++ b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/TestHelper.java
@@ -23,6 +23,9 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
 import java.util.BitSet;
 import java.util.Collections;
 import java.util.Enumeration;
@@ -41,10 +44,13 @@
 import org.apache.commons.compress.utils.IOUtils;
 import org.apache.commons.io.FileUtils;
 import org.apache.hyracks.util.file.FileUtil;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.MapperFeature;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.ObjectWriter;
 import com.fasterxml.jackson.databind.SerializationFeature;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
@@ -52,9 +58,29 @@
 
 public final class TestHelper {
 
+    private static final Logger LOGGER = LogManager.getLogger();
     private static final String TEST_DIR_BASE_PATH = System.getProperty("user.dir") + File.separator + "target";
     private static final String[] TEST_DIRS = new String[] { "txnLogDir", "IODevice", "spill_area", "config" };
 
+    private static final ObjectMapper SORTED_MAPPER = new ObjectMapper();
+    private static final ObjectWriter PRETTY_SORTED_WRITER;
+
+    static {
+        SORTED_MAPPER.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);
+        SORTED_MAPPER.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true);
+        PRETTY_SORTED_WRITER = SORTED_MAPPER.writerWithDefaultPrettyPrinter();
+    }
+
+    public static Reader asPrettyJson(final Reader rawJson) throws IOException {
+        try {
+            StringWriter sw = new StringWriter();
+            PRETTY_SORTED_WRITER.writeValue(sw, SORTED_MAPPER.readTree(rawJson));
+            return new StringReader(sw.toString());
+        } finally {
+            IOUtils.closeQuietly(rawJson);
+        }
+    }
+
     public static boolean isInPrefixList(List<String> prefixList, String s) {
         for (String s2 : prefixList) {
             if (s.startsWith(s2)) {
@@ -141,7 +167,8 @@
         return RequestParameters.deserializeParameterValues(RequestParameters.serializeParameterValues(stmtParams));
     }
 
-    public static boolean equalJson(JsonNode expectedJson, JsonNode actualJson, boolean compareUnorderedArray) {
+    public static boolean equalJson(JsonNode expectedJson, JsonNode actualJson, boolean compareUnorderedArray,
+            boolean ignoreExtraFields, boolean withinUnorderedComparison, String context) {
         if (expectedJson == actualJson) {
             return true;
         }
@@ -151,29 +178,50 @@
         }
         if ((expectedJson.isMissingNode() && !actualJson.isMissingNode())
                 || (!expectedJson.isMissingNode() && actualJson.isMissingNode())) {
+            if (!withinUnorderedComparison) {
+                LOGGER.info("missing node mismatch: expected={} actual={} context={}", expectedJson, actualJson,
+                        context);
+            }
             return false;
         }
         // both are not null
         if (isRegexField(expectedJson)) {
             String expectedRegex = expectedJson.asText();
-            String actualAsString = actualJson.isValueNode() ? actualJson.asText() : actualJson.toString();
+            String actualAsString = stringify(actualJson);
             expectedRegex = expectedRegex.substring(2, expectedRegex.length() - 1);
-            return actualAsString.matches(expectedRegex);
+            final boolean matches = actualAsString.matches(expectedRegex);
+            if (!matches && !withinUnorderedComparison) {
+                LOGGER.info("regex mismatch: expected={} actual={} context={}", expectedRegex, actualAsString, context);
+            }
+            return matches;
         } else if (expectedJson.getNodeType() != actualJson.getNodeType()) {
+            if (!withinUnorderedComparison) {
+                LOGGER.info("node type mismatch: expected={}({}) actual={}({})", stringify(expectedJson),
+                        expectedJson.getNodeType(), stringify(actualJson), actualJson.getNodeType());
+            }
             return false;
         } else if (expectedJson.isArray() && actualJson.isArray()) {
             ArrayNode expectedArray = (ArrayNode) expectedJson;
             ArrayNode actualArray = (ArrayNode) actualJson;
             if (expectedArray.size() != actualArray.size()) {
+                if (!withinUnorderedComparison) {
+                    LOGGER.info("array size mismatch: expected={} actual={}", stringify(expectedArray),
+                            stringify(actualArray));
+                }
                 return false;
             }
-            return compareUnorderedArray ? compareUnordered(expectedArray, actualArray)
-                    : compareOrdered(expectedArray, actualArray);
+            return compareUnorderedArray ? compareUnordered(expectedArray, actualArray, ignoreExtraFields)
+                    : compareOrdered(expectedArray, actualArray, ignoreExtraFields);
         } else if (expectedJson.isObject() && actualJson.isObject()) {
             // assumes no duplicates in field names
             ObjectNode expectedObject = (ObjectNode) expectedJson;
             ObjectNode actualObject = (ObjectNode) actualJson;
-            if (expectedObject.size() != actualObject.size()) {
+            if (!ignoreExtraFields && expectedObject.size() != actualObject.size()
+                    || (ignoreExtraFields && expectedObject.size() > actualObject.size())) {
+                if (!withinUnorderedComparison) {
+                    LOGGER.info("object size mismatch: expected={} actual={} context={}", stringify(expectedObject),
+                            stringify(actualObject), context);
+                }
                 return false;
             }
             Iterator<Map.Entry<String, JsonNode>> expectedFields = expectedObject.fields();
@@ -182,41 +230,62 @@
             while (expectedFields.hasNext()) {
                 expectedField = expectedFields.next();
                 actualFieldValue = actualObject.get(expectedField.getKey());
-                if (actualFieldValue == null
-                        || !equalJson(expectedField.getValue(), actualFieldValue, compareUnorderedArray)) {
+                if (actualFieldValue == null) {
+                    if (!withinUnorderedComparison) {
+                        LOGGER.info("actual field value null: expected name={} expected value={}",
+                                expectedField.getKey(), expectedField.getValue().asText());
+                    }
+                    return false;
+                }
+                if (!equalJson(expectedField.getValue(), actualFieldValue, compareUnorderedArray, ignoreExtraFields,
+                        withinUnorderedComparison, expectedField.getKey())) {
                     return false;
                 }
             }
             return true;
         }
         // value node
-        String expectedAsString = expectedJson.isValueNode() ? expectedJson.asText() : expectedJson.toString();
-        String actualAsString = actualJson.isValueNode() ? actualJson.asText() : actualJson.toString();
-        return expectedAsString.equals(actualAsString);
+        String expectedAsString = stringify(expectedJson);
+        String actualAsString = stringify(actualJson);
+        if (!expectedAsString.equals(actualAsString)) {
+            if (!withinUnorderedComparison) {
+                LOGGER.info("value node mismatch: expected={} actual={} context={}", expectedAsString, actualAsString,
+                        context);
+            }
+            return false;
+        }
+        return true;
     }
 
-    private static boolean compareUnordered(ArrayNode expectedArray, ArrayNode actualArray) {
+    private static String stringify(JsonNode jsonNode) {
+        return jsonNode == null ? null : (jsonNode.isValueNode() ? jsonNode.asText() : jsonNode.toString());
+    }
+
+    private static boolean compareUnordered(ArrayNode expectedArray, ArrayNode actualArray, boolean ignoreExtraFields) {
         BitSet alreadyMatched = new BitSet(actualArray.size());
         for (int i = 0; i < expectedArray.size(); i++) {
             boolean found = false;
             JsonNode expectedElement = expectedArray.get(i);
             for (int k = 0; k < actualArray.size(); k++) {
-                if (!alreadyMatched.get(k) && equalJson(expectedElement, actualArray.get(k), true)) {
+                if (!alreadyMatched.get(k) && equalJson(expectedElement, actualArray.get(k), true, ignoreExtraFields,
+                        true, stringify(actualArray))) {
                     alreadyMatched.set(k);
                     found = true;
                     break;
                 }
             }
             if (!found) {
+                LOGGER.info("unordered array comparison failed; expected={} actual={}", expectedArray, actualArray);
                 return false;
             }
         }
         return true;
     }
 
-    private static boolean compareOrdered(ArrayNode expectedArray, ArrayNode actualArray) {
+    private static boolean compareOrdered(ArrayNode expectedArray, ArrayNode actualArray, boolean ignoreExtraFields) {
         for (int i = 0, size = expectedArray.size(); i < size; i++) {
-            if (!equalJson(expectedArray.get(i), actualArray.get(i), false)) {
+            if (!equalJson(expectedArray.get(i), actualArray.get(i), false, ignoreExtraFields, false,
+                    stringify(actualArray))) {
                 return false;
             }
         }
diff --git a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/runtime/SqlppRQGTestBase.java b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/runtime/SqlppRQGTestBase.java
index 0c13cdf..434b368 100644
--- a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/runtime/SqlppRQGTestBase.java
+++ b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/runtime/SqlppRQGTestBase.java
@@ -164,7 +164,7 @@
                     ResultExtractor.extract(resultStream, StandardCharsets.UTF_8, TestCaseContext.OutputFormat.ADM));
         }
 
-        boolean eq = TestHelper.equalJson(sqlResult, sqlppResult, false);
+        boolean eq = TestHelper.equalJson(sqlResult, sqlppResult, false, false, false, null);
 
         File sqlResultFile = writeResult(sqlResult, testcaseId, "sql", testcaseDescription);
         File sqlppResultFile = writeResult(sqlppResult, testcaseId, "sqlpp", testcaseDescription);
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries/api/cluster_state_1/cluster_state_1.1.get.http b/asterixdb/asterix-app/src/test/resources/runtimets/queries/api/cluster_state_1/cluster_state_1.1.get.http
index 08577ae..729c5d5 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries/api/cluster_state_1/cluster_state_1.1.get.http
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries/api/cluster_state_1/cluster_state_1.1.get.http
@@ -22,4 +22,5 @@
  * Expected Result : Success
  * Date            : 7th September 2016
  */
+--prettifyjsonresult=true
 /admin/cluster
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries/api/diagnostics_1/diagnostics_1.1.get.http b/asterixdb/asterix-app/src/test/resources/runtimets/queries/api/diagnostics_1/diagnostics_1.1.get.http
index c309110..58fa336 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries/api/diagnostics_1/diagnostics_1.1.get.http
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries/api/diagnostics_1/diagnostics_1.1.get.http
@@ -22,4 +22,5 @@
  * Expected Result : Positive
  * Date            : 8th September 2016
  */
+--prettifyjsonresult=true
 /admin/diagnostics
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries/api/net-diagnostics/net-diagnostics.1.get.http b/asterixdb/asterix-app/src/test/resources/runtimets/queries/api/net-diagnostics/net-diagnostics.1.get.http
index 12288a4..d98e4e8 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries/api/net-diagnostics/net-diagnostics.1.get.http
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries/api/net-diagnostics/net-diagnostics.1.get.http
@@ -16,4 +16,5 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+--prettifyjsonresult=true
 nc:asterix_nc1 /admin/net
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql/sum/scalar_sum_type/scalar_sum_type.1.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql/sum/scalar_sum_type/scalar_sum_type.1.query.sqlpp
new file mode 100644
index 0000000..932661c9
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql/sum/scalar_sum_type/scalar_sum_type.1.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 that scalar sum() produces correct output type
+ */
+
+select array_sum(array_reverse(lst))
+let lst = (
+  from range(1, 3) r
+  select value int32(to_string(r))
+)
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate/sum/scalar_sum_type/scalar_sum_type.1.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate/sum/scalar_sum_type/scalar_sum_type.1.query.sqlpp
new file mode 100644
index 0000000..361a59b
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate/sum/scalar_sum_type/scalar_sum_type.1.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 that scalar sum() produces correct output type
+ */
+
+select strict_sum(array_reverse(lst))
+let lst = (
+  from range(1, 3) r
+  select value int32(to_string(r))
+)
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/add_replica/add_replica.2.pollget.http b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/add_replica/add_replica.2.pollget.http
index 6867a5d..c007931 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/add_replica/add_replica.2.pollget.http
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/add_replica/add_replica.2.pollget.http
@@ -17,5 +17,6 @@
  * under the License.
  */
 //polltimeoutsecs=30
+//prettifyjsonresult=true
 
 nc:asterix_nc1 /admin/storage/partition/0
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/bulkload/bulkload.11.pollget.http b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/bulkload/bulkload.11.pollget.http
index 32e2f78..274e20f 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/bulkload/bulkload.11.pollget.http
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/bulkload/bulkload.11.pollget.http
@@ -17,5 +17,6 @@
  * under the License.
  */
 //polltimeoutsecs=30
+//prettifyjsonresult=true
 
 /admin/cluster/summary
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/bulkload/bulkload.3.pollget.http b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/bulkload/bulkload.3.pollget.http
index 4ea16d7..dc82c61 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/bulkload/bulkload.3.pollget.http
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/bulkload/bulkload.3.pollget.http
@@ -17,5 +17,6 @@
  * under the License.
  */
 //polltimeoutsecs=30
+//prettifyjsonresult=true
 
 nc:asterix_nc2 /admin/storage/partition/2
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/bulkload/bulkload.4.pollget.http b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/bulkload/bulkload.4.pollget.http
index 22558bc..c114930 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/bulkload/bulkload.4.pollget.http
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/bulkload/bulkload.4.pollget.http
@@ -17,5 +17,6 @@
  * under the License.
  */
 //polltimeoutsecs=30
+//prettifyjsonresult=true
 
 nc:asterix_nc2 /admin/storage/partition/3
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/bulkload_with_compression/bulkload.11.pollget.http b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/bulkload_with_compression/bulkload.11.pollget.http
index 32e2f78..274e20f 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/bulkload_with_compression/bulkload.11.pollget.http
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/bulkload_with_compression/bulkload.11.pollget.http
@@ -17,5 +17,6 @@
  * under the License.
  */
 //polltimeoutsecs=30
+//prettifyjsonresult=true
 
 /admin/cluster/summary
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/bulkload_with_compression/bulkload.3.pollget.http b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/bulkload_with_compression/bulkload.3.pollget.http
index 4ea16d7..dc82c61 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/bulkload_with_compression/bulkload.3.pollget.http
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/bulkload_with_compression/bulkload.3.pollget.http
@@ -17,5 +17,6 @@
  * under the License.
  */
 //polltimeoutsecs=30
+//prettifyjsonresult=true
 
 nc:asterix_nc2 /admin/storage/partition/2
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/bulkload_with_compression/bulkload.4.pollget.http b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/bulkload_with_compression/bulkload.4.pollget.http
index 22558bc..c114930 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/bulkload_with_compression/bulkload.4.pollget.http
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/bulkload_with_compression/bulkload.4.pollget.http
@@ -17,5 +17,6 @@
  * under the License.
  */
 //polltimeoutsecs=30
+//prettifyjsonresult=true
 
 nc:asterix_nc2 /admin/storage/partition/3
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/flushed_component/flushed_component.2.pollget.http b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/flushed_component/flushed_component.2.pollget.http
index 6867a5d..c007931 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/flushed_component/flushed_component.2.pollget.http
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/flushed_component/flushed_component.2.pollget.http
@@ -17,5 +17,6 @@
  * under the License.
  */
 //polltimeoutsecs=30
+//prettifyjsonresult=true
 
 nc:asterix_nc1 /admin/storage/partition/0
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/flushed_component/flushed_component.8.pollget.http b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/flushed_component/flushed_component.8.pollget.http
index 32e2f78..274e20f 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/flushed_component/flushed_component.8.pollget.http
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/flushed_component/flushed_component.8.pollget.http
@@ -17,5 +17,6 @@
  * under the License.
  */
 //polltimeoutsecs=30
+//prettifyjsonresult=true
 
 /admin/cluster/summary
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/flushed_component_compressed/flushed_component_compressed.12.pollget.http b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/flushed_component_compressed/flushed_component_compressed.12.pollget.http
index 32e2f78..274e20f 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/flushed_component_compressed/flushed_component_compressed.12.pollget.http
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/flushed_component_compressed/flushed_component_compressed.12.pollget.http
@@ -17,5 +17,6 @@
  * under the License.
  */
 //polltimeoutsecs=30
+//prettifyjsonresult=true
 
 /admin/cluster/summary
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/flushed_component_compressed/flushed_component_compressed.3.pollget.http b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/flushed_component_compressed/flushed_component_compressed.3.pollget.http
index 4ea16d7..dc82c61 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/flushed_component_compressed/flushed_component_compressed.3.pollget.http
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/flushed_component_compressed/flushed_component_compressed.3.pollget.http
@@ -17,5 +17,6 @@
  * under the License.
  */
 //polltimeoutsecs=30
+//prettifyjsonresult=true
 
 nc:asterix_nc2 /admin/storage/partition/2
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/flushed_component_compressed/flushed_component_compressed.4.pollget.http b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/flushed_component_compressed/flushed_component_compressed.4.pollget.http
index 22558bc..c114930 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/flushed_component_compressed/flushed_component_compressed.4.pollget.http
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/flushed_component_compressed/flushed_component_compressed.4.pollget.http
@@ -17,5 +17,6 @@
  * under the License.
  */
 //polltimeoutsecs=30
+//prettifyjsonresult=true
 
 nc:asterix_nc2 /admin/storage/partition/3
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/mem_component_recovery/mem_component_recovery.11.pollget.http b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/mem_component_recovery/mem_component_recovery.11.pollget.http
index 32e2f78..274e20f 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/mem_component_recovery/mem_component_recovery.11.pollget.http
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/mem_component_recovery/mem_component_recovery.11.pollget.http
@@ -17,5 +17,6 @@
  * under the License.
  */
 //polltimeoutsecs=30
+//prettifyjsonresult=true
 
 /admin/cluster/summary
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/mem_component_recovery/mem_component_recovery.3.pollget.http b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/mem_component_recovery/mem_component_recovery.3.pollget.http
index 4ea16d7..dc82c61 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/mem_component_recovery/mem_component_recovery.3.pollget.http
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/mem_component_recovery/mem_component_recovery.3.pollget.http
@@ -17,5 +17,6 @@
  * under the License.
  */
 //polltimeoutsecs=30
+//prettifyjsonresult=true
 
 nc:asterix_nc2 /admin/storage/partition/2
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/mem_component_recovery/mem_component_recovery.4.pollget.http b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/mem_component_recovery/mem_component_recovery.4.pollget.http
index 22558bc..c114930 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/mem_component_recovery/mem_component_recovery.4.pollget.http
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/mem_component_recovery/mem_component_recovery.4.pollget.http
@@ -17,5 +17,6 @@
  * under the License.
  */
 //polltimeoutsecs=30
+//prettifyjsonresult=true
 
 nc:asterix_nc2 /admin/storage/partition/3
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/metadata_failover/metadata_failover.2.pollget.http b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/metadata_failover/metadata_failover.2.pollget.http
index 6867a5d..c007931 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/metadata_failover/metadata_failover.2.pollget.http
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/metadata_failover/metadata_failover.2.pollget.http
@@ -17,5 +17,6 @@
  * under the License.
  */
 //polltimeoutsecs=30
+//prettifyjsonresult=true
 
 nc:asterix_nc1 /admin/storage/partition/0
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/metadata_failover/metadata_failover.5.get.http b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/metadata_failover/metadata_failover.5.get.http
index 4a53aed..1a63ce7 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/metadata_failover/metadata_failover.5.get.http
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/metadata_failover/metadata_failover.5.get.http
@@ -16,4 +16,6 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+//prettifyjsonresult=true
+
 nc:asterix_nc2 /admin/storage
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/metadata_failover/metadata_failover.8.pollget.http b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/metadata_failover/metadata_failover.8.pollget.http
index 32e2f78..274e20f 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/metadata_failover/metadata_failover.8.pollget.http
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/metadata_failover/metadata_failover.8.pollget.http
@@ -17,5 +17,6 @@
  * under the License.
  */
 //polltimeoutsecs=30
+//prettifyjsonresult=true
 
 /admin/cluster/summary
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/release_partition/release_partition.2.pollget.http b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/release_partition/release_partition.2.pollget.http
index 6867a5d..c007931 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/release_partition/release_partition.2.pollget.http
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/release_partition/release_partition.2.pollget.http
@@ -17,5 +17,6 @@
  * under the License.
  */
 //polltimeoutsecs=30
+//prettifyjsonresult=true
 
 nc:asterix_nc1 /admin/storage/partition/0
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/release_partition/release_partition.4.get.http b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/release_partition/release_partition.4.get.http
index 74ebe94..53d75bc 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/release_partition/release_partition.4.get.http
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/replication/release_partition/release_partition.4.get.http
@@ -16,4 +16,6 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+//prettifyjsonresult=true
+
 nc:asterix_nc1:19004 /admin/storage/partition/0
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/unnest/ASTERIXDB-2844_unnest_syntax/ASTERIXDB-2844_unnest_syntax.1.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/unnest/ASTERIXDB-2844_unnest_syntax/ASTERIXDB-2844_unnest_syntax.1.query.sqlpp
new file mode 100644
index 0000000..110eefd
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/unnest/ASTERIXDB-2844_unnest_syntax/ASTERIXDB-2844_unnest_syntax.1.query.sqlpp
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+with t as (select r, [r*10,r*10+1] ra from range(1, 2) r)
+select *
+from t unnest t.ra
+order by ra
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql/sum/scalar_sum_type/scalar_sum_type.1.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql/sum/scalar_sum_type/scalar_sum_type.1.adm
new file mode 100644
index 0000000..d9b1127
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql/sum/scalar_sum_type/scalar_sum_type.1.adm
@@ -0,0 +1 @@
+{ "$1": 6 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate/sum/scalar_sum_type/scalar_sum_type.1.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate/sum/scalar_sum_type/scalar_sum_type.1.adm
new file mode 100644
index 0000000..d9b1127
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate/sum/scalar_sum_type/scalar_sum_type.1.adm
@@ -0,0 +1 @@
+{ "$1": 6 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/replication/metadata_failover/metadata_failover.11.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/replication/metadata_failover/metadata_failover.11.adm
index d0138cb..56a6051 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/replication/metadata_failover/metadata_failover.11.adm
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/replication/metadata_failover/metadata_failover.11.adm
@@ -1,3 +1 @@
-1
-
-
+1
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/unnest/ASTERIXDB-2844_unnest_syntax/ASTERIXDB-2844_unnest_syntax.1.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/unnest/ASTERIXDB-2844_unnest_syntax/ASTERIXDB-2844_unnest_syntax.1.adm
new file mode 100644
index 0000000..6b71814
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/unnest/ASTERIXDB-2844_unnest_syntax/ASTERIXDB-2844_unnest_syntax.1.adm
@@ -0,0 +1,4 @@
+{ "t": { "r": 1, "ra": [ 10, 11 ] }, "ra": 10 }
+{ "t": { "r": 1, "ra": [ 10, 11 ] }, "ra": 11 }
+{ "t": { "r": 2, "ra": [ 20, 21 ] }, "ra": 20 }
+{ "t": { "r": 2, "ra": [ 20, 21 ] }, "ra": 21 }
\ 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 1d7f09e..2cdf027 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
@@ -834,6 +834,11 @@
       </compilation-unit>
     </test-case>
     <test-case FilePath="aggregate">
+      <compilation-unit name="sum/scalar_sum_type">
+        <output-dir compare="Text">sum/scalar_sum_type</output-dir>
+      </compilation-unit>
+    </test-case>
+    <test-case FilePath="aggregate">
       <compilation-unit name="scalar_var">
         <output-dir compare="Text">scalar_var</output-dir>
       </compilation-unit>
@@ -2097,6 +2102,11 @@
       </compilation-unit>
     </test-case>
     <test-case FilePath="aggregate-sql">
+      <compilation-unit name="sum/scalar_sum_type">
+        <output-dir compare="Text">sum/scalar_sum_type</output-dir>
+      </compilation-unit>
+    </test-case>
+    <test-case FilePath="aggregate-sql">
       <compilation-unit name="scalar_var">
         <output-dir compare="Text">scalar_var</output-dir>
       </compilation-unit>
@@ -14089,6 +14099,11 @@
       </compilation-unit>
     </test-case>
     <test-case FilePath="unnest">
+      <compilation-unit name="ASTERIXDB-2844_unnest_syntax">
+        <output-dir compare="Text">ASTERIXDB-2844_unnest_syntax</output-dir>
+      </compilation-unit>
+    </test-case>
+    <test-case FilePath="unnest">
       <compilation-unit name="left-outer-unnest">
         <output-dir compare="Text">left-outer-unnest</output-dir>
       </compilation-unit>
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj b/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
index 2089658..4dac902 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
+++ b/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
@@ -4411,12 +4411,12 @@
 UnnestClause UnnestClause(UnnestType unnestType) throws ParseException :
 {
     Token startToken = null;
-    Expression rightExpr;
-    VariableExpr rightVar;
+    Expression rightExpr = null;
+    VariableExpr rightVar = null;
     VariableExpr posVar = null;
 }
 {
-  (<UNNEST>|<CORRELATE>|<FLATTEN>) { startToken = token; } rightExpr = Expression() ((<AS>)? rightVar = Variable()) (<AT> posVar = Variable())?
+  (<UNNEST>|<CORRELATE>|<FLATTEN>) { startToken = token; } rightExpr = Expression() ((<AS>)? rightVar = Variable())? (<AT> posVar = Variable())?
   {
     if (rightVar == null) {
       rightVar = ExpressionToVariableUtil.getGeneratedVariable(rightExpr, true);
diff --git a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/MetadataManager.java b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/MetadataManager.java
index 699ad74..0ab5c7b 100644
--- a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/MetadataManager.java
+++ b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/MetadataManager.java
@@ -133,6 +133,11 @@
 
     @Override
     public MetadataTransactionContext beginTransaction() throws RemoteException {
+        try {
+            INSTANCE.init();
+        } catch (HyracksDataException e) {
+            throw new ACIDException(e);
+        }
         TxnId txnId = createTxnId();
         metadataNode.beginTransaction(txnId);
         return new MetadataTransactionContext(txnId);
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 268c1f6..63573a1 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
@@ -1832,6 +1832,11 @@
         addFunction(NEGINF_IF, DoubleIfTypeComputer.INSTANCE, true);
 
         // Aggregate Functions
+        ScalarVersionOfAggregateResultType scalarNumericSumTypeComputer =
+                new ScalarVersionOfAggregateResultType(NumericSumAggTypeComputer.INSTANCE);
+        ScalarVersionOfAggregateResultType scalarMinMaxTypeComputer =
+                new ScalarVersionOfAggregateResultType(MinMaxAggTypeComputer.INSTANCE);
+
         addPrivateFunction(LISTIFY, OrderedListConstructorTypeComputer.INSTANCE, true);
         addFunction(SCALAR_ARRAYAGG, ScalarArrayAggTypeComputer.INSTANCE, true);
         addFunction(MAX, MinMaxAggTypeComputer.INSTANCE, true);
@@ -1877,7 +1882,7 @@
 
         // SUM
         addFunction(SUM, NumericSumAggTypeComputer.INSTANCE, true);
-        addFunction(SCALAR_SUM, ScalarVersionOfAggregateResultType.INSTANCE, true);
+        addFunction(SCALAR_SUM, scalarNumericSumTypeComputer, true);
         addPrivateFunction(LOCAL_SUM, NumericSumAggTypeComputer.INSTANCE, true);
         addPrivateFunction(INTERMEDIATE_SUM, NumericSumAggTypeComputer.INSTANCE, true);
         addPrivateFunction(GLOBAL_SUM, NumericSumAggTypeComputer.INSTANCE, true);
@@ -1893,8 +1898,8 @@
         addPrivateFunction(SERIAL_INTERMEDIATE_SQL_AVG, LocalAvgTypeComputer.INSTANCE, true);
         addFunction(SCALAR_AVG, NullableDoubleTypeComputer.INSTANCE, true);
         addFunction(SCALAR_COUNT, AInt64TypeComputer.INSTANCE, true);
-        addFunction(SCALAR_MAX, ScalarVersionOfAggregateResultType.INSTANCE, true);
-        addFunction(SCALAR_MIN, ScalarVersionOfAggregateResultType.INSTANCE, true);
+        addFunction(SCALAR_MAX, scalarMinMaxTypeComputer, true);
+        addFunction(SCALAR_MIN, scalarMinMaxTypeComputer, true);
         addPrivateFunction(INTERMEDIATE_AVG, LocalAvgTypeComputer.INSTANCE, true);
         addFunction(SCALAR_STDDEV_SAMP, NullableDoubleTypeComputer.INSTANCE, true);
         addPrivateFunction(INTERMEDIATE_STDDEV_SAMP, LocalSingleVarStatisticsTypeComputer.INSTANCE, true);
@@ -1935,7 +1940,7 @@
 
         // SQL SUM
         addFunction(SQL_SUM, NumericSumAggTypeComputer.INSTANCE, true);
-        addFunction(SCALAR_SQL_SUM, ScalarVersionOfAggregateResultType.INSTANCE, true);
+        addFunction(SCALAR_SQL_SUM, scalarNumericSumTypeComputer, true);
         addPrivateFunction(LOCAL_SQL_SUM, NumericSumAggTypeComputer.INSTANCE, true);
         addPrivateFunction(INTERMEDIATE_SQL_SUM, NumericSumAggTypeComputer.INSTANCE, true);
         addPrivateFunction(GLOBAL_SQL_SUM, NumericSumAggTypeComputer.INSTANCE, true);
@@ -1959,8 +1964,8 @@
         addPrivateFunction(GLOBAL_SQL_MIN, MinMaxAggTypeComputer.INSTANCE, true);
         addFunction(SCALAR_SQL_AVG, NullableDoubleTypeComputer.INSTANCE, true);
         addFunction(SCALAR_SQL_COUNT, AInt64TypeComputer.INSTANCE, true);
-        addFunction(SCALAR_SQL_MAX, ScalarVersionOfAggregateResultType.INSTANCE, true);
-        addFunction(SCALAR_SQL_MIN, ScalarVersionOfAggregateResultType.INSTANCE, true);
+        addFunction(SCALAR_SQL_MAX, scalarMinMaxTypeComputer, true);
+        addFunction(SCALAR_SQL_MIN, scalarMinMaxTypeComputer, true);
         addPrivateFunction(INTERMEDIATE_SQL_AVG, LocalAvgTypeComputer.INSTANCE, true);
         addFunction(SQL_STDDEV_SAMP, NullableDoubleTypeComputer.INSTANCE, true);
         addPrivateFunction(GLOBAL_SQL_STDDEV_SAMP, NullableDoubleTypeComputer.INSTANCE, true);
@@ -2035,9 +2040,9 @@
         addFunction(SCALAR_SQL_COUNT_DISTINCT, AInt64TypeComputer.INSTANCE, true);
 
         addFunction(SUM_DISTINCT, NumericSumAggTypeComputer.INSTANCE, true);
-        addFunction(SCALAR_SUM_DISTINCT, ScalarVersionOfAggregateResultType.INSTANCE, true);
+        addFunction(SCALAR_SUM_DISTINCT, scalarNumericSumTypeComputer, true);
         addFunction(SQL_SUM_DISTINCT, NumericSumAggTypeComputer.INSTANCE, true);
-        addFunction(SCALAR_SQL_SUM_DISTINCT, ScalarVersionOfAggregateResultType.INSTANCE, true);
+        addFunction(SCALAR_SQL_SUM_DISTINCT, scalarNumericSumTypeComputer, true);
 
         addFunction(AVG_DISTINCT, NullableDoubleTypeComputer.INSTANCE, true);
         addFunction(SCALAR_AVG_DISTINCT, NullableDoubleTypeComputer.INSTANCE, true);
@@ -2045,14 +2050,14 @@
         addFunction(SCALAR_SQL_AVG_DISTINCT, NullableDoubleTypeComputer.INSTANCE, true);
 
         addFunction(MAX_DISTINCT, MinMaxAggTypeComputer.INSTANCE, true);
-        addFunction(SCALAR_MAX_DISTINCT, ScalarVersionOfAggregateResultType.INSTANCE, true);
+        addFunction(SCALAR_MAX_DISTINCT, scalarMinMaxTypeComputer, true);
         addFunction(SQL_MAX_DISTINCT, MinMaxAggTypeComputer.INSTANCE, true);
-        addFunction(SCALAR_SQL_MAX_DISTINCT, ScalarVersionOfAggregateResultType.INSTANCE, true);
+        addFunction(SCALAR_SQL_MAX_DISTINCT, scalarMinMaxTypeComputer, true);
 
         addFunction(MIN_DISTINCT, MinMaxAggTypeComputer.INSTANCE, true);
-        addFunction(SCALAR_MIN_DISTINCT, ScalarVersionOfAggregateResultType.INSTANCE, true);
+        addFunction(SCALAR_MIN_DISTINCT, scalarMinMaxTypeComputer, true);
         addFunction(SQL_MIN_DISTINCT, MinMaxAggTypeComputer.INSTANCE, true);
-        addFunction(SCALAR_SQL_MIN_DISTINCT, ScalarVersionOfAggregateResultType.INSTANCE, true);
+        addFunction(SCALAR_SQL_MIN_DISTINCT, scalarMinMaxTypeComputer, true);
 
         addFunction(STDDEV_SAMP_DISTINCT, NullableDoubleTypeComputer.INSTANCE, true);
         addFunction(SCALAR_STDDEV_SAMP_DISTINCT, NullableDoubleTypeComputer.INSTANCE, true);
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/AggregateResultTypeComputer.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/AggregateResultTypeComputer.java
new file mode 100644
index 0000000..8e663a7
--- /dev/null
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/AggregateResultTypeComputer.java
@@ -0,0 +1,39 @@
+/*
+ * 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.om.typecomputer.impl;
+
+import org.apache.asterix.om.typecomputer.base.AbstractResultTypeComputer;
+import org.apache.asterix.om.types.IAType;
+import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
+import org.apache.hyracks.algebricks.core.algebra.base.ILogicalExpression;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+import org.apache.hyracks.api.exceptions.SourceLocation;
+
+public abstract class AggregateResultTypeComputer extends AbstractResultTypeComputer {
+    @Override
+    protected void checkArgType(FunctionIdentifier funcId, int argIndex, IAType type, SourceLocation sourceLoc)
+            throws AlgebricksException {
+        super.checkArgType(funcId, argIndex, type, sourceLoc);
+    }
+
+    @Override
+    protected abstract IAType getResultType(ILogicalExpression expr, IAType... strippedInputTypes)
+            throws AlgebricksException;
+}
\ No newline at end of file
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/MinMaxAggTypeComputer.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/MinMaxAggTypeComputer.java
index c34b5ed..fc1eee5 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/MinMaxAggTypeComputer.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/MinMaxAggTypeComputer.java
@@ -19,7 +19,6 @@
 package org.apache.asterix.om.typecomputer.impl;
 
 import org.apache.asterix.dataflow.data.common.ILogicalBinaryComparator;
-import org.apache.asterix.om.typecomputer.base.AbstractResultTypeComputer;
 import org.apache.asterix.om.types.ATypeTag;
 import org.apache.asterix.om.types.AUnionType;
 import org.apache.asterix.om.types.BuiltinType;
@@ -27,7 +26,7 @@
 import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
 import org.apache.hyracks.algebricks.core.algebra.base.ILogicalExpression;
 
-public class MinMaxAggTypeComputer extends AbstractResultTypeComputer {
+public class MinMaxAggTypeComputer extends AggregateResultTypeComputer {
 
     public static final MinMaxAggTypeComputer INSTANCE = new MinMaxAggTypeComputer();
 
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/NumericSumAggTypeComputer.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/NumericSumAggTypeComputer.java
index 1c67e56..a4b5e34 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/NumericSumAggTypeComputer.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/NumericSumAggTypeComputer.java
@@ -18,42 +18,20 @@
  */
 package org.apache.asterix.om.typecomputer.impl;
 
-import org.apache.asterix.om.exceptions.UnsupportedTypeException;
-import org.apache.asterix.om.typecomputer.base.AbstractResultTypeComputer;
 import org.apache.asterix.om.types.ATypeTag;
 import org.apache.asterix.om.types.AUnionType;
 import org.apache.asterix.om.types.BuiltinType;
 import org.apache.asterix.om.types.IAType;
 import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
 import org.apache.hyracks.algebricks.core.algebra.base.ILogicalExpression;
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.exceptions.SourceLocation;
 
-public class NumericSumAggTypeComputer extends AbstractResultTypeComputer {
+public class NumericSumAggTypeComputer extends AggregateResultTypeComputer {
     public static final NumericSumAggTypeComputer INSTANCE = new NumericSumAggTypeComputer();
 
     private NumericSumAggTypeComputer() {
     }
 
     @Override
-    protected void checkArgType(FunctionIdentifier funcId, int argIndex, IAType type, SourceLocation sourceLoc)
-            throws AlgebricksException {
-        ATypeTag tag = type.getTypeTag();
-        switch (tag) {
-            case DOUBLE:
-            case FLOAT:
-            case BIGINT:
-            case INTEGER:
-            case SMALLINT:
-            case TINYINT:
-            case ANY:
-                break;
-            default:
-                throw new UnsupportedTypeException(sourceLoc, funcId, tag);
-        }
-    }
-
-    @Override
     protected IAType getResultType(ILogicalExpression expr, IAType... strippedInputTypes) throws AlgebricksException {
         ATypeTag tag = strippedInputTypes[0].getTypeTag();
         switch (tag) {
@@ -61,15 +39,12 @@
             case SMALLINT:
             case INTEGER:
             case BIGINT:
-                IAType int64Type = BuiltinType.AINT64;
-                return AUnionType.createNullableType(int64Type, "AggResult");
+                return AUnionType.createNullableType(BuiltinType.AINT64);
             case FLOAT:
             case DOUBLE:
-                IAType doubleType = BuiltinType.ADOUBLE;
-                return AUnionType.createNullableType(doubleType, "AggResult");
+                return AUnionType.createNullableType(BuiltinType.ADOUBLE);
             case ANY:
-                IAType anyType = strippedInputTypes[0];
-                return AUnionType.createNullableType(anyType, "AggResult");
+                return BuiltinType.ANY;
             default:
                 // All other possible cases.
                 return BuiltinType.ANULL;
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/ScalarVersionOfAggregateResultType.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/ScalarVersionOfAggregateResultType.java
index 5b90974..fda0285 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/ScalarVersionOfAggregateResultType.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/ScalarVersionOfAggregateResultType.java
@@ -18,9 +18,7 @@
  */
 package org.apache.asterix.om.typecomputer.impl;
 
-import org.apache.asterix.om.exceptions.TypeMismatchException;
 import org.apache.asterix.om.typecomputer.base.AbstractResultTypeComputer;
-import org.apache.asterix.om.types.ATypeTag;
 import org.apache.asterix.om.types.AUnionType;
 import org.apache.asterix.om.types.AbstractCollectionType;
 import org.apache.asterix.om.types.BuiltinType;
@@ -32,32 +30,48 @@
 
 public class ScalarVersionOfAggregateResultType extends AbstractResultTypeComputer {
 
-    public static final ScalarVersionOfAggregateResultType INSTANCE = new ScalarVersionOfAggregateResultType();
+    private final AggregateResultTypeComputer aggResultTypeComputer;
 
-    private ScalarVersionOfAggregateResultType() {
+    public ScalarVersionOfAggregateResultType(AggregateResultTypeComputer aggResultTypeComputer) {
+        this.aggResultTypeComputer = aggResultTypeComputer;
     }
 
     @Override
-    public void checkArgType(FunctionIdentifier funcId, int argIndex, IAType type, SourceLocation sourceLoc)
+    protected void checkArgType(FunctionIdentifier funcId, int argIndex, IAType argType, SourceLocation sourceLoc)
             throws AlgebricksException {
-        ATypeTag tag = type.getTypeTag();
-        if (tag != ATypeTag.ANY && tag != ATypeTag.ARRAY && tag != ATypeTag.MULTISET) {
-            throw new TypeMismatchException(sourceLoc, funcId, argIndex, tag, ATypeTag.ARRAY, ATypeTag.MULTISET);
+        if (argIndex == 0) {
+            switch (argType.getTypeTag()) {
+                case ARRAY:
+                case MULTISET:
+                    AbstractCollectionType act = (AbstractCollectionType) argType;
+                    aggResultTypeComputer.checkArgType(funcId, argIndex, act.getItemType(), sourceLoc);
+                    break;
+            }
         }
     }
 
     @Override
     protected IAType getResultType(ILogicalExpression expr, IAType... strippedInputTypes) throws AlgebricksException {
-        ATypeTag tag = strippedInputTypes[0].getTypeTag();
-        if (tag == ATypeTag.ANY) {
-            return BuiltinType.ANY;
+        IAType argType = strippedInputTypes[0];
+        switch (argType.getTypeTag()) {
+            case ARRAY:
+            case MULTISET:
+                AbstractCollectionType act = (AbstractCollectionType) argType;
+                IAType[] strippedInputTypes2 = strippedInputTypes.clone();
+                strippedInputTypes2[0] = act.getItemType();
+                IAType resultType = aggResultTypeComputer.getResultType(expr, strippedInputTypes2);
+                switch (resultType.getTypeTag()) {
+                    case NULL:
+                    case MISSING:
+                    case ANY:
+                        return resultType;
+                    case UNION:
+                        return AUnionType.createUnknownableType(((AUnionType) resultType).getActualType());
+                    default:
+                        return AUnionType.createUnknownableType(resultType);
+                }
+            default:
+                return BuiltinType.ANY;
         }
-        if (tag != ATypeTag.ARRAY && tag != ATypeTag.MULTISET) {
-            // this condition being true would've thrown an exception above, no?
-            return strippedInputTypes[0];
-        }
-        AbstractCollectionType act = (AbstractCollectionType) strippedInputTypes[0];
-        IAType t = act.getItemType();
-        return AUnionType.createUnknownableType(t);
     }
 }
diff --git a/asterixdb/asterix-replication/src/main/java/org/apache/asterix/replication/management/LogReplicationManager.java b/asterixdb/asterix-replication/src/main/java/org/apache/asterix/replication/management/LogReplicationManager.java
index be372df..6a23ae6 100644
--- a/asterixdb/asterix-replication/src/main/java/org/apache/asterix/replication/management/LogReplicationManager.java
+++ b/asterixdb/asterix-replication/src/main/java/org/apache/asterix/replication/management/LogReplicationManager.java
@@ -211,7 +211,7 @@
         }
     }
 
-    private synchronized void handleFailure(ISocketChannel replicaSocket, IOException e) {
+    private synchronized void handleFailure(ISocketChannel replicaSocket, Exception e) {
         if (failedSockets.contains(replicaSocket)) {
             return;
         }
@@ -249,7 +249,7 @@
                 }
             } catch (AsynchronousCloseException e) {
                 LOGGER.debug(() -> "Stopped listening on socket:" + dest, e);
-            } catch (IOException e) {
+            } catch (Exception e) {
                 handleFailure(replicaSocket, e);
             }
         }
diff --git a/asterixdb/asterix-runtime/src/test/java/org/apache/asterix/runtime/functions/ScalarAggregateTypeComputerTest.java b/asterixdb/asterix-runtime/src/test/java/org/apache/asterix/runtime/functions/ScalarAggregateTypeComputerTest.java
new file mode 100644
index 0000000..cbde36c
--- /dev/null
+++ b/asterixdb/asterix-runtime/src/test/java/org/apache/asterix/runtime/functions/ScalarAggregateTypeComputerTest.java
@@ -0,0 +1,239 @@
+/*
+ * 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.runtime.functions;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+import org.apache.asterix.dataflow.data.common.ExpressionTypeComputer;
+import org.apache.asterix.om.base.ABoolean;
+import org.apache.asterix.om.base.ADate;
+import org.apache.asterix.om.base.ADateTime;
+import org.apache.asterix.om.base.ADayTimeDuration;
+import org.apache.asterix.om.base.ADouble;
+import org.apache.asterix.om.base.ADuration;
+import org.apache.asterix.om.base.AFloat;
+import org.apache.asterix.om.base.AInt16;
+import org.apache.asterix.om.base.AInt32;
+import org.apache.asterix.om.base.AInt64;
+import org.apache.asterix.om.base.AInt8;
+import org.apache.asterix.om.base.AInterval;
+import org.apache.asterix.om.base.AOrderedList;
+import org.apache.asterix.om.base.ARecord;
+import org.apache.asterix.om.base.AString;
+import org.apache.asterix.om.base.ATime;
+import org.apache.asterix.om.base.AUnorderedList;
+import org.apache.asterix.om.base.AYearMonthDuration;
+import org.apache.asterix.om.base.IAObject;
+import org.apache.asterix.om.constants.AsterixConstantValue;
+import org.apache.asterix.om.exceptions.UnsupportedTypeException;
+import org.apache.asterix.om.functions.BuiltinFunctionInfo;
+import org.apache.asterix.om.functions.BuiltinFunctions;
+import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
+import org.apache.asterix.om.types.AOrderedListType;
+import org.apache.asterix.om.types.ARecordType;
+import org.apache.asterix.om.types.ATypeTag;
+import org.apache.asterix.om.types.AUnionType;
+import org.apache.asterix.om.types.AUnorderedListType;
+import org.apache.asterix.om.types.BuiltinType;
+import org.apache.asterix.om.types.IAType;
+import org.apache.commons.lang3.mutable.MutableObject;
+import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
+import org.apache.hyracks.algebricks.core.algebra.base.ILogicalExpression;
+import org.apache.hyracks.algebricks.core.algebra.base.LogicalVariable;
+import org.apache.hyracks.algebricks.core.algebra.expressions.AbstractFunctionCallExpression;
+import org.apache.hyracks.algebricks.core.algebra.expressions.AggregateFunctionCallExpression;
+import org.apache.hyracks.algebricks.core.algebra.expressions.ConstantExpression;
+import org.apache.hyracks.algebricks.core.algebra.expressions.IVariableTypeEnvironment;
+import org.apache.hyracks.algebricks.core.algebra.expressions.ScalarFunctionCallExpression;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/**
+ * Test alignment of type computers between aggregate functions and their scalar versions
+ */
+@RunWith(Parameterized.class)
+public class ScalarAggregateTypeComputerTest {
+
+    private static final IAObject[] ITEMS = {
+            //
+            ABoolean.TRUE,
+            //
+            new AInt8((byte) 0),
+            //
+            new AInt16((short) 0),
+            //
+            new AInt32(0),
+            //
+            new AInt64(0),
+            //
+            new AFloat(0),
+            //
+            new ADouble(0),
+            //
+            new AString(""),
+            //
+            new ADate(0),
+            //
+            new ADateTime(0),
+            //
+            new ATime(0),
+            //
+            new ADuration(0, 0),
+            //
+            new AYearMonthDuration(0),
+            //
+            new ADayTimeDuration(0),
+            //
+            new AInterval(0, 0, ATypeTag.DATETIME.serialize()),
+            //
+            new AOrderedList(AOrderedListType.FULL_OPEN_ORDEREDLIST_TYPE, Collections.singletonList(new AString(""))),
+            //
+            new AUnorderedList(AUnorderedListType.FULLY_OPEN_UNORDEREDLIST_TYPE,
+                    Collections.singletonList(new AString(""))),
+            //
+            new ARecord(
+                    new ARecordType("record-type", new String[] { "a" }, new IAType[] { BuiltinType.ASTRING }, false),
+                    new IAObject[] { new AString("") }) };
+
+    // Test parameters
+    @Parameterized.Parameter
+    public String testName;
+
+    @Parameterized.Parameter(1)
+    public FunctionIdentifier scalarfid;
+
+    @Parameterized.Parameter(2)
+    public FunctionIdentifier aggfid;
+
+    @Parameterized.Parameter(3)
+    public IAObject item;
+
+    @Parameterized.Parameters(name = "ScalarAggregateTypeComputerTest {index}: {0}({3})")
+    public static Collection<Object[]> tests() {
+        List<Object[]> tests = new ArrayList<>();
+
+        FunctionCollection fcoll = FunctionCollection.createDefaultFunctionCollection();
+        for (IFunctionDescriptorFactory fdf : fcoll.getFunctionDescriptorFactories()) {
+            FunctionIdentifier fid = fdf.createFunctionDescriptor().getIdentifier();
+            FunctionIdentifier aggfid = BuiltinFunctions.getAggregateFunction(fid);
+            if (aggfid == null) {
+                continue;
+            }
+            for (IAObject item : ITEMS) {
+                tests.add(new Object[] { fid.getName(), fid, aggfid, item });
+            }
+
+        }
+        return tests;
+    }
+
+    @Test
+    public void test() throws Exception {
+
+        AOrderedListType listType = new AOrderedListType(item.getType(), null);
+        AOrderedList list = new AOrderedList(listType, Collections.singletonList(item));
+        ConstantExpression scalarArgExpr = new ConstantExpression(new AsterixConstantValue(list));
+        BuiltinFunctionInfo scalarfi = BuiltinFunctions.getBuiltinFunctionInfo(scalarfid);
+        ScalarFunctionCallExpression scalarCallExpr =
+                new ScalarFunctionCallExpression(scalarfi, new MutableObject<>(scalarArgExpr));
+        IAType scalarResultType = computeType(scalarCallExpr);
+
+        ConstantExpression aggArgExpr = new ConstantExpression(new AsterixConstantValue(item));
+        BuiltinFunctionInfo aggfi = BuiltinFunctions.getBuiltinFunctionInfo(aggfid);
+        AggregateFunctionCallExpression aggCallExpr = new AggregateFunctionCallExpression(aggfi, false,
+                Collections.singletonList(new MutableObject<>(aggArgExpr)));
+        IAType aggResultType = computeType(aggCallExpr);
+
+        if (!compareResultTypes(scalarResultType, aggResultType)) {
+            Assert.fail(String.format("%s(%s) returns %s != %s(%s) returns %s", scalarfid.getName(), item.getType(),
+                    formatResultType(scalarResultType), aggfid.getName(), item.getType(),
+                    formatResultType(aggResultType)));
+        }
+    }
+
+    private boolean compareResultTypes(IAType t1, IAType t2) {
+        // null means ERROR
+        if (t1 == null) {
+            // OK if both types are ERROR
+            return t2 == null;
+        } else if (t2 == null) {
+            return false;
+        }
+        boolean t1Union = false, t2Union = false;
+        if (t1.getTypeTag() == ATypeTag.UNION) {
+            t1Union = true;
+            t1 = ((AUnionType) t1).getActualType();
+        }
+        if (t2.getTypeTag() == ATypeTag.UNION) {
+            t2Union = true;
+            t2 = ((AUnionType) t2).getActualType();
+        }
+        return (t1Union == t2Union) && t1.deepEqual(t2);
+    }
+
+    private String formatResultType(IAType t) {
+        return t == null ? "ERROR" : t.toString();
+    }
+
+    private IAType computeType(AbstractFunctionCallExpression callExpr) throws AlgebricksException {
+        try {
+            BuiltinFunctionInfo fi = Objects.requireNonNull((BuiltinFunctionInfo) callExpr.getFunctionInfo());
+            return fi.getResultTypeComputer().computeType(callExpr, EMPTY_TYPE_ENV, null);
+        } catch (UnsupportedTypeException e) {
+            return null;
+        }
+    }
+
+    private static final IVariableTypeEnvironment EMPTY_TYPE_ENV = new IVariableTypeEnvironment() {
+
+        @Override
+        public boolean substituteProducedVariable(LogicalVariable v1, LogicalVariable v2) {
+            throw new IllegalStateException();
+        }
+
+        @Override
+        public void setVarType(LogicalVariable var, Object type) {
+            throw new IllegalStateException();
+        }
+
+        @Override
+        public Object getVarType(LogicalVariable var, List<LogicalVariable> nonNullVariables,
+                List<List<LogicalVariable>> correlatedNullableVariableLists) {
+            throw new IllegalStateException();
+        }
+
+        @Override
+        public Object getVarType(LogicalVariable var) {
+            throw new IllegalStateException();
+        }
+
+        @Override
+        public Object getType(ILogicalExpression expr) throws AlgebricksException {
+            return ExpressionTypeComputer.INSTANCE.getType(expr, null, this);
+        }
+    };
+}
diff --git a/asterixdb/asterix-server/src/test/resources/integrationts/NcLifecycle/queries/networking/reuse_data_port/reuse_data_port.4.pollget.http b/asterixdb/asterix-server/src/test/resources/integrationts/NcLifecycle/queries/networking/reuse_data_port/reuse_data_port.4.pollget.http
index 777e3dd..6f904df 100644
--- a/asterixdb/asterix-server/src/test/resources/integrationts/NcLifecycle/queries/networking/reuse_data_port/reuse_data_port.4.pollget.http
+++ b/asterixdb/asterix-server/src/test/resources/integrationts/NcLifecycle/queries/networking/reuse_data_port/reuse_data_port.4.pollget.http
@@ -17,5 +17,6 @@
  * under the License.
  */
 //polltimeoutsecs=60
+//prettifyjsonresult=true
 
 /admin/cluster/summary
\ No newline at end of file
diff --git a/asterixdb/asterix-server/src/test/resources/integrationts/NcLifecycle/queries/networking/reuse_data_port/reuse_data_port.7.pollget.http b/asterixdb/asterix-server/src/test/resources/integrationts/NcLifecycle/queries/networking/reuse_data_port/reuse_data_port.7.pollget.http
index 777e3dd..6f904df 100644
--- a/asterixdb/asterix-server/src/test/resources/integrationts/NcLifecycle/queries/networking/reuse_data_port/reuse_data_port.7.pollget.http
+++ b/asterixdb/asterix-server/src/test/resources/integrationts/NcLifecycle/queries/networking/reuse_data_port/reuse_data_port.7.pollget.http
@@ -17,5 +17,6 @@
  * under the License.
  */
 //polltimeoutsecs=60
+//prettifyjsonresult=true
 
 /admin/cluster/summary
\ No newline at end of file
diff --git a/asterixdb/asterix-server/src/test/resources/integrationts/NcLifecycle/results/networking/reuse_data_port/reuse_data_port.5.adm b/asterixdb/asterix-server/src/test/resources/integrationts/NcLifecycle/results/networking/reuse_data_port/reuse_data_port.4.json
similarity index 100%
rename from asterixdb/asterix-server/src/test/resources/integrationts/NcLifecycle/results/networking/reuse_data_port/reuse_data_port.5.adm
rename to asterixdb/asterix-server/src/test/resources/integrationts/NcLifecycle/results/networking/reuse_data_port/reuse_data_port.4.json
diff --git a/asterixdb/asterix-server/src/test/resources/integrationts/NcLifecycle/results/networking/reuse_data_port/reuse_data_port.7.adm b/asterixdb/asterix-server/src/test/resources/integrationts/NcLifecycle/results/networking/reuse_data_port/reuse_data_port.7.json
similarity index 100%
rename from asterixdb/asterix-server/src/test/resources/integrationts/NcLifecycle/results/networking/reuse_data_port/reuse_data_port.7.adm
rename to asterixdb/asterix-server/src/test/resources/integrationts/NcLifecycle/results/networking/reuse_data_port/reuse_data_port.7.json
diff --git a/asterixdb/asterix-server/src/test/resources/integrationts/replication/queries/failover/resync_failed_replica/resync_failed_replica.10.pollget.http b/asterixdb/asterix-server/src/test/resources/integrationts/replication/queries/failover/resync_failed_replica/resync_failed_replica.10.pollget.http
index 6867a5d..c007931 100644
--- a/asterixdb/asterix-server/src/test/resources/integrationts/replication/queries/failover/resync_failed_replica/resync_failed_replica.10.pollget.http
+++ b/asterixdb/asterix-server/src/test/resources/integrationts/replication/queries/failover/resync_failed_replica/resync_failed_replica.10.pollget.http
@@ -17,5 +17,6 @@
  * under the License.
  */
 //polltimeoutsecs=30
+//prettifyjsonresult=true
 
 nc:asterix_nc1 /admin/storage/partition/0
\ No newline at end of file
diff --git a/asterixdb/asterix-server/src/test/resources/integrationts/replication/queries/failover/resync_failed_replica/resync_failed_replica.13.pollget.http b/asterixdb/asterix-server/src/test/resources/integrationts/replication/queries/failover/resync_failed_replica/resync_failed_replica.13.pollget.http
index 32e2f78..274e20f 100644
--- a/asterixdb/asterix-server/src/test/resources/integrationts/replication/queries/failover/resync_failed_replica/resync_failed_replica.13.pollget.http
+++ b/asterixdb/asterix-server/src/test/resources/integrationts/replication/queries/failover/resync_failed_replica/resync_failed_replica.13.pollget.http
@@ -17,5 +17,6 @@
  * under the License.
  */
 //polltimeoutsecs=30
+//prettifyjsonresult=true
 
 /admin/cluster/summary
\ No newline at end of file
diff --git a/asterixdb/asterix-server/src/test/resources/integrationts/replication/queries/failover/resync_failed_replica/resync_failed_replica.2.pollget.http b/asterixdb/asterix-server/src/test/resources/integrationts/replication/queries/failover/resync_failed_replica/resync_failed_replica.2.pollget.http
index 6867a5d..c007931 100644
--- a/asterixdb/asterix-server/src/test/resources/integrationts/replication/queries/failover/resync_failed_replica/resync_failed_replica.2.pollget.http
+++ b/asterixdb/asterix-server/src/test/resources/integrationts/replication/queries/failover/resync_failed_replica/resync_failed_replica.2.pollget.http
@@ -17,5 +17,6 @@
  * under the License.
  */
 //polltimeoutsecs=30
+//prettifyjsonresult=true
 
 nc:asterix_nc1 /admin/storage/partition/0
\ No newline at end of file
diff --git a/asterixdb/asterix-server/src/test/resources/integrationts/replication/queries/failover/resync_failed_replica/resync_failed_replica.5.pollget.http b/asterixdb/asterix-server/src/test/resources/integrationts/replication/queries/failover/resync_failed_replica/resync_failed_replica.5.pollget.http
index 777e3dd..6f904df 100644
--- a/asterixdb/asterix-server/src/test/resources/integrationts/replication/queries/failover/resync_failed_replica/resync_failed_replica.5.pollget.http
+++ b/asterixdb/asterix-server/src/test/resources/integrationts/replication/queries/failover/resync_failed_replica/resync_failed_replica.5.pollget.http
@@ -17,5 +17,6 @@
  * under the License.
  */
 //polltimeoutsecs=60
+//prettifyjsonresult=true
 
 /admin/cluster/summary
\ No newline at end of file
diff --git a/asterixdb/asterix-server/src/test/resources/integrationts/replication/queries/failover/resync_failed_replica/resync_failed_replica.6.pollget.http b/asterixdb/asterix-server/src/test/resources/integrationts/replication/queries/failover/resync_failed_replica/resync_failed_replica.6.pollget.http
index 6867a5d..c007931 100644
--- a/asterixdb/asterix-server/src/test/resources/integrationts/replication/queries/failover/resync_failed_replica/resync_failed_replica.6.pollget.http
+++ b/asterixdb/asterix-server/src/test/resources/integrationts/replication/queries/failover/resync_failed_replica/resync_failed_replica.6.pollget.http
@@ -17,5 +17,6 @@
  * under the License.
  */
 //polltimeoutsecs=30
+//prettifyjsonresult=true
 
 nc:asterix_nc1 /admin/storage/partition/0
\ No newline at end of file
diff --git a/asterixdb/asterix-server/src/test/resources/integrationts/replication/queries/failover/resync_failed_replica/resync_failed_replica.8.pollget.http b/asterixdb/asterix-server/src/test/resources/integrationts/replication/queries/failover/resync_failed_replica/resync_failed_replica.8.pollget.http
index 777e3dd..6f904df 100644
--- a/asterixdb/asterix-server/src/test/resources/integrationts/replication/queries/failover/resync_failed_replica/resync_failed_replica.8.pollget.http
+++ b/asterixdb/asterix-server/src/test/resources/integrationts/replication/queries/failover/resync_failed_replica/resync_failed_replica.8.pollget.http
@@ -17,5 +17,6 @@
  * under the License.
  */
 //polltimeoutsecs=60
+//prettifyjsonresult=true
 
 /admin/cluster/summary
\ No newline at end of file
diff --git a/asterixdb/pom.xml b/asterixdb/pom.xml
index 5cc17af..f89f0ac 100644
--- a/asterixdb/pom.xml
+++ b/asterixdb/pom.xml
@@ -200,6 +200,8 @@
             <exclude>**/*.iml</exclude>
             <exclude>**/*.json</exclude>
             <exclude>**/*.adm</exclude>
+            <exclude>**/*.regexadm</exclude>
+            <exclude>**/*.regexjson</exclude>
             <exclude>**/*.template</exclude>
             <exclude>asterix-installer/**</exclude> <!-- in case -DskipInstaller -->
           </excludes>
diff --git a/hyracks-fullstack/hyracks/hyracks-util/src/main/java/org/apache/hyracks/util/JSONUtil.java b/hyracks-fullstack/hyracks/hyracks-util/src/main/java/org/apache/hyracks/util/JSONUtil.java
index b66d31d..5c1b292 100644
--- a/hyracks-fullstack/hyracks/hyracks-util/src/main/java/org/apache/hyracks/util/JSONUtil.java
+++ b/hyracks-fullstack/hyracks/hyracks-util/src/main/java/org/apache/hyracks/util/JSONUtil.java
@@ -28,27 +28,18 @@
 import java.util.stream.LongStream;
 import java.util.stream.Stream;
 
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.MapperFeature;
 import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.ObjectWriter;
 import com.fasterxml.jackson.databind.SerializationFeature;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 
 public class JSONUtil {
 
-    private static final Logger LOGGER = LogManager.getLogger();
     private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
-
-    private static final String INDENT = "\t";
-
     private static final ObjectMapper SORTED_MAPPER = new ObjectMapper();
-    private static final ObjectWriter PRETTY_SORTED_WRITER;
 
     private JSONUtil() {
     }
@@ -56,11 +47,10 @@
     static {
         SORTED_MAPPER.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);
         SORTED_MAPPER.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true);
-        PRETTY_SORTED_WRITER = SORTED_MAPPER.writerWithDefaultPrettyPrinter();
     }
 
     public static String convertNode(final JsonNode node) throws JsonProcessingException {
-        return PRETTY_SORTED_WRITER.writeValueAsString(SORTED_MAPPER.treeToValue(node, Object.class));
+        return SORTED_MAPPER.writeValueAsString(SORTED_MAPPER.treeToValue(node, Object.class));
     }
 
     public static String convertNodeOrThrow(final JsonNode node) {
@@ -72,87 +62,7 @@
     }
 
     public static void writeNode(final Writer writer, final JsonNode node) throws IOException {
-        PRETTY_SORTED_WRITER.writeValue(writer, SORTED_MAPPER.treeToValue(node, Object.class));
-    }
-
-    public static String indent(String str, int initialIndent) {
-        ObjectMapper om = new ObjectMapper();
-        try {
-            return appendObj(new StringBuilder(), om.readTree(str), initialIndent).toString();
-        } catch (IOException e) {
-            LOGGER.trace(String.valueOf(e));
-            LOGGER.trace("Could not indent JSON string, returning the input string: " + str);
-            return str;
-        }
-    }
-
-    private static StringBuilder appendOrd(StringBuilder sb, JsonNode o, int indent) {
-        if (o.isObject()) {
-            return appendObj(sb, o, indent);
-        } else if (o.isArray()) {
-            return appendAry(sb, o, indent);
-        } else if (o.isTextual()) {
-            return quoteAndEscape(sb, o.asText());
-        } else if (o.isNull() || o.isIntegralNumber() || o.isBoolean()) {
-            return sb.append(String.valueOf(o));
-        }
-        throw new UnsupportedOperationException(o.getClass().getSimpleName());
-    }
-
-    private static StringBuilder appendObj(final StringBuilder sb, final JsonNode outer, final int indent) {
-        sb.append("{\n");
-        boolean first = true;
-        for (JsonNode inner : outer) {
-            final String key = inner.asText();
-            if (first) {
-                first = false;
-            } else {
-                sb.append(",\n");
-            }
-            indent(sb, indent + 1);
-            quote(sb, key);
-            sb.append(": ");
-            appendVal(sb, outer.get(key), indent);
-        }
-        sb.append("\n");
-        return indent(sb, indent).append("}");
-    }
-
-    private static StringBuilder appendVal(final StringBuilder sb, final JsonNode value, final int indent) {
-        if (value.isArray()) {
-            appendAry(sb, value, indent + 1);
-        } else if (value.isObject()) {
-            appendObj(sb, value, indent + 1);
-        } else {
-            appendOrd(sb, value, indent + 1);
-        }
-        return sb;
-    }
-
-    private static StringBuilder appendAry(final StringBuilder sb, JsonNode jarr, int indent) {
-        sb.append("[\n");
-        for (int i = 0; i < jarr.size(); ++i) {
-            if (i > 0) {
-                sb.append(",\n");
-            }
-            indent(sb, indent + 1);
-            appendVal(sb, jarr.get(i), indent);
-        }
-        sb.append("\n");
-        return indent(sb, indent).append("]");
-    }
-
-    private static StringBuilder quote(StringBuilder sb, String str) {
-        return sb.append('"').append(str).append('"');
-    }
-
-    private static StringBuilder indent(StringBuilder sb, int i) {
-        int indent = i;
-        while (indent > 0) {
-            sb.append(INDENT);
-            --indent;
-        }
-        return sb;
+        SORTED_MAPPER.writeValue(writer, SORTED_MAPPER.treeToValue(node, Object.class));
     }
 
     public static String quoteAndEscape(String str) {