You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@asterixdb.apache.org by bu...@apache.org on 2016/04/05 05:40:42 UTC

[03/10] incubator-asterixdb git commit: Rewrite SQL++ functions.

http://git-wip-us.apache.org/repos/asf/incubator-asterixdb/blob/3dd80ec4/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppInlineUdfsVisitor.java
----------------------------------------------------------------------
diff --git a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppInlineUdfsVisitor.java b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppInlineUdfsVisitor.java
new file mode 100644
index 0000000..e7832bb
--- /dev/null
+++ b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppInlineUdfsVisitor.java
@@ -0,0 +1,237 @@
+/*
+ * 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.List;
+import java.util.Map;
+
+import org.apache.asterix.common.exceptions.AsterixException;
+import org.apache.asterix.lang.common.base.Expression;
+import org.apache.asterix.lang.common.base.IRewriterFactory;
+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.common.statement.FunctionDecl;
+import org.apache.asterix.lang.common.visitor.AbstractInlineUdfsVisitor;
+import org.apache.asterix.lang.sqlpp.clause.AbstractBinaryCorrelateClause;
+import org.apache.asterix.lang.sqlpp.clause.FromClause;
+import org.apache.asterix.lang.sqlpp.clause.FromTerm;
+import org.apache.asterix.lang.sqlpp.clause.HavingClause;
+import org.apache.asterix.lang.sqlpp.clause.JoinClause;
+import org.apache.asterix.lang.sqlpp.clause.NestClause;
+import org.apache.asterix.lang.sqlpp.clause.Projection;
+import org.apache.asterix.lang.sqlpp.clause.SelectBlock;
+import org.apache.asterix.lang.sqlpp.clause.SelectClause;
+import org.apache.asterix.lang.sqlpp.clause.SelectElement;
+import org.apache.asterix.lang.sqlpp.clause.SelectRegular;
+import org.apache.asterix.lang.sqlpp.clause.SelectSetOperation;
+import org.apache.asterix.lang.sqlpp.clause.UnnestClause;
+import org.apache.asterix.lang.sqlpp.expression.SelectExpression;
+import org.apache.asterix.lang.sqlpp.struct.SetOperationRight;
+import org.apache.asterix.lang.sqlpp.util.SqlppVariableSubstitutionUtil;
+import org.apache.asterix.lang.sqlpp.visitor.SqlppCloneAndSubstituteVariablesVisitor;
+import org.apache.asterix.lang.sqlpp.visitor.base.ISqlppVisitor;
+import org.apache.asterix.metadata.declared.AqlMetadataProvider;
+import org.apache.hyracks.algebricks.common.utils.Pair;
+
+public class SqlppInlineUdfsVisitor extends AbstractInlineUdfsVisitor
+        implements ISqlppVisitor<Boolean, List<FunctionDecl>> {
+
+    /**
+     * @param context,
+     *            manages ids of variables and guarantees uniqueness of variables.
+     * @param rewriterFactory,
+     *            a rewrite factory for rewriting user-defined functions.
+     * @param declaredFunctions,
+     *            a list of declared functions associated with the query.
+     * @param metadataProvider,
+     *            providing the definition of created (i.e., stored) user-defined functions.
+     */
+    public SqlppInlineUdfsVisitor(LangRewritingContext context, IRewriterFactory rewriterFactory,
+            List<FunctionDecl> declaredFunctions, AqlMetadataProvider metadataProvider) {
+        super(context, rewriterFactory, declaredFunctions, metadataProvider,
+                new SqlppCloneAndSubstituteVariablesVisitor(context));
+    }
+
+    @Override
+    protected Expression generateQueryExpression(List<LetClause> letClauses, Expression returnExpr)
+            throws AsterixException {
+        Map<VariableExpr, Expression> varExprMap = extractLetBindingVariableExpressionMappings(letClauses);
+        Expression inlinedReturnExpr = (Expression) SqlppVariableSubstitutionUtil
+                .substituteVariableWithoutContext(returnExpr, varExprMap);
+        return inlinedReturnExpr;
+    }
+
+    @Override
+    public Boolean visit(FromClause fromClause, List<FunctionDecl> func) throws AsterixException {
+        boolean changed = false;
+        for (FromTerm fromTerm : fromClause.getFromTerms()) {
+            changed |= fromTerm.accept(this, func);
+        }
+        return changed;
+    }
+
+    @Override
+    public Boolean visit(FromTerm fromTerm, List<FunctionDecl> func) throws AsterixException {
+        boolean changed = false;
+        Pair<Boolean, Expression> p = inlineUdfsInExpr(fromTerm.getLeftExpression(), func);
+        fromTerm.setLeftExpression(p.second);
+        changed |= p.first;
+        for (AbstractBinaryCorrelateClause correlateClause : fromTerm.getCorrelateClauses()) {
+            changed |= correlateClause.accept(this, func);
+        }
+        return changed;
+    }
+
+    @Override
+    public Boolean visit(JoinClause joinClause, List<FunctionDecl> funcs) throws AsterixException {
+        Pair<Boolean, Expression> p1 = inlineUdfsInExpr(joinClause.getRightExpression(), funcs);
+        joinClause.setRightExpression(p1.second);
+        Pair<Boolean, Expression> p2 = inlineUdfsInExpr(joinClause.getConditionExpression(), funcs);
+        joinClause.setConditionExpression(p2.second);
+        return p1.first || p2.first;
+    }
+
+    @Override
+    public Boolean visit(NestClause nestClause, List<FunctionDecl> funcs) throws AsterixException {
+        Pair<Boolean, Expression> p1 = inlineUdfsInExpr(nestClause.getRightExpression(), funcs);
+        nestClause.setRightExpression(p1.second);
+        Pair<Boolean, Expression> p2 = inlineUdfsInExpr(nestClause.getConditionExpression(), funcs);
+        nestClause.setConditionExpression(p2.second);
+        return p1.first || p2.first;
+    }
+
+    @Override
+    public Boolean visit(Projection projection, List<FunctionDecl> funcs) throws AsterixException {
+        Pair<Boolean, Expression> p = inlineUdfsInExpr(projection.getExpression(), funcs);
+        projection.setExpression(p.second);
+        return p.first;
+    }
+
+    @Override
+    public Boolean visit(SelectBlock selectBlock, List<FunctionDecl> funcs) throws AsterixException {
+        boolean changed = false;
+        if (selectBlock.hasFromClause()) {
+            changed |= selectBlock.getFromClause().accept(this, funcs);
+        }
+        if (selectBlock.hasLetClauses()) {
+            for (LetClause letClause : selectBlock.getLetList()) {
+                changed |= letClause.accept(this, funcs);
+            }
+        }
+        if (selectBlock.hasWhereClause()) {
+            changed |= selectBlock.getWhereClause().accept(this, funcs);
+        }
+        if (selectBlock.hasGroupbyClause()) {
+            changed |= selectBlock.getGroupbyClause().accept(this, funcs);
+        }
+        if (selectBlock.hasLetClausesAfterGroupby()) {
+            for (LetClause letClause : selectBlock.getLetListAfterGroupby()) {
+                changed |= letClause.accept(this, funcs);
+            }
+        }
+        if (selectBlock.hasHavingClause()) {
+            changed |= selectBlock.getHavingClause().accept(this, funcs);
+        }
+        changed |= selectBlock.getSelectClause().accept(this, funcs);
+        return changed;
+    }
+
+    @Override
+    public Boolean visit(SelectClause selectClause, List<FunctionDecl> funcs) throws AsterixException {
+        boolean changed = false;
+        if (selectClause.selectElement()) {
+            changed |= selectClause.getSelectElement().accept(this, funcs);
+        } else {
+            changed |= selectClause.getSelectRegular().accept(this, funcs);
+        }
+        return changed;
+    }
+
+    @Override
+    public Boolean visit(SelectElement selectElement, List<FunctionDecl> funcs) throws AsterixException {
+        Pair<Boolean, Expression> p = inlineUdfsInExpr(selectElement.getExpression(), funcs);
+        selectElement.setExpression(p.second);
+        return p.first;
+    }
+
+    @Override
+    public Boolean visit(SelectRegular selectRegular, List<FunctionDecl> funcs) throws AsterixException {
+        boolean changed = false;
+        for (Projection projection : selectRegular.getProjections()) {
+            changed |= projection.accept(this, funcs);
+        }
+        return changed;
+    }
+
+    @Override
+    public Boolean visit(SelectSetOperation selectSetOperation, List<FunctionDecl> funcs) throws AsterixException {
+        boolean changed = false;
+        changed |= selectSetOperation.getLeftInput().accept(this, funcs);
+        for (SetOperationRight right : selectSetOperation.getRightInputs()) {
+            changed |= right.getSetOperationRightInput().accept(this, funcs);
+        }
+        return changed;
+    }
+
+    @Override
+    public Boolean visit(SelectExpression selectExpression, List<FunctionDecl> funcs) throws AsterixException {
+        boolean changed = false;
+        if (selectExpression.hasLetClauses()) {
+            for (LetClause letClause : selectExpression.getLetList()) {
+                changed |= letClause.accept(this, funcs);
+            }
+        }
+        changed |= selectExpression.getSelectSetOperation().accept(this, funcs);
+        if (selectExpression.hasOrderby()) {
+            changed |= selectExpression.getOrderbyClause().accept(this, funcs);
+        }
+        if (selectExpression.hasLimit()) {
+            changed |= selectExpression.getLimitClause().accept(this, funcs);
+        }
+        return changed;
+    }
+
+    @Override
+    public Boolean visit(UnnestClause unnestClause, List<FunctionDecl> funcs) throws AsterixException {
+        Pair<Boolean, Expression> p = inlineUdfsInExpr(unnestClause.getRightExpression(), funcs);
+        unnestClause.setRightExpression(p.second);
+        return p.first;
+    }
+
+    @Override
+    public Boolean visit(HavingClause havingClause, List<FunctionDecl> funcs) throws AsterixException {
+        Pair<Boolean, Expression> p = inlineUdfsInExpr(havingClause.getFilterExpression(), funcs);
+        havingClause.setFilterExpression(p.second);
+        return p.first;
+    }
+
+    private Map<VariableExpr, Expression> extractLetBindingVariableExpressionMappings(List<LetClause> letClauses)
+            throws AsterixException {
+        Map<VariableExpr, Expression> varExprMap = new HashMap<VariableExpr, Expression>();
+        for (LetClause lc : letClauses) {
+            // inline let variables one by one iteratively.
+            lc.setBindingExpr((Expression) SqlppVariableSubstitutionUtil
+                    .substituteVariableWithoutContext(lc.getBindingExpr(), varExprMap));
+            varExprMap.put(lc.getVarExpr(), lc.getBindingExpr());
+        }
+        return varExprMap;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-asterixdb/blob/3dd80ec4/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/VariableCheckAndRewriteVisitor.java
----------------------------------------------------------------------
diff --git a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/VariableCheckAndRewriteVisitor.java b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/VariableCheckAndRewriteVisitor.java
new file mode 100644
index 0000000..5ca2533
--- /dev/null
+++ b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/VariableCheckAndRewriteVisitor.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.asterix.lang.sqlpp.rewrites.visitor;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.asterix.common.config.MetadataConstants;
+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.expression.CallExpr;
+import org.apache.asterix.lang.common.expression.LiteralExpr;
+import org.apache.asterix.lang.common.expression.VariableExpr;
+import org.apache.asterix.lang.common.literal.StringLiteral;
+import org.apache.asterix.lang.common.rewrites.LangRewritingContext;
+import org.apache.asterix.lang.common.struct.Identifier;
+import org.apache.asterix.lang.common.struct.VarIdentifier;
+import org.apache.asterix.lang.sqlpp.util.SqlppVariableUtil;
+import org.apache.asterix.lang.sqlpp.visitor.base.AbstractSqlppExpressionScopingVisitor;
+import org.apache.asterix.metadata.declared.AqlMetadataProvider;
+
+public class VariableCheckAndRewriteVisitor extends AbstractSqlppExpressionScopingVisitor {
+
+    protected final boolean overwrite;
+    protected final AqlMetadataProvider metadataProvider;
+
+    /**
+     * @param context,
+     *            manages ids of variables and guarantees uniqueness of variables.
+     * @param overwrite,
+     *            whether rewrite unbounded variables to dataset function calls.
+     *            This flag can only be true for rewriting a top-level query.
+     *            It should be false for rewriting the body expression of a user-defined function.
+     */
+    public VariableCheckAndRewriteVisitor(LangRewritingContext context, boolean overwrite,
+            AqlMetadataProvider metadataProvider) {
+        super(context);
+        this.overwrite = overwrite;
+        this.metadataProvider = metadataProvider;
+    }
+
+    @Override
+    public Expression visit(VariableExpr varExpr, Expression arg) throws AsterixException {
+        String varName = varExpr.getVar().getValue();
+        if (scopeChecker.isInForbiddenScopes(varName)) {
+            throw new AsterixException(
+                    "Inside limit clauses, it is disallowed to reference a variable having the same name as any variable bound in the same scope as the limit clause.");
+        }
+        if (rewriteNeeded(varExpr)) {
+            return datasetRewrite(varExpr);
+        } else {
+            return varExpr;
+        }
+    }
+
+    // Whether a rewrite is needed for a variable reference expression.
+    private boolean rewriteNeeded(VariableExpr varExpr) throws AsterixException {
+        String varName = varExpr.getVar().getValue();
+        Identifier ident = scopeChecker.lookupSymbol(varName);
+        if (ident != null) {
+            // Exists such an identifier
+            varExpr.setIsNewVar(false);
+            varExpr.setVar((VarIdentifier) ident);
+            return false;
+        } else {
+            // Meets a undefined variable
+            return true;
+        }
+    }
+
+    // Rewrites for global variable (e.g., dataset) references.
+    private Expression datasetRewrite(VariableExpr expr) throws AsterixException {
+        if (!overwrite) {
+            return expr;
+        }
+        String funcName = "dataset";
+        String dataverse = MetadataConstants.METADATA_DATAVERSE_NAME;
+        FunctionSignature signature = new FunctionSignature(dataverse, funcName, 1);
+        List<Expression> argList = new ArrayList<Expression>();
+        //Ignore the parser-generated prefix "$" for a dataset.
+        String dataset = SqlppVariableUtil.toUserDefinedVariableName(expr.getVar()).getValue();
+        argList.add(new LiteralExpr(new StringLiteral(dataset)));
+        return new CallExpr(signature, argList);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-asterixdb/blob/3dd80ec4/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/FunctionMapUtil.java
----------------------------------------------------------------------
diff --git a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/FunctionMapUtil.java b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/FunctionMapUtil.java
new file mode 100644
index 0000000..cbf05b5
--- /dev/null
+++ b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/FunctionMapUtil.java
@@ -0,0 +1,150 @@
+/*
+ * 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.util;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.asterix.common.exceptions.AsterixException;
+import org.apache.asterix.common.functions.FunctionConstants;
+import org.apache.asterix.common.functions.FunctionSignature;
+import org.apache.asterix.lang.common.util.FunctionUtil;
+import org.apache.asterix.om.functions.AsterixBuiltinFunctions;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+import org.apache.hyracks.algebricks.core.algebra.functions.IFunctionInfo;
+
+public class FunctionMapUtil {
+
+    private final static String CORE_AGGREGATE_PREFIX = "coll_";
+
+    // Maps from a SQL function name to an AQL function name (i.e., AsterixDB internal name).
+    private static final Map<String, String> FUNCTION_NAME_MAP = new HashMap<>();
+
+    static {
+        FUNCTION_NAME_MAP.put("ceil", "ceiling"); //SQL: ceil,  AQL: ceiling
+        FUNCTION_NAME_MAP.put("length", "string-length"); // SQL: length,  AQL: string-length
+        FUNCTION_NAME_MAP.put("lower", "lowercase"); // SQL: lower, AQL: lowercase
+        FUNCTION_NAME_MAP.put("substr", "substring"); // SQL: substr,  AQL: substring
+        FUNCTION_NAME_MAP.put("upper", "uppercase"); //SQL: upper, AQL: uppercase
+    }
+
+    /**
+     * Whether a function signature is a SQL-92 core aggregate function.
+     *
+     * @param fs,
+     *            the function signature.
+     * @return true if the function signature is a SQL-92 core aggregate,
+     *         false otherwise.
+     */
+    public static boolean isSql92AggregateFunction(FunctionSignature signature) throws AsterixException {
+        IFunctionInfo finfo = FunctionUtil.getFunctionInfo(new FunctionIdentifier(FunctionConstants.ASTERIX_NS,
+                signature.getName().toLowerCase(), signature.getArity()));
+        if (finfo == null) {
+            return false;
+        }
+        return AsterixBuiltinFunctions.getAggregateFunction(finfo.getFunctionIdentifier()) != null;
+    }
+
+    /**
+     * Whether a function signature is a SQL++ core aggregate function.
+     *
+     * @param fs,
+     *            the function signature.
+     * @return true if the function signature is a SQL++ core aggregate,
+     *         false otherwise.
+     */
+    public static boolean isCoreAggregateFunction(FunctionSignature fs) {
+        String name = fs.getName().toLowerCase();
+        if (!name.startsWith(CORE_AGGREGATE_PREFIX)) {
+            return false;
+        }
+        IFunctionInfo finfo = FunctionUtil.getFunctionInfo(new FunctionIdentifier(FunctionConstants.ASTERIX_NS,
+                name.substring(CORE_AGGREGATE_PREFIX.length()), fs.getArity()));
+        if (finfo == null) {
+            return false;
+        }
+        return AsterixBuiltinFunctions.getAggregateFunction(finfo.getFunctionIdentifier()) != null;
+    }
+
+    /**
+     * Get the corresponding SQL++ core aggregate function from the SQL-92 aggregate function.
+     *
+     * @param fs,
+     *            the SQL-92 aggregate function signature.
+     * @return the SQL++ aggregate function signature.
+     * @throws AsterixException
+     */
+    public static FunctionSignature sql92ToCoreAggregateFunction(FunctionSignature fs) throws AsterixException {
+        if (!isSql92AggregateFunction(fs)) {
+            return fs;
+        }
+        return new FunctionSignature(fs.getNamespace(), CORE_AGGREGATE_PREFIX + fs.getName(), fs.getArity());
+    }
+
+    /**
+     * Maps a user invoked function signature to a system internal function signature.
+     *
+     * @param fs,
+     *            the user typed function.
+     * @return the system internal function.
+     */
+    public static FunctionSignature normalizeBuiltinFunctionSignature(FunctionSignature fs, boolean checkSql92Aggregate)
+            throws AsterixException {
+        String mappedName = internalizeBuiltinScalarFunctionName(fs.getName());
+        if (isCoreAggregateFunction(fs)) {
+            mappedName = internalizeCoreAggregateFunctionName(mappedName);
+        } else if (checkSql92Aggregate && isSql92AggregateFunction(fs)) {
+            throw new AsterixException(fs.getName()
+                    + " is a SQL-92 aggregate function. The SQL++ core aggregate function " + CORE_AGGREGATE_PREFIX
+                    + fs.getName().toLowerCase() + " could potentially express the intent.");
+        }
+        return new FunctionSignature(fs.getNamespace(), mappedName, fs.getArity());
+    }
+
+    /**
+     * Removes the "coll_" prefix for user-facing SQL++ core aggregate function names.
+     *
+     * @param name,
+     *            the name of a user-facing SQL++ core aggregate function name.
+     * @return the AsterixDB internal function name for the aggregate function.
+     * @throws AsterixException
+     */
+    private static String internalizeCoreAggregateFunctionName(String name) throws AsterixException {
+        String lowerCaseName = name.toLowerCase();
+        return lowerCaseName.substring(CORE_AGGREGATE_PREFIX.length());
+    }
+
+    /**
+     * Note: function name normalization can ONLY be called
+     * after all user-defined functions (by either "DECLARE FUNCTION" or "CREATE FUNCTION")
+     * are inlined, because user-defined function names are case-sensitive.
+     *
+     * @param name
+     *            the user-input function name in the query.
+     * @return the mapped internal name.
+     */
+    private static String internalizeBuiltinScalarFunctionName(String name) {
+        String lowerCaseName = name.toLowerCase();
+        String mappedName = FUNCTION_NAME_MAP.get(lowerCaseName);
+        if (mappedName != null) {
+            return mappedName;
+        }
+        return lowerCaseName;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-asterixdb/blob/3dd80ec4/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppRewriteUtil.java
----------------------------------------------------------------------
diff --git a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppRewriteUtil.java b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppRewriteUtil.java
index 0f8488a..6c737d6 100644
--- a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppRewriteUtil.java
+++ b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppRewriteUtil.java
@@ -27,8 +27,9 @@ import org.apache.asterix.lang.common.base.Expression;
 import org.apache.asterix.lang.common.base.ILangExpression;
 import org.apache.asterix.lang.common.expression.VariableExpr;
 import org.apache.asterix.lang.common.rewrites.LangRewritingContext;
-import org.apache.asterix.lang.sqlpp.visitor.SqlppGroupBySugarVisitor;
-import org.apache.asterix.lang.sqlpp.visitor.UsedVariableVisitor;
+import org.apache.asterix.lang.sqlpp.rewrites.visitor.SqlppGroupBySugarVisitor;
+import org.apache.asterix.lang.sqlpp.visitor.DeepCopyVisitor;
+import org.apache.asterix.lang.sqlpp.visitor.FreeVariableVisitor;
 
 public class SqlppRewriteUtil {
 
@@ -36,15 +37,23 @@ public class SqlppRewriteUtil {
     public static Expression rewriteExpressionUsingGroupVariable(VariableExpr groupVar,
             Collection<VariableExpr> targetVarList, ILangExpression expr, LangRewritingContext context)
                     throws AsterixException {
-        SqlppGroupBySugarVisitor visitor = new SqlppGroupBySugarVisitor(context, null, groupVar, targetVarList);
+        SqlppGroupBySugarVisitor visitor = new SqlppGroupBySugarVisitor(context, groupVar, targetVarList);
         return expr.accept(visitor, null);
     }
 
-    public static Set<VariableExpr> getUsedVariable(Expression expr) throws AsterixException {
+    public static Set<VariableExpr> getFreeVariable(Expression expr) throws AsterixException {
         Set<VariableExpr> vars = new HashSet<>();
-        UsedVariableVisitor visitor = new UsedVariableVisitor();
+        FreeVariableVisitor visitor = new FreeVariableVisitor();
         expr.accept(visitor, vars);
         return vars;
     }
 
+    public static ILangExpression deepCopy(ILangExpression expr) throws AsterixException {
+        if (expr == null) {
+            return expr;
+        }
+        DeepCopyVisitor visitor = new DeepCopyVisitor();
+        return expr.accept(visitor, null);
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-asterixdb/blob/3dd80ec4/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppVariableUtil.java
----------------------------------------------------------------------
diff --git a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppVariableUtil.java b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppVariableUtil.java
index 8a63aa5..59e9389 100644
--- a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppVariableUtil.java
+++ b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppVariableUtil.java
@@ -18,7 +18,22 @@
  */
 package org.apache.asterix.lang.sqlpp.util;
 
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.asterix.common.exceptions.AsterixException;
+import org.apache.asterix.lang.common.base.ILangExpression;
+import org.apache.asterix.lang.common.clause.GroupbyClause;
+import org.apache.asterix.lang.common.clause.LetClause;
+import org.apache.asterix.lang.common.expression.GbyVariableExpressionPair;
+import org.apache.asterix.lang.common.expression.VariableExpr;
 import org.apache.asterix.lang.common.struct.VarIdentifier;
+import org.apache.asterix.lang.sqlpp.clause.AbstractBinaryCorrelateClause;
+import org.apache.asterix.lang.sqlpp.clause.FromClause;
+import org.apache.asterix.lang.sqlpp.clause.FromTerm;
+import org.apache.asterix.lang.sqlpp.visitor.FreeVariableVisitor;
 
 public class SqlppVariableUtil {
 
@@ -44,4 +59,73 @@ public class SqlppVariableUtil {
         return new VarIdentifier(USER_VAR_PREFIX + idName);
     }
 
+    public static Collection<VariableExpr> getFreeVariables(ILangExpression langExpr) throws AsterixException {
+        Collection<VariableExpr> freeVars = new HashSet<>();
+        FreeVariableVisitor visitor = new FreeVariableVisitor();
+        langExpr.accept(visitor, freeVars);
+        return freeVars;
+    }
+
+    public static Collection<VariableExpr> getBindingVariables(FromClause fromClause) {
+        Set<VariableExpr> bindingVars = new HashSet<>();
+        if (fromClause == null) {
+            return bindingVars;
+        }
+        for (FromTerm fromTerm : fromClause.getFromTerms()) {
+            bindingVars.addAll(getBindingVariables(fromTerm));
+        }
+        return bindingVars;
+    }
+
+    public static Collection<VariableExpr> getBindingVariables(FromTerm fromTerm) {
+        Set<VariableExpr> bindingVars = new HashSet<>();
+        if (fromTerm == null) {
+            return bindingVars;
+        }
+        bindingVars.add(fromTerm.getLeftVariable());
+        if (fromTerm.hasPositionalVariable()) {
+            bindingVars.add(fromTerm.getPositionalVariable());
+        }
+        for (AbstractBinaryCorrelateClause correlateClause : fromTerm.getCorrelateClauses()) {
+            bindingVars.add(correlateClause.getRightVariable());
+            if (correlateClause.hasPositionalVariable()) {
+                bindingVars.add(correlateClause.getPositionalVariable());
+            }
+        }
+        return bindingVars;
+    }
+
+    public static Collection<VariableExpr> getBindingVariables(GroupbyClause gbyClause) {
+        Set<VariableExpr> bindingVars = new HashSet<>();
+        if (gbyClause == null) {
+            return bindingVars;
+        }
+        for (GbyVariableExpressionPair gbyKey : gbyClause.getGbyPairList()) {
+            VariableExpr var = gbyKey.getVar();
+            if (var != null) {
+                bindingVars.add(var);
+            }
+        }
+        for (GbyVariableExpressionPair gbyKey : gbyClause.getDecorPairList()) {
+            VariableExpr var = gbyKey.getVar();
+            if (var != null) {
+                bindingVars.add(var);
+            }
+        }
+        bindingVars.addAll(gbyClause.getWithVarList());
+        bindingVars.add(gbyClause.getGroupVar());
+        return bindingVars;
+    }
+
+    public static Collection<VariableExpr> getBindingVariables(List<LetClause> letClauses) {
+        Set<VariableExpr> bindingVars = new HashSet<>();
+        if (letClauses == null || letClauses.isEmpty()) {
+            return bindingVars;
+        }
+        for (LetClause letClause : letClauses) {
+            bindingVars.add(letClause.getVarExpr());
+        }
+        return bindingVars;
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-asterixdb/blob/3dd80ec4/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/CheckSql92AggregateVisitor.java
----------------------------------------------------------------------
diff --git a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/CheckSql92AggregateVisitor.java b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/CheckSql92AggregateVisitor.java
new file mode 100644
index 0000000..1bca7ac
--- /dev/null
+++ b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/CheckSql92AggregateVisitor.java
@@ -0,0 +1,265 @@
+/*
+ * 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.visitor;
+
+import java.util.List;
+
+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.ILangExpression;
+import org.apache.asterix.lang.common.clause.GroupbyClause;
+import org.apache.asterix.lang.common.clause.LetClause;
+import org.apache.asterix.lang.common.clause.LimitClause;
+import org.apache.asterix.lang.common.clause.OrderbyClause;
+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.statement.FunctionDecl;
+import org.apache.asterix.lang.common.statement.Query;
+import org.apache.asterix.lang.sqlpp.clause.FromClause;
+import org.apache.asterix.lang.sqlpp.clause.FromTerm;
+import org.apache.asterix.lang.sqlpp.clause.HavingClause;
+import org.apache.asterix.lang.sqlpp.clause.JoinClause;
+import org.apache.asterix.lang.sqlpp.clause.NestClause;
+import org.apache.asterix.lang.sqlpp.clause.Projection;
+import org.apache.asterix.lang.sqlpp.clause.SelectBlock;
+import org.apache.asterix.lang.sqlpp.clause.SelectClause;
+import org.apache.asterix.lang.sqlpp.clause.SelectElement;
+import org.apache.asterix.lang.sqlpp.clause.SelectRegular;
+import org.apache.asterix.lang.sqlpp.clause.SelectSetOperation;
+import org.apache.asterix.lang.sqlpp.clause.UnnestClause;
+import org.apache.asterix.lang.sqlpp.expression.SelectExpression;
+import org.apache.asterix.lang.sqlpp.util.FunctionMapUtil;
+import org.apache.asterix.lang.sqlpp.visitor.base.AbstractSqlppQueryExpressionVisitor;
+
+/**
+ * This visitor checks if a language construct contains SQL-92 aggregates.
+ */
+public class CheckSql92AggregateVisitor extends AbstractSqlppQueryExpressionVisitor<Boolean, ILangExpression> {
+
+    @Override
+    public Boolean visit(Query q, ILangExpression parentSelectBlock) throws AsterixException {
+        return false;
+    }
+
+    @Override
+    public Boolean visit(FunctionDecl fd, ILangExpression parentSelectBlock) throws AsterixException {
+        return false;
+    }
+
+    @Override
+    public Boolean visit(LiteralExpr l, ILangExpression parentSelectBlock) throws AsterixException {
+        return false;
+    }
+
+    @Override
+    public Boolean visit(VariableExpr v, ILangExpression parentSelectBlock) throws AsterixException {
+        return false;
+    }
+
+    @Override
+    public Boolean visit(ListConstructor lc, ILangExpression parentSelectBlock) throws AsterixException {
+        return visitExprList(lc.getExprList(), parentSelectBlock);
+    }
+
+    @Override
+    public Boolean visit(RecordConstructor rc, ILangExpression parentSelectBlock) throws AsterixException {
+        for (FieldBinding fieldBinding : rc.getFbList()) {
+            ILangExpression leftExpr = fieldBinding.getLeftExpr();
+            ILangExpression rightExpr = fieldBinding.getRightExpr();
+            if (leftExpr.accept(this, parentSelectBlock)) {
+                return true;
+            }
+            if (rightExpr.accept(this, parentSelectBlock)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public Boolean visit(OperatorExpr ifbo, ILangExpression parentSelectBlock) throws AsterixException {
+        return visitExprList(ifbo.getExprList(), parentSelectBlock);
+    }
+
+    @Override
+    public Boolean visit(FieldAccessor fa, ILangExpression parentSelectBlock) throws AsterixException {
+        return fa.getExpr().accept(this, parentSelectBlock);
+    }
+
+    @Override
+    public Boolean visit(IndexAccessor ia, ILangExpression parentSelectBlock) throws AsterixException {
+        return ia.getExpr().accept(this, parentSelectBlock);
+    }
+
+    @Override
+    public Boolean visit(IfExpr ifexpr, ILangExpression parentSelectBlock) throws AsterixException {
+        if (ifexpr.getCondExpr().accept(this, parentSelectBlock)) {
+            return true;
+        } else {
+            return ifexpr.getThenExpr().accept(this, parentSelectBlock)
+                    || ifexpr.getElseExpr().accept(this, parentSelectBlock);
+        }
+    }
+
+    @Override
+    public Boolean visit(QuantifiedExpression qe, ILangExpression parentSelectBlock) throws AsterixException {
+        return false;
+    }
+
+    @Override
+    public Boolean visit(UnaryExpr u, ILangExpression parentSelectBlock) throws AsterixException {
+        return u.getExpr().accept(this, parentSelectBlock);
+    }
+
+    @Override
+    public Boolean visit(CallExpr pf, ILangExpression parentSelectBlock) throws AsterixException {
+        FunctionSignature fs = pf.getFunctionSignature();
+        if (FunctionMapUtil.isSql92AggregateFunction(fs)) {
+            return true;
+        }
+        for (Expression parameter : pf.getExprList()) {
+            if (parameter.accept(this, parentSelectBlock)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public Boolean visit(LetClause lc, ILangExpression parentSelectBlock) throws AsterixException {
+        return false;
+    }
+
+    @Override
+    public Boolean visit(WhereClause wc, ILangExpression parentSelectBlock) throws AsterixException {
+        return false;
+    }
+
+    @Override
+    public Boolean visit(OrderbyClause oc, ILangExpression parentSelectBlock) throws AsterixException {
+        return false;
+    }
+
+    @Override
+    public Boolean visit(GroupbyClause gc, ILangExpression parentSelectBlock) throws AsterixException {
+        return false;
+    }
+
+    @Override
+    public Boolean visit(LimitClause lc, ILangExpression parentSelectBlock) throws AsterixException {
+        return false;
+    }
+
+    @Override
+    public Boolean visit(FromClause fromClause, ILangExpression parentSelectBlock) throws AsterixException {
+        return false;
+    }
+
+    @Override
+    public Boolean visit(FromTerm fromTerm, ILangExpression parentSelectBlock) throws AsterixException {
+        return false;
+    }
+
+    @Override
+    public Boolean visit(JoinClause joinClause, ILangExpression parentSelectBlock) throws AsterixException {
+        return false;
+    }
+
+    @Override
+    public Boolean visit(NestClause nestClause, ILangExpression parentSelectBlock) throws AsterixException {
+        return false;
+    }
+
+    @Override
+    public Boolean visit(Projection projection, ILangExpression parentSelectBlock) throws AsterixException {
+        return projection.getExpression().accept(this, parentSelectBlock);
+    }
+
+    @Override
+    public Boolean visit(SelectBlock selectBlock, ILangExpression parentSelectBlock) throws AsterixException {
+        return selectBlock.getSelectClause().accept(this, selectBlock);
+    }
+
+    @Override
+    public Boolean visit(SelectClause selectClause, ILangExpression parentSelectBlock) throws AsterixException {
+        if (selectClause.selectElement()) {
+            return selectClause.getSelectElement().accept(this, parentSelectBlock);
+        } else {
+            return selectClause.getSelectRegular().accept(this, parentSelectBlock);
+        }
+    }
+
+    @Override
+    public Boolean visit(SelectElement selectElement, ILangExpression parentSelectBlock) throws AsterixException {
+        return selectElement.getExpression().accept(this, parentSelectBlock);
+    }
+
+    @Override
+    public Boolean visit(SelectRegular selectRegular, ILangExpression parentSelectBlock) throws AsterixException {
+        for (Projection projection : selectRegular.getProjections()) {
+            if (projection.accept(this, parentSelectBlock)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public Boolean visit(SelectSetOperation selectSetOperation, ILangExpression parentSelectBlock)
+            throws AsterixException {
+        return false;
+    }
+
+    @Override
+    public Boolean visit(SelectExpression selectStatement, ILangExpression parentSelectBlock) throws AsterixException {
+        return false;
+    }
+
+    @Override
+    public Boolean visit(UnnestClause unnestClause, ILangExpression parentSelectBlock) throws AsterixException {
+        return false;
+    }
+
+    @Override
+    public Boolean visit(HavingClause havingClause, ILangExpression parentSelectBlock) throws AsterixException {
+        return false;
+    }
+
+    private Boolean visitExprList(List<Expression> exprs, ILangExpression parentSelectBlock) throws AsterixException {
+        for (Expression item : exprs) {
+            if (item.accept(this, parentSelectBlock)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-asterixdb/blob/3dd80ec4/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/DeepCopyVisitor.java
----------------------------------------------------------------------
diff --git a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/DeepCopyVisitor.java b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/DeepCopyVisitor.java
new file mode 100644
index 0000000..2d891e0
--- /dev/null
+++ b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/DeepCopyVisitor.java
@@ -0,0 +1,415 @@
+/*
+ * 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.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.base.ILangExpression;
+import org.apache.asterix.lang.common.clause.GroupbyClause;
+import org.apache.asterix.lang.common.clause.LetClause;
+import org.apache.asterix.lang.common.clause.LimitClause;
+import org.apache.asterix.lang.common.clause.OrderbyClause;
+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.GbyVariableExpressionPair;
+import org.apache.asterix.lang.common.expression.IfExpr;
+import org.apache.asterix.lang.common.expression.IndexAccessor;
+import org.apache.asterix.lang.common.expression.ListConstructor;
+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.statement.FunctionDecl;
+import org.apache.asterix.lang.common.statement.Query;
+import org.apache.asterix.lang.common.struct.Identifier;
+import org.apache.asterix.lang.common.struct.QuantifiedPair;
+import org.apache.asterix.lang.sqlpp.clause.AbstractBinaryCorrelateClause;
+import org.apache.asterix.lang.sqlpp.clause.FromClause;
+import org.apache.asterix.lang.sqlpp.clause.FromTerm;
+import org.apache.asterix.lang.sqlpp.clause.HavingClause;
+import org.apache.asterix.lang.sqlpp.clause.JoinClause;
+import org.apache.asterix.lang.sqlpp.clause.NestClause;
+import org.apache.asterix.lang.sqlpp.clause.Projection;
+import org.apache.asterix.lang.sqlpp.clause.SelectBlock;
+import org.apache.asterix.lang.sqlpp.clause.SelectClause;
+import org.apache.asterix.lang.sqlpp.clause.SelectElement;
+import org.apache.asterix.lang.sqlpp.clause.SelectRegular;
+import org.apache.asterix.lang.sqlpp.clause.SelectSetOperation;
+import org.apache.asterix.lang.sqlpp.clause.UnnestClause;
+import org.apache.asterix.lang.sqlpp.expression.SelectExpression;
+import org.apache.asterix.lang.sqlpp.struct.SetOperationInput;
+import org.apache.asterix.lang.sqlpp.struct.SetOperationRight;
+import org.apache.asterix.lang.sqlpp.visitor.base.AbstractSqlppQueryExpressionVisitor;
+import org.apache.hyracks.algebricks.common.utils.Pair;
+
+public class DeepCopyVisitor extends AbstractSqlppQueryExpressionVisitor<ILangExpression, Void> {
+
+    @Override
+    public FromClause visit(FromClause fromClause, Void arg) throws AsterixException {
+        List<FromTerm> fromTerms = new ArrayList<>();
+        for (FromTerm fromTerm : fromClause.getFromTerms()) {
+            fromTerms.add((FromTerm) fromTerm.accept(this, arg));
+        }
+        return new FromClause(fromTerms);
+    }
+
+    @Override
+    public FromTerm visit(FromTerm fromTerm, Void arg) throws AsterixException {
+        // Visit the left expression of a from term.
+        Expression fromExpr = (Expression) fromTerm.getLeftExpression().accept(this, arg);
+        VariableExpr fromVar = (VariableExpr) fromTerm.getLeftVariable().accept(this, arg);
+        VariableExpr positionVar = fromTerm.getPositionalVariable() == null ? null
+                : (VariableExpr) fromTerm.getPositionalVariable().accept(this, arg);
+
+        // Visits join/unnest/nest clauses.
+        List<AbstractBinaryCorrelateClause> correlateClauses = new ArrayList<>();
+        for (AbstractBinaryCorrelateClause correlateClause : fromTerm.getCorrelateClauses()) {
+            correlateClauses.add((AbstractBinaryCorrelateClause) correlateClause.accept(this, arg));
+        }
+        return new FromTerm(fromExpr, fromVar, positionVar, correlateClauses);
+    }
+
+    @Override
+    public JoinClause visit(JoinClause joinClause, Void arg) throws AsterixException {
+        Expression rightExpression = (Expression) joinClause.getRightExpression().accept(this, arg);
+        VariableExpr rightVar = (VariableExpr) joinClause.getRightVariable().accept(this, arg);
+        VariableExpr rightPositionVar = joinClause.getPositionalVariable() == null ? null
+                : (VariableExpr) joinClause.getPositionalVariable().accept(this, arg);
+        Expression conditionExpresion = (Expression) joinClause.getConditionExpression().accept(this, arg);
+        return new JoinClause(joinClause.getJoinType(), rightExpression, rightVar, rightPositionVar,
+                conditionExpresion);
+    }
+
+    @Override
+    public NestClause visit(NestClause nestClause, Void arg) throws AsterixException {
+        Expression rightExpression = (Expression) nestClause.getRightExpression().accept(this, arg);
+        VariableExpr rightVar = (VariableExpr) nestClause.getRightVariable().accept(this, arg);
+        VariableExpr rightPositionVar = nestClause.getPositionalVariable() == null ? null
+                : (VariableExpr) nestClause.getPositionalVariable().accept(this, arg);
+        Expression conditionExpresion = (Expression) nestClause.getConditionExpression().accept(this, arg);
+        return new NestClause(nestClause.getJoinType(), rightExpression, rightVar, rightPositionVar,
+                conditionExpresion);
+    }
+
+    @Override
+    public UnnestClause visit(UnnestClause unnestClause, Void arg) throws AsterixException {
+        Expression rightExpression = (Expression) unnestClause.getRightExpression().accept(this, arg);
+        VariableExpr rightVar = (VariableExpr) unnestClause.getRightVariable().accept(this, arg);
+        VariableExpr rightPositionVar = unnestClause.getPositionalVariable() == null ? null
+                : (VariableExpr) unnestClause.getPositionalVariable().accept(this, arg);
+        return new UnnestClause(unnestClause.getJoinType(), rightExpression, rightVar, rightPositionVar);
+    }
+
+    @Override
+    public Projection visit(Projection projection, Void arg) throws AsterixException {
+        return new Projection((Expression) projection.getExpression().accept(this, arg), projection.getName(),
+                projection.star(), projection.exprStar());
+    }
+
+    @Override
+    public SelectBlock visit(SelectBlock selectBlock, Void arg) throws AsterixException {
+        FromClause fromClause = null;
+        List<LetClause> letClauses = new ArrayList<>();
+        WhereClause whereClause = null;
+        GroupbyClause gbyClause = null;
+        List<LetClause> gbyLetClauses = new ArrayList<>();
+        HavingClause havingClause = null;
+        SelectClause selectCluase = null;
+        // Traverses the select block in the order of "from", "let"s, "where",
+        // "group by", "let"s, "having" and "select".
+        if (selectBlock.hasFromClause()) {
+            fromClause = (FromClause) selectBlock.getFromClause().accept(this, arg);
+        }
+        if (selectBlock.hasLetClauses()) {
+            List<LetClause> letList = selectBlock.getLetList();
+            for (LetClause letClause : letList) {
+                letClauses.add((LetClause) letClause.accept(this, arg));
+            }
+        }
+        if (selectBlock.hasWhereClause()) {
+            whereClause = (WhereClause) selectBlock.getWhereClause().accept(this, arg);
+        }
+        if (selectBlock.hasGroupbyClause()) {
+            gbyClause = (GroupbyClause) selectBlock.getGroupbyClause().accept(this, arg);
+        }
+        if (selectBlock.hasLetClausesAfterGroupby()) {
+            List<LetClause> letListAfterGby = selectBlock.getLetListAfterGroupby();
+            for (LetClause letClauseAfterGby : letListAfterGby) {
+                gbyLetClauses.add((LetClause) letClauseAfterGby.accept(this, arg));
+            }
+        }
+        if (selectBlock.hasHavingClause()) {
+            havingClause = (HavingClause) selectBlock.getHavingClause().accept(this, arg);
+        }
+        selectCluase = (SelectClause) selectBlock.getSelectClause().accept(this, arg);
+        return new SelectBlock(selectCluase, fromClause, letClauses, whereClause, gbyClause, gbyLetClauses,
+                havingClause);
+    }
+
+    @Override
+    public SelectClause visit(SelectClause selectClause, Void arg) throws AsterixException {
+        SelectElement selectElement = null;
+        SelectRegular selectRegular = null;
+        if (selectClause.selectElement()) {
+            selectElement = (SelectElement) selectClause.getSelectElement().accept(this, arg);
+        }
+        if (selectClause.selectRegular()) {
+            selectRegular = (SelectRegular) selectClause.getSelectRegular().accept(this, arg);
+        }
+        return new SelectClause(selectElement, selectRegular, selectClause.distinct());
+    }
+
+    @Override
+    public SelectElement visit(SelectElement selectElement, Void arg) throws AsterixException {
+        return new SelectElement((Expression) selectElement.getExpression().accept(this, arg));
+    }
+
+    @Override
+    public SelectRegular visit(SelectRegular selectRegular, Void arg) throws AsterixException {
+        List<Projection> projections = new ArrayList<>();
+        for (Projection projection : selectRegular.getProjections()) {
+            projections.add((Projection) projection.accept(this, arg));
+        }
+        return new SelectRegular(projections);
+    }
+
+    @Override
+    public SelectSetOperation visit(SelectSetOperation selectSetOperation, Void arg) throws AsterixException {
+        SetOperationInput leftInput = selectSetOperation.getLeftInput();
+        SetOperationInput newLeftInput = null;
+        if (leftInput.selectBlock()) {
+            newLeftInput = new SetOperationInput((SelectBlock) leftInput.accept(this, arg), null);
+        } else {
+            newLeftInput = new SetOperationInput(null, (SelectExpression) leftInput.accept(this, arg));
+        }
+        List<SetOperationRight> rightInputs = new ArrayList<>();
+        for (SetOperationRight right : selectSetOperation.getRightInputs()) {
+            SetOperationInput newRightInput = null;
+            SetOperationInput setOpRightInput = right.getSetOperationRightInput();
+            if (setOpRightInput.selectBlock()) {
+                newRightInput = new SetOperationInput((SelectBlock) leftInput.accept(this, arg), null);
+            } else {
+                newRightInput = new SetOperationInput(null, (SelectExpression) leftInput.accept(this, arg));
+            }
+            rightInputs.add(new SetOperationRight(right.getSetOpType(), right.isSetSemantics(), newRightInput));
+        }
+        return new SelectSetOperation(newLeftInput, rightInputs);
+    }
+
+    @Override
+    public HavingClause visit(HavingClause havingClause, Void arg) throws AsterixException {
+        return new HavingClause((Expression) havingClause.getFilterExpression().accept(this, arg));
+    }
+
+    @Override
+    public Query visit(Query q, Void arg) throws AsterixException {
+        return new Query(q.isTopLevel(), (Expression) q.getBody().accept(this, arg), q.getVarCounter(),
+                q.getDataverses(), q.getDatasets());
+    }
+
+    @Override
+    public FunctionDecl visit(FunctionDecl fd, Void arg) throws AsterixException {
+        return new FunctionDecl(fd.getSignature(), fd.getParamList(), (Expression) fd.getFuncBody().accept(this, arg));
+    }
+
+    @Override
+    public WhereClause visit(WhereClause whereClause, Void arg) throws AsterixException {
+        return new WhereClause((Expression) whereClause.getWhereExpr().accept(this, arg));
+    }
+
+    @Override
+    public OrderbyClause visit(OrderbyClause oc, Void arg) throws AsterixException {
+        List<Expression> newOrderbyList = new ArrayList<Expression>();
+        for (Expression orderExpr : oc.getOrderbyList()) {
+            newOrderbyList.add((Expression) orderExpr.accept(this, arg));
+        }
+        return new OrderbyClause(newOrderbyList, oc.getModifierList());
+    }
+
+    @Override
+    public GroupbyClause visit(GroupbyClause gc, Void arg) throws AsterixException {
+        List<GbyVariableExpressionPair> gbyPairList = new ArrayList<>();
+        List<GbyVariableExpressionPair> decorPairList = new ArrayList<>();
+        List<VariableExpr> withVarList = new ArrayList<>();
+        VariableExpr groupVarExpr = null;
+        List<Pair<Expression, Identifier>> groupFieldList = new ArrayList<>();
+        for (GbyVariableExpressionPair gbyVarExpr : gc.getGbyPairList()) {
+            gbyPairList.add(new GbyVariableExpressionPair((VariableExpr) gbyVarExpr.getVar().accept(this, arg),
+                    (Expression) gbyVarExpr.getExpr().accept(this, arg)));
+        }
+        for (GbyVariableExpressionPair gbyVarExpr : gc.getDecorPairList()) {
+            decorPairList.add(new GbyVariableExpressionPair((VariableExpr) gbyVarExpr.getVar().accept(this, arg),
+                    (Expression) gbyVarExpr.getExpr().accept(this, arg)));
+        }
+        for (VariableExpr withVar : gc.getWithVarList()) {
+            withVarList.add((VariableExpr) withVar.accept(this, arg));
+        }
+        if (gc.hasGroupVar()) {
+            groupVarExpr = (VariableExpr) gc.getGroupVar().accept(this, arg);
+        }
+        for (Pair<Expression, Identifier> field : gc.getGroupFieldList()) {
+            groupFieldList.add(new Pair<>((Expression) field.first.accept(this, arg), field.second));
+        }
+        return new GroupbyClause(gbyPairList, decorPairList, withVarList, groupVarExpr, groupFieldList,
+                gc.hasHashGroupByHint(), gc.isGroupAll());
+    }
+
+    @Override
+    public LimitClause visit(LimitClause limitClause, Void arg) throws AsterixException {
+        Expression limitExpr = (Expression) limitClause.getLimitExpr().accept(this, arg);
+        Expression offsetExpr = limitClause.hasOffset() ? (Expression) limitClause.getOffset().accept(this, arg) : null;
+        return new LimitClause(limitExpr, offsetExpr);
+    }
+
+    @Override
+    public LetClause visit(LetClause letClause, Void arg) throws AsterixException {
+        return new LetClause((VariableExpr) letClause.getVarExpr().accept(this, arg),
+                (Expression) letClause.getBindingExpr().accept(this, arg));
+    }
+
+    @Override
+    public SelectExpression visit(SelectExpression selectExpression, Void arg) throws AsterixException {
+        List<LetClause> lets = new ArrayList<>();
+        SelectSetOperation select = null;
+        OrderbyClause orderby = null;
+        LimitClause limit = null;
+
+        // visit let list
+        if (selectExpression.hasLetClauses()) {
+            for (LetClause letClause : selectExpression.getLetList()) {
+                lets.add((LetClause) letClause.accept(this, arg));
+            }
+        }
+
+        // visit the main select.
+        select = (SelectSetOperation) selectExpression.getSelectSetOperation().accept(this, arg);
+
+        // visit order by
+        if (selectExpression.hasOrderby()) {
+            List<Expression> orderExprs = new ArrayList<>();
+            for (Expression orderExpr : selectExpression.getOrderbyClause().getOrderbyList()) {
+                orderExprs.add((Expression) orderExpr.accept(this, arg));
+            }
+            orderby = new OrderbyClause(orderExprs, selectExpression.getOrderbyClause().getModifierList());
+        }
+
+        // visit limit
+        if (selectExpression.hasLimit()) {
+            limit = (LimitClause) selectExpression.getLimitClause().accept(this, arg);
+        }
+        return new SelectExpression(lets, select, orderby, limit, selectExpression.isSubquery());
+    }
+
+    @Override
+    public LiteralExpr visit(LiteralExpr l, Void arg) throws AsterixException {
+        return l;
+    }
+
+    @Override
+    public ListConstructor visit(ListConstructor lc, Void arg) throws AsterixException {
+        List<Expression> newExprList = new ArrayList<Expression>();
+        for (Expression expr : lc.getExprList()) {
+            newExprList.add((Expression) expr.accept(this, arg));
+        }
+        return new ListConstructor(lc.getType(), newExprList);
+    }
+
+    @Override
+    public RecordConstructor visit(RecordConstructor rc, Void arg) throws AsterixException {
+        List<FieldBinding> bindings = new ArrayList<>();
+        for (FieldBinding binding : rc.getFbList()) {
+            FieldBinding fb = new FieldBinding((Expression) binding.getLeftExpr().accept(this, arg),
+                    (Expression) binding.getRightExpr().accept(this, arg));
+            bindings.add(fb);
+        }
+        return new RecordConstructor(bindings);
+    }
+
+    @Override
+    public OperatorExpr visit(OperatorExpr operatorExpr, Void arg) throws AsterixException {
+        List<Expression> newExprList = new ArrayList<Expression>();
+        for (Expression expr : operatorExpr.getExprList()) {
+            newExprList.add((Expression) expr.accept(this, arg));
+        }
+        return new OperatorExpr(newExprList, operatorExpr.getExprBroadcastIdx(), operatorExpr.getOpList(),
+                operatorExpr.isCurrentop());
+    }
+
+    @Override
+    public IfExpr visit(IfExpr ifExpr, Void arg) throws AsterixException {
+        Expression conditionExpr = (Expression) ifExpr.getCondExpr().accept(this, arg);
+        Expression thenExpr = (Expression) ifExpr.getThenExpr().accept(this, arg);
+        Expression elseExpr = (Expression) ifExpr.getElseExpr().accept(this, arg);
+        return new IfExpr(conditionExpr, thenExpr, elseExpr);
+    }
+
+    @Override
+    public QuantifiedExpression visit(QuantifiedExpression qe, Void arg) throws AsterixException {
+        List<QuantifiedPair> quantifiedPairs = new ArrayList<>();
+        for (QuantifiedPair pair : qe.getQuantifiedList()) {
+            Expression expr = (Expression) pair.getExpr().accept(this, arg);
+            VariableExpr var = (VariableExpr) pair.getVarExpr().accept(this, arg);
+            quantifiedPairs.add(new QuantifiedPair(var, expr));
+        }
+        Expression condition = (Expression) qe.getSatisfiesExpr().accept(this, arg);
+        return new QuantifiedExpression(qe.getQuantifier(), quantifiedPairs, condition);
+    }
+
+    @Override
+    public CallExpr visit(CallExpr callExpr, Void arg) throws AsterixException {
+        List<Expression> newExprList = new ArrayList<Expression>();
+        for (Expression expr : callExpr.getExprList()) {
+            newExprList.add((Expression) expr.accept(this, arg));
+        }
+        return new CallExpr(callExpr.getFunctionSignature(), newExprList);
+    }
+
+    @Override
+    public VariableExpr visit(VariableExpr varExpr, Void arg) throws AsterixException {
+        return new VariableExpr(varExpr.getVar());
+    }
+
+    @Override
+    public UnaryExpr visit(UnaryExpr u, Void arg) throws AsterixException {
+        return new UnaryExpr(u.getSign(), (Expression) u.getExpr().accept(this, arg));
+    }
+
+    @Override
+    public FieldAccessor visit(FieldAccessor fa, Void arg) throws AsterixException {
+        return new FieldAccessor((Expression) fa.getExpr().accept(this, arg), fa.getIdent());
+    }
+
+    @Override
+    public Expression visit(IndexAccessor ia, Void arg) throws AsterixException {
+        Expression expr = (Expression) ia.getExpr().accept(this, arg);
+        Expression indexExpr = null;
+        if (ia.getIndexExpr() != null) {
+            indexExpr = ia.getIndexExpr();
+        }
+        return new IndexAccessor(expr, indexExpr);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-asterixdb/blob/3dd80ec4/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/FreeVariableVisitor.java
----------------------------------------------------------------------
diff --git a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/FreeVariableVisitor.java b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/FreeVariableVisitor.java
new file mode 100644
index 0000000..6e70455
--- /dev/null
+++ b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/FreeVariableVisitor.java
@@ -0,0 +1,471 @@
+/*
+ * 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.visitor;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+
+import org.apache.asterix.common.exceptions.AsterixException;
+import org.apache.asterix.lang.common.base.Clause.ClauseType;
+import org.apache.asterix.lang.common.base.Expression;
+import org.apache.asterix.lang.common.clause.GroupbyClause;
+import org.apache.asterix.lang.common.clause.LetClause;
+import org.apache.asterix.lang.common.clause.LimitClause;
+import org.apache.asterix.lang.common.clause.OrderbyClause;
+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.GbyVariableExpressionPair;
+import org.apache.asterix.lang.common.expression.IfExpr;
+import org.apache.asterix.lang.common.expression.IndexAccessor;
+import org.apache.asterix.lang.common.expression.ListConstructor;
+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.statement.FunctionDecl;
+import org.apache.asterix.lang.common.statement.Query;
+import org.apache.asterix.lang.common.struct.Identifier;
+import org.apache.asterix.lang.common.struct.QuantifiedPair;
+import org.apache.asterix.lang.sqlpp.clause.AbstractBinaryCorrelateClause;
+import org.apache.asterix.lang.sqlpp.clause.FromClause;
+import org.apache.asterix.lang.sqlpp.clause.FromTerm;
+import org.apache.asterix.lang.sqlpp.clause.HavingClause;
+import org.apache.asterix.lang.sqlpp.clause.JoinClause;
+import org.apache.asterix.lang.sqlpp.clause.NestClause;
+import org.apache.asterix.lang.sqlpp.clause.Projection;
+import org.apache.asterix.lang.sqlpp.clause.SelectBlock;
+import org.apache.asterix.lang.sqlpp.clause.SelectClause;
+import org.apache.asterix.lang.sqlpp.clause.SelectElement;
+import org.apache.asterix.lang.sqlpp.clause.SelectRegular;
+import org.apache.asterix.lang.sqlpp.clause.SelectSetOperation;
+import org.apache.asterix.lang.sqlpp.clause.UnnestClause;
+import org.apache.asterix.lang.sqlpp.expression.SelectExpression;
+import org.apache.asterix.lang.sqlpp.struct.SetOperationRight;
+import org.apache.asterix.lang.sqlpp.util.SqlppVariableUtil;
+import org.apache.asterix.lang.sqlpp.visitor.base.AbstractSqlppQueryExpressionVisitor;
+import org.apache.hyracks.algebricks.common.utils.Pair;
+
+public class FreeVariableVisitor extends AbstractSqlppQueryExpressionVisitor<Void, Collection<VariableExpr>> {
+
+    @Override
+    public Void visit(FromClause fromClause, Collection<VariableExpr> freeVars) throws AsterixException {
+        Collection<VariableExpr> bindingVars = new HashSet<>();
+        for (FromTerm fromTerm : fromClause.getFromTerms()) {
+            Collection<VariableExpr> fromTermFreeVars = new HashSet<>();
+            fromTerm.accept(this, fromTermFreeVars);
+
+            // Since a right from term can refer to variables defined in a left from term,
+            // we remove binding variables from the free variables.
+            fromTermFreeVars.removeAll(bindingVars);
+
+            // Adds binding variables.
+            bindingVars.addAll(SqlppVariableUtil.getBindingVariables(fromTerm));
+
+            // Adds into freeVars.
+            freeVars.addAll(fromTermFreeVars);
+        }
+        return null;
+    }
+
+    @Override
+    public Void visit(FromTerm fromTerm, Collection<VariableExpr> freeVars) throws AsterixException {
+        // The encountered binding variables so far in the fromterm.
+        Collection<VariableExpr> bindingVariables = new HashSet<>();
+
+        // Visit the left expression of a from term.
+        fromTerm.getLeftExpression().accept(this, freeVars);
+
+        // Adds binding variables.
+        bindingVariables.add(fromTerm.getLeftVariable());
+        if (fromTerm.hasPositionalVariable()) {
+            bindingVariables.add(fromTerm.getPositionalVariable());
+        }
+
+        // Visits join/unnest/nest clauses.
+        for (AbstractBinaryCorrelateClause correlateClause : fromTerm.getCorrelateClauses()) {
+            Collection<VariableExpr> correlateFreeVars = new HashSet<>();
+            correlateClause.accept(this, correlateFreeVars);
+            if (correlateClause.getClauseType() != ClauseType.JOIN_CLAUSE) {
+                // Correlation is allowed if the clause is not a join clause,
+                // therefore we remove left-side binding variables for these cases.
+                correlateFreeVars.removeAll(bindingVariables);
+
+                // Adds binding variables.
+                bindingVariables.add(correlateClause.getRightVariable());
+                if (correlateClause.hasPositionalVariable()) {
+                    bindingVariables.add(correlateClause.getPositionalVariable());
+                }
+            }
+            freeVars.addAll(correlateFreeVars);
+        }
+        return null;
+    }
+
+    @Override
+    public Void visit(JoinClause joinClause, Collection<VariableExpr> freeVars) throws AsterixException {
+        visitJoinAndNest(joinClause, joinClause.getConditionExpression(), freeVars);
+        return null;
+    }
+
+    @Override
+    public Void visit(NestClause nestClause, Collection<VariableExpr> freeVars) throws AsterixException {
+        visitJoinAndNest(nestClause, nestClause.getConditionExpression(), freeVars);
+        return null;
+    }
+
+    @Override
+    public Void visit(UnnestClause unnestClause, Collection<VariableExpr> freeVars) throws AsterixException {
+        unnestClause.getRightExpression().accept(this, freeVars);
+        return null;
+    }
+
+    @Override
+    public Void visit(Projection projection, Collection<VariableExpr> freeVars) throws AsterixException {
+        projection.getExpression().accept(this, freeVars);
+        return null;
+    }
+
+    @Override
+    public Void visit(SelectBlock selectBlock, Collection<VariableExpr> freeVars) throws AsterixException {
+        Collection<VariableExpr> selectFreeVars = new HashSet<>();
+        Collection<VariableExpr> fromFreeVars = new HashSet<>();
+        Collection<VariableExpr> letsFreeVars = new HashSet<>();
+        Collection<VariableExpr> whereFreeVars = new HashSet<>();
+        Collection<VariableExpr> gbyFreeVars = new HashSet<>();
+        Collection<VariableExpr> gbyLetsFreeVars = new HashSet<>();
+
+        Collection<VariableExpr> fromBindingVars = SqlppVariableUtil.getBindingVariables(selectBlock.getFromClause());
+        Collection<VariableExpr> letsBindingVars = SqlppVariableUtil.getBindingVariables(selectBlock.getLetList());
+        Collection<VariableExpr> gbyBindingVars = SqlppVariableUtil.getBindingVariables(selectBlock.getGroupbyClause());
+        Collection<VariableExpr> gbyLetsBindingVars = SqlppVariableUtil
+                .getBindingVariables(selectBlock.getLetListAfterGroupby());
+
+        selectBlock.getSelectClause().accept(this, selectFreeVars);
+        // Removes group-by, from, let, and gby-let binding vars.
+        removeAllBindingVarsInSelectBlock(selectFreeVars, fromBindingVars, letsBindingVars, gbyLetsBindingVars);
+
+        if (selectBlock.hasFromClause()) {
+            selectBlock.getFromClause().accept(this, fromFreeVars);
+        }
+        if (selectBlock.hasLetClauses()) {
+            visitLetClauses(selectBlock.getLetList(), letsFreeVars);
+            letsFreeVars.removeAll(fromBindingVars);
+        }
+        if (selectBlock.hasWhereClause()) {
+            selectBlock.getWhereClause().accept(this, whereFreeVars);
+            whereFreeVars.removeAll(fromBindingVars);
+            whereFreeVars.removeAll(letsBindingVars);
+        }
+        if (selectBlock.hasGroupbyClause()) {
+            selectBlock.getGroupbyClause().accept(this, gbyFreeVars);
+            // Remove group-by and let binding vars.
+            gbyFreeVars.removeAll(fromBindingVars);
+            gbyFreeVars.removeAll(letsBindingVars);
+            if (selectBlock.hasLetClausesAfterGroupby()) {
+                visitLetClauses(selectBlock.getLetListAfterGroupby(), gbyLetsFreeVars);
+                gbyLetsFreeVars.removeAll(fromBindingVars);
+                gbyLetsFreeVars.removeAll(letsBindingVars);
+                gbyLetsFreeVars.removeAll(gbyBindingVars);
+            }
+            if (selectBlock.hasHavingClause()) {
+                selectBlock.getHavingClause().accept(this, selectFreeVars);
+                removeAllBindingVarsInSelectBlock(selectFreeVars, fromBindingVars, letsBindingVars, gbyLetsBindingVars);
+            }
+        }
+
+        // Removes all binding vars from <code>freeVars</code>, which contains the free
+        // vars in the order-by and limit.
+        removeAllBindingVarsInSelectBlock(freeVars, fromBindingVars, letsBindingVars, gbyLetsBindingVars);
+
+        // Adds all free vars.
+        freeVars.addAll(selectFreeVars);
+        freeVars.addAll(fromFreeVars);
+        freeVars.addAll(letsFreeVars);
+        freeVars.addAll(whereFreeVars);
+        freeVars.addAll(gbyFreeVars);
+        freeVars.addAll(gbyLetsFreeVars);
+        return null;
+    }
+
+    @Override
+    public Void visit(SelectClause selectClause, Collection<VariableExpr> freeVars) throws AsterixException {
+        if (selectClause.selectElement()) {
+            selectClause.getSelectElement().accept(this, freeVars);
+        }
+        if (selectClause.selectRegular()) {
+            selectClause.getSelectRegular().accept(this, freeVars);
+        }
+        return null;
+    }
+
+    @Override
+    public Void visit(SelectElement selectElement, Collection<VariableExpr> freeVars) throws AsterixException {
+        selectElement.getExpression().accept(this, freeVars);
+        return null;
+    }
+
+    @Override
+    public Void visit(SelectRegular selectRegular, Collection<VariableExpr> freeVars) throws AsterixException {
+        for (Projection projection : selectRegular.getProjections()) {
+            projection.accept(this, freeVars);
+        }
+        return null;
+    }
+
+    @Override
+    public Void visit(SelectSetOperation selectSetOperation, Collection<VariableExpr> freeVars)
+            throws AsterixException {
+        selectSetOperation.getLeftInput().accept(this, freeVars);
+        for (SetOperationRight right : selectSetOperation.getRightInputs()) {
+            right.getSetOperationRightInput().accept(this, freeVars);
+        }
+        return null;
+    }
+
+    @Override
+    public Void visit(HavingClause havingClause, Collection<VariableExpr> freeVars) throws AsterixException {
+        havingClause.getFilterExpression().accept(this, freeVars);
+        return null;
+    }
+
+    @Override
+    public Void visit(Query q, Collection<VariableExpr> freeVars) throws AsterixException {
+        q.getBody().accept(this, freeVars);
+        return null;
+    }
+
+    @Override
+    public Void visit(FunctionDecl fd, Collection<VariableExpr> freeVars) throws AsterixException {
+        fd.getFuncBody().accept(this, freeVars);
+        return null;
+    }
+
+    @Override
+    public Void visit(WhereClause whereClause, Collection<VariableExpr> freeVars) throws AsterixException {
+        whereClause.getWhereExpr().accept(this, freeVars);
+        return null;
+    }
+
+    @Override
+    public Void visit(OrderbyClause oc, Collection<VariableExpr> freeVars) throws AsterixException {
+        for (Expression orderExpr : oc.getOrderbyList()) {
+            orderExpr.accept(this, freeVars);
+        }
+        return null;
+    }
+
+    @Override
+    public Void visit(GroupbyClause gc, Collection<VariableExpr> freeVars) throws AsterixException {
+        // Puts all group-by variables into the symbol set of the new scope.
+        for (GbyVariableExpressionPair gbyVarExpr : gc.getGbyPairList()) {
+            gbyVarExpr.getExpr().accept(this, freeVars);
+        }
+        for (GbyVariableExpressionPair decorVarExpr : gc.getDecorPairList()) {
+            decorVarExpr.getExpr().accept(this, freeVars);
+        }
+        if (gc.hasGroupFieldList()) {
+            for (Pair<Expression, Identifier> groupField : gc.getGroupFieldList()) {
+                groupField.first.accept(this, freeVars);
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public Void visit(LimitClause limitClause, Collection<VariableExpr> freeVars) throws AsterixException {
+        limitClause.getLimitExpr().accept(this, freeVars);
+        return null;
+    }
+
+    @Override
+    public Void visit(LetClause letClause, Collection<VariableExpr> freeVars) throws AsterixException {
+        letClause.getBindingExpr().accept(this, freeVars);
+        return null;
+    }
+
+    @Override
+    public Void visit(SelectExpression selectExpression, Collection<VariableExpr> freeVars) throws AsterixException {
+        Collection<VariableExpr> letsFreeVars = new HashSet<>();
+        Collection<VariableExpr> selectFreeVars = new HashSet<>();
+        visitLetClauses(selectExpression.getLetList(), letsFreeVars);
+
+        // visit order by
+        if (selectExpression.hasOrderby()) {
+            for (Expression orderExpr : selectExpression.getOrderbyClause().getOrderbyList()) {
+                orderExpr.accept(this, selectFreeVars);
+            }
+        }
+
+        // visit limit
+        if (selectExpression.hasLimit()) {
+            selectExpression.getLimitClause().accept(this, selectFreeVars);
+        }
+
+        // visit the main select
+        selectExpression.getSelectSetOperation().accept(this, selectFreeVars);
+
+        // Removed let binding variables.
+        selectFreeVars.removeAll(SqlppVariableUtil.getBindingVariables(selectExpression.getLetList()));
+        freeVars.addAll(letsFreeVars);
+        freeVars.addAll(selectFreeVars);
+        return null;
+    }
+
+    @Override
+    public Void visit(LiteralExpr l, Collection<VariableExpr> freeVars) throws AsterixException {
+        return null;
+    }
+
+    @Override
+    public Void visit(ListConstructor lc, Collection<VariableExpr> freeVars) throws AsterixException {
+        for (Expression expr : lc.getExprList()) {
+            expr.accept(this, freeVars);
+        }
+        return null;
+    }
+
+    @Override
+    public Void visit(RecordConstructor rc, Collection<VariableExpr> freeVars) throws AsterixException {
+        for (FieldBinding binding : rc.getFbList()) {
+            binding.getLeftExpr().accept(this, freeVars);
+            binding.getRightExpr().accept(this, freeVars);
+        }
+        return null;
+    }
+
+    @Override
+    public Void visit(OperatorExpr operatorExpr, Collection<VariableExpr> freeVars) throws AsterixException {
+        for (Expression expr : operatorExpr.getExprList()) {
+            expr.accept(this, freeVars);
+        }
+        return null;
+    }
+
+    @Override
+    public Void visit(IfExpr ifExpr, Collection<VariableExpr> freeVars) throws AsterixException {
+        ifExpr.getCondExpr().accept(this, freeVars);
+        ifExpr.getThenExpr().accept(this, freeVars);
+        ifExpr.getElseExpr().accept(this, freeVars);
+        return null;
+    }
+
+    @Override
+    public Void visit(QuantifiedExpression qe, Collection<VariableExpr> freeVars) throws AsterixException {
+        for (QuantifiedPair pair : qe.getQuantifiedList()) {
+            pair.getExpr().accept(this, freeVars);
+        }
+        qe.getSatisfiesExpr().accept(this, freeVars);
+        return null;
+    }
+
+    @Override
+    public Void visit(CallExpr callExpr, Collection<VariableExpr> freeVars) throws AsterixException {
+        for (Expression expr : callExpr.getExprList()) {
+            expr.accept(this, freeVars);
+        }
+        return null;
+    }
+
+    @Override
+    public Void visit(VariableExpr varExpr, Collection<VariableExpr> freeVars) throws AsterixException {
+        freeVars.add(varExpr);
+        return null;
+    }
+
+    @Override
+    public Void visit(UnaryExpr u, Collection<VariableExpr> freeVars) throws AsterixException {
+        u.getExpr().accept(this, freeVars);
+        return null;
+    }
+
+    @Override
+    public Void visit(FieldAccessor fa, Collection<VariableExpr> freeVars) throws AsterixException {
+        fa.getExpr().accept(this, freeVars);
+        return null;
+    }
+
+    @Override
+    public Void visit(IndexAccessor ia, Collection<VariableExpr> freeVars) throws AsterixException {
+        ia.getExpr().accept(this, freeVars);
+        if (ia.getIndexExpr() != null) {
+            ia.getIndexExpr();
+        }
+        return null;
+    }
+
+    private void visitLetClauses(List<LetClause> letClauses, Collection<VariableExpr> freeVars)
+            throws AsterixException {
+        if (letClauses == null || letClauses.isEmpty()) {
+            return;
+        }
+        Collection<VariableExpr> bindingVars = new HashSet<>();
+        for (LetClause letClause : letClauses) {
+            Collection<VariableExpr> letFreeVars = new HashSet<>();
+            letClause.accept(this, letFreeVars);
+
+            // Removes previous binding variables.
+            letFreeVars.removeAll(bindingVars);
+            freeVars.addAll(letFreeVars);
+
+            // Adds let binding variables into the binding variable collection.
+            bindingVars.add(letClause.getVarExpr());
+        }
+    }
+
+    private void visitJoinAndNest(AbstractBinaryCorrelateClause clause, Expression condition,
+            Collection<VariableExpr> freeVars) throws AsterixException {
+        clause.getRightExpression().accept(this, freeVars);
+        Collection<VariableExpr> conditionFreeVars = new HashSet<>();
+        condition.accept(this, freeVars);
+
+        // The condition expression can free binding variables defined in the join clause.
+        conditionFreeVars.remove(clause.getRightVariable());
+        if (clause.hasPositionalVariable()) {
+            conditionFreeVars.remove(clause.getPositionalVariable());
+        }
+        freeVars.addAll(conditionFreeVars);
+    }
+
+    /**
+     * Removes all binding variables defined in the select block for a free variable collection.
+     *
+     * @param freeVars,
+     *            free variables.
+     * @param fromBindingVars,
+     *            binding variables defined in the from clause of a select block.
+     * @param letsBindingVars,
+     *            binding variables defined in the let clauses of the select block.
+     * @param gbyLetsBindingVars,
+     *            binding variables defined in the let clauses after a group-by in the select block.
+     */
+    private void removeAllBindingVarsInSelectBlock(Collection<VariableExpr> selectFreeVars,
+            Collection<VariableExpr> fromBindingVars, Collection<VariableExpr> letsBindingVars,
+            Collection<VariableExpr> gbyLetsBindingVars) {
+        selectFreeVars.removeAll(fromBindingVars);
+        selectFreeVars.removeAll(letsBindingVars);
+        selectFreeVars.removeAll(gbyLetsBindingVars);
+        selectFreeVars.removeAll(gbyLetsBindingVars);
+    }
+
+}