[NO ISSUE][COMP] Introduce AbstractCallExpression

- user model changes: no
- storage format changes: no
- interface changes: no

Details:
- Introduce AbstractCallExpression - base class for
  CallExpr and WindowExpression
- Modify implementations of IQueryRewriter.getFunctionCalls()
  and IFunctionCollector.getFunctionCalls() to return
  WindowExpression in addition to CallExpr

Change-Id: I40599e7f8083d8bb28aa3c8a87eed8afdc59f739
Reviewed-on: https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/10044
Integration-Tests: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Tested-by: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Reviewed-by: Dmitry Lychagin <dmitry.lychagin@couchbase.com>
Reviewed-by: Ali Alsuliman <ali.al.solaiman@gmail.com>
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 c000d8b..40a3c10 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
@@ -2168,7 +2168,7 @@
                 apiFramework.reWriteQuery(declaredFunctions, metadataProvider, wrappedQuery, sessionOutput, false,
                         paramVars, warningCollector);
                 List<List<Triple<DataverseName, String, String>>> dependencies = FunctionUtil.getFunctionDependencies(
-                        rewriterFactory.createQueryRewriter(), cfs.getFunctionBodyExpression(), metadataProvider);
+                        rewriterFactory.createQueryRewriter(), cfs.getFunctionBodyExpression());
 
                 newInlineTypes = Collections.emptyMap();
                 function = new Function(functionSignature, paramNames, null, null, cfs.getFunctionBody(),
diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/base/IQueryRewriter.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/base/IQueryRewriter.java
index 7500ab9..3e1851c 100644
--- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/base/IQueryRewriter.java
+++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/base/IQueryRewriter.java
@@ -23,7 +23,7 @@
 import java.util.Set;
 
 import org.apache.asterix.common.exceptions.CompilationException;
-import org.apache.asterix.lang.common.expression.CallExpr;
+import org.apache.asterix.lang.common.expression.AbstractCallExpression;
 import org.apache.asterix.lang.common.expression.VariableExpr;
 import org.apache.asterix.lang.common.rewrites.LangRewritingContext;
 import org.apache.asterix.lang.common.statement.FunctionDecl;
@@ -52,11 +52,10 @@
     /**
      * Find the function calls used by a given expression
      */
-    Set<CallExpr> getFunctionCalls(Expression expression) throws CompilationException;
+    Set<AbstractCallExpression> getFunctionCalls(Expression expression) throws CompilationException;
 
     /**
      * Find all external variables (positional and named variables) in given expression
      */
     Set<VariableExpr> getExternalVariables(Expression expr) throws CompilationException;
-
 }
diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/expression/AbstractCallExpression.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/expression/AbstractCallExpression.java
new file mode 100644
index 0000000..c8de5c5
--- /dev/null
+++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/expression/AbstractCallExpression.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.asterix.lang.common.expression;
+
+import java.util.List;
+import java.util.Objects;
+
+import org.apache.asterix.common.functions.FunctionSignature;
+import org.apache.asterix.lang.common.base.AbstractExpression;
+import org.apache.asterix.lang.common.base.Expression;
+
+public abstract class AbstractCallExpression extends AbstractExpression {
+
+    protected FunctionSignature functionSignature;
+
+    protected List<Expression> exprList;
+
+    protected Expression aggFilterExpr;
+
+    protected AbstractCallExpression(FunctionSignature functionSignature, List<Expression> exprList,
+            Expression aggFilterExpr) {
+        this.functionSignature = Objects.requireNonNull(functionSignature);
+        this.exprList = Objects.requireNonNull(exprList);
+        this.aggFilterExpr = aggFilterExpr;
+    }
+
+    public FunctionSignature getFunctionSignature() {
+        return functionSignature;
+    }
+
+    public void setFunctionSignature(FunctionSignature functionSignature) {
+        this.functionSignature = Objects.requireNonNull(functionSignature);
+    }
+
+    public List<Expression> getExprList() {
+        return exprList;
+    }
+
+    public void setExprList(List<Expression> exprList) {
+        this.exprList = Objects.requireNonNull(exprList);
+    }
+
+    public boolean hasAggregateFilterExpr() {
+        return aggFilterExpr != null;
+    }
+
+    public Expression getAggregateFilterExpr() {
+        return aggFilterExpr;
+    }
+
+    public void setAggregateFilterExpr(Expression aggFilterExpr) {
+        this.aggFilterExpr = aggFilterExpr;
+    }
+}
diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/expression/CallExpr.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/expression/CallExpr.java
index d0c29d5..6895a65 100644
--- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/expression/CallExpr.java
+++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/expression/CallExpr.java
@@ -23,39 +23,17 @@
 
 import org.apache.asterix.common.exceptions.CompilationException;
 import org.apache.asterix.common.functions.FunctionSignature;
-import org.apache.asterix.lang.common.base.AbstractExpression;
 import org.apache.asterix.lang.common.base.Expression;
 import org.apache.asterix.lang.common.visitor.base.ILangVisitor;
 
-public class CallExpr extends AbstractExpression {
-    private FunctionSignature functionSignature;
-    private List<Expression> exprList;
-    private Expression aggFilterExpr;
+public class CallExpr extends AbstractCallExpression {
 
     public CallExpr(FunctionSignature functionSignature, List<Expression> exprList) {
-        this(functionSignature, exprList, null);
+        super(functionSignature, exprList, null);
     }
 
     public CallExpr(FunctionSignature functionSignature, List<Expression> exprList, Expression aggFilterExpr) {
-        this.functionSignature = functionSignature;
-        this.exprList = exprList;
-        this.aggFilterExpr = aggFilterExpr;
-    }
-
-    public FunctionSignature getFunctionSignature() {
-        return functionSignature;
-    }
-
-    public List<Expression> getExprList() {
-        return exprList;
-    }
-
-    public boolean hasAggregateFilterExpr() {
-        return aggFilterExpr != null;
-    }
-
-    public Expression getAggregateFilterExpr() {
-        return aggFilterExpr;
+        super(functionSignature, exprList, aggFilterExpr);
     }
 
     @Override
@@ -63,18 +41,6 @@
         return Kind.CALL_EXPRESSION;
     }
 
-    public void setFunctionSignature(FunctionSignature functionSignature) {
-        this.functionSignature = functionSignature;
-    }
-
-    public void setExprList(List<Expression> exprList) {
-        this.exprList = exprList;
-    }
-
-    public void setAggregateFilterExpr(Expression aggFilterExpr) {
-        this.aggFilterExpr = aggFilterExpr;
-    }
-
     @Override
     public <R, T> R accept(ILangVisitor<R, T> visitor, T arg) throws CompilationException {
         return visitor.visit(this, arg);
diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/util/FunctionUtil.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/util/FunctionUtil.java
index 0592d1e..b491e82 100644
--- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/util/FunctionUtil.java
+++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/util/FunctionUtil.java
@@ -35,6 +35,7 @@
 import org.apache.asterix.common.metadata.DataverseName;
 import org.apache.asterix.lang.common.base.Expression;
 import org.apache.asterix.lang.common.base.IQueryRewriter;
+import org.apache.asterix.lang.common.expression.AbstractCallExpression;
 import org.apache.asterix.lang.common.expression.CallExpr;
 import org.apache.asterix.lang.common.expression.OrderedListTypeDefinition;
 import org.apache.asterix.lang.common.expression.TypeExpression;
@@ -105,7 +106,7 @@
 
     @FunctionalInterface
     public interface IFunctionCollector {
-        Set<CallExpr> getFunctionCalls(Expression expression) throws CompilationException;
+        Set<AbstractCallExpression> getFunctionCalls(Expression expression) throws CompilationException;
     }
 
     public static FunctionSignature resolveFunctionCall(FunctionSignature fs, SourceLocation sourceLoc,
@@ -220,61 +221,82 @@
         }
         List<FunctionDecl> functionDecls =
                 inputFunctionDecls == null ? new ArrayList<>() : new ArrayList<>(inputFunctionDecls);
-        Set<CallExpr> functionCalls = functionCollector.getFunctionCalls(expression);
+        Set<AbstractCallExpression> functionCalls = functionCollector.getFunctionCalls(expression);
         Set<FunctionSignature> functionSignatures = new HashSet<>();
-        for (CallExpr functionCall : functionCalls) {
-            FunctionSignature fs = functionCall.getFunctionSignature();
-            if (fs.getDataverseName() == null) {
-                throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, functionCall.getSourceLocation(),
-                        fs);
-            }
-            if (!functionSignatures.add(fs)) {
-                // already seen this signature
-                continue;
-            }
-            if (declaredFunctions != null && declaredFunctions.contains(fs)) {
-                continue;
-            }
-            Function function;
-            try {
-                function = metadataProvider.lookupUserDefinedFunction(fs);
-            } catch (AlgebricksException e) {
-                throw new CompilationException(ErrorCode.COMPILATION_ERROR, e, functionCall.getSourceLocation(),
-                        e.toString());
-            }
-            if (function == null || !functionParser.getLanguage().equals(function.getLanguage())) {
-                // the function is either unknown, builtin, or in a different language.
-                // either way we ignore it here because it will be handled by the function inlining rule later
-                continue;
-            }
+        for (AbstractCallExpression functionCall : functionCalls) {
+            switch (functionCall.getKind()) {
+                case CALL_EXPRESSION:
+                    FunctionSignature fs = functionCall.getFunctionSignature();
+                    if (fs.getDataverseName() == null) {
+                        throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE,
+                                functionCall.getSourceLocation(), fs);
+                    }
+                    if (!functionSignatures.add(fs)) {
+                        // already seen this signature
+                        continue;
+                    }
+                    if (declaredFunctions != null && declaredFunctions.contains(fs)) {
+                        continue;
+                    }
+                    Function function;
+                    try {
+                        function = metadataProvider.lookupUserDefinedFunction(fs);
+                    } catch (AlgebricksException e) {
+                        throw new CompilationException(ErrorCode.COMPILATION_ERROR, e, functionCall.getSourceLocation(),
+                                e.toString());
+                    }
+                    if (function == null || !functionParser.getLanguage().equals(function.getLanguage())) {
+                        // the function is either unknown, builtin, or in a different language.
+                        // either way we ignore it here because it will be handled by the function inlining rule later
+                        continue;
+                    }
 
-            FunctionDecl functionDecl = functionParser.getFunctionDecl(function, warningCollector);
-            if (functionDecls.contains(functionDecl)) {
-                throw new CompilationException(ErrorCode.COMPILATION_ERROR, functionCall.getSourceLocation(),
-                        "Recursive invocation " + functionDecls.get(functionDecls.size() - 1).getSignature() + " <==> "
-                                + functionDecl.getSignature());
+                    FunctionDecl functionDecl = functionParser.getFunctionDecl(function, warningCollector);
+                    if (functionDecls.contains(functionDecl)) {
+                        throw new CompilationException(ErrorCode.COMPILATION_ERROR, functionCall.getSourceLocation(),
+                                "Recursive invocation " + functionDecls.get(functionDecls.size() - 1).getSignature()
+                                        + " <==> " + functionDecl.getSignature());
+                    }
+                    functionDecls.add(functionDecl);
+                    functionDecls = retrieveUsedStoredFunctions(metadataProvider, functionDecl.getFuncBody(),
+                            declaredFunctions, functionDecls, functionCollector, functionParser, warningCollector);
+                    break;
+                case WINDOW_EXPRESSION:
+                    // there cannot be used-defined window functions
+                    break;
+                default:
+                    throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, expression.getSourceLocation(),
+                            functionCall.getFunctionSignature().toString(false));
             }
-            functionDecls.add(functionDecl);
-            functionDecls = retrieveUsedStoredFunctions(metadataProvider, functionDecl.getFuncBody(), declaredFunctions,
-                    functionDecls, functionCollector, functionParser, warningCollector);
         }
         return functionDecls;
     }
 
     public static List<List<Triple<DataverseName, String, String>>> getFunctionDependencies(IQueryRewriter rewriter,
-            Expression expression, MetadataProvider metadataProvider) throws CompilationException {
-        Set<CallExpr> functionCalls = rewriter.getFunctionCalls(expression);
+            Expression expression) throws CompilationException {
+        Set<AbstractCallExpression> functionCalls = rewriter.getFunctionCalls(expression);
         //Get the List of used functions and used datasets
         List<Triple<DataverseName, String, String>> datasourceDependencies = new ArrayList<>();
         List<Triple<DataverseName, String, String>> functionDependencies = new ArrayList<>();
-        for (CallExpr functionCall : functionCalls) {
-            FunctionSignature signature = functionCall.getFunctionSignature();
-            if (isBuiltinDatasetFunction(signature)) {
-                Pair<DataverseName, String> datasetReference = parseDatasetFunctionArguments(functionCall);
-                datasourceDependencies.add(new Triple<>(datasetReference.first, datasetReference.second, null));
-            } else if (BuiltinFunctions.getBuiltinFunctionInfo(signature.createFunctionIdentifier()) == null) {
-                functionDependencies.add(new Triple<>(signature.getDataverseName(), signature.getName(),
-                        Integer.toString(signature.getArity())));
+        for (AbstractCallExpression functionCall : functionCalls) {
+            switch (functionCall.getKind()) {
+                case CALL_EXPRESSION:
+                    FunctionSignature signature = functionCall.getFunctionSignature();
+                    if (isBuiltinDatasetFunction(signature)) {
+                        Pair<DataverseName, String> datasetReference =
+                                parseDatasetFunctionArguments((CallExpr) functionCall);
+                        datasourceDependencies.add(new Triple<>(datasetReference.first, datasetReference.second, null));
+                    } else if (BuiltinFunctions.getBuiltinFunctionInfo(signature.createFunctionIdentifier()) == null) {
+                        functionDependencies.add(new Triple<>(signature.getDataverseName(), signature.getName(),
+                                Integer.toString(signature.getArity())));
+                    }
+                    break;
+                case WINDOW_EXPRESSION:
+                    // there cannot be used-defined window functions
+                    break;
+                default:
+                    throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, expression.getSourceLocation(),
+                            functionCall.getFunctionSignature().toString(false));
             }
         }
         List<List<Triple<DataverseName, String, String>>> dependencies = new ArrayList<>(3);
diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/GatherFunctionCallsVisitor.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/GatherFunctionCallsVisitor.java
index 44b2092..3ad0f1b 100644
--- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/GatherFunctionCallsVisitor.java
+++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/GatherFunctionCallsVisitor.java
@@ -31,6 +31,7 @@
 import org.apache.asterix.lang.common.clause.LimitClause;
 import org.apache.asterix.lang.common.clause.OrderbyClause;
 import org.apache.asterix.lang.common.clause.WhereClause;
+import org.apache.asterix.lang.common.expression.AbstractCallExpression;
 import org.apache.asterix.lang.common.expression.CallExpr;
 import org.apache.asterix.lang.common.expression.FieldAccessor;
 import org.apache.asterix.lang.common.expression.FieldBinding;
@@ -57,7 +58,7 @@
 
 public class GatherFunctionCallsVisitor extends AbstractQueryExpressionVisitor<Void, Void> {
 
-    protected final Set<CallExpr> calls = new LinkedHashSet<>();
+    protected final Set<AbstractCallExpression> calls = new LinkedHashSet<>();
 
     @Override
     public Void visit(CallExpr callExpr, Void arg) throws CompilationException {
@@ -239,7 +240,7 @@
         return null;
     }
 
-    public Set<CallExpr> getCalls() {
+    public Set<AbstractCallExpression> getCalls() {
         return calls;
     }
 
@@ -247,5 +248,4 @@
     public Void visit(FunctionDecl fd, Void arg) throws CompilationException {
         return null;
     }
-
 }
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/expression/WindowExpression.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/expression/WindowExpression.java
index 9b603cd..417cae6 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/expression/WindowExpression.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/expression/WindowExpression.java
@@ -24,9 +24,9 @@
 
 import org.apache.asterix.common.exceptions.CompilationException;
 import org.apache.asterix.common.functions.FunctionSignature;
-import org.apache.asterix.lang.common.base.AbstractExpression;
 import org.apache.asterix.lang.common.base.Expression;
 import org.apache.asterix.lang.common.clause.OrderbyClause;
+import org.apache.asterix.lang.common.expression.AbstractCallExpression;
 import org.apache.asterix.lang.common.expression.VariableExpr;
 import org.apache.asterix.lang.common.struct.Identifier;
 import org.apache.asterix.lang.common.util.ExpressionUtils;
@@ -35,11 +35,7 @@
 import org.apache.commons.lang3.StringUtils;
 import org.apache.hyracks.algebricks.common.utils.Pair;
 
-public class WindowExpression extends AbstractExpression {
-
-    private FunctionSignature functionSignature;
-    private List<Expression> exprList;
-    private Expression aggFilterExpr;
+public class WindowExpression extends AbstractCallExpression {
 
     private List<Expression> partitionList;
     private List<Expression> orderbyList;
@@ -64,12 +60,7 @@
             FrameBoundaryKind frameStartKind, Expression frameStartExpr, FrameBoundaryKind frameEndKind,
             Expression frameEndExpr, FrameExclusionKind frameExclusionKind, VariableExpr windowVar,
             List<Pair<Expression, Identifier>> windowFieldList, Boolean ignoreNulls, Boolean fromLast) {
-        if (functionSignature == null || exprList == null) {
-            throw new NullPointerException();
-        }
-        this.functionSignature = functionSignature;
-        this.exprList = exprList;
-        this.aggFilterExpr = aggFilterExpr;
+        super(functionSignature, exprList, aggFilterExpr);
         this.partitionList = partitionList;
         this.orderbyList = orderbyList;
         this.orderbyModifierList = orderbyModifierList;
@@ -90,40 +81,6 @@
         return Kind.WINDOW_EXPRESSION;
     }
 
-    public FunctionSignature getFunctionSignature() {
-        return functionSignature;
-    }
-
-    public void setFunctionSignature(FunctionSignature functionSignature) {
-        if (functionSignature == null) {
-            throw new NullPointerException();
-        }
-        this.functionSignature = functionSignature;
-    }
-
-    public List<Expression> getExprList() {
-        return exprList;
-    }
-
-    public void setExprList(List<Expression> exprList) {
-        if (exprList == null) {
-            throw new NullPointerException();
-        }
-        this.exprList = exprList;
-    }
-
-    public boolean hasAggregateFilterExpr() {
-        return aggFilterExpr != null;
-    }
-
-    public Expression getAggregateFilterExpr() {
-        return aggFilterExpr;
-    }
-
-    public void setAggregateFilterExpr(Expression filterExpr) {
-        this.aggFilterExpr = filterExpr;
-    }
-
     public boolean hasPartitionList() {
         return partitionList != null && !partitionList.isEmpty();
     }
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppQueryRewriter.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppQueryRewriter.java
index 755c1df..af1bb41 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppQueryRewriter.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppQueryRewriter.java
@@ -33,7 +33,7 @@
 import org.apache.asterix.lang.common.base.IQueryRewriter;
 import org.apache.asterix.lang.common.base.IReturningStatement;
 import org.apache.asterix.lang.common.clause.LetClause;
-import org.apache.asterix.lang.common.expression.CallExpr;
+import org.apache.asterix.lang.common.expression.AbstractCallExpression;
 import org.apache.asterix.lang.common.expression.ListSliceExpression;
 import org.apache.asterix.lang.common.expression.VariableExpr;
 import org.apache.asterix.lang.common.parser.FunctionParser;
@@ -345,7 +345,7 @@
     }
 
     @Override
-    public Set<CallExpr> getFunctionCalls(Expression expression) throws CompilationException {
+    public Set<AbstractCallExpression> getFunctionCalls(Expression expression) throws CompilationException {
         GatherFunctionCalls gfc = new GatherFunctionCalls();
         expression.accept(gfc, null);
         return gfc.getCalls();
@@ -507,6 +507,7 @@
 
         @Override
         public Void visit(WindowExpression winExpr, Void arg) throws CompilationException {
+            calls.add(winExpr);
             if (winExpr.hasPartitionList()) {
                 for (Expression expr : winExpr.getPartitionList()) {
                     expr.accept(this, arg);