asterixdb-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From buyin...@apache.org
Subject [2/4] asterixdb git commit: Support SQL-compliant group-by syntax.
Date Thu, 14 Jul 2016 02:30:14 GMT
http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5b2d4c89/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/CloneAndSubstituteVariablesVisitor.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/CloneAndSubstituteVariablesVisitor.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/CloneAndSubstituteVariablesVisitor.java
index d380862..57264be 100644
--- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/CloneAndSubstituteVariablesVisitor.java
+++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/CloneAndSubstituteVariablesVisitor.java
@@ -71,25 +71,24 @@ public class CloneAndSubstituteVariablesVisitor extends
         VariableExpr varExpr = lc.getVarExpr();
         VariableExpr newVe = generateNewVariable(context, varExpr);
         LetClause newLet = new LetClause(newVe, (Expression) p1.first);
-        return new Pair<ILangExpression, VariableSubstitutionEnvironment>(newLet,
-                VariableCloneAndSubstitutionUtil.eliminateSubstFromList(lc.getVarExpr(), env));
+        return new Pair<>(newLet, VariableCloneAndSubstitutionUtil.eliminateSubstFromList(lc.getVarExpr(), env));
     }
 
     @Override
     public Pair<ILangExpression, VariableSubstitutionEnvironment> visit(GroupbyClause gc,
             VariableSubstitutionEnvironment env) throws AsterixException {
         VariableSubstitutionEnvironment newSubs = env;
-        List<GbyVariableExpressionPair> newGbyList = VariableCloneAndSubstitutionUtil.substInVarExprPair(context,
-                gc.getGbyPairList(), env, newSubs, this);
-        List<GbyVariableExpressionPair> newDecorList = gc.hasDecorList() ? VariableCloneAndSubstitutionUtil
-                .substInVarExprPair(context, gc.getDecorPairList(), env, newSubs, this)
-                : new ArrayList<GbyVariableExpressionPair>();
+        List<GbyVariableExpressionPair> newGbyList =
+                VariableCloneAndSubstitutionUtil.substInVarExprPair(context, gc.getGbyPairList(), newSubs, this);
+        List<GbyVariableExpressionPair> newDecorList = gc.hasDecorList()
+                ? VariableCloneAndSubstitutionUtil.substInVarExprPair(context, gc.getDecorPairList(), newSubs, this)
+                : new ArrayList<>();
 
         VariableExpr newGroupVar = null;
         if (gc.hasGroupVar()) {
             newGroupVar = generateNewVariable(context, gc.getGroupVar());
         }
-        List<VariableExpr> wList = new LinkedList<VariableExpr>();
+        List<VariableExpr> wList = new LinkedList<>();
         if (gc.hasWithList()) {
             for (VariableExpr w : gc.getWithVarList()) {
                 VarIdentifier newVar = context.getRewrittenVar(w.getVar().getId());
@@ -109,26 +108,26 @@ public class CloneAndSubstituteVariablesVisitor extends
         }
         GroupbyClause newGroup = new GroupbyClause(newGbyList, newDecorList, wList, newGroupVar, newGroupFieldList,
                 gc.hasHashGroupByHint(), gc.isGroupAll());
-        return new Pair<ILangExpression, VariableSubstitutionEnvironment>(newGroup, newSubs);
+        return new Pair<>(newGroup, newSubs);
     }
 
     @Override
     public Pair<ILangExpression, VariableSubstitutionEnvironment> visit(QuantifiedExpression qe,
             VariableSubstitutionEnvironment env) throws AsterixException {
         List<QuantifiedPair> oldPairs = qe.getQuantifiedList();
-        List<QuantifiedPair> newPairs = new ArrayList<QuantifiedPair>(oldPairs.size());
+        List<QuantifiedPair> newPairs = new ArrayList<>(oldPairs.size());
         VariableSubstitutionEnvironment newSubs = env;
         for (QuantifiedPair t : oldPairs) {
             VariableExpr newVar = generateNewVariable(context, t.getVarExpr());
             newSubs = VariableCloneAndSubstitutionUtil.eliminateSubstFromList(newVar, newSubs);
-            Pair<ILangExpression, VariableSubstitutionEnvironment> p1 = visitUnnesBindingExpression(t.getExpr(),
-                    newSubs);
+            Pair<ILangExpression, VariableSubstitutionEnvironment> p1 =
+                    visitUnnesBindingExpression(t.getExpr(), newSubs);
             QuantifiedPair t2 = new QuantifiedPair(newVar, (Expression) p1.first);
             newPairs.add(t2);
         }
         Pair<ILangExpression, VariableSubstitutionEnvironment> p2 = qe.getSatisfiesExpr().accept(this, newSubs);
         QuantifiedExpression qe2 = new QuantifiedExpression(qe.getQuantifier(), newPairs, (Expression) p2.first);
-        return new Pair<ILangExpression, VariableSubstitutionEnvironment>(qe2, newSubs);
+        return new Pair<>(qe2, newSubs);
     }
 
     @Override
@@ -136,7 +135,7 @@ public class CloneAndSubstituteVariablesVisitor extends
             VariableSubstitutionEnvironment env) throws AsterixException {
         Pair<ILangExpression, VariableSubstitutionEnvironment> p1 = wc.getWhereExpr().accept(this, env);
         WhereClause newW = new WhereClause((Expression) p1.first);
-        return new Pair<ILangExpression, VariableSubstitutionEnvironment>(newW, p1.second);
+        return new Pair<>(newW, p1.second);
     }
 
     @Override
@@ -144,13 +143,13 @@ public class CloneAndSubstituteVariablesVisitor extends
             VariableSubstitutionEnvironment env) throws AsterixException {
         List<Expression> exprList = VariableCloneAndSubstitutionUtil.visitAndCloneExprList(pf.getExprList(), env, this);
         CallExpr f = new CallExpr(pf.getFunctionSignature(), exprList);
-        return new Pair<ILangExpression, VariableSubstitutionEnvironment>(f, env);
+        return new Pair<>(f, env);
     }
 
     @Override
     public Pair<ILangExpression, VariableSubstitutionEnvironment> visit(FunctionDecl fd,
             VariableSubstitutionEnvironment env) throws AsterixException {
-        List<VarIdentifier> newList = new ArrayList<VarIdentifier>(fd.getParamList().size());
+        List<VarIdentifier> newList = new ArrayList<>(fd.getParamList().size());
         for (VarIdentifier vi : fd.getParamList()) {
             VariableExpr varExpr = new VariableExpr(vi);
             if (!env.constainsOldVar(varExpr)) {
@@ -165,7 +164,7 @@ public class CloneAndSubstituteVariablesVisitor extends
 
         Pair<ILangExpression, VariableSubstitutionEnvironment> p1 = fd.getFuncBody().accept(this, env);
         FunctionDecl newF = new FunctionDecl(fd.getSignature(), newList, (Expression) p1.first);
-        return new Pair<ILangExpression, VariableSubstitutionEnvironment>(newF, env);
+        return new Pair<>(newF, env);
     }
 
     @Override
@@ -175,22 +174,22 @@ public class CloneAndSubstituteVariablesVisitor extends
         Pair<ILangExpression, VariableSubstitutionEnvironment> p2 = ifexpr.getThenExpr().accept(this, env);
         Pair<ILangExpression, VariableSubstitutionEnvironment> p3 = ifexpr.getElseExpr().accept(this, env);
         IfExpr i = new IfExpr((Expression) p1.first, (Expression) p2.first, (Expression) p3.first);
-        return new Pair<ILangExpression, VariableSubstitutionEnvironment>(i, env);
+        return new Pair<>(i, env);
     }
 
     @Override
     public Pair<ILangExpression, VariableSubstitutionEnvironment> visit(LimitClause lc,
             VariableSubstitutionEnvironment env) throws AsterixException {
         Pair<ILangExpression, VariableSubstitutionEnvironment> p1 = lc.getLimitExpr().accept(this, env);
-        Pair<ILangExpression, VariableSubstitutionEnvironment> p2 = null;
+        Pair<ILangExpression, VariableSubstitutionEnvironment> p2;
         Expression lcOffsetExpr = lc.getOffset();
         if (lcOffsetExpr != null) {
             p2 = lcOffsetExpr.accept(this, env);
         } else {
-            p2 = new Pair<ILangExpression, VariableSubstitutionEnvironment>(null, null);
+            p2 = new Pair<>(null, null);
         }
         LimitClause c = new LimitClause((Expression) p1.first, (Expression) p2.first);
-        return new Pair<ILangExpression, VariableSubstitutionEnvironment>(c, env);
+        return new Pair<>(c, env);
     }
 
     @Override
@@ -199,38 +198,38 @@ public class CloneAndSubstituteVariablesVisitor extends
         List<Expression> oldExprList = lc.getExprList();
         List<Expression> exprs = VariableCloneAndSubstitutionUtil.visitAndCloneExprList(oldExprList, env, this);
         ListConstructor c = new ListConstructor(lc.getType(), exprs);
-        return new Pair<ILangExpression, VariableSubstitutionEnvironment>(c, env);
+        return new Pair<>(c, env);
     }
 
     @Override
     public Pair<ILangExpression, VariableSubstitutionEnvironment> visit(LiteralExpr l,
             VariableSubstitutionEnvironment env) throws AsterixException {
-        return new Pair<ILangExpression, VariableSubstitutionEnvironment>(l, env);
+        return new Pair<>(l, env);
     }
 
     @Override
     public Pair<ILangExpression, VariableSubstitutionEnvironment> visit(OperatorExpr op,
             VariableSubstitutionEnvironment env) throws AsterixException {
         List<Expression> oldExprList = op.getExprList();
-        List<Expression> exprs = new ArrayList<Expression>(oldExprList.size());
+        List<Expression> exprs = new ArrayList<>(oldExprList.size());
         for (Expression e : oldExprList) {
             Pair<ILangExpression, VariableSubstitutionEnvironment> p1 = e.accept(this, env);
             exprs.add((Expression) p1.first);
         }
         OperatorExpr oe = new OperatorExpr(exprs, op.getExprBroadcastIdx(), op.getOpList(), op.isCurrentop());
-        return new Pair<ILangExpression, VariableSubstitutionEnvironment>(oe, env);
+        return new Pair<>(oe, env);
     }
 
     @Override
     public Pair<ILangExpression, VariableSubstitutionEnvironment> visit(OrderbyClause oc,
             VariableSubstitutionEnvironment env) throws AsterixException {
-        List<Expression> exprList = VariableCloneAndSubstitutionUtil.visitAndCloneExprList(oc.getOrderbyList(), env,
-                this);
+        List<Expression> exprList =
+                VariableCloneAndSubstitutionUtil.visitAndCloneExprList(oc.getOrderbyList(), env, this);
         OrderbyClause oc2 = new OrderbyClause(exprList, oc.getModifierList());
         oc2.setNumFrames(oc.getNumFrames());
         oc2.setNumTuples(oc.getNumTuples());
         oc2.setRangeMap(oc.getRangeMap());
-        return new Pair<ILangExpression, VariableSubstitutionEnvironment>(oc2, env);
+        return new Pair<>(oc2, env);
     }
 
     @Override
@@ -239,14 +238,14 @@ public class CloneAndSubstituteVariablesVisitor extends
         Query newQ = new Query();
         Pair<ILangExpression, VariableSubstitutionEnvironment> p1 = q.getBody().accept(this, env);
         newQ.setBody((Expression) p1.first);
-        return new Pair<ILangExpression, VariableSubstitutionEnvironment>(newQ, p1.second);
+        return new Pair<>(newQ, p1.second);
     }
 
     @Override
     public Pair<ILangExpression, VariableSubstitutionEnvironment> visit(RecordConstructor rc,
             VariableSubstitutionEnvironment env) throws AsterixException {
         List<FieldBinding> oldFbs = rc.getFbList();
-        ArrayList<FieldBinding> newFbs = new ArrayList<FieldBinding>(oldFbs.size());
+        ArrayList<FieldBinding> newFbs = new ArrayList<>(oldFbs.size());
         for (FieldBinding fb : oldFbs) {
             Pair<ILangExpression, VariableSubstitutionEnvironment> p1 = fb.getLeftExpr().accept(this, env);
             Pair<ILangExpression, VariableSubstitutionEnvironment> p2 = fb.getRightExpr().accept(this, env);
@@ -254,7 +253,7 @@ public class CloneAndSubstituteVariablesVisitor extends
             newFbs.add(fb2);
         }
         RecordConstructor newRc = new RecordConstructor(newFbs);
-        return new Pair<ILangExpression, VariableSubstitutionEnvironment>(newRc, env);
+        return new Pair<>(newRc, env);
     }
 
     @Override
@@ -262,7 +261,7 @@ public class CloneAndSubstituteVariablesVisitor extends
             VariableSubstitutionEnvironment env) throws AsterixException {
         Pair<ILangExpression, VariableSubstitutionEnvironment> p1 = u.getExpr().accept(this, env);
         UnaryExpr newU = new UnaryExpr(u.getSign(), (Expression) p1.first);
-        return new Pair<ILangExpression, VariableSubstitutionEnvironment>(newU, env);
+        return new Pair<>(newU, env);
     }
 
     @Override
@@ -276,7 +275,7 @@ public class CloneAndSubstituteVariablesVisitor extends
         }
         IndexAccessor i = new IndexAccessor((Expression) p1.first, indexExpr);
         i.setAny(ia.isAny());
-        return new Pair<ILangExpression, VariableSubstitutionEnvironment>(i, env);
+        return new Pair<>(i, env);
     }
 
     @Override
@@ -284,13 +283,13 @@ public class CloneAndSubstituteVariablesVisitor extends
             VariableSubstitutionEnvironment env) throws AsterixException {
         Pair<ILangExpression, VariableSubstitutionEnvironment> p = fa.getExpr().accept(this, env);
         FieldAccessor newF = new FieldAccessor((Expression) p.first, fa.getIdent());
-        return new Pair<ILangExpression, VariableSubstitutionEnvironment>(newF, p.second);
+        return new Pair<>(newF, p.second);
     }
 
     @Override
     public Pair<ILangExpression, VariableSubstitutionEnvironment> visit(VariableExpr v,
             VariableSubstitutionEnvironment env) throws AsterixException {
-        return new Pair<ILangExpression, VariableSubstitutionEnvironment>(rewriteVariableExpr(v, env), env);
+        return new Pair<>(rewriteVariableExpr(v, env), env);
     }
 
     // Replace a variable expression if the variable is to-be substituted.
@@ -320,8 +319,7 @@ public class CloneAndSubstituteVariablesVisitor extends
     public VariableExpr generateNewVariable(LangRewritingContext context, VariableExpr varExpr) {
         VarIdentifier vi = varExpr.getVar();
         VarIdentifier newVar = context.mapOldId(vi.getId(), vi.getValue());
-        VariableExpr newVarExpr = new VariableExpr(newVar);
-        return newVarExpr;
+        return new VariableExpr(newVar);
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5b2d4c89/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/SubstituteExpressionVisitor.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/SubstituteExpressionVisitor.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/SubstituteExpressionVisitor.java
new file mode 100644
index 0000000..1fd561e
--- /dev/null
+++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/SubstituteExpressionVisitor.java
@@ -0,0 +1,245 @@
+/*
+ * 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.visitor;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.asterix.common.exceptions.AsterixException;
+import org.apache.asterix.lang.common.base.Expression;
+import org.apache.asterix.lang.common.clause.LetClause;
+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.CallExpr;
+import org.apache.asterix.lang.common.expression.FieldAccessor;
+import org.apache.asterix.lang.common.expression.FieldBinding;
+import org.apache.asterix.lang.common.expression.IfExpr;
+import org.apache.asterix.lang.common.expression.IndexAccessor;
+import org.apache.asterix.lang.common.expression.ListConstructor;
+import org.apache.asterix.lang.common.expression.LiteralExpr;
+import org.apache.asterix.lang.common.expression.OperatorExpr;
+import org.apache.asterix.lang.common.expression.QuantifiedExpression;
+import org.apache.asterix.lang.common.expression.RecordConstructor;
+import org.apache.asterix.lang.common.expression.UnaryExpr;
+import org.apache.asterix.lang.common.expression.VariableExpr;
+import org.apache.asterix.lang.common.rewrites.ExpressionSubstitutionEnvironment;
+import org.apache.asterix.lang.common.rewrites.ExpressionSubstitutionEnvironment.DeepCopier;
+import org.apache.asterix.lang.common.statement.FunctionDecl;
+import org.apache.asterix.lang.common.statement.Query;
+import org.apache.asterix.lang.common.struct.QuantifiedPair;
+import org.apache.asterix.lang.common.visitor.base.AbstractQueryExpressionVisitor;
+
+public abstract class SubstituteExpressionVisitor
+        extends AbstractQueryExpressionVisitor<Expression, ExpressionSubstitutionEnvironment> {
+    private final DeepCopier deepCopier;
+
+    public SubstituteExpressionVisitor(DeepCopier deepCopier) {
+        this.deepCopier = deepCopier;
+    }
+
+    @Override
+    public Expression visit(LetClause lc, ExpressionSubstitutionEnvironment env) throws AsterixException {
+        // Marks the binding variable for future visiting until it exists its scope.
+        env.disableVariable(lc.getVarExpr());
+        lc.setBindingExpr(lc.getBindingExpr().accept(this, env));
+        return null;
+    }
+
+    @Override
+    public Expression visit(Query q, ExpressionSubstitutionEnvironment env) throws AsterixException {
+        q.setBody(q.getBody().accept(this, env));
+        return null;
+    }
+
+    @Override
+    public Expression visit(FunctionDecl fd, ExpressionSubstitutionEnvironment env) throws AsterixException {
+        // Do nothing for a function declaration.
+        return null;
+    }
+
+    @Override
+    public Expression visit(LiteralExpr l, ExpressionSubstitutionEnvironment env) throws AsterixException {
+        return env.findSubstitution(l, deepCopier);
+    }
+
+    @Override
+    public Expression visit(VariableExpr v, ExpressionSubstitutionEnvironment env) throws AsterixException {
+        return env.findSubstitution(v, deepCopier);
+    }
+
+    @Override
+    public Expression visit(ListConstructor lc, ExpressionSubstitutionEnvironment env) throws AsterixException {
+        Expression newLc = env.findSubstitution(lc, deepCopier);
+        if (newLc == lc) {
+            lc.setExprList(rewriteExpressionList(lc.getExprList(), env));
+            return lc;
+        } else {
+            return newLc.accept(this, env);
+        }
+    }
+
+    @Override
+    public Expression visit(RecordConstructor rc, ExpressionSubstitutionEnvironment env) throws AsterixException {
+        Expression newRc = env.findSubstitution(rc, deepCopier);
+        if (newRc == rc) {
+            for (FieldBinding fb : rc.getFbList()) {
+                fb.setLeftExpr(fb.getLeftExpr().accept(this, env));
+                fb.setRightExpr(fb.getRightExpr().accept(this, env));
+            }
+            return rc;
+        } else {
+            return newRc.accept(this, env);
+        }
+    }
+
+    @Override
+    public Expression visit(OperatorExpr operatorExpr, ExpressionSubstitutionEnvironment env) throws AsterixException {
+        Expression newOpertorExpr = env.findSubstitution(operatorExpr, deepCopier);
+        if (newOpertorExpr == operatorExpr) {
+            operatorExpr.setExprList(rewriteExpressionList(operatorExpr.getExprList(), env));
+            return operatorExpr;
+        } else {
+            return newOpertorExpr.accept(this, env);
+        }
+    }
+
+    @Override
+    public Expression visit(FieldAccessor fa, ExpressionSubstitutionEnvironment env) throws AsterixException {
+        Expression newFa = env.findSubstitution(fa, deepCopier);
+        if (newFa == fa) {
+            fa.setExpr(fa.getExpr().accept(this, env));
+            return fa;
+        } else {
+            return newFa.accept(this, env);
+        }
+    }
+
+    @Override
+    public Expression visit(IndexAccessor ia, ExpressionSubstitutionEnvironment env) throws AsterixException {
+        Expression newIa = env.findSubstitution(ia, deepCopier);
+        if (newIa == ia) {
+            ia.setExpr(ia.getExpr().accept(this, env));
+            ia.setIndexExpr(ia.getIndexExpr().accept(this, env));
+            return ia;
+        } else {
+            return newIa.accept(this, env);
+        }
+    }
+
+    @Override
+    public Expression visit(IfExpr ifexpr, ExpressionSubstitutionEnvironment env) throws AsterixException {
+        Expression newIfExpr = env.findSubstitution(ifexpr, deepCopier);
+        if (newIfExpr == ifexpr) {
+            ifexpr.setCondExpr(ifexpr.getCondExpr().accept(this, env));
+            ifexpr.setThenExpr(ifexpr.getThenExpr().accept(this, env));
+            ifexpr.setElseExpr(ifexpr.getElseExpr().accept(this, env));
+            return ifexpr;
+        } else {
+            return newIfExpr.accept(this, env);
+        }
+    }
+
+    @Override
+    public Expression visit(QuantifiedExpression qe, ExpressionSubstitutionEnvironment env) throws AsterixException {
+        Expression newQe = env.findSubstitution(qe, deepCopier);
+        if (newQe == qe) {
+            // Rewrites the quantifier list.
+            for (QuantifiedPair pair : qe.getQuantifiedList()) {
+                pair.setExpr(pair.getExpr().accept(this, env));
+            }
+
+            // Rewrites the condition.
+            for (QuantifiedPair pair : qe.getQuantifiedList()) {
+                // Marks each binding var.
+                env.disableVariable(pair.getVarExpr());
+            }
+            qe.setSatisfiesExpr(qe.getSatisfiesExpr().accept(this, env));
+            for (QuantifiedPair pair : qe.getQuantifiedList()) {
+                // Let each binding var exit its scope.
+                env.enableVariable(pair.getVarExpr());
+            }
+            return qe;
+        } else {
+            return newQe.accept(this, env);
+        }
+    }
+
+    @Override
+    public Expression visit(WhereClause wc, ExpressionSubstitutionEnvironment env) throws AsterixException {
+        wc.setWhereExpr(wc.getWhereExpr().accept(this, env));
+        return null;
+    }
+
+    @Override
+    public Expression visit(OrderbyClause oc, ExpressionSubstitutionEnvironment env) throws AsterixException {
+        oc.setOrderbyList(rewriteExpressionList(oc.getOrderbyList(), env));
+        return null;
+    }
+
+    @Override
+    public Expression visit(LimitClause lc, ExpressionSubstitutionEnvironment env) throws AsterixException {
+        lc.setLimitExpr(lc.getLimitExpr().accept(this, env));
+        if (lc.hasOffset()) {
+            lc.setOffset(lc.getOffset().accept(this, env));
+        }
+        return null;
+    }
+
+    @Override
+    public Expression visit(UnaryExpr u, ExpressionSubstitutionEnvironment env) throws AsterixException {
+        Expression newU = env.findSubstitution(u, deepCopier);
+        if (newU == u) {
+            u.setExpr(u.getExpr().accept(this, env));
+            return u;
+        } else {
+            return newU.accept(this, env);
+        }
+    }
+
+    @Override
+    public Expression visit(CallExpr callExpr, ExpressionSubstitutionEnvironment env) throws AsterixException {
+        Expression newCallExpr = env.findSubstitution(callExpr, deepCopier);
+        if (newCallExpr == callExpr) {
+            callExpr.setExprList(rewriteExpressionList(callExpr.getExprList(), env));
+            return callExpr;
+        } else {
+            return newCallExpr.accept(this, env);
+        }
+    }
+
+    /**
+     * Rewrites the expression list.
+     *
+     * @param exprs,
+     *            list of expressions.
+     * @param env,
+     *            ExpressionSubstitutionEnvironment.
+     * @return a list of rewritten expressions.
+     * @throws AsterixException
+     */
+    protected List<Expression> rewriteExpressionList(List<Expression> exprs, ExpressionSubstitutionEnvironment env)
+            throws AsterixException {
+        List<Expression> newExprs = new ArrayList<>();
+        for (Expression expr : exprs) {
+            newExprs.add(env.findSubstitution(expr, deepCopier));
+        }
+        return newExprs;
+    }
+}

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5b2d4c89/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/AbstractBinaryCorrelateClause.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/AbstractBinaryCorrelateClause.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/AbstractBinaryCorrelateClause.java
index b1dbba5..ed5f404 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/AbstractBinaryCorrelateClause.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/AbstractBinaryCorrelateClause.java
@@ -23,6 +23,7 @@ import org.apache.asterix.lang.common.base.Clause;
 import org.apache.asterix.lang.common.base.Expression;
 import org.apache.asterix.lang.common.expression.VariableExpr;
 import org.apache.asterix.lang.sqlpp.optype.JoinType;
+import org.apache.commons.lang3.ObjectUtils;
 
 public abstract class AbstractBinaryCorrelateClause implements Clause {
 
@@ -63,4 +64,22 @@ public abstract class AbstractBinaryCorrelateClause implements Clause {
         return rightPosVar != null;
     }
 
+    @Override
+    public int hashCode() {
+        return ObjectUtils.hashCodeMulti(joinType, rightExpr, rightPosVar, rightVar);
+    }
+
+    @Override
+    public boolean equals(Object object) {
+        if (this == object) {
+            return true;
+        }
+        if (!(object instanceof AbstractBinaryCorrelateClause)) {
+            return false;
+        }
+        AbstractBinaryCorrelateClause target = (AbstractBinaryCorrelateClause) object;
+        return ObjectUtils.equals(joinType, target.joinType) && ObjectUtils.equals(rightExpr, target.rightExpr)
+                && ObjectUtils.equals(rightPosVar, target.rightPosVar) && ObjectUtils.equals(rightVar, target.rightVar);
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5b2d4c89/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/AbstractBinaryCorrelateWithConditionClause.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/AbstractBinaryCorrelateWithConditionClause.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/AbstractBinaryCorrelateWithConditionClause.java
index e1d4a7b..3773ea9 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/AbstractBinaryCorrelateWithConditionClause.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/AbstractBinaryCorrelateWithConditionClause.java
@@ -41,4 +41,21 @@ public abstract class AbstractBinaryCorrelateWithConditionClause extends Abstrac
         this.conditionExpr = conditionExpr;
     }
 
+    @Override
+    public int hashCode() {
+        return 31 * super.hashCode() + conditionExpr.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object object) {
+        if (this == object) {
+            return true;
+        }
+        if (!(object instanceof AbstractBinaryCorrelateWithConditionClause)) {
+            return false;
+        }
+        AbstractBinaryCorrelateWithConditionClause target = (AbstractBinaryCorrelateWithConditionClause) object;
+        return super.equals(target) && conditionExpr.equals(target.getConditionExpression());
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5b2d4c89/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/FromClause.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/FromClause.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/FromClause.java
index da6c512..aff731b 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/FromClause.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/FromClause.java
@@ -30,7 +30,7 @@ import org.apache.asterix.lang.sqlpp.visitor.base.ISqlppVisitor;
 
 public class FromClause implements Clause {
 
-    private List<FromTerm> fromTerms = new ArrayList<FromTerm>();
+    private List<FromTerm> fromTerms = new ArrayList<>();
 
     public FromClause(List<FromTerm> fromTerms) {
         this.fromTerms = fromTerms;
@@ -54,4 +54,21 @@ public class FromClause implements Clause {
     public String toString() {
         return fromTerms.stream().map(String::valueOf).collect(Collectors.joining(", "));
     }
+
+    @Override
+    public int hashCode() {
+        return fromTerms.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object object) {
+        if (this == object) {
+            return true;
+        }
+        if (!(object instanceof FromClause)) {
+            return false;
+        }
+        FromClause target = (FromClause) object;
+        return fromTerms.equals(target.getFromTerms());
+    }
 }

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5b2d4c89/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/FromTerm.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/FromTerm.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/FromTerm.java
index 82e1f02..d4e522a 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/FromTerm.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/FromTerm.java
@@ -28,19 +28,22 @@ import org.apache.asterix.lang.common.base.Expression;
 import org.apache.asterix.lang.common.expression.VariableExpr;
 import org.apache.asterix.lang.common.visitor.base.ILangVisitor;
 import org.apache.asterix.lang.sqlpp.visitor.base.ISqlppVisitor;
+import org.apache.commons.lang3.ObjectUtils;
 
 public class FromTerm implements Clause {
     private Expression leftExpr;
     private VariableExpr leftVar;
     private VariableExpr posVar;
-    private List<AbstractBinaryCorrelateClause> correlateClauses;
+    private List<AbstractBinaryCorrelateClause> correlateClauses = new ArrayList<>();
 
     public FromTerm(Expression leftExpr, VariableExpr leftVar, VariableExpr posVar,
             List<AbstractBinaryCorrelateClause> correlateClauses) {
         this.leftExpr = leftExpr;
         this.leftVar = leftVar;
         this.posVar = posVar;
-        this.correlateClauses = correlateClauses == null ? new ArrayList<>() : correlateClauses;
+        if (correlateClauses != null) {
+            this.correlateClauses.addAll(correlateClauses);
+        }
     }
 
     @Override
@@ -85,4 +88,23 @@ public class FromTerm implements Clause {
     public String toString() {
         return String.valueOf(leftExpr) + " AS " + leftVar;
     }
+
+    @Override
+    public int hashCode() {
+        return ObjectUtils.hashCodeMulti(correlateClauses, leftExpr, leftVar, posVar);
+    }
+
+    @Override
+    public boolean equals(Object object) {
+        if (this == object) {
+            return true;
+        }
+        if (!(object instanceof FromTerm)) {
+            return false;
+        }
+        FromTerm target = (FromTerm) object;
+        return ObjectUtils.equals(correlateClauses, target.correlateClauses)
+                && ObjectUtils.equals(leftExpr, target.leftExpr) && ObjectUtils.equals(leftVar, target.leftVar)
+                && ObjectUtils.equals(posVar, target.posVar);
+    }
 }

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5b2d4c89/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/HavingClause.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/HavingClause.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/HavingClause.java
index 98cca9c..19b7692 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/HavingClause.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/HavingClause.java
@@ -50,4 +50,21 @@ public class HavingClause implements Clause {
         this.filterExpression = filterExpression;
     }
 
+    @Override
+    public int hashCode() {
+        return filterExpression.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object object) {
+        if (this == object) {
+            return true;
+        }
+        if (!(object instanceof HavingClause)) {
+            return false;
+        }
+        HavingClause target = (HavingClause) object;
+        return filterExpression.equals(target.getFilterExpression());
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5b2d4c89/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/Projection.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/Projection.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/Projection.java
index f73ec1e..148cc36 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/Projection.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/Projection.java
@@ -24,6 +24,7 @@ import org.apache.asterix.lang.common.base.Clause;
 import org.apache.asterix.lang.common.base.Expression;
 import org.apache.asterix.lang.common.visitor.base.ILangVisitor;
 import org.apache.asterix.lang.sqlpp.visitor.base.ISqlppVisitor;
+import org.apache.commons.lang3.ObjectUtils;
 
 public class Projection implements Clause {
 
@@ -81,4 +82,22 @@ public class Projection implements Clause {
     public String toString() {
         return star ? "*" : (String.valueOf(expr) + (exprStar ? ".*" : (hasName() ? " as " + getName() : "")));
     }
+
+    @Override
+    public int hashCode() {
+        return ObjectUtils.hashCodeMulti(expr, exprStar, name, star);
+    }
+
+    @Override
+    public boolean equals(Object object) {
+        if (this == object) {
+            return true;
+        }
+        if (!(object instanceof Projection)) {
+            return false;
+        }
+        Projection target = (Projection) object;
+        return ObjectUtils.equals(expr, target.expr) && ObjectUtils.equals(exprStar, target.exprStar)
+                && ObjectUtils.equals(name, target.name) && ObjectUtils.equals(star, target.star);
+    }
 }

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5b2d4c89/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/SelectBlock.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/SelectBlock.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/SelectBlock.java
index f63eced..e297335 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/SelectBlock.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/SelectBlock.java
@@ -19,6 +19,7 @@
 
 package org.apache.asterix.lang.sqlpp.clause;
 
+import java.util.ArrayList;
 import java.util.List;
 
 import org.apache.asterix.common.exceptions.AsterixException;
@@ -28,15 +29,16 @@ import org.apache.asterix.lang.common.clause.LetClause;
 import org.apache.asterix.lang.common.clause.WhereClause;
 import org.apache.asterix.lang.common.visitor.base.ILangVisitor;
 import org.apache.asterix.lang.sqlpp.visitor.base.ISqlppVisitor;
+import org.apache.commons.lang3.ObjectUtils;
 
 public class SelectBlock implements Clause {
 
     private SelectClause selectClause;
     private FromClause fromClause;
-    private List<LetClause> letClauses;
+    private List<LetClause> letClauses = new ArrayList<>();
     private WhereClause whereClause;
     private GroupbyClause groupbyClause;
-    private List<LetClause> letClausesAfterGby;
+    private List<LetClause> letClausesAfterGby = new ArrayList<>();
     private HavingClause havingClause;
 
     public SelectBlock(SelectClause selectClause, FromClause fromClause, List<LetClause> letClauses,
@@ -44,11 +46,15 @@ public class SelectBlock implements Clause {
             HavingClause havingClause) {
         this.selectClause = selectClause;
         this.fromClause = fromClause;
-        this.letClauses = letClauses;
+        if (letClauses != null) {
+            this.letClauses.addAll(letClauses);
+        }
         this.whereClause = whereClause;
         this.groupbyClause = groupbyClause;
         this.havingClause = havingClause;
-        this.letClausesAfterGby = letClausesAfterGby;
+        if (letClausesAfterGby != null) {
+            this.letClausesAfterGby.addAll(letClausesAfterGby);
+        }
     }
 
     @Override
@@ -90,7 +96,7 @@ public class SelectBlock implements Clause {
     }
 
     public boolean hasLetClauses() {
-        return letClauses != null && letClauses.size() > 0;
+        return letClauses != null && !letClauses.isEmpty();
     }
 
     public boolean hasWhereClause() {
@@ -102,7 +108,7 @@ public class SelectBlock implements Clause {
     }
 
     public boolean hasLetClausesAfterGroupby() {
-        return letClausesAfterGby != null && letClausesAfterGby.size() > 0;
+        return letClausesAfterGby != null && !letClausesAfterGby.isEmpty();
     }
 
     public List<LetClause> getLetListAfterGroupby() {
@@ -116,4 +122,53 @@ public class SelectBlock implements Clause {
     public void setGroupbyClause(GroupbyClause groupbyClause) {
         this.groupbyClause = groupbyClause;
     }
+
+    @Override
+    public int hashCode() {
+        return ObjectUtils.hashCodeMulti(fromClause, groupbyClause, havingClause, letClauses, letClausesAfterGby,
+                selectClause, whereClause);
+    }
+
+    @Override
+    public boolean equals(Object object) {
+        if (this == object) {
+            return true;
+        }
+        if (!(object instanceof SelectBlock)) {
+            return false;
+        }
+        SelectBlock target = (SelectBlock) object;
+        boolean equals = ObjectUtils.equals(fromClause, target.fromClause)
+                && ObjectUtils.equals(groupbyClause, target.groupbyClause)
+                && ObjectUtils.equals(havingClause, target.havingClause)
+                && ObjectUtils.equals(letClauses, target.letClauses);
+        return equals && ObjectUtils.equals(letClausesAfterGby, target.letClausesAfterGby)
+                && ObjectUtils.equals(selectClause, target.selectClause)
+                && ObjectUtils.equals(whereClause, target.whereClause);
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(selectClause);
+        if (hasFromClause()) {
+            sb.append(fromClause);
+        }
+        if (hasLetClauses()) {
+            sb.append(letClauses);
+        }
+        if (hasWhereClause()) {
+            sb.append(whereClause);
+        }
+        if (hasGroupbyClause()) {
+            sb.append(groupbyClause);
+        }
+        if (hasLetClausesAfterGroupby()) {
+            sb.append(letClausesAfterGby);
+        }
+        if (hasHavingClause()) {
+            sb.append(havingClause);
+        }
+        return sb.toString();
+    }
 }

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5b2d4c89/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/SelectClause.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/SelectClause.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/SelectClause.java
index 42c8b29..12e501b 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/SelectClause.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/SelectClause.java
@@ -23,6 +23,7 @@ import org.apache.asterix.common.exceptions.AsterixException;
 import org.apache.asterix.lang.common.base.Clause;
 import org.apache.asterix.lang.common.visitor.base.ILangVisitor;
 import org.apache.asterix.lang.sqlpp.visitor.base.ISqlppVisitor;
+import org.apache.commons.lang3.ObjectUtils;
 
 public class SelectClause implements Clause {
 
@@ -69,6 +70,24 @@ public class SelectClause implements Clause {
     @Override
     public String toString() {
         return "select " + (distinct ? "distinct " : "")
-                + (selectElement() ? "element " + String.valueOf(selectElement) : String.valueOf(selectRegular));
+                + (selectElement() ? "element " + selectElement : String.valueOf(selectRegular));
+    }
+
+    @Override
+    public int hashCode() {
+        return ObjectUtils.hashCodeMulti(distinct, selectElement, selectRegular);
+    }
+
+    @Override
+    public boolean equals(Object object) {
+        if (this == object) {
+            return true;
+        }
+        if (!(object instanceof SelectClause)) {
+            return false;
+        }
+        SelectClause target = (SelectClause) object;
+        return distinct == target.distinct && ObjectUtils.equals(selectElement, target.selectElement)
+                && ObjectUtils.equals(selectRegular, target.selectRegular);
     }
 }

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5b2d4c89/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/SelectElement.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/SelectElement.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/SelectElement.java
index 14efd0f..6a630b5 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/SelectElement.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/SelectElement.java
@@ -55,4 +55,21 @@ public class SelectElement implements Clause {
     public String toString() {
         return String.valueOf(expr);
     }
+
+    @Override
+    public int hashCode() {
+        return expr.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object object) {
+        if (this == object) {
+            return true;
+        }
+        if (!(object instanceof SelectElement)) {
+            return false;
+        }
+        SelectElement target = (SelectElement) object;
+        return expr.equals(target.expr);
+    }
 }

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5b2d4c89/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/SelectRegular.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/SelectRegular.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/SelectRegular.java
index 4703e01..1df1e7a 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/SelectRegular.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/SelectRegular.java
@@ -53,4 +53,21 @@ public class SelectRegular implements Clause {
     public String toString() {
         return projections.stream().map(String::valueOf).collect(Collectors.joining(", "));
     }
+
+    @Override
+    public int hashCode() {
+        return projections.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object object) {
+        if (this == object) {
+            return true;
+        }
+        if (!(object instanceof SelectRegular)) {
+            return false;
+        }
+        SelectRegular target = (SelectRegular) object;
+        return projections.equals(target.getProjections());
+    }
 }

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5b2d4c89/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/SelectSetOperation.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/SelectSetOperation.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/SelectSetOperation.java
index 3af93a7..6e0fb21 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/SelectSetOperation.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/SelectSetOperation.java
@@ -28,15 +28,18 @@ import org.apache.asterix.lang.common.visitor.base.ILangVisitor;
 import org.apache.asterix.lang.sqlpp.struct.SetOperationInput;
 import org.apache.asterix.lang.sqlpp.struct.SetOperationRight;
 import org.apache.asterix.lang.sqlpp.visitor.base.ISqlppVisitor;
+import org.apache.commons.lang3.ObjectUtils;
 
 public class SelectSetOperation implements Clause {
 
     private SetOperationInput leftInput;
-    private List<SetOperationRight> rightInputs;
+    private List<SetOperationRight> rightInputs = new ArrayList<>();
 
     public SelectSetOperation(SetOperationInput leftInput, List<SetOperationRight> rightInputs) {
         this.leftInput = leftInput;
-        this.rightInputs = rightInputs == null ? new ArrayList<SetOperationRight>() : rightInputs;
+        if (rightInputs != null) {
+            this.rightInputs.addAll(rightInputs);
+        }
     }
 
     @Override
@@ -58,7 +61,34 @@ public class SelectSetOperation implements Clause {
     }
 
     public boolean hasRightInputs() {
-        return rightInputs != null && rightInputs.size() > 0;
+        return rightInputs != null && !rightInputs.isEmpty();
+    }
+
+    @Override
+    public int hashCode() {
+        return ObjectUtils.hashCodeMulti(leftInput, rightInputs);
+    }
+
+    @Override
+    public boolean equals(Object object) {
+        if (this == object) {
+            return true;
+        }
+        if (!(object instanceof SelectSetOperation)) {
+            return false;
+        }
+        SelectSetOperation target = (SelectSetOperation) object;
+        return ObjectUtils.equals(leftInput, target.leftInput) && ObjectUtils.equals(rightInputs, target.rightInputs);
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(leftInput);
+        for (SetOperationRight right : rightInputs) {
+            sb.append(" " + right);
+        }
+        return sb.toString();
     }
 
 }

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5b2d4c89/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/expression/IndependentSubquery.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/expression/IndependentSubquery.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/expression/IndependentSubquery.java
index 3825092..3b4c1aa 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/expression/IndependentSubquery.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/expression/IndependentSubquery.java
@@ -54,4 +54,21 @@ public class IndependentSubquery implements Expression {
     public String toString() {
         return String.valueOf(expr);
     }
+
+    @Override
+    public int hashCode() {
+        return expr.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object object) {
+        if (this == object) {
+            return true;
+        }
+        if (!(object instanceof IndependentSubquery)) {
+            return false;
+        }
+        IndependentSubquery target = (IndependentSubquery) object;
+        return this.expr.equals(target.getExpr());
+    }
 }

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5b2d4c89/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/expression/SelectExpression.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/expression/SelectExpression.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/expression/SelectExpression.java
index 7f9daf8..a940c47 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/expression/SelectExpression.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/expression/SelectExpression.java
@@ -18,6 +18,7 @@
  */
 package org.apache.asterix.lang.sqlpp.expression;
 
+import java.util.ArrayList;
 import java.util.List;
 
 import org.apache.asterix.common.exceptions.AsterixException;
@@ -28,10 +29,11 @@ import org.apache.asterix.lang.common.clause.OrderbyClause;
 import org.apache.asterix.lang.common.visitor.base.ILangVisitor;
 import org.apache.asterix.lang.sqlpp.clause.SelectSetOperation;
 import org.apache.asterix.lang.sqlpp.visitor.base.ISqlppVisitor;
+import org.apache.commons.lang3.ObjectUtils;
 
 public class SelectExpression implements Expression {
 
-    private List<LetClause> letList;
+    private List<LetClause> letList = new ArrayList<>();
     private SelectSetOperation selectSetOperation;
     private OrderbyClause orderbyClause;
     private LimitClause limitClause;
@@ -39,7 +41,9 @@ public class SelectExpression implements Expression {
 
     public SelectExpression(List<LetClause> letList, SelectSetOperation selectSetOperation, OrderbyClause orderbyClause,
             LimitClause limitClause, boolean subquery) {
-        this.letList = letList;
+        if (letList != null) {
+            this.letList.addAll(letList);
+        }
         this.selectSetOperation = selectSetOperation;
         this.orderbyClause = orderbyClause;
         this.limitClause = limitClause;
@@ -92,4 +96,42 @@ public class SelectExpression implements Expression {
         subquery = setSubquery;
     }
 
+    @Override
+    public int hashCode() {
+        return ObjectUtils.hashCodeMulti(letList, limitClause, orderbyClause, selectSetOperation, subquery);
+    }
+
+    @Override
+    public boolean equals(Object object) {
+        if (this == object) {
+            return true;
+        }
+        if (!(object instanceof SelectExpression)) {
+            return false;
+        }
+        SelectExpression target = (SelectExpression) object;
+        boolean equals =
+                ObjectUtils.equals(letList, target.letList) && ObjectUtils.equals(limitClause, target.limitClause)
+                        && ObjectUtils.equals(orderbyClause, target.orderbyClause)
+                        && ObjectUtils.equals(selectSetOperation, target.selectSetOperation);
+        return equals && subquery == target.subquery;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(subquery ? "(" : "");
+        if (this.hasLetClauses()) {
+            sb.append(letList.toString());
+        }
+        sb.append(selectSetOperation);
+        if (hasOrderby()) {
+            sb.append(orderbyClause);
+        }
+        if (hasLimit()) {
+            sb.append(limitClause);
+        }
+        sb.append(subquery ? ")" : "");
+        return sb.toString();
+    }
 }

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5b2d4c89/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppFunctionBodyRewriter.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppFunctionBodyRewriter.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppFunctionBodyRewriter.java
index 49823e7..6ddfa40 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppFunctionBodyRewriter.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppFunctionBodyRewriter.java
@@ -37,6 +37,12 @@ class SqlppFunctionBodyRewriter extends SqlppQueryRewriter {
         // Inlines column aliases.
         inlineColumnAlias();
 
+        // Generates column names.
+        generateColumnNames();
+
+        // Substitutes group-by key expressions.
+        substituteGroupbyKeyExpression();
+
         // Inlines WITH expressions.
         inlineWithExpressions();
 

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5b2d4c89/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppQueryRewriter.java
----------------------------------------------------------------------
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 5a62e1e..d0e2aa3 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
@@ -49,6 +49,7 @@ import org.apache.asterix.lang.sqlpp.expression.IndependentSubquery;
 import org.apache.asterix.lang.sqlpp.expression.SelectExpression;
 import org.apache.asterix.lang.sqlpp.parser.FunctionParser;
 import org.apache.asterix.lang.sqlpp.parser.SqlppParserFactory;
+import org.apache.asterix.lang.sqlpp.rewrites.visitor.GenerateColumnNameVisitor;
 import org.apache.asterix.lang.sqlpp.rewrites.visitor.InlineColumnAliasVisitor;
 import org.apache.asterix.lang.sqlpp.rewrites.visitor.InlineWithExpressionVisitor;
 import org.apache.asterix.lang.sqlpp.rewrites.visitor.OperatorExpressionVisitor;
@@ -56,6 +57,7 @@ import org.apache.asterix.lang.sqlpp.rewrites.visitor.SqlppBuiltinFunctionRewrit
 import org.apache.asterix.lang.sqlpp.rewrites.visitor.SqlppGlobalAggregationSugarVisitor;
 import org.apache.asterix.lang.sqlpp.rewrites.visitor.SqlppGroupByVisitor;
 import org.apache.asterix.lang.sqlpp.rewrites.visitor.SqlppInlineUdfsVisitor;
+import org.apache.asterix.lang.sqlpp.rewrites.visitor.SubstituteGroupbyExpressionWithVariableVisitor;
 import org.apache.asterix.lang.sqlpp.rewrites.visitor.VariableCheckAndRewriteVisitor;
 import org.apache.asterix.lang.sqlpp.struct.SetOperationRight;
 import org.apache.asterix.lang.sqlpp.util.FunctionMapUtil;
@@ -97,6 +99,12 @@ class SqlppQueryRewriter implements IQueryRewriter {
         // Inlines column aliases.
         inlineColumnAlias();
 
+        // Generates column names.
+        generateColumnNames();
+
+        // Substitutes group-by key expressions.
+        substituteGroupbyKeyExpression();
+
         // Inlines WITH expressions.
         inlineWithExpressions();
 
@@ -157,10 +165,29 @@ class SqlppQueryRewriter implements IQueryRewriter {
             return;
         }
         // Inlines with expressions.
-        InlineWithExpressionVisitor inlineWithExpressionVisitor = new InlineWithExpressionVisitor(context);
+        InlineWithExpressionVisitor inlineWithExpressionVisitor = new InlineWithExpressionVisitor();
         inlineWithExpressionVisitor.visit(topExpr, null);
     }
 
+    protected void generateColumnNames() throws AsterixException {
+        if (topExpr == null) {
+            return;
+        }
+        // Generate column names if they are missing in the user query.
+        GenerateColumnNameVisitor generateColumnNameVisitor = new GenerateColumnNameVisitor(context);
+        generateColumnNameVisitor.visit(topExpr, null);
+    }
+
+    protected void substituteGroupbyKeyExpression() throws AsterixException {
+        if (topExpr == null) {
+            return;
+        }
+        // Substitute group-by key expressions that appear in the select clause.
+        SubstituteGroupbyExpressionWithVariableVisitor substituteGbyExprVisitor =
+                new SubstituteGroupbyExpressionWithVariableVisitor();
+        substituteGbyExprVisitor.visit(topExpr, null);
+    }
+
     protected void rewriteOperatorExpression() throws AsterixException {
         if (topExpr == null) {
             return;
@@ -183,8 +210,8 @@ class SqlppQueryRewriter implements IQueryRewriter {
         if (topExpr == null) {
             return;
         }
-        VariableCheckAndRewriteVisitor variableCheckAndRewriteVisitor = new VariableCheckAndRewriteVisitor(context,
-                overwrite, metadataProvider);
+        VariableCheckAndRewriteVisitor variableCheckAndRewriteVisitor =
+                new VariableCheckAndRewriteVisitor(context, overwrite, metadataProvider);
         variableCheckAndRewriteVisitor.visit(topExpr, null);
     }
 
@@ -235,8 +262,8 @@ class SqlppQueryRewriter implements IQueryRewriter {
 
             Function function = lookupUserDefinedFunctionDecl(signature);
             if (function == null) {
-                FunctionSignature normalizedSignature = FunctionMapUtil.normalizeBuiltinFunctionSignature(signature,
-                        false);
+                FunctionSignature normalizedSignature =
+                        FunctionMapUtil.normalizeBuiltinFunctionSignature(signature, false);
                 if (AsterixBuiltinFunctions.isBuiltinCompilerFunction(normalizedSignature, includePrivateFunctions)) {
                     continue;
                 }

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5b2d4c89/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/GenerateColumnNameVisitor.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/GenerateColumnNameVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/GenerateColumnNameVisitor.java
new file mode 100644
index 0000000..e2edb3a
--- /dev/null
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/GenerateColumnNameVisitor.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.asterix.lang.sqlpp.rewrites.visitor;
+
+import org.apache.asterix.common.exceptions.AsterixException;
+import org.apache.asterix.lang.common.base.Expression;
+import org.apache.asterix.lang.common.base.ILangExpression;
+import org.apache.asterix.lang.common.clause.GroupbyClause;
+import org.apache.asterix.lang.common.expression.GbyVariableExpressionPair;
+import org.apache.asterix.lang.common.expression.VariableExpr;
+import org.apache.asterix.lang.common.rewrites.LangRewritingContext;
+import org.apache.asterix.lang.sqlpp.clause.Projection;
+import org.apache.asterix.lang.sqlpp.clause.SelectBlock;
+import org.apache.asterix.lang.sqlpp.util.SqlppVariableUtil;
+import org.apache.asterix.lang.sqlpp.visitor.base.AbstractSqlppExpressionScopingVisitor;
+
+// Generates implicit column names for projections in a SELECT clause and group-by keys.
+public class GenerateColumnNameVisitor extends AbstractSqlppExpressionScopingVisitor {
+
+    public GenerateColumnNameVisitor(LangRewritingContext context) {
+        super(context);
+    }
+
+    @Override
+    public Expression visit(SelectBlock selectBlock, ILangExpression arg) throws AsterixException {
+        // Visit selectBlock first so that column names starts from $1.
+        selectBlock.getSelectClause().accept(this, arg);
+        return super.visit(selectBlock, arg);
+    }
+
+    @Override
+    public Expression visit(Projection projection, ILangExpression arg) throws AsterixException {
+        if (!projection.star() && projection.getName() == null) {
+            projection.setName(SqlppVariableUtil.variableNameToDisplayedFieldName(context.newVariable().getValue()));
+        }
+        return super.visit(projection, arg);
+    }
+
+    @Override
+    public Expression visit(GroupbyClause groupbyClause, ILangExpression arg) throws AsterixException {
+        for (GbyVariableExpressionPair gbyKeyPair : groupbyClause.getGbyPairList()) {
+            if (gbyKeyPair.getVar() == null) {
+                gbyKeyPair.setVar(new VariableExpr(context.newVariable()));
+            }
+        }
+        return super.visit(groupbyClause, arg);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5b2d4c89/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/InlineColumnAliasVisitor.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/InlineColumnAliasVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/InlineColumnAliasVisitor.java
index 6b74d3f..57cbdc5 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/InlineColumnAliasVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/InlineColumnAliasVisitor.java
@@ -130,12 +130,12 @@ public class InlineColumnAliasVisitor extends AbstractSqlppQueryExpressionVisito
 
     @Override
     public Void visit(Projection projection, Boolean overwriteWithGbyKeyVarRefs) throws AsterixException {
-        if (projection.star()) {
+        if (projection.star() || projection.getName() == null) {
             return null;
         }
         projection.getExpression().accept(this, overwriteWithGbyKeyVarRefs);
-        VariableExpr columnAlias = new VariableExpr(
-                SqlppVariableUtil.toInternalVariableIdentifier(projection.getName()));
+        VariableExpr columnAlias =
+                new VariableExpr(SqlppVariableUtil.toInternalVariableIdentifier(projection.getName()));
         VariableSubstitutionEnvironment env = scopeChecker.getCurrentScope().getVarSubstitutionEnvironment();
         Expression gbyKey = (Expression) SqlppRewriteUtil.deepCopy(env.findSubstitution(columnAlias));
         if (overwriteWithGbyKeyVarRefs) {
@@ -220,26 +220,25 @@ public class InlineColumnAliasVisitor extends AbstractSqlppQueryExpressionVisito
             // We only need to deal with the case that the left expression (for a field name) is
             // a string literal. Otherwise, it is different from a column alias in a projection
             // (e.g., foo.name AS name) in regular SQL SELECT.
-            if (leftExpr.getKind() == Kind.LITERAL_EXPRESSION) {
-                LiteralExpr literalExpr = (LiteralExpr) leftExpr;
-                if (literalExpr.getValue().getLiteralType() == Literal.Type.STRING) {
-                    String fieldName = literalExpr.getValue().getStringValue();
-                    VariableExpr columnAlias = new VariableExpr(
-                            SqlppVariableUtil.toInternalVariableIdentifier(fieldName));
-                    VariableSubstitutionEnvironment env = scopeChecker.getCurrentScope()
-                            .getVarSubstitutionEnvironment();
-                    if (overwriteWithGbyKeyVarRefs) {
-                        // Rewrites the field value expression by the mapped grouping key
-                        // (for the column alias) if there exists such a mapping.
-                        Expression gbyKey = (Expression) SqlppRewriteUtil.deepCopy(env.findSubstitution(columnAlias));
-                        if (gbyKey != null) {
-                            binding.setRightExpr(gbyKey);
-                        }
-                    } else {
-                        // If this is the first pass, map a field name (i.e., column alias) to the field expression.
-                        scopeChecker.getCurrentScope().addSymbolExpressionMappingToScope(columnAlias,
-                                binding.getRightExpr());
+            if (leftExpr.getKind() != Kind.LITERAL_EXPRESSION) {
+                continue;
+            }
+            LiteralExpr literalExpr = (LiteralExpr) leftExpr;
+            if (literalExpr.getValue().getLiteralType() == Literal.Type.STRING) {
+                String fieldName = literalExpr.getValue().getStringValue();
+                VariableExpr columnAlias = new VariableExpr(SqlppVariableUtil.toInternalVariableIdentifier(fieldName));
+                VariableSubstitutionEnvironment env = scopeChecker.getCurrentScope().getVarSubstitutionEnvironment();
+                if (overwriteWithGbyKeyVarRefs) {
+                    // Rewrites the field value expression by the mapped grouping key
+                    // (for the column alias) if there exists such a mapping.
+                    Expression gbyKey = (Expression) SqlppRewriteUtil.deepCopy(env.findSubstitution(columnAlias));
+                    if (gbyKey != null) {
+                        binding.setRightExpr(gbyKey);
                     }
+                } else {
+                    // If this is the first pass, map a field name (i.e., column alias) to the field expression.
+                    scopeChecker.getCurrentScope().addSymbolExpressionMappingToScope(columnAlias,
+                            binding.getRightExpr());
                 }
             }
         }
@@ -324,8 +323,8 @@ public class InlineColumnAliasVisitor extends AbstractSqlppQueryExpressionVisito
         Map<VariableExpr, VariableExpr> oldGbyExprsToNewGbyVarMap = new HashMap<>();
         for (GbyVariableExpressionPair gbyVarExpr : gc.getGbyPairList()) {
             Expression oldGbyExpr = gbyVarExpr.getExpr();
-            Expression newExpr = (Expression) SqlppVariableSubstitutionUtil.substituteVariableWithoutContext(oldGbyExpr,
-                    env);
+            Expression newExpr =
+                    (Expression) SqlppVariableSubstitutionUtil.substituteVariableWithoutContext(oldGbyExpr, env);
             newExpr.accept(this, overwriteWithGbyKeyVarRefs);
             gbyVarExpr.setExpr(newExpr);
             if (oldGbyExpr.getKind() == Kind.VARIABLE_EXPRESSION) {

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5b2d4c89/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/InlineWithExpressionVisitor.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/InlineWithExpressionVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/InlineWithExpressionVisitor.java
index bec1523..a01c09f 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/InlineWithExpressionVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/InlineWithExpressionVisitor.java
@@ -28,17 +28,12 @@ import org.apache.asterix.lang.common.base.Expression;
 import org.apache.asterix.lang.common.base.ILangExpression;
 import org.apache.asterix.lang.common.clause.LetClause;
 import org.apache.asterix.lang.common.expression.VariableExpr;
-import org.apache.asterix.lang.common.rewrites.LangRewritingContext;
 import org.apache.asterix.lang.sqlpp.expression.IndependentSubquery;
 import org.apache.asterix.lang.sqlpp.expression.SelectExpression;
 import org.apache.asterix.lang.sqlpp.util.SqlppVariableSubstitutionUtil;
-import org.apache.asterix.lang.sqlpp.visitor.base.AbstractSqlppExpressionScopingVisitor;
+import org.apache.asterix.lang.sqlpp.visitor.base.AbstractSqlppSimpleExpressionVisitor;
 
-public class InlineWithExpressionVisitor extends AbstractSqlppExpressionScopingVisitor {
-
-    public InlineWithExpressionVisitor(LangRewritingContext context) {
-        super(context);
-    }
+public class InlineWithExpressionVisitor extends AbstractSqlppSimpleExpressionVisitor {
 
     @Override
     public Expression visit(SelectExpression selectExpression, ILangExpression arg) throws AsterixException {
@@ -66,7 +61,9 @@ public class InlineWithExpressionVisitor extends AbstractSqlppExpressionScopingV
 
             // Continues to visit the rewritten select expression.
             return super.visit(newSelectExpression, arg);
+        } else {
+            // Continues to visit inside the select expression.
+            return super.visit(selectExpression, arg);
         }
-        return selectExpression;
     }
 }

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5b2d4c89/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SubstituteGroupbyExpressionWithVariableVisitor.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SubstituteGroupbyExpressionWithVariableVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SubstituteGroupbyExpressionWithVariableVisitor.java
new file mode 100644
index 0000000..c5cd60e
--- /dev/null
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SubstituteGroupbyExpressionWithVariableVisitor.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.asterix.lang.sqlpp.rewrites.visitor;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.asterix.common.exceptions.AsterixException;
+import org.apache.asterix.common.functions.FunctionSignature;
+import org.apache.asterix.lang.common.base.Expression;
+import org.apache.asterix.lang.common.base.Expression.Kind;
+import org.apache.asterix.lang.common.base.ILangExpression;
+import org.apache.asterix.lang.common.expression.CallExpr;
+import org.apache.asterix.lang.common.expression.GbyVariableExpressionPair;
+import org.apache.asterix.lang.common.rewrites.ExpressionSubstitutionEnvironment;
+import org.apache.asterix.lang.sqlpp.clause.SelectBlock;
+import org.apache.asterix.lang.sqlpp.expression.SelectExpression;
+import org.apache.asterix.lang.sqlpp.util.FunctionMapUtil;
+import org.apache.asterix.lang.sqlpp.util.SqlppVariableUtil;
+import org.apache.asterix.lang.sqlpp.visitor.SqlppSubstituteExpressionsVisitor;
+import org.apache.asterix.lang.sqlpp.visitor.base.AbstractSqlppSimpleExpressionVisitor;
+
+// Replaces expressions that appear in having/select/order-by/limit clause and are identical to some
+// group by key expression with the group by key expression.
+public class SubstituteGroupbyExpressionWithVariableVisitor extends AbstractSqlppSimpleExpressionVisitor {
+
+    @Override
+    public Expression visit(SelectBlock selectBlock, ILangExpression arg) throws AsterixException {
+        if (selectBlock.hasGroupbyClause()) {
+            Map<Expression, Expression> map = new HashMap<>();
+            for (GbyVariableExpressionPair gbyKeyPair : selectBlock.getGroupbyClause().getGbyPairList()) {
+                Expression gbyKeyExpr = gbyKeyPair.getExpr();
+                if (gbyKeyExpr.getKind() != Kind.VARIABLE_EXPRESSION) {
+                    map.put(gbyKeyExpr, gbyKeyPair.getVar());
+                }
+            }
+
+            // Creates a substitution visitor.
+            ExpressionSubstitutionEnvironment env =
+                    new ExpressionSubstitutionEnvironment(map, SqlppVariableUtil::getFreeVariables);
+            SubstituteGroupbyExpressionVisitor visitor = new SubstituteGroupbyExpressionVisitor();
+
+            // Rewrites having/select/order-by/limit clauses.
+            if (selectBlock.hasHavingClause()) {
+                selectBlock.getHavingClause().accept(visitor, env);
+            }
+            selectBlock.getSelectClause().accept(visitor, env);
+            SelectExpression selectExpression = (SelectExpression) arg;
+            if (selectExpression.hasOrderby()) {
+                selectExpression.getOrderbyClause().accept(visitor, env);
+            }
+            if (selectExpression.hasLimit()) {
+                selectExpression.getLimitClause().accept(visitor, env);
+            }
+        }
+        return super.visit(selectBlock, arg);
+    }
+
+}
+
+class SubstituteGroupbyExpressionVisitor extends SqlppSubstituteExpressionsVisitor {
+
+    @Override
+    public Expression visit(CallExpr callExpr, ExpressionSubstitutionEnvironment env) throws AsterixException {
+        FunctionSignature signature = callExpr.getFunctionSignature();
+        if (FunctionMapUtil.isSql92AggregateFunction(signature)) {
+            return callExpr;
+        } else {
+            return super.visit(callExpr, env);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5b2d4c89/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/struct/SetOperationInput.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/struct/SetOperationInput.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/struct/SetOperationInput.java
index da89709..55245fe 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/struct/SetOperationInput.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/struct/SetOperationInput.java
@@ -23,6 +23,7 @@ import org.apache.asterix.lang.common.visitor.base.ILangVisitor;
 import org.apache.asterix.lang.sqlpp.clause.SelectBlock;
 import org.apache.asterix.lang.sqlpp.expression.SelectExpression;
 import org.apache.asterix.lang.sqlpp.visitor.base.ISqlppVisitor;
+import org.apache.commons.lang3.ObjectUtils;
 
 public class SetOperationInput {
 
@@ -57,4 +58,26 @@ public class SetOperationInput {
             return ((ISqlppVisitor<R, T>) visitor).visit(subquery, arg);
         }
     }
+
+    @Override
+    public int hashCode() {
+        return ObjectUtils.hashCodeMulti(selectBlock, subquery);
+    }
+
+    @Override
+    public boolean equals(Object object) {
+        if (this == object) {
+            return true;
+        }
+        if (!(object instanceof SetOperationInput)) {
+            return false;
+        }
+        SetOperationInput target = (SetOperationInput) object;
+        return ObjectUtils.equals(selectBlock, target.selectBlock) && ObjectUtils.equals(subquery, target.subquery);
+    }
+
+    @Override
+    public String toString() {
+        return selectBlock.toString();
+    }
 }

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5b2d4c89/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/struct/SetOperationRight.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/struct/SetOperationRight.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/struct/SetOperationRight.java
index 5e061e0..ade4119 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/struct/SetOperationRight.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/struct/SetOperationRight.java
@@ -19,6 +19,7 @@
 package org.apache.asterix.lang.sqlpp.struct;
 
 import org.apache.asterix.lang.sqlpp.optype.SetOpType;
+import org.apache.commons.lang3.ObjectUtils;
 
 public class SetOperationRight {
 
@@ -44,4 +45,30 @@ public class SetOperationRight {
         return setOperationRightInput;
     }
 
+    @Override
+    public int hashCode() {
+        return ObjectUtils.hashCodeMulti(opType, setOperationRightInput, setSemantics);
+    }
+
+    @Override
+    public boolean equals(Object object) {
+        if (this == object) {
+            return true;
+        }
+        if (!(object instanceof SetOperationRight)) {
+            return false;
+        }
+        SetOperationRight target = (SetOperationRight) object;
+        return ObjectUtils.equals(opType, target.opType)
+                && ObjectUtils.equals(setOperationRightInput, target.setOperationRightInput)
+                && setSemantics == target.setSemantics;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(opType + " " + (setSemantics ? "" : " all "));
+        sb.append(setOperationRightInput);
+        return sb.toString();
+    }
 }

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5b2d4c89/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/ExpressionToVariableUtil.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/ExpressionToVariableUtil.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/ExpressionToVariableUtil.java
index 8817d76..93e1828 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/ExpressionToVariableUtil.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/ExpressionToVariableUtil.java
@@ -19,19 +19,23 @@
 
 package org.apache.asterix.lang.sqlpp.util;
 
+import org.apache.asterix.common.exceptions.AsterixException;
 import org.apache.asterix.lang.common.base.Expression;
 import org.apache.asterix.lang.common.base.Expression.Kind;
 import org.apache.asterix.lang.common.expression.FieldAccessor;
 import org.apache.asterix.lang.common.expression.VariableExpr;
 import org.apache.asterix.lang.common.struct.VarIdentifier;
 import org.apache.asterix.lang.sqlpp.parser.ParseException;
+import org.apache.log4j.Logger;
 
 public class ExpressionToVariableUtil {
 
+    private static final Logger LOGGER = Logger.getLogger(ExpressionToVariableUtil.class);
+
     private ExpressionToVariableUtil() {
     }
 
-    public static String getGeneratedIdentifier(Expression expr) throws ParseException {
+    private static String getGeneratedIdentifier(Expression expr) throws ParseException {
         if (expr.getKind() == Kind.VARIABLE_EXPRESSION) {
             VariableExpr bindingVarExpr = (VariableExpr) expr;
             return bindingVarExpr.getVar().getValue();
@@ -39,15 +43,62 @@ public class ExpressionToVariableUtil {
             FieldAccessor fa = (FieldAccessor) expr;
             return SqlppVariableUtil.toInternalVariableName(fa.getIdent().getValue());
         } else {
-            throw new ParseException("Need an alias for the enclosed " + expr.getKind() + " expression.");
+            try {
+                throw new ParseException(
+                        "Need an alias for the enclosed expression:\n" + SqlppFormatPrintUtil.toString(expr));
+            } catch (AsterixException e) {
+                LOGGER.error(e.getLocalizedMessage(), e);
+                throw new ParseException(e.getLocalizedMessage());
+            }
         }
     }
 
-    public static VariableExpr getGeneratedVariable(Expression expr) throws ParseException {
-        VarIdentifier var = new VarIdentifier(getGeneratedIdentifier(expr));
-        VariableExpr varExpr = new VariableExpr();
-        varExpr.setVar(var);
-        return varExpr;
+    /**
+     * Generates an identifier according to an expression.
+     *
+     * @param expr
+     *            the input expression.
+     * @param raiseError,
+     *            if it is not possible to generate an identifier from the input expression,
+     *            to raise the error if true, and to return a null if false.
+     * @return the generated identifier.
+     * @throws ParseException
+     */
+    public static String getGeneratedIdentifier(Expression expr, boolean raiseError) throws ParseException {
+        try {
+            return getGeneratedIdentifier(expr);
+        } catch (ParseException e) {
+            if (raiseError) {
+                throw e;
+            }
+            return null;
+        }
+    }
+
+    /**
+     * Generates a variable according to an expression.
+     *
+     * @param expr
+     *            the input expression.
+     * @param raiseError,
+     *            if it is not possible to generate a variable from the input expression,
+     *            to raise the error if true, and to return a null if false.
+     * @return the generated variable.
+     * @throws ParseException
+     */
+    public static VariableExpr getGeneratedVariable(Expression expr, boolean raiseError) throws ParseException {
+        try {
+            String varName = getGeneratedIdentifier(expr);
+            VarIdentifier var = new VarIdentifier(varName);
+            VariableExpr varExpr = new VariableExpr();
+            varExpr.setVar(var);
+            return varExpr;
+        } catch (ParseException e) {
+            if (raiseError) {
+                throw e;
+            }
+            return null;
+        }
     }
 
 }

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5b2d4c89/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppAstPrintUtil.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppAstPrintUtil.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppAstPrintUtil.java
index 5888194..f700439 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppAstPrintUtil.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppAstPrintUtil.java
@@ -32,7 +32,11 @@ import org.apache.asterix.lang.sqlpp.visitor.SqlppAstPrintVisitorFactory;
 
 public class SqlppAstPrintUtil {
 
-    private static final IAstPrintVisitorFactory astPrintVisitor = new SqlppAstPrintVisitorFactory();
+    private static final IAstPrintVisitorFactory astPrintVisitorFactory = new SqlppAstPrintVisitorFactory();
+
+    private SqlppAstPrintUtil() {
+
+    }
 
     /**
      * Prints the AST (abstract syntax tree) of an ILangExpression.
@@ -44,7 +48,7 @@ public class SqlppAstPrintUtil {
      * @throws AsterixException
      */
     public static void print(ILangExpression expr, PrintWriter output) throws AsterixException {
-        QueryPrintVisitor visitor = astPrintVisitor.createLangVisitor(output);
+        QueryPrintVisitor visitor = astPrintVisitorFactory.createLangVisitor(output);
         expr.accept(visitor, 0);
         output.flush();
     }
@@ -59,7 +63,7 @@ public class SqlppAstPrintUtil {
      * @throws AsterixException
      */
     public static void print(List<Statement> statements, PrintWriter output) throws AsterixException {
-        QueryPrintVisitor visitor = astPrintVisitor.createLangVisitor(output);
+        QueryPrintVisitor visitor = astPrintVisitorFactory.createLangVisitor(output);
         for (Statement statement : statements) {
             statement.accept(visitor, 0);
         }
@@ -73,7 +77,7 @@ public class SqlppAstPrintUtil {
      * @throws AsterixException
      */
     public static String toString(ILangExpression expr) throws AsterixException {
-        List<ILangExpression> exprs = new ArrayList<ILangExpression>();
+        List<ILangExpression> exprs = new ArrayList<>();
         exprs.add(expr);
         return toString(exprs);
     }
@@ -87,7 +91,7 @@ public class SqlppAstPrintUtil {
     public static String toString(List<ILangExpression> exprs) throws AsterixException {
         ByteArrayOutputStream bos = new ByteArrayOutputStream();
         PrintWriter output = new PrintWriter(bos);
-        QueryPrintVisitor visitor = astPrintVisitor.createLangVisitor(output);
+        QueryPrintVisitor visitor = astPrintVisitorFactory.createLangVisitor(output);
         for (ILangExpression expr : exprs) {
             expr.accept(visitor, 0);
         }

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5b2d4c89/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppFormatPrintUtil.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppFormatPrintUtil.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppFormatPrintUtil.java
index 41760f5..ff0a8e1 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppFormatPrintUtil.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppFormatPrintUtil.java
@@ -30,6 +30,10 @@ import org.apache.asterix.lang.sqlpp.visitor.SqlppFormatPrintVisitor;
 
 public class SqlppFormatPrintUtil {
 
+    private SqlppFormatPrintUtil() {
+
+    }
+
     /**
      * Prints the formatted output of an ILangExpression.
      *
@@ -67,7 +71,7 @@ public class SqlppFormatPrintUtil {
      * @throws AsterixException
      */
     public static String toString(ILangExpression expr) throws AsterixException {
-        List<ILangExpression> exprs = new ArrayList<ILangExpression>();
+        List<ILangExpression> exprs = new ArrayList<>();
         exprs.add(expr);
         return toString(exprs);
     }

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5b2d4c89/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppRewriteUtil.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppRewriteUtil.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppRewriteUtil.java
index 122bde3..4a8c69d 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppRewriteUtil.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppRewriteUtil.java
@@ -40,8 +40,8 @@ public class SqlppRewriteUtil {
     public static Expression rewriteExpressionUsingGroupVariable(VariableExpr groupVar,
             Collection<VariableExpr> targetVarList, Collection<VariableExpr> allVisableVars, ILangExpression expr,
             LangRewritingContext context) throws AsterixException {
-        SqlppGroupBySugarVisitor visitor = new SqlppGroupBySugarVisitor(context, groupVar, targetVarList,
-                allVisableVars);
+        SqlppGroupBySugarVisitor visitor =
+                new SqlppGroupBySugarVisitor(context, groupVar, targetVarList, allVisableVars);
         return expr.accept(visitor, null);
     }
 


Mime
View raw message