SQL++ parser:
1. refactored asterix-aql to become asterix-lang-common and asterix-lang-aql, where the former is the common part for different languages;
2. added asterix-lang-sqlpp on top of asterix-lang-common;
3. ported parser tests, optimizer tests and runtime tests in asterix-app to their sql++ version, and added parser tests for all the queries.

Change-Id: Ie5af4e3b692ca017ec047a1ba3b404a51beb3a2e
Reviewed-on: https://asterix-gerrit.ics.uci.edu/466
Tested-by: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Reviewed-by: Till Westmann <tillw@apache.org>
diff --git a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/AbstractBinaryCorrelateClause.java b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/AbstractBinaryCorrelateClause.java
new file mode 100644
index 0000000..b1dbba5
--- /dev/null
+++ b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/AbstractBinaryCorrelateClause.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.clause;
+
+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;
+
+public abstract class AbstractBinaryCorrelateClause implements Clause {
+
+    private JoinType joinType;
+    private Expression rightExpr;
+    private VariableExpr rightVar;
+    private VariableExpr rightPosVar;
+
+    public AbstractBinaryCorrelateClause(JoinType joinType, Expression rightExpr, VariableExpr rightVar,
+            VariableExpr rightPosVar) {
+        this.joinType = joinType;
+        this.rightExpr = rightExpr;
+        this.rightVar = rightVar;
+        this.rightPosVar = rightPosVar;
+    }
+
+    public JoinType getJoinType() {
+        return joinType;
+    }
+
+    public Expression getRightExpression() {
+        return rightExpr;
+    }
+
+    public void setRightExpression(Expression rightExpr) {
+        this.rightExpr = rightExpr;
+    }
+
+    public VariableExpr getRightVariable() {
+        return rightVar;
+    }
+
+    public VariableExpr getPositionalVariable() {
+        return rightPosVar;
+    }
+
+    public boolean hasPositionalVariable() {
+        return rightPosVar != null;
+    }
+
+}
diff --git a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/AbstractBinaryCorrelateWithConditionClause.java b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/AbstractBinaryCorrelateWithConditionClause.java
new file mode 100644
index 0000000..e1d4a7b
--- /dev/null
+++ b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/AbstractBinaryCorrelateWithConditionClause.java
@@ -0,0 +1,44 @@
+/*
+ * 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.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;
+
+public abstract class AbstractBinaryCorrelateWithConditionClause extends AbstractBinaryCorrelateClause {
+
+    private Expression conditionExpr;
+
+    public AbstractBinaryCorrelateWithConditionClause(JoinType joinType, Expression rightExpr, VariableExpr rightVar,
+            VariableExpr rightPosVar, Expression conditionExpr) {
+        super(joinType, rightExpr, rightVar, rightPosVar);
+        this.conditionExpr = conditionExpr;
+    }
+
+    public Expression getConditionExpression() {
+        return conditionExpr;
+    }
+
+    public void setConditionExpression(Expression conditionExpr) {
+        this.conditionExpr = conditionExpr;
+    }
+
+}
diff --git a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/FromClause.java b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/FromClause.java
new file mode 100644
index 0000000..802869d
--- /dev/null
+++ b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/FromClause.java
@@ -0,0 +1,51 @@
+/*
+ * 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.clause;
+
+import java.util.ArrayList;
+import java.util.List;
+
+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;
+
+public class FromClause implements Clause {
+
+    private List<FromTerm> fromTerms = new ArrayList<FromTerm>();
+
+    public FromClause(List<FromTerm> fromTerms) {
+        this.fromTerms = fromTerms;
+    }
+
+    @Override
+    public <R, T> R accept(ILangVisitor<R, T> visitor, T arg) throws AsterixException {
+        return ((ISqlppVisitor<R, T>) visitor).visit(this, arg);
+    }
+
+    @Override
+    public ClauseType getClauseType() {
+        return ClauseType.FROM_CLAUSE;
+    }
+
+    public List<FromTerm> getFromTerms() {
+        return fromTerms;
+    }
+}
diff --git a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/FromTerm.java b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/FromTerm.java
new file mode 100644
index 0000000..d7ab068
--- /dev/null
+++ b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/FromTerm.java
@@ -0,0 +1,83 @@
+/*
+ * 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.clause;
+
+import java.util.List;
+
+import org.apache.asterix.common.exceptions.AsterixException;
+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.common.visitor.base.ILangVisitor;
+import org.apache.asterix.lang.sqlpp.visitor.base.ISqlppVisitor;
+
+public class FromTerm implements Clause {
+    private Expression leftExpr;
+    private VariableExpr leftVar;
+    private VariableExpr posVar;
+    private List<AbstractBinaryCorrelateClause> correlateClauses;
+
+    public FromTerm(Expression leftExpr, VariableExpr leftVar, VariableExpr posVar,
+            List<AbstractBinaryCorrelateClause> correlateClauses) {
+        this.leftExpr = leftExpr;
+        this.leftVar = leftVar;
+        this.posVar = posVar;
+        this.correlateClauses = correlateClauses;
+    }
+
+    @Override
+    public <R, T> R accept(ILangVisitor<R, T> visitor, T arg) throws AsterixException {
+        return ((ISqlppVisitor<R, T>) visitor).visit(this, arg);
+    }
+
+    @Override
+    public ClauseType getClauseType() {
+        return ClauseType.FROM_TERM;
+    }
+
+    public Expression getLeftExpression() {
+        return leftExpr;
+    }
+
+    public void setLeftExpression(Expression expr) {
+        this.leftExpr = expr;
+    }
+
+    public VariableExpr getLeftVariable() {
+        return leftVar;
+    }
+
+    public VariableExpr getPositionalVariable() {
+        return posVar;
+    }
+
+    public boolean hasCorrelateClauses() {
+        return correlateClauses != null && correlateClauses.size() > 0;
+    }
+
+    public List<AbstractBinaryCorrelateClause> getCorrelateClauses() {
+        return correlateClauses;
+    }
+
+    public boolean hasPositionalVariable() {
+        return posVar != null;
+    }
+
+}
diff --git a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/HavingClause.java b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/HavingClause.java
new file mode 100644
index 0000000..98cca9c
--- /dev/null
+++ b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/HavingClause.java
@@ -0,0 +1,53 @@
+/*
+ * 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.clause;
+
+import org.apache.asterix.common.exceptions.AsterixException;
+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;
+
+public class HavingClause implements Clause {
+
+    private Expression filterExpression;
+
+    public HavingClause(Expression filterExpression) {
+        this.filterExpression = filterExpression;
+    }
+
+    @Override
+    public <R, T> R accept(ILangVisitor<R, T> visitor, T arg) throws AsterixException {
+        return ((ISqlppVisitor<R, T>) visitor).visit(this, arg);
+    }
+
+    @Override
+    public ClauseType getClauseType() {
+        return ClauseType.HAVING_CLAUSE;
+    }
+
+    public Expression getFilterExpression() {
+        return filterExpression;
+    }
+
+    public void setFilterExpression(Expression filterExpression) {
+        this.filterExpression = filterExpression;
+    }
+
+}
diff --git a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/JoinClause.java b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/JoinClause.java
new file mode 100644
index 0000000..0da25cd
--- /dev/null
+++ b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/JoinClause.java
@@ -0,0 +1,46 @@
+/*
+ * 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.clause;
+
+import org.apache.asterix.common.exceptions.AsterixException;
+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.optype.JoinType;
+import org.apache.asterix.lang.sqlpp.visitor.base.ISqlppVisitor;
+
+public class JoinClause extends AbstractBinaryCorrelateWithConditionClause {
+
+    public JoinClause(JoinType joinType, Expression rightExpr, VariableExpr rightVar, VariableExpr rightPosVar,
+            Expression conditionExpr) {
+        super(joinType, rightExpr, rightVar, rightPosVar, conditionExpr);
+    }
+
+    @Override
+    public <R, T> R accept(ILangVisitor<R, T> visitor, T arg) throws AsterixException {
+        return ((ISqlppVisitor<R, T>) visitor).visit(this, arg);
+    }
+
+    @Override
+    public ClauseType getClauseType() {
+        return ClauseType.JOIN_CLAUSE;
+    }
+
+}
diff --git a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/NestClause.java b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/NestClause.java
new file mode 100644
index 0000000..5719572
--- /dev/null
+++ b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/NestClause.java
@@ -0,0 +1,46 @@
+/*
+ * 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.clause;
+
+import org.apache.asterix.common.exceptions.AsterixException;
+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.optype.JoinType;
+import org.apache.asterix.lang.sqlpp.visitor.base.ISqlppVisitor;
+
+public class NestClause extends AbstractBinaryCorrelateWithConditionClause {
+
+    public NestClause(JoinType joinType, Expression rightExpr, VariableExpr rightVar, VariableExpr rightPosVar,
+            Expression conditionExpr) {
+        super(joinType, rightExpr, rightVar, rightPosVar, conditionExpr);
+    }
+
+    @Override
+    public <R, T> R accept(ILangVisitor<R, T> visitor, T arg) throws AsterixException {
+        return ((ISqlppVisitor<R, T>) visitor).visit(this, arg);
+    }
+
+    @Override
+    public ClauseType getClauseType() {
+        return ClauseType.NEST_CLAUSE;
+    }
+
+}
\ No newline at end of file
diff --git a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/Projection.java b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/Projection.java
new file mode 100644
index 0000000..bfb5abf
--- /dev/null
+++ b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/Projection.java
@@ -0,0 +1,75 @@
+/*
+ * 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.clause;
+
+import org.apache.asterix.common.exceptions.AsterixException;
+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;
+
+public class Projection implements Clause {
+
+    private Expression expr;
+    private String name;
+    private boolean star;
+    private boolean exprStar;
+
+    public Projection(Expression expr, String name, boolean star, boolean exprStar) {
+        this.expr = expr;
+        this.name = name;
+        this.star = star;
+        this.exprStar = exprStar;
+    }
+
+    @Override
+    public <R, T> R accept(ILangVisitor<R, T> visitor, T arg) throws AsterixException {
+        return ((ISqlppVisitor<R, T>) visitor).visit(this, arg);
+    }
+
+    @Override
+    public ClauseType getClauseType() {
+        return ClauseType.PROJECTION;
+    }
+
+    public Expression getExpression() {
+        return expr;
+    }
+
+    public void setExpression(Expression expr) {
+        this.expr = expr;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public boolean hasName() {
+        return name != null;
+    }
+
+    public boolean star() {
+        return star;
+    }
+
+    public boolean exprStar() {
+        return exprStar;
+    }
+}
diff --git a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/SelectBlock.java b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/SelectBlock.java
new file mode 100644
index 0000000..646e150
--- /dev/null
+++ b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/SelectBlock.java
@@ -0,0 +1,115 @@
+/*
+ * 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.clause;
+
+import java.util.List;
+
+import org.apache.asterix.common.exceptions.AsterixException;
+import org.apache.asterix.lang.common.base.Clause;
+import org.apache.asterix.lang.common.clause.GroupbyClause;
+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;
+
+public class SelectBlock implements Clause {
+
+    private SelectClause selectClause;
+    private FromClause fromClause;
+    private List<LetClause> letClauses;
+    private WhereClause whereClause;
+    private GroupbyClause groupbyClause;
+    private List<LetClause> letClausesAfterGby;
+    private HavingClause havingClause;
+
+    public SelectBlock(SelectClause selectClause, FromClause fromClause, List<LetClause> letClauses,
+            WhereClause whereClause, GroupbyClause groupbyClause, List<LetClause> letClausesAfterGby,
+            HavingClause havingClause) {
+        this.selectClause = selectClause;
+        this.fromClause = fromClause;
+        this.letClauses = letClauses;
+        this.whereClause = whereClause;
+        this.groupbyClause = groupbyClause;
+        this.havingClause = havingClause;
+        this.letClausesAfterGby = letClausesAfterGby;
+    }
+
+    @Override
+    public <R, T> R accept(ILangVisitor<R, T> visitor, T arg) throws AsterixException {
+        return ((ISqlppVisitor<R, T>) visitor).visit(this, arg);
+    }
+
+    @Override
+    public ClauseType getClauseType() {
+        return ClauseType.SELECT_BLOCK;
+    }
+
+    public SelectClause getSelectClause() {
+        return selectClause;
+    }
+
+    public FromClause getFromClause() {
+        return fromClause;
+    }
+
+    public List<LetClause> getLetList() {
+        return letClauses;
+    }
+
+    public WhereClause getWhereClause() {
+        return whereClause;
+    }
+
+    public GroupbyClause getGroupbyClause() {
+        return groupbyClause;
+    }
+
+    public HavingClause getHavingClause() {
+        return havingClause;
+    }
+
+    public boolean hasFromClause() {
+        return fromClause != null;
+    }
+
+    public boolean hasLetClauses() {
+        return letClauses != null && letClauses.size() > 0;
+    }
+
+    public boolean hasWhereClause() {
+        return whereClause != null;
+    }
+
+    public boolean hasGroupbyClause() {
+        return groupbyClause != null;
+    }
+
+    public boolean hasLetClausesAfterGroupby() {
+        return letClausesAfterGby != null && letClausesAfterGby.size() > 0;
+    }
+
+    public List<LetClause> getLetListAfterGroupby() {
+        return letClausesAfterGby;
+    }
+
+    public boolean hasHavingClause() {
+        return havingClause != null;
+    }
+}
diff --git a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/SelectClause.java b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/SelectClause.java
new file mode 100644
index 0000000..c6b4e24
--- /dev/null
+++ b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/SelectClause.java
@@ -0,0 +1,69 @@
+/*
+ * 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.clause;
+
+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;
+
+public class SelectClause implements Clause {
+
+    private SelectElement selectElement;
+    private SelectRegular selectRegular;
+    private boolean distinct;
+
+    public SelectClause(SelectElement selectElement, SelectRegular selectRegular, boolean distinct) {
+        this.selectElement = selectElement;
+        this.selectRegular = selectRegular;
+        this.distinct = distinct;
+    }
+
+    @Override
+    public <R, T> R accept(ILangVisitor<R, T> visitor, T arg) throws AsterixException {
+        return ((ISqlppVisitor<R, T>) visitor).visit(this, arg);
+    }
+
+    @Override
+    public ClauseType getClauseType() {
+        return ClauseType.SELECT_CLAUSE;
+    }
+
+    public SelectElement getSelectElement() {
+        return selectElement;
+    }
+
+    public SelectRegular getSelectRegular() {
+        return selectRegular;
+    }
+
+    public boolean selectElement() {
+        return selectElement != null;
+    }
+
+    public boolean selectRegular() {
+        return selectRegular != null;
+    }
+
+    public boolean distinct() {
+        return distinct;
+    }
+
+}
diff --git a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/SelectElement.java b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/SelectElement.java
new file mode 100644
index 0000000..e13506a
--- /dev/null
+++ b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/SelectElement.java
@@ -0,0 +1,53 @@
+/*
+ * 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.clause;
+
+import org.apache.asterix.common.exceptions.AsterixException;
+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;
+
+public class SelectElement implements Clause {
+
+    private Expression expr;
+
+    public SelectElement(Expression expr) {
+        this.expr = expr;
+    }
+
+    @Override
+    public <R, T> R accept(ILangVisitor<R, T> visitor, T arg) throws AsterixException {
+        return ((ISqlppVisitor<R, T>) visitor).visit(this, arg);
+    }
+
+    @Override
+    public ClauseType getClauseType() {
+        return ClauseType.SELECT_ELEMENT;
+    }
+
+    public Expression getExpression() {
+        return expr;
+    }
+
+    public void setExpression(Expression expr) {
+        this.expr = expr;
+    }
+}
diff --git a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/SelectRegular.java b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/SelectRegular.java
new file mode 100644
index 0000000..e8a14ea
--- /dev/null
+++ b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/SelectRegular.java
@@ -0,0 +1,50 @@
+/*
+ * 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.clause;
+
+import java.util.List;
+
+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;
+
+public class SelectRegular implements Clause {
+
+    private List<Projection> projections;
+
+    public SelectRegular(List<Projection> projections) {
+        this.projections = projections;
+    }
+
+    @Override
+    public <R, T> R accept(ILangVisitor<R, T> visitor, T arg) throws AsterixException {
+        return ((ISqlppVisitor<R, T>) visitor).visit(this, arg);
+    }
+
+    @Override
+    public ClauseType getClauseType() {
+        return ClauseType.SELECT_REGULAR;
+    }
+
+    public List<Projection> getProjections() {
+        return projections;
+    }
+}
diff --git a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/SelectSetOperation.java b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/SelectSetOperation.java
new file mode 100644
index 0000000..8e77745
--- /dev/null
+++ b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/SelectSetOperation.java
@@ -0,0 +1,63 @@
+/*
+ * 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.clause;
+
+import java.util.List;
+
+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.struct.SetOperationInput;
+import org.apache.asterix.lang.sqlpp.struct.SetOperationRight;
+import org.apache.asterix.lang.sqlpp.visitor.base.ISqlppVisitor;
+
+public class SelectSetOperation implements Clause {
+
+    private SetOperationInput leftInput;
+    private List<SetOperationRight> rightInputs;
+
+    public SelectSetOperation(SetOperationInput leftInput, List<SetOperationRight> rightInputs) {
+        this.leftInput = leftInput;
+        this.rightInputs = rightInputs;
+    }
+
+    @Override
+    public <R, T> R accept(ILangVisitor<R, T> visitor, T arg) throws AsterixException {
+        return ((ISqlppVisitor<R, T>) visitor).visit(this, arg);
+    }
+
+    @Override
+    public ClauseType getClauseType() {
+        return ClauseType.SELECT_SET_OPERATION;
+    }
+
+    public SetOperationInput getLeftInput() {
+        return leftInput;
+    }
+
+    public List<SetOperationRight> getRightInputs() {
+        return rightInputs;
+    }
+
+    public boolean hasRightInputs() {
+        return rightInputs != null && rightInputs.size() > 0;
+    }
+
+}
diff --git a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/UnnestClause.java b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/UnnestClause.java
new file mode 100644
index 0000000..b71838b
--- /dev/null
+++ b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/UnnestClause.java
@@ -0,0 +1,45 @@
+/*
+ * 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.clause;
+
+import org.apache.asterix.common.exceptions.AsterixException;
+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.optype.JoinType;
+import org.apache.asterix.lang.sqlpp.visitor.base.ISqlppVisitor;
+
+public class UnnestClause extends AbstractBinaryCorrelateClause {
+
+    public UnnestClause(JoinType joinType, Expression rightExpr, VariableExpr rightVar, VariableExpr rightPosVar) {
+        super(joinType, rightExpr, rightVar, rightPosVar);
+    }
+
+    @Override
+    public <R, T> R accept(ILangVisitor<R, T> visitor, T arg) throws AsterixException {
+        return ((ISqlppVisitor<R, T>) visitor).visit(this, arg);
+    }
+
+    @Override
+    public ClauseType getClauseType() {
+        return ClauseType.UNNEST_CLAUSE;
+    }
+
+}
diff --git a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/expression/SelectExpression.java b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/expression/SelectExpression.java
new file mode 100644
index 0000000..eb3f8d8
--- /dev/null
+++ b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/expression/SelectExpression.java
@@ -0,0 +1,91 @@
+/*
+ * 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.expression;
+
+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.visitor.base.ILangVisitor;
+import org.apache.asterix.lang.sqlpp.clause.SelectSetOperation;
+import org.apache.asterix.lang.sqlpp.visitor.base.ISqlppVisitor;
+
+public class SelectExpression implements Expression {
+
+    private List<LetClause> letList;
+    private SelectSetOperation selectSetOperation;
+    private OrderbyClause orderbyClause;
+    private LimitClause limitClause;
+    private boolean subquery;
+
+    public SelectExpression(List<LetClause> letList, SelectSetOperation selectSetOperation, OrderbyClause orderbyClause,
+            LimitClause limitClause, boolean subquery) {
+        this.letList = letList;
+        this.selectSetOperation = selectSetOperation;
+        this.orderbyClause = orderbyClause;
+        this.limitClause = limitClause;
+        this.subquery = subquery;
+    }
+
+    @Override
+    public <R, T> R accept(ILangVisitor<R, T> visitor, T arg) throws AsterixException {
+        return ((ISqlppVisitor<R, T>) visitor).visit(this, arg);
+    }
+
+    @Override
+    public Kind getKind() {
+        return Kind.SELECT_EXPRESSION;
+    }
+
+    public List<LetClause> getLetList() {
+        return letList;
+    }
+
+    public SelectSetOperation getSelectSetOperation() {
+        return selectSetOperation;
+    }
+
+    public OrderbyClause getOrderbyClause() {
+        return orderbyClause;
+    }
+
+    public LimitClause getLimitClause() {
+        return limitClause;
+    }
+
+    public boolean hasOrderby() {
+        return orderbyClause != null;
+    }
+
+    public boolean hasLimit() {
+        return limitClause != null;
+    }
+
+    public boolean hasLetClauses() {
+        return letList != null && letList.size() > 0;
+    }
+
+    public boolean isSubquery() {
+        return subquery;
+    }
+
+}
diff --git a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/optype/JoinType.java b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/optype/JoinType.java
new file mode 100644
index 0000000..f4b84f5
--- /dev/null
+++ b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/optype/JoinType.java
@@ -0,0 +1,27 @@
+/*
+ * 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.optype;
+
+public enum JoinType {
+
+    INNER,
+    LEFTOUTER
+
+}
diff --git a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/optype/SetOpType.java b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/optype/SetOpType.java
new file mode 100644
index 0000000..9036c89
--- /dev/null
+++ b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/optype/SetOpType.java
@@ -0,0 +1,25 @@
+/*
+ * 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.optype;
+
+public enum SetOpType {
+    UNION,
+    INTERSECT,
+    EXCEPT
+}
diff --git a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppRewriter.java b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppRewriter.java
new file mode 100644
index 0000000..4caecfe
--- /dev/null
+++ b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppRewriter.java
@@ -0,0 +1,318 @@
+/*
+ * 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;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+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.clause.LetClause;
+import org.apache.asterix.lang.common.rewrites.LangRewritingContext;
+import org.apache.asterix.lang.common.statement.FunctionDecl;
+import org.apache.asterix.lang.common.statement.Query;
+import org.apache.asterix.lang.common.visitor.GatherFunctionCallsVisitor;
+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.FunctionUtils;
+import org.apache.asterix.lang.sqlpp.visitor.InlineColumnAliasVisitor;
+import org.apache.asterix.lang.sqlpp.visitor.SqlppInlineUdfsVisitor;
+import org.apache.asterix.lang.sqlpp.visitor.VariableCheckAndRewriteVisitor;
+import org.apache.asterix.lang.sqlpp.visitor.base.ISqlppVisitor;
+import org.apache.asterix.metadata.MetadataManager;
+import org.apache.asterix.metadata.MetadataTransactionContext;
+import org.apache.asterix.metadata.declared.AqlMetadataProvider;
+import org.apache.asterix.metadata.entities.Function;
+import org.apache.asterix.om.functions.AsterixBuiltinFunctions;
+
+public final class SqlppRewriter {
+
+    private Query topExpr;
+    private final List<FunctionDecl> declaredFunctions;
+    private final LangRewritingContext context;
+    private final MetadataTransactionContext mdTxnCtx;
+    private final AqlMetadataProvider metadataProvider;
+
+    public SqlppRewriter(List<FunctionDecl> declaredFunctions, Query topExpr, AqlMetadataProvider metadataProvider) {
+        this.topExpr = topExpr;
+        context = new LangRewritingContext(topExpr.getVarCounter());
+        this.declaredFunctions = declaredFunctions;
+        this.mdTxnCtx = metadataProvider.getMetadataTxnContext();
+        this.metadataProvider = metadataProvider;
+    }
+
+    public Query getExpr() {
+        return topExpr;
+    }
+
+    public int getVarCounter() {
+        return context.getVarCounter();
+    }
+
+    public void rewrite() throws AsterixException {
+        // Inlines column aliases.
+        inlineColumnAlias();
+
+        // Replace global variable access with the dataset function.
+        variableCheckAndRewrite();
+
+        // Inlines functions.
+        inlineDeclaredUdfs();
+    }
+
+    private void inlineColumnAlias() throws AsterixException {
+        if (topExpr == null) {
+            return;
+        }
+        // Inline column aliases.
+        InlineColumnAliasVisitor inlineColumnAliasVisitor = new InlineColumnAliasVisitor();
+        inlineColumnAliasVisitor.visit(topExpr, false);
+    }
+
+    private void variableCheckAndRewrite() throws AsterixException {
+        if (topExpr == null) {
+            return;
+        }
+        VariableCheckAndRewriteVisitor variableCheckAndRewriteVisitor = new VariableCheckAndRewriteVisitor();
+        variableCheckAndRewriteVisitor.visit(topExpr, null);
+    }
+
+    private void inlineDeclaredUdfs() throws AsterixException {
+        if (topExpr == null) {
+            return;
+        }
+        List<FunctionSignature> funIds = new ArrayList<FunctionSignature>();
+        for (FunctionDecl fdecl : declaredFunctions) {
+            funIds.add(fdecl.getSignature());
+        }
+
+        List<FunctionDecl> otherFDecls = new ArrayList<FunctionDecl>();
+        buildOtherUdfs(topExpr.getBody(), otherFDecls, funIds);
+        declaredFunctions.addAll(otherFDecls);
+        if (!declaredFunctions.isEmpty()) {
+            SqlppInlineUdfsVisitor visitor = new SqlppInlineUdfsVisitor(context);
+            while (topExpr.accept(visitor, declaredFunctions)) {
+                // loop until no more changes
+            }
+        }
+        declaredFunctions.removeAll(otherFDecls);
+    }
+
+    private void buildOtherUdfs(Expression expression, List<FunctionDecl> functionDecls,
+            List<FunctionSignature> declaredFunctions) throws AsterixException {
+        if (expression == null) {
+            return;
+        }
+        String value = metadataProvider.getConfig().get(FunctionUtils.IMPORT_PRIVATE_FUNCTIONS);
+        boolean includePrivateFunctions = (value != null) ? Boolean.valueOf(value.toLowerCase()) : false;
+        Set<FunctionSignature> functionCalls = getFunctionCalls(expression);
+        for (FunctionSignature signature : functionCalls) {
+
+            if (declaredFunctions != null && declaredFunctions.contains(signature)) {
+                continue;
+            }
+
+            Function function = lookupUserDefinedFunctionDecl(signature);
+            if (function == null) {
+                if (AsterixBuiltinFunctions.isBuiltinCompilerFunction(signature, includePrivateFunctions)) {
+                    continue;
+                }
+                StringBuilder messageBuilder = new StringBuilder();
+                if (functionDecls.size() > 0) {
+                    messageBuilder.append(" function " + functionDecls.get(functionDecls.size() - 1).getSignature()
+                            + " depends upon function " + signature + " which is undefined");
+                } else {
+                    messageBuilder.append(" function " + signature + " is undefined ");
+                }
+                throw new AsterixException(messageBuilder.toString());
+            }
+
+            if (function.getLanguage().equalsIgnoreCase(Function.LANGUAGE_AQL)) {
+                FunctionDecl functionDecl = FunctionUtils.getFunctionDecl(function);
+                if (functionDecl != null) {
+                    if (functionDecls.contains(functionDecl)) {
+                        throw new AsterixException("ERROR:Recursive invocation "
+                                + functionDecls.get(functionDecls.size() - 1).getSignature() + " <==> "
+                                + functionDecl.getSignature());
+                    }
+                    functionDecls.add(functionDecl);
+                    buildOtherUdfs(functionDecl.getFuncBody(), functionDecls, declaredFunctions);
+                }
+            }
+        }
+
+    }
+
+    private Function lookupUserDefinedFunctionDecl(FunctionSignature signature) throws AsterixException {
+        if (signature.getNamespace() == null) {
+            return null;
+        }
+        return MetadataManager.INSTANCE.getFunction(mdTxnCtx, signature);
+    }
+
+    private Set<FunctionSignature> getFunctionCalls(Expression expression) throws AsterixException {
+        GatherFunctionCalls gfc = new GatherFunctionCalls();
+        expression.accept(gfc, null);
+        return gfc.getCalls();
+    }
+
+    private static class GatherFunctionCalls extends GatherFunctionCallsVisitor implements ISqlppVisitor<Void, Void> {
+
+        public GatherFunctionCalls() {
+        }
+
+        @Override
+        public Void visit(FromClause fromClause, Void arg) throws AsterixException {
+            for (FromTerm fromTerm : fromClause.getFromTerms()) {
+                fromTerm.accept(this, arg);
+            }
+            return null;
+        }
+
+        @Override
+        public Void visit(FromTerm fromTerm, Void arg) throws AsterixException {
+            fromTerm.getLeftExpression().accept(this, arg);
+            for (AbstractBinaryCorrelateClause correlateClause : fromTerm.getCorrelateClauses()) {
+                correlateClause.accept(this, arg);
+            }
+            return null;
+        }
+
+        @Override
+        public Void visit(JoinClause joinClause, Void arg) throws AsterixException {
+            joinClause.getRightExpression().accept(this, arg);
+            joinClause.getConditionExpression().accept(this, arg);
+            return null;
+        }
+
+        @Override
+        public Void visit(NestClause nestClause, Void arg) throws AsterixException {
+            nestClause.getRightExpression().accept(this, arg);
+            nestClause.getConditionExpression().accept(this, arg);
+            return null;
+        }
+
+        @Override
+        public Void visit(Projection projection, Void arg) throws AsterixException {
+            projection.getExpression().accept(this, arg);
+            return null;
+        }
+
+        @Override
+        public Void visit(SelectBlock selectBlock, Void arg) throws AsterixException {
+            if (selectBlock.hasFromClause()) {
+                selectBlock.getFromClause().accept(this, arg);
+            }
+            if (selectBlock.hasLetClauses()) {
+                for (LetClause letClause : selectBlock.getLetList()) {
+                    letClause.accept(this, arg);
+                }
+            }
+            if (selectBlock.hasWhereClause()) {
+                selectBlock.getWhereClause().accept(this, arg);
+            }
+            if (selectBlock.hasGroupbyClause()) {
+                selectBlock.getGroupbyClause().accept(this, arg);
+            }
+            if (selectBlock.hasLetClausesAfterGroupby()) {
+                for (LetClause letClause : selectBlock.getLetListAfterGroupby()) {
+                    letClause.accept(this, arg);
+                }
+            }
+            if (selectBlock.hasHavingClause()) {
+                selectBlock.getHavingClause().accept(this, arg);
+            }
+            selectBlock.getSelectClause().accept(this, arg);
+            return null;
+        }
+
+        @Override
+        public Void visit(SelectClause selectClause, Void arg) throws AsterixException {
+            if (selectClause.selectElement()) {
+                selectClause.getSelectElement().accept(this, arg);
+            } else {
+                selectClause.getSelectRegular().accept(this, arg);
+            }
+            return null;
+        }
+
+        @Override
+        public Void visit(SelectElement selectElement, Void arg) throws AsterixException {
+            selectElement.getExpression().accept(this, arg);
+            return null;
+        }
+
+        @Override
+        public Void visit(SelectRegular selectRegular, Void arg) throws AsterixException {
+            for (Projection projection : selectRegular.getProjections()) {
+                projection.accept(this, arg);
+            }
+            return null;
+        }
+
+        @Override
+        public Void visit(SelectSetOperation selectSetOperation, Void arg) throws AsterixException {
+            selectSetOperation.getLeftInput().accept(this, arg);
+            for (SetOperationRight setOperationRight : selectSetOperation.getRightInputs()) {
+                setOperationRight.getSetOperationRightInput().accept(this, arg);
+            }
+            return null;
+        }
+
+        @Override
+        public Void visit(SelectExpression selectStatement, Void arg) throws AsterixException {
+            selectStatement.getSelectSetOperation().accept(this, arg);
+            if (selectStatement.hasOrderby()) {
+                selectStatement.getOrderbyClause().accept(this, arg);
+            }
+            if (selectStatement.hasLimit()) {
+                selectStatement.getLimitClause().accept(this, arg);
+            }
+            return null;
+        }
+
+        @Override
+        public Void visit(UnnestClause unnestClause, Void arg) throws AsterixException {
+            unnestClause.getRightExpression().accept(this, arg);
+            return null;
+        }
+
+        @Override
+        public Void visit(HavingClause havingClause, Void arg) throws AsterixException {
+            havingClause.getFilterExpression().accept(this, arg);
+            return null;
+        }
+
+    }
+}
diff --git a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/struct/SetOperationInput.java b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/struct/SetOperationInput.java
new file mode 100644
index 0000000..da89709
--- /dev/null
+++ b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/struct/SetOperationInput.java
@@ -0,0 +1,60 @@
+/*
+ * 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.struct;
+
+import org.apache.asterix.common.exceptions.AsterixException;
+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;
+
+public class SetOperationInput {
+
+    private SelectBlock selectBlock;
+    private SelectExpression subquery;
+
+    public SetOperationInput(SelectBlock selectBlock, SelectExpression subquery) {
+        this.selectBlock = selectBlock;
+        this.subquery = subquery;
+    }
+
+    public SelectBlock getSelectBlock() {
+        return selectBlock;
+    }
+
+    public SelectExpression getSubquery() {
+        return subquery;
+    }
+
+    public boolean selectBlock() {
+        return selectBlock != null;
+    }
+
+    public boolean subquery() {
+        return subquery != null;
+    }
+
+    public <R, T> R accept(ILangVisitor<R, T> visitor, T arg) throws AsterixException {
+        if (selectBlock != null) {
+            return ((ISqlppVisitor<R, T>) visitor).visit(selectBlock, arg);
+        } else {
+            return ((ISqlppVisitor<R, T>) visitor).visit(subquery, arg);
+        }
+    }
+}
diff --git a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/struct/SetOperationRight.java b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/struct/SetOperationRight.java
new file mode 100644
index 0000000..ae0572d
--- /dev/null
+++ b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/struct/SetOperationRight.java
@@ -0,0 +1,47 @@
+/*
+ * 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.struct;
+
+import org.apache.asterix.lang.sqlpp.optype.SetOpType;
+
+public class SetOperationRight {
+
+    private SetOpType opType;
+    private boolean setSemantics;
+    private SetOperationInput setOperationRightInput;
+
+    public SetOperationRight(SetOpType opType, boolean setSemantics, SetOperationInput setOperationRight) {
+        this.opType = opType;
+        this.setSemantics = setSemantics;
+        this.setOperationRightInput = setOperationRight;
+    }
+
+    public SetOpType getSetOpType() {
+        return opType;
+    }
+
+    public boolean setSemantics() {
+        return setSemantics;
+    }
+
+    public SetOperationInput getSetOperationRightInput() {
+        return setOperationRightInput;
+    }
+
+}
diff --git a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/FunctionUtils.java b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/FunctionUtils.java
new file mode 100644
index 0000000..9100ecd
--- /dev/null
+++ b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/FunctionUtils.java
@@ -0,0 +1,87 @@
+/*
+ * 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.io.StringReader;
+import java.util.ArrayList;
+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.Statement;
+import org.apache.asterix.lang.common.statement.FunctionDecl;
+import org.apache.asterix.lang.common.struct.VarIdentifier;
+import org.apache.asterix.lang.sqlpp.parser.SQLPPParser;
+import org.apache.asterix.metadata.entities.Function;
+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 FunctionUtils {
+
+    public static final String IMPORT_PRIVATE_FUNCTIONS = "import-private-functions";
+
+    public static FunctionDecl getFunctionDecl(Function function) throws AsterixException {
+        String functionBody = function.getFunctionBody();
+        List<String> params = function.getParams();
+        List<VarIdentifier> varIdentifiers = new ArrayList<VarIdentifier>();
+
+        StringBuilder builder = new StringBuilder();
+        builder.append(" use dataverse " + function.getDataverseName() + ";");
+        builder.append(" declare function " + function.getName().split("@")[0]);
+        builder.append("(");
+        for (String param : params) {
+            VarIdentifier varId = new VarIdentifier(param);
+            varIdentifiers.add(varId);
+            builder.append(param);
+            builder.append(",");
+        }
+        if (params.size() > 0) {
+            builder.delete(builder.length() - 1, builder.length());
+        }
+        builder.append(")");
+        builder.append("{");
+        builder.append("\n");
+        builder.append(functionBody);
+        builder.append("\n");
+        builder.append("}");
+
+        SQLPPParser parser = new SQLPPParser(new StringReader(new String(builder)));
+
+        List<Statement> statements = null;
+        try {
+            statements = parser.parse();
+        } catch (AsterixException pe) {
+            throw new AsterixException(pe);
+        }
+
+        FunctionDecl decl = (FunctionDecl) statements.get(1);
+        return decl;
+    }
+
+    public static IFunctionInfo getFunctionInfo(FunctionIdentifier fi) {
+        return AsterixBuiltinFunctions.getAsterixFunctionInfo(fi);
+    }
+
+    public static IFunctionInfo getFunctionInfo(FunctionSignature fs) {
+        return getFunctionInfo(new FunctionIdentifier(fs.getNamespace(), fs.getName(), fs.getArity()));
+    }
+
+}
diff --git a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppAstPrintUtil.java b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppAstPrintUtil.java
new file mode 100644
index 0000000..0f7ec4a
--- /dev/null
+++ b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppAstPrintUtil.java
@@ -0,0 +1,92 @@
+/*
+ * 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.io.ByteArrayOutputStream;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.asterix.common.exceptions.AsterixException;
+import org.apache.asterix.lang.common.base.ILangExpression;
+import org.apache.asterix.lang.common.base.Statement;
+import org.apache.asterix.lang.sqlpp.visitor.SqlppPrintVisitor;
+
+public class SqlppAstPrintUtil {
+
+    /**
+     * Prints the AST (abstract syntax tree) of an ILangExpression.
+     * 
+     * @param expr
+     *            the language expression.
+     * @param output
+     *            a writer for printing strings.
+     * @throws AsterixException
+     */
+    public static void print(ILangExpression expr, PrintWriter output) throws AsterixException {
+        SqlppPrintVisitor visitor = new SqlppPrintVisitor(output);
+        expr.accept(visitor, 0);
+    }
+
+    /**
+     * Prints the AST of a list of top-level language statements.
+     * 
+     * @param statements
+     *            a list of statements of a query
+     * @param output
+     *            a writer for printing strings.
+     * @throws AsterixException
+     */
+    public static void print(List<Statement> statements, PrintWriter output) throws AsterixException {
+        SqlppPrintVisitor visitor = new SqlppPrintVisitor(output);
+        for (Statement statement : statements) {
+            statement.accept(visitor, 0);
+        }
+    }
+
+    /**
+     * @param expr
+     *            a language expression.
+     * @return the AST of a language expression.
+     * @throws AsterixException
+     */
+    public static String toString(ILangExpression expr) throws AsterixException {
+        List<ILangExpression> exprs = new ArrayList<ILangExpression>();
+        exprs.add(expr);
+        return toString(exprs);
+    }
+
+    /**
+     * @param exprs
+     *            a list of language expression.
+     * @return an AST of the input language expressions.
+     * @throws AsterixException
+     */
+    public static String toString(List<ILangExpression> exprs) throws AsterixException {
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        PrintWriter output = new PrintWriter(bos);
+        SqlppPrintVisitor visitor = new SqlppPrintVisitor(output);
+        for (ILangExpression expr : exprs) {
+            expr.accept(visitor, 0);
+        }
+        output.close();
+        return new String(bos.toByteArray());
+    }
+
+}
diff --git a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppFormatPrintUtil.java b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppFormatPrintUtil.java
new file mode 100644
index 0000000..f10c9fc
--- /dev/null
+++ b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppFormatPrintUtil.java
@@ -0,0 +1,92 @@
+/*
+ * 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.io.ByteArrayOutputStream;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.asterix.common.exceptions.AsterixException;
+import org.apache.asterix.lang.common.base.ILangExpression;
+import org.apache.asterix.lang.common.base.Statement;
+import org.apache.asterix.lang.sqlpp.visitor.SqlppFormatPrintVisitor;
+
+public class SqlppFormatPrintUtil {
+
+    /**
+     * Prints the formatted output of an ILangExpression.
+     * 
+     * @param expr
+     *            the language expression.
+     * @param output
+     *            a writer for printing strings.
+     * @throws AsterixException
+     */
+    public static void print(ILangExpression expr, PrintWriter output) throws AsterixException {
+        SqlppFormatPrintVisitor visitor = new SqlppFormatPrintVisitor(output);
+        expr.accept(visitor, 0);
+    }
+
+    /**
+     * Prints the formatted output of a list of top-level language statements.
+     * 
+     * @param statements
+     *            a list of statements of a query
+     * @param output
+     *            a writer for printing strings.
+     * @throws AsterixException
+     */
+    public static void print(List<Statement> statements, PrintWriter output) throws AsterixException {
+        SqlppFormatPrintVisitor visitor = new SqlppFormatPrintVisitor(output);
+        for (Statement statement : statements) {
+            statement.accept(visitor, 0);
+        }
+    }
+
+    /**
+     * @param expr
+     *            a language expression.
+     * @return a formatted string of a language expression.
+     * @throws AsterixException
+     */
+    public static String toString(ILangExpression expr) throws AsterixException {
+        List<ILangExpression> exprs = new ArrayList<ILangExpression>();
+        exprs.add(expr);
+        return toString(exprs);
+    }
+
+    /**
+     * @param exprs
+     *            a list of language expression.
+     * @return a formatted string of the input language expressions.
+     * @throws AsterixException
+     */
+    public static String toString(List<ILangExpression> exprs) throws AsterixException {
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        PrintWriter output = new PrintWriter(bos);
+        SqlppFormatPrintVisitor visitor = new SqlppFormatPrintVisitor(output);
+        for (ILangExpression expr : exprs) {
+            expr.accept(visitor, 0);
+        }
+        output.close();
+        return new String(bos.toByteArray());
+    }
+
+}
diff --git a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppVariableSubstitutionUtil.java b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppVariableSubstitutionUtil.java
new file mode 100644
index 0000000..0a4c2c4
--- /dev/null
+++ b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppVariableSubstitutionUtil.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.asterix.lang.sqlpp.util;
+
+import java.util.ArrayList;
+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.ILangExpression;
+import org.apache.asterix.lang.common.expression.VariableExpr;
+import org.apache.asterix.lang.common.rewrites.LangRewritingContext;
+import org.apache.asterix.lang.common.rewrites.VariableSubstitutionEnvironment;
+import org.apache.asterix.lang.sqlpp.visitor.SqlppCloneAndSubstituteVariablesVisitor;
+import org.apache.asterix.lang.sqlpp.visitor.SqlppSubstituteVariablesVisitor;
+
+public class SqlppVariableSubstitutionUtil {
+
+    public static List<ILangExpression> substituteVariable(List<ILangExpression> expressions,
+            VariableSubstitutionEnvironment env) throws AsterixException {
+        SqlppCloneAndSubstituteVariablesVisitor visitor = new SqlppSubstituteVariablesVisitor(
+                new LangRewritingContext(0));
+        List<ILangExpression> newExprs = new ArrayList<ILangExpression>();
+        for (ILangExpression expression : expressions) {
+            newExprs.add(expression.accept(visitor, env).first);
+        }
+        return newExprs;
+    }
+
+    public static ILangExpression substituteVariable(ILangExpression expression, VariableSubstitutionEnvironment env)
+            throws AsterixException {
+        SqlppSubstituteVariablesVisitor visitor = new SqlppSubstituteVariablesVisitor(new LangRewritingContext(0));
+        return expression.accept(visitor, env).first;
+    }
+
+    public static List<ILangExpression> substituteVariable(List<ILangExpression> expressions,
+            Map<VariableExpr, Expression> varExprMap) throws AsterixException {
+        SqlppSubstituteVariablesVisitor visitor = new SqlppSubstituteVariablesVisitor(new LangRewritingContext(0));
+        VariableSubstitutionEnvironment env = new VariableSubstitutionEnvironment(varExprMap);
+        List<ILangExpression> newExprs = new ArrayList<ILangExpression>();
+        for (ILangExpression expression : expressions) {
+            newExprs.add(expression.accept(visitor, env).first);
+        }
+        return newExprs;
+    }
+
+    public static ILangExpression substituteVariable(ILangExpression expression,
+            Map<VariableExpr, Expression> varExprMap) throws AsterixException {
+        SqlppSubstituteVariablesVisitor visitor = new SqlppSubstituteVariablesVisitor(new LangRewritingContext(0));
+        VariableSubstitutionEnvironment env = new VariableSubstitutionEnvironment(varExprMap);
+        return expression.accept(visitor, env).first;
+    }
+
+}
diff --git a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/InlineColumnAliasVisitor.java b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/InlineColumnAliasVisitor.java
new file mode 100644
index 0000000..d6955c0
--- /dev/null
+++ b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/InlineColumnAliasVisitor.java
@@ -0,0 +1,394 @@
+/*
+ * 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.Expression.Kind;
+import org.apache.asterix.lang.common.base.Literal;
+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.parser.ScopeChecker;
+import org.apache.asterix.lang.common.rewrites.VariableSubstitutionEnvironment;
+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.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.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.base.AbstractSqlppQueryExpressionVisitor;
+
+public class InlineColumnAliasVisitor extends AbstractSqlppQueryExpressionVisitor<Void, Boolean> {
+
+    private final ScopeChecker scopeChecker = new ScopeChecker();
+
+    @Override
+    public Void visit(WhereClause whereClause, Boolean arg) throws AsterixException {
+        whereClause.getWhereExpr().accept(this, arg);
+        return null;
+    }
+
+    @Override
+    public Void visit(FromClause fromClause, Boolean arg) throws AsterixException {
+        for (FromTerm fromTerm : fromClause.getFromTerms()) {
+            fromTerm.accept(this, arg);
+        }
+        return null;
+    }
+
+    @Override
+    public Void visit(FromTerm fromTerm, Boolean arg) throws AsterixException {
+        fromTerm.getLeftExpression().accept(this, arg);
+        // A from binding variable will override the alias to substitute.
+        scopeChecker.getCurrentScope().removeSymbolExpressionMapping(fromTerm.getLeftVariable());
+        if (fromTerm.hasPositionalVariable()) {
+            scopeChecker.getCurrentScope().removeSymbolExpressionMapping(fromTerm.getPositionalVariable());
+        }
+
+        for (AbstractBinaryCorrelateClause correlate : fromTerm.getCorrelateClauses()) {
+            correlate.accept(this, arg);
+        }
+        return null;
+    }
+
+    @Override
+    public Void visit(JoinClause joinClause, Boolean arg) throws AsterixException {
+        joinClause.getRightExpression().accept(this, arg);
+        removeSubsutitions(joinClause);
+        joinClause.getConditionExpression().accept(this, arg);
+        return null;
+    }
+
+    @Override
+    public Void visit(NestClause nestClause, Boolean arg) throws AsterixException {
+        nestClause.getRightExpression().accept(this, arg);
+        nestClause.getConditionExpression().accept(this, arg);
+        removeSubsutitions(nestClause);
+        return null;
+    }
+
+    @Override
+    public Void visit(UnnestClause unnestClause, Boolean arg) throws AsterixException {
+        unnestClause.getRightExpression().accept(this, arg);
+        removeSubsutitions(unnestClause);
+        return null;
+    }
+
+    @Override
+    public Void visit(Projection projection, Boolean arg) throws AsterixException {
+        projection.getExpression().accept(this, arg);
+        scopeChecker.getCurrentScope().addSymbolExpressionMappingToScope(
+                new VariableExpr(new VarIdentifier(projection.getName())), projection.getExpression());
+        return null;
+    }
+
+    @Override
+    public Void visit(SelectBlock selectBlock, Boolean arg) throws AsterixException {
+        // Traverses the select block in the order of "select", "group-by",
+        // "group-by" lets and "having".
+        selectBlock.getSelectClause().accept(this, arg);
+
+        if (selectBlock.hasFromClause()) {
+            selectBlock.getFromClause().accept(this, arg);
+        }
+        if (selectBlock.hasLetClauses()) {
+            for (LetClause letClause : selectBlock.getLetList()) {
+                letClause.accept(this, arg);
+            }
+        }
+        if (selectBlock.hasGroupbyClause()) {
+            selectBlock.getGroupbyClause().accept(this, arg);
+        }
+        if (selectBlock.hasLetClausesAfterGroupby()) {
+            for (LetClause letClauseAfterGby : selectBlock.getLetListAfterGroupby()) {
+                letClauseAfterGby.accept(this, true);
+            }
+        }
+        if (selectBlock.hasHavingClause()) {
+            selectBlock.getHavingClause().accept(this, arg);
+        }
+        return null;
+    }
+
+    @Override
+    public Void visit(SelectClause selectClause, Boolean arg) throws AsterixException {
+        if (selectClause.selectElement()) {
+            selectClause.getSelectElement().accept(this, arg);
+        }
+        if (selectClause.selectRegular()) {
+            selectClause.getSelectRegular().accept(this, arg);
+        }
+        return null;
+    }
+
+    @Override
+    public Void visit(SelectElement selectElement, Boolean arg) throws AsterixException {
+        selectElement.getExpression().accept(this, true);
+        return null;
+    }
+
+    @Override
+    public Void visit(SelectRegular selectRegular, Boolean arg) throws AsterixException {
+        for (Projection projection : selectRegular.getProjections()) {
+            projection.accept(this, arg);
+        }
+        return null;
+    }
+
+    @Override
+    public Void visit(SelectSetOperation selectSetOperation, Boolean arg) throws AsterixException {
+        selectSetOperation.getLeftInput().accept(this, arg);
+        for (SetOperationRight right : selectSetOperation.getRightInputs()) {
+            right.getSetOperationRightInput().accept(this, arg);
+        }
+        return null;
+    }
+
+    @Override
+    public Void visit(SelectExpression selectExpression, Boolean arg) throws AsterixException {
+        scopeChecker.createNewScope();
+
+        // Visits let bindings.
+        if (selectExpression.hasLetClauses()) {
+            for (LetClause lc : selectExpression.getLetList()) {
+                lc.accept(this, arg);
+            }
+        }
+
+        // Visits selectSetOperation.
+        selectExpression.getSelectSetOperation().accept(this, arg);
+
+        // Visits order by.
+        if (selectExpression.hasOrderby()) {
+            selectExpression.getOrderbyClause().accept(this, arg);
+        }
+
+        // Visits limit.
+        if (selectExpression.hasLimit()) {
+            selectExpression.getLimitClause().accept(this, arg);
+        }
+
+        // Exits the scope that were entered within this select expression
+        scopeChecker.removeCurrentScope();
+        return null;
+    }
+
+    @Override
+    public Void visit(LetClause letClause, Boolean rewrite) throws AsterixException {
+        VariableSubstitutionEnvironment env = scopeChecker.getCurrentScope().getVarSubstitutionEnvironment();
+        if (rewrite) {
+            Expression newBindExpr = (Expression) SqlppVariableSubstitutionUtil
+                    .substituteVariable(letClause.getBindingExpr(), env);
+            letClause.setBindingExpr(newBindExpr);
+        }
+        letClause.getBindingExpr().accept(this, false);
+        // A let binding variable will override the alias to substitute.
+        scopeChecker.getCurrentScope().removeSymbolExpressionMapping(letClause.getVarExpr());
+        return null;
+    }
+
+    @Override
+    public Void visit(OrderbyClause oc, Boolean arg) throws AsterixException {
+        VariableSubstitutionEnvironment env = scopeChecker.getCurrentScope().getVarSubstitutionEnvironment();
+        List<Expression> orderExprs = new ArrayList<Expression>();
+        for (Expression orderExpr : oc.getOrderbyList()) {
+            orderExprs.add((Expression) SqlppVariableSubstitutionUtil.substituteVariable(orderExpr, env));
+            orderExpr.accept(this, arg);
+        }
+        oc.setOrderbyList(orderExprs);
+        return null;
+    }
+
+    @Override
+    public Void visit(GroupbyClause gc, Boolean arg) throws AsterixException {
+        VariableSubstitutionEnvironment env = scopeChecker.getCurrentScope().getVarSubstitutionEnvironment();
+        for (GbyVariableExpressionPair gbyVarExpr : gc.getGbyPairList()) {
+            Expression newExpr = (Expression) SqlppVariableSubstitutionUtil.substituteVariable(gbyVarExpr.getExpr(),
+                    env);
+            newExpr.accept(this, arg);
+            gbyVarExpr.setExpr(newExpr);
+        }
+        for (GbyVariableExpressionPair gbyVarExpr : gc.getGbyPairList()) {
+            // A group-by variable will override the alias to substitute.
+            scopeChecker.getCurrentScope().removeSymbolExpressionMapping(gbyVarExpr.getVar());
+        }
+        return null;
+    }
+
+    @Override
+    public Void visit(LimitClause limitClause, Boolean arg) throws AsterixException {
+        limitClause.getLimitExpr().accept(this, arg);
+        return null;
+    }
+
+    @Override
+    public Void visit(HavingClause havingClause, Boolean arg) throws AsterixException {
+        VariableSubstitutionEnvironment env = scopeChecker.getCurrentScope().getVarSubstitutionEnvironment();
+        Expression newFilterExpr = (Expression) SqlppVariableSubstitutionUtil
+                .substituteVariable(havingClause.getFilterExpression(), env);
+        newFilterExpr.accept(this, arg);
+        havingClause.setFilterExpression(newFilterExpr);
+        return null;
+    }
+
+    @Override
+    public Void visit(Query q, Boolean arg) throws AsterixException {
+        q.getBody().accept(this, arg);
+        return null;
+    }
+
+    @Override
+    public Void visit(FunctionDecl fd, Boolean arg) throws AsterixException {
+        scopeChecker.createNewScope();
+        fd.getFuncBody().accept(this, arg);
+        scopeChecker.removeCurrentScope();
+        return null;
+    }
+
+    @Override
+    public Void visit(LiteralExpr l, Boolean arg) throws AsterixException {
+        return null;
+    }
+
+    @Override
+    public Void visit(ListConstructor lc, Boolean arg) throws AsterixException {
+        for (Expression expr : lc.getExprList()) {
+            expr.accept(this, arg);
+        }
+        return null;
+    }
+
+    @Override
+    public Void visit(RecordConstructor rc, Boolean rewrite) throws AsterixException {
+        for (FieldBinding binding : rc.getFbList()) {
+            Expression leftExpr = binding.getLeftExpr();
+            leftExpr.accept(this, false);
+            binding.getRightExpr().accept(this, false);
+            if (rewrite && leftExpr.getKind() == Kind.LITERAL_EXPRESSION) {
+                LiteralExpr literalExpr = (LiteralExpr) leftExpr;
+                if (literalExpr.getValue().getLiteralType() == Literal.Type.STRING) {
+                    String fieldName = literalExpr.getValue().getStringValue();
+                    scopeChecker.getCurrentScope().addSymbolExpressionMappingToScope(
+                            new VariableExpr(new VarIdentifier(fieldName)), binding.getRightExpr());
+                }
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public Void visit(OperatorExpr operatorExpr, Boolean arg) throws AsterixException {
+        for (Expression expr : operatorExpr.getExprList()) {
+            expr.accept(this, arg);
+        }
+        return null;
+    }
+
+    @Override
+    public Void visit(IfExpr ifExpr, Boolean arg) throws AsterixException {
+        ifExpr.getCondExpr().accept(this, arg);
+        ifExpr.getThenExpr().accept(this, arg);
+        ifExpr.getElseExpr().accept(this, arg);
+        return null;
+    }
+
+    @Override
+    public Void visit(QuantifiedExpression qe, Boolean arg) throws AsterixException {
+        for (QuantifiedPair pair : qe.getQuantifiedList()) {
+            pair.getExpr().accept(this, arg);
+        }
+        qe.getSatisfiesExpr().accept(this, arg);
+        return null;
+    }
+
+    @Override
+    public Void visit(CallExpr callExpr, Boolean arg) throws AsterixException {
+        for (Expression expr : callExpr.getExprList()) {
+            expr.accept(this, arg);
+        }
+        return null;
+    }
+
+    @Override
+    public Void visit(VariableExpr varExpr, Boolean arg) throws AsterixException {
+        return null;
+    }
+
+    @Override
+    public Void visit(UnaryExpr u, Boolean arg) throws AsterixException {
+        u.getExpr().accept(this, arg);
+        return null;
+    }
+
+    @Override
+    public Void visit(FieldAccessor fa, Boolean arg) throws AsterixException {
+        fa.getExpr().accept(this, arg);
+        return null;
+    }
+
+    @Override
+    public Void visit(IndexAccessor ia, Boolean arg) throws AsterixException {
+        ia.getExpr().accept(this, arg);
+        Expression indexExpr = ia.getExpr();
+        if (indexExpr != null) {
+            indexExpr.accept(this, arg);
+        }
+        return null;
+    }
+
+    private void removeSubsutitions(AbstractBinaryCorrelateClause unnestClause) {
+        scopeChecker.getCurrentScope().removeSymbolExpressionMapping(unnestClause.getRightVariable());
+        if (unnestClause.hasPositionalVariable()) {
+            scopeChecker.getCurrentScope().removeSymbolExpressionMapping(unnestClause.getPositionalVariable());
+        }
+    }
+}
diff --git a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppCloneAndSubstituteVariablesVisitor.java b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppCloneAndSubstituteVariablesVisitor.java
new file mode 100644
index 0000000..3cb7b28
--- /dev/null
+++ b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppCloneAndSubstituteVariablesVisitor.java
@@ -0,0 +1,369 @@
+/*
+ * 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.Clause.ClauseType;
+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.VariableExpr;
+import org.apache.asterix.lang.common.rewrites.LangRewritingContext;
+import org.apache.asterix.lang.common.rewrites.VariableSubstitutionEnvironment;
+import org.apache.asterix.lang.common.visitor.CloneAndSubstituteVariablesVisitor;
+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.ISqlppVisitor;
+import org.apache.hyracks.algebricks.common.utils.Pair;
+
+public class SqlppCloneAndSubstituteVariablesVisitor extends CloneAndSubstituteVariablesVisitor implements
+        ISqlppVisitor<Pair<ILangExpression, VariableSubstitutionEnvironment>, VariableSubstitutionEnvironment> {
+
+    private LangRewritingContext context;
+
+    public SqlppCloneAndSubstituteVariablesVisitor(LangRewritingContext context) {
+        super(context);
+        this.context = context;
+    }
+
+    @Override
+    public Pair<ILangExpression, VariableSubstitutionEnvironment> visit(FromClause fromClause,
+            VariableSubstitutionEnvironment env) throws AsterixException {
+        VariableSubstitutionEnvironment currentEnv = new VariableSubstitutionEnvironment(env);
+        List<FromTerm> newFromTerms = new ArrayList<FromTerm>();
+        for (FromTerm fromTerm : fromClause.getFromTerms()) {
+            Pair<ILangExpression, VariableSubstitutionEnvironment> p = fromTerm.accept(this, currentEnv);
+            newFromTerms.add((FromTerm) p.first);
+            // A right from term could be correlated from a left from term,
+            // therefore we propagate the substitution environment.
+            currentEnv = p.second;
+        }
+        return new Pair<ILangExpression, VariableSubstitutionEnvironment>(new FromClause(newFromTerms), currentEnv);
+    }
+
+    @Override
+    public Pair<ILangExpression, VariableSubstitutionEnvironment> visit(FromTerm fromTerm,
+            VariableSubstitutionEnvironment env) throws AsterixException {
+        VariableExpr leftVar = fromTerm.getLeftVariable();
+        VariableExpr newLeftVar = generateNewVariable(context, leftVar);
+        VariableExpr newLeftPosVar = fromTerm.hasPositionalVariable()
+                ? generateNewVariable(context, fromTerm.getPositionalVariable()) : null;
+        Expression newLeftExpr = (Expression) fromTerm.getLeftExpression().accept(this, env).first;
+        List<AbstractBinaryCorrelateClause> newCorrelateClauses = new ArrayList<AbstractBinaryCorrelateClause>();
+
+        VariableSubstitutionEnvironment currentEnv = new VariableSubstitutionEnvironment(env);
+        currentEnv.removeSubstitution(newLeftVar);
+        if (newLeftPosVar != null) {
+            currentEnv.removeSubstitution(newLeftPosVar);
+        }
+
+        for (AbstractBinaryCorrelateClause correlateClause : fromTerm.getCorrelateClauses()) {
+            if (correlateClause.getClauseType() == ClauseType.UNNEST_CLAUSE) {
+                // The right-hand-side of unnest could be correlated with the left side,
+                // therefore we propagate the substitution environment of the left-side.
+                Pair<ILangExpression, VariableSubstitutionEnvironment> p = correlateClause.accept(this, currentEnv);
+                currentEnv = p.second;
+                newCorrelateClauses.add((AbstractBinaryCorrelateClause) p.first);
+            } else {
+                // The right-hand-side of join and nest could not be correlated with the left side,
+                // therefore we propagate the original substitution environment.
+                newCorrelateClauses.add((AbstractBinaryCorrelateClause) correlateClause.accept(this, env).first);
+            }
+        }
+        return new Pair<ILangExpression, VariableSubstitutionEnvironment>(
+                new FromTerm(newLeftExpr, newLeftVar, newLeftPosVar, newCorrelateClauses), currentEnv);
+    }
+
+    @Override
+    public Pair<ILangExpression, VariableSubstitutionEnvironment> visit(JoinClause joinClause,
+            VariableSubstitutionEnvironment env) throws AsterixException {
+        VariableExpr rightVar = joinClause.getRightVariable();
+        VariableExpr newRightVar = generateNewVariable(context, rightVar);
+        VariableExpr newRightPosVar = joinClause.hasPositionalVariable()
+                ? generateNewVariable(context, joinClause.getPositionalVariable()) : null;
+
+        // Visits the right expression.
+        Expression newRightExpr = (Expression) joinClause.getRightExpression().accept(this, env).first;
+
+        // Visits the condition.
+        VariableSubstitutionEnvironment currentEnv = new VariableSubstitutionEnvironment(env);
+        currentEnv.removeSubstitution(newRightVar);
+        if (newRightPosVar != null) {
+            currentEnv.removeSubstitution(newRightPosVar);
+        }
+        // The condition can refer to the newRightVar and newRightPosVar.
+        Expression conditionExpr = (Expression) joinClause.getConditionExpression().accept(this, currentEnv).first;
+
+        JoinClause newJoinClause = new JoinClause(joinClause.getJoinType(), newRightExpr, newRightVar, newRightPosVar,
+                conditionExpr);
+        return new Pair<ILangExpression, VariableSubstitutionEnvironment>(newJoinClause, currentEnv);
+    }
+
+    @Override
+    public Pair<ILangExpression, VariableSubstitutionEnvironment> visit(NestClause nestClause,
+            VariableSubstitutionEnvironment env) throws AsterixException {
+        VariableExpr rightVar = nestClause.getRightVariable();
+        VariableExpr newRightVar = generateNewVariable(context, rightVar);
+        VariableExpr newRightPosVar = nestClause.hasPositionalVariable()
+                ? generateNewVariable(context, nestClause.getPositionalVariable()) : null;
+
+        // Visits the right expression.
+        Expression rightExpr = (Expression) nestClause.getRightExpression().accept(this, env).first;
+
+        // Visits the condition.
+        VariableSubstitutionEnvironment currentEnv = new VariableSubstitutionEnvironment(env);
+        currentEnv.removeSubstitution(newRightVar);
+        if (newRightPosVar != null) {
+            currentEnv.removeSubstitution(newRightPosVar);
+        }
+        // The condition can refer to the newRightVar and newRightPosVar.
+        Expression conditionExpr = (Expression) nestClause.getConditionExpression().accept(this, currentEnv).first;
+
+        NestClause newJoinClause = new NestClause(nestClause.getJoinType(), rightExpr, newRightVar, newRightPosVar,
+                conditionExpr);
+        return new Pair<ILangExpression, VariableSubstitutionEnvironment>(newJoinClause, currentEnv);
+    }
+
+    @Override
+    public Pair<ILangExpression, VariableSubstitutionEnvironment> visit(UnnestClause unnestClause,
+            VariableSubstitutionEnvironment env) throws AsterixException {
+        VariableExpr rightVar = unnestClause.getRightVariable();
+        VariableExpr newRightVar = generateNewVariable(context, rightVar);
+        VariableExpr newRightPosVar = unnestClause.hasPositionalVariable()
+                ? generateNewVariable(context, unnestClause.getPositionalVariable()) : null;
+
+        // Visits the right expression.
+        Expression rightExpr = (Expression) unnestClause.getRightExpression().accept(this, env).first;
+
+        // Visits the condition.
+        VariableSubstitutionEnvironment currentEnv = new VariableSubstitutionEnvironment(env);
+        currentEnv.removeSubstitution(newRightVar);
+        if (newRightPosVar != null) {
+            currentEnv.removeSubstitution(newRightPosVar);
+        }
+        // The condition can refer to the newRightVar and newRightPosVar.
+        UnnestClause newJoinClause = new UnnestClause(unnestClause.getJoinType(), rightExpr, newRightVar,
+                newRightPosVar);
+        return new Pair<ILangExpression, VariableSubstitutionEnvironment>(newJoinClause, currentEnv);
+    }
+
+    @Override
+    public Pair<ILangExpression, VariableSubstitutionEnvironment> visit(Projection projection,
+            VariableSubstitutionEnvironment env) throws AsterixException {
+        Projection newProjection = (Projection) projection.getExpression().accept(this, env).first;
+        return new Pair<ILangExpression, VariableSubstitutionEnvironment>(newProjection, env);
+    }
+
+    @Override
+    public Pair<ILangExpression, VariableSubstitutionEnvironment> visit(SelectBlock selectBlock,
+            VariableSubstitutionEnvironment env) throws AsterixException {
+        Pair<ILangExpression, VariableSubstitutionEnvironment> newFrom = null;
+        Pair<ILangExpression, VariableSubstitutionEnvironment> newLet = null;
+        Pair<ILangExpression, VariableSubstitutionEnvironment> newWhere = null;
+        Pair<ILangExpression, VariableSubstitutionEnvironment> newGroupby = null;
+        Pair<ILangExpression, VariableSubstitutionEnvironment> newHaving = null;
+        Pair<ILangExpression, VariableSubstitutionEnvironment> newSelect = null;
+        List<LetClause> newLetClauses = new ArrayList<LetClause>();
+        List<LetClause> newLetClausesAfterGby = new ArrayList<LetClause>();
+        VariableSubstitutionEnvironment currentEnv = new VariableSubstitutionEnvironment(env);
+
+        if (selectBlock.hasFromClause()) {
+            newFrom = selectBlock.getFromClause().accept(this, currentEnv);
+            currentEnv = newFrom.second;
+        }
+
+        if (selectBlock.hasLetClauses()) {
+            for (LetClause letClause : selectBlock.getLetList()) {
+                newLet = letClause.accept(this, env);
+                currentEnv = newLet.second;
+                newLetClauses.add(letClause);
+            }
+        }
+
+        if (selectBlock.hasWhereClause()) {
+            newWhere = selectBlock.getWhereClause().accept(this, currentEnv);
+            currentEnv = newWhere.second;
+        }
+
+        if (selectBlock.hasGroupbyClause()) {
+            newGroupby = selectBlock.getGroupbyClause().accept(this, currentEnv);
+            currentEnv = newGroupby.second;
+            if (selectBlock.hasLetClausesAfterGroupby()) {
+                for (LetClause letClauseAfterGby : selectBlock.getLetListAfterGroupby()) {
+                    newLet = letClauseAfterGby.accept(this, env);
+                    currentEnv = newLet.second;
+                    newLetClausesAfterGby.add(letClauseAfterGby);
+                }
+            }
+        }
+
+        if (selectBlock.hasHavingClause()) {
+            newHaving = selectBlock.getHavingClause().accept(this, currentEnv);
+            currentEnv = newHaving.second;
+        }
+
+        newSelect = selectBlock.getSelectClause().accept(this, currentEnv);
+        currentEnv = newSelect.second;
+        return new Pair<ILangExpression, VariableSubstitutionEnvironment>(
+                new SelectBlock((SelectClause) newSelect.first, newFrom == null ? null : (FromClause) newFrom.first,
+                        newLetClauses, newWhere == null ? null : (WhereClause) newWhere.first,
+                        newGroupby == null ? null : (GroupbyClause) newGroupby.first, newLetClausesAfterGby,
+                        newHaving == null ? null : (HavingClause) newHaving.first),
+                currentEnv);
+    }
+
+    @Override
+    public Pair<ILangExpression, VariableSubstitutionEnvironment> visit(SelectClause selectClause,
+            VariableSubstitutionEnvironment env) throws AsterixException {
+        boolean distinct = selectClause.distinct();
+        if (selectClause.selectElement()) {
+            Pair<ILangExpression, VariableSubstitutionEnvironment> newSelectElement = selectClause.getSelectElement()
+                    .accept(this, env);
+            return new Pair<ILangExpression, VariableSubstitutionEnvironment>(
+                    new SelectClause((SelectElement) newSelectElement.first, null, distinct), newSelectElement.second);
+        } else {
+            Pair<ILangExpression, VariableSubstitutionEnvironment> newSelectRegular = selectClause.getSelectRegular()
+                    .accept(this, env);
+            return new Pair<ILangExpression, VariableSubstitutionEnvironment>(
+                    new SelectClause(null, (SelectRegular) newSelectRegular.first, distinct), newSelectRegular.second);
+        }
+    }
+
+    @Override
+    public Pair<ILangExpression, VariableSubstitutionEnvironment> visit(SelectElement selectElement,
+            VariableSubstitutionEnvironment env) throws AsterixException {
+        Pair<ILangExpression, VariableSubstitutionEnvironment> newExpr = selectElement.getExpression().accept(this,
+                env);
+        return new Pair<ILangExpression, VariableSubstitutionEnvironment>(new SelectElement((Expression) newExpr.first),
+                newExpr.second);
+    }
+
+    @Override
+    public Pair<ILangExpression, VariableSubstitutionEnvironment> visit(SelectRegular selectRegular,
+            VariableSubstitutionEnvironment env) throws AsterixException {
+        List<Projection> newProjections = new ArrayList<Projection>();
+        for (Projection projection : selectRegular.getProjections()) {
+            newProjections.add((Projection) projection.accept(this, env).first);
+        }
+        return new Pair<ILangExpression, VariableSubstitutionEnvironment>(new SelectRegular(newProjections), env);
+    }
+
+    @Override
+    public Pair<ILangExpression, VariableSubstitutionEnvironment> visit(SelectSetOperation selectSetOperation,
+            VariableSubstitutionEnvironment env) throws AsterixException {
+        SetOperationInput leftInput = selectSetOperation.getLeftInput();
+        SetOperationInput newLeftInput = null;
+
+        // Sets the left input.
+        if (leftInput.selectBlock()) {
+            Pair<ILangExpression, VariableSubstitutionEnvironment> p = leftInput.getSelectBlock().accept(this, env);
+            newLeftInput = new SetOperationInput((SelectBlock) p.first, null);
+        } else {
+            Pair<ILangExpression, VariableSubstitutionEnvironment> p = leftInput.getSubquery().accept(this, env);
+            newLeftInput = new SetOperationInput(null, (SelectExpression) p.first);
+        }
+
+        // Sets the right input
+        List<SetOperationRight> newRightInputs = new ArrayList<SetOperationRight>();
+        for (SetOperationRight right : selectSetOperation.getRightInputs()) {
+            SetOperationInput newRightInput = null;
+            SetOperationInput rightInput = right.getSetOperationRightInput();
+            if (rightInput.selectBlock()) {
+                Pair<ILangExpression, VariableSubstitutionEnvironment> p = rightInput.getSelectBlock().accept(this,
+                        env);
+                newRightInput = new SetOperationInput((SelectBlock) p.first, null);
+            } else {
+                Pair<ILangExpression, VariableSubstitutionEnvironment> p = rightInput.getSubquery().accept(this, env);
+                newRightInput = new SetOperationInput(null, (SelectExpression) p.first);
+            }
+            newRightInputs.add(new SetOperationRight(right.getSetOpType(), right.setSemantics(), newRightInput));
+        }
+        SelectSetOperation newSelectSetOperation = new SelectSetOperation(newLeftInput, newRightInputs);
+        return new Pair<ILangExpression, VariableSubstitutionEnvironment>(newSelectSetOperation, env);
+    }
+
+    @Override
+    public Pair<ILangExpression, VariableSubstitutionEnvironment> visit(SelectExpression selectExpression,
+            VariableSubstitutionEnvironment env) throws AsterixException {
+        boolean subquery = selectExpression.isSubquery();
+        List<LetClause> newLetList = new ArrayList<LetClause>();
+        SelectSetOperation newSelectSetOperation = null;
+        OrderbyClause newOrderbyClause = null;
+        LimitClause newLimitClause = null;
+
+        VariableSubstitutionEnvironment currentEnv = env;
+        Pair<ILangExpression, VariableSubstitutionEnvironment> p = null;
+        if (selectExpression.hasLetClauses()) {
+            for (LetClause letClause : selectExpression.getLetList()) {
+                p = letClause.accept(this, currentEnv);
+                newLetList.add(letClause);
+                currentEnv = p.second;
+            }
+        }
+
+        p = selectExpression.getSelectSetOperation().accept(this, env);
+        newSelectSetOperation = (SelectSetOperation) p.first;
+        currentEnv = p.second;
+
+        if (selectExpression.hasOrderby()) {
+            p = selectExpression.getOrderbyClause().accept(this, env);
+            newOrderbyClause = (OrderbyClause) p.first;
+            currentEnv = p.second;
+        }
+
+        if (selectExpression.hasLimit()) {
+            p = selectExpression.getLimitClause().accept(this, env);
+            newLimitClause = (LimitClause) p.first;
+            currentEnv = p.second;
+        }
+        return new Pair<ILangExpression, VariableSubstitutionEnvironment>(
+                new SelectExpression(newLetList, newSelectSetOperation, newOrderbyClause, newLimitClause, subquery),
+                currentEnv);
+    }
+
+    @Override
+    public Pair<ILangExpression, VariableSubstitutionEnvironment> visit(HavingClause havingClause,
+            VariableSubstitutionEnvironment env) throws AsterixException {
+        Pair<ILangExpression, VariableSubstitutionEnvironment> p = havingClause.getFilterExpression().accept(this, env);
+        HavingClause newHavingClause = new HavingClause((Expression) p.first);
+        return new Pair<ILangExpression, VariableSubstitutionEnvironment>(newHavingClause, p.second);
+    }
+
+}
\ No newline at end of file
diff --git a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppFormatPrintVisitor.java b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppFormatPrintVisitor.java
new file mode 100644
index 0000000..4a583c2
--- /dev/null
+++ b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppFormatPrintVisitor.java
@@ -0,0 +1,300 @@
+/*
+ * 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.io.PrintWriter;
+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.GroupbyClause;
+import org.apache.asterix.lang.common.clause.LetClause;
+import org.apache.asterix.lang.common.expression.GbyVariableExpressionPair;
+import org.apache.asterix.lang.common.statement.InsertStatement;
+import org.apache.asterix.lang.common.visitor.FormatPrintVisitor;
+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.visitor.base.ISqlppVisitor;
+
+public class SqlppFormatPrintVisitor extends FormatPrintVisitor implements ISqlppVisitor<Void, Integer> {
+
+    private final PrintWriter out;
+
+    public SqlppFormatPrintVisitor() {
+        this(new PrintWriter(System.out));
+    }
+
+    public SqlppFormatPrintVisitor(PrintWriter out) {
+        super(out);
+        this.out = out;
+
+        // Initialize symbols
+        dataverseSymbol = " database ";
+        datasetSymbol = " table ";
+        assignSymbol = "=";
+    }
+
+    @Override
+    public Void visit(FromClause fromClause, Integer step) throws AsterixException {
+        out.print(skip(step) + "from ");
+        int index = 0;
+        for (FromTerm fromTerm : fromClause.getFromTerms()) {
+            if (index > 0) {
+                out.print(COMMA + "\n" + skip(step + 2));
+            }
+            fromTerm.accept(this, step + 2);
+            ++index;
+        }
+        out.println();
+        return null;
+    }
+
+    @Override
+    public Void visit(FromTerm fromTerm, Integer step) throws AsterixException {
+        fromTerm.getLeftExpression().accept(this, step + 2);
+        out.print(" as ");
+        fromTerm.getLeftVariable().accept(this, step + 2);
+        if (fromTerm.hasPositionalVariable()) {
+            out.print(" at ");
+            fromTerm.getPositionalVariable().accept(this, step + 2);
+        }
+        if (fromTerm.hasCorrelateClauses()) {
+            for (AbstractBinaryCorrelateClause correlateClause : fromTerm.getCorrelateClauses()) {
+                correlateClause.accept(this, step);
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public Void visit(JoinClause joinClause, Integer step) throws AsterixException {
+        out.print(joinClause.getJoinType());
+        joinClause.getRightExpression().accept(this, step + 2);
+        out.print(" as ");
+        joinClause.getRightVariable().accept(this, step + 2);
+        if (joinClause.hasPositionalVariable()) {
+            out.print(" at ");
+            joinClause.getPositionalVariable().accept(this, step + 2);
+        }
+        joinClause.getConditionExpression().accept(this, step + 2);
+        return null;
+    }
+
+    @Override
+    public Void visit(NestClause nestClause, Integer step) throws AsterixException {
+        out.print(nestClause.getJoinType());
+        nestClause.getRightExpression().accept(this, step + 2);
+        out.println(skip(step + 1) + " as ");
+        nestClause.getRightVariable().accept(this, step + 2);
+        if (nestClause.hasPositionalVariable()) {
+            out.println(skip(step + 1) + " at ");
+            nestClause.getPositionalVariable().accept(this, step + 2);
+        }
+        nestClause.getConditionExpression().accept(this, step + 2);
+        return null;
+    }
+
+    @Override
+    public Void visit(Projection projection, Integer step) throws AsterixException {
+        projection.getExpression().accept(this, step);
+        out.print(" as " + projection.getName());
+        return null;
+    }
+
+    @Override
+    public Void visit(SelectBlock selectBlock, Integer step) throws AsterixException {
+        selectBlock.getSelectClause().accept(this, step);
+        if (selectBlock.hasFromClause()) {
+            selectBlock.getFromClause().accept(this, step);
+        }
+        if (selectBlock.hasLetClauses()) {
+            for (LetClause letClause : selectBlock.getLetList()) {
+                letClause.accept(this, step);
+            }
+        }
+        if (selectBlock.hasWhereClause()) {
+            selectBlock.getWhereClause().accept(this, step);
+        }
+        if (selectBlock.hasGroupbyClause()) {
+            selectBlock.getGroupbyClause().accept(this, step);
+            if (selectBlock.hasLetClausesAfterGroupby()) {
+                for (LetClause letClause : selectBlock.getLetListAfterGroupby()) {
+                    letClause.accept(this, step);
+                }
+            }
+        }
+        if (selectBlock.hasHavingClause()) {
+            selectBlock.getHavingClause().accept(this, step);
+        }
+        return null;
+    }
+
+    @Override
+    public Void visit(SelectClause selectClause, Integer step) throws AsterixException {
+        if (selectClause.selectRegular()) {
+            selectClause.getSelectRegular().accept(this, step);
+        }
+        if (selectClause.selectElement()) {
+            selectClause.getSelectElement().accept(this, step);
+        }
+        out.println();
+        return null;
+    }
+
+    @Override
+    public Void visit(SelectElement selectElement, Integer step) throws AsterixException {
+        out.print("select element ");
+        selectElement.getExpression().accept(this, step);
+        return null;
+    }
+
+    @Override
+    public Void visit(SelectRegular selectRegular, Integer step) throws AsterixException {
+        out.print("select ");
+        int index = 0;
+        for (Projection projection : selectRegular.getProjections()) {
+            if (index > 0) {
+                out.print(COMMA);
+            }
+            projection.accept(this, step);
+            ++index;
+        }
+        return null;
+    }
+
+    @Override
+    public Void visit(SelectSetOperation selectSetOperation, Integer step) throws AsterixException {
+        selectSetOperation.getLeftInput().accept(this, step);
+        if (selectSetOperation.hasRightInputs()) {
+            for (SetOperationRight right : selectSetOperation.getRightInputs()) {
+                String all = right.setSemantics() ? " " : " all ";
+                out.print(right.getSetOpType() + all);
+                right.getSetOperationRightInput().accept(this, step);
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public Void visit(SelectExpression selectStatement, Integer step) throws AsterixException {
+        if (selectStatement.isSubquery()) {
+            out.print("(");
+        }
+        int selectStep = selectStatement.isSubquery() ? step + 2 : step;
+        if (selectStatement.hasLetClauses()) {
+            for (LetClause letClause : selectStatement.getLetList()) {
+                letClause.accept(this, selectStep);
+            }
+        }
+        selectStatement.getSelectSetOperation().accept(this, selectStep);
+        if (selectStatement.hasOrderby()) {
+            selectStatement.getOrderbyClause().accept(this, selectStep);
+        }
+        if (selectStatement.hasLimit()) {
+            selectStatement.getLimitClause().accept(this, selectStep);
+        }
+        if (selectStatement.isSubquery()) {
+            out.print(skip(step) + " )");
+        }
+        return null;
+    }
+
+    @Override
+    public Void visit(UnnestClause unnestClause, Integer step) throws AsterixException {
+        out.print(unnestClause.getJoinType());
+        unnestClause.getRightExpression().accept(this, step + 2);
+        out.print(" as ");
+        unnestClause.getRightVariable().accept(this, step + 2);
+        if (unnestClause.hasPositionalVariable()) {
+            out.print(" at ");
+            unnestClause.getPositionalVariable().accept(this, step + 2);
+        }
+        return null;
+    }
+
+    @Override
+    public Void visit(HavingClause havingClause, Integer step) throws AsterixException {
+        out.print(skip(step) + " having ");
+        havingClause.getFilterExpression().accept(this, step + 2);
+        out.println();
+        return null;
+    }
+
+    @Override
+    public Void visit(GroupbyClause gc, Integer step) throws AsterixException {
+        if (gc.hasHashGroupByHint()) {
+            out.println(skip(step) + "/* +hash */");
+        }
+        out.print(skip(step) + "group by ");
+        printDelimitedGbyExpressions(gc.getGbyPairList(), step + 2);
+        out.println();
+        return null;
+    }
+
+    @Override
+    public Void visit(InsertStatement insert, Integer step) throws AsterixException {
+        out.print(skip(step) + "insert into " + datasetSymbol
+                + generateFullName(insert.getDataverseName(), insert.getDatasetName()) + "\n");
+        insert.getQuery().accept(this, step);
+        out.println(SEMICOLON);
+        return null;
+    }
+
+    @Override
+    public Void visit(LetClause lc, Integer step) throws AsterixException {
+        out.print(skip(step) + "with ");
+        Expression bindingExpr = lc.getBindingExpr();
+        bindingExpr.accept(this, step + 2);
+        out.print(" as ");
+        lc.getVarExpr().accept(this, step + 2);
+        out.println();
+        return null;
+    }
+
+    @Override
+    protected void printDelimitedGbyExpressions(List<GbyVariableExpressionPair> gbyList, int step)
+            throws AsterixException {
+        int gbySize = gbyList.size();
+        int gbyIndex = 0;
+        for (GbyVariableExpressionPair pair : gbyList) {
+            pair.getExpr().accept(this, step);
+            if (pair.getVar() != null) {
+                out.print(" as ");
+                pair.getVar().accept(this, step);
+            }
+            if (++gbyIndex < gbySize) {
+                out.print(COMMA);
+            }
+        }
+    }
+
+}
diff --git a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppInlineUdfsVisitor.java b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppInlineUdfsVisitor.java
new file mode 100644
index 0000000..2931d25
--- /dev/null
+++ b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppInlineUdfsVisitor.java
@@ -0,0 +1,218 @@
+/*
+ * 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.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.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.base.ISqlppVisitor;
+import org.apache.hyracks.algebricks.common.utils.Pair;
+
+public class SqlppInlineUdfsVisitor extends AbstractInlineUdfsVisitor
+        implements ISqlppVisitor<Boolean, List<FunctionDecl>> {
+
+    public SqlppInlineUdfsVisitor(LangRewritingContext context) {
+        super(context, new SqlppCloneAndSubstituteVariablesVisitor(context));
+    }
+
+    @Override
+    protected Expression generateQueryExpression(List<LetClause> letClauses, Expression returnExpr)
+            throws AsterixException {
+        Map<VariableExpr, Expression> varExprMap = extractLetBindingVariableExpressionMappings(letClauses);
+        return (Expression) SqlppVariableSubstitutionUtil.substituteVariable(returnExpr, varExprMap);
+    }
+
+    @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;
+        changed |= fromTerm.getLeftExpression().accept(this, func);
+        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.substituteVariable(lc.getBindingExpr(), varExprMap));
+            varExprMap.put(lc.getVarExpr(), lc.getBindingExpr());
+        }
+        return varExprMap;
+    }
+}
diff --git a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppPrintVisitor.java b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppPrintVisitor.java
new file mode 100644
index 0000000..9938c1e
--- /dev/null
+++ b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppPrintVisitor.java
@@ -0,0 +1,238 @@
+/*
+ * 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.io.PrintWriter;
+
+import org.apache.asterix.common.exceptions.AsterixException;
+import org.apache.asterix.lang.common.clause.LetClause;
+import org.apache.asterix.lang.common.visitor.QueryPrintVisitor;
+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.visitor.base.ISqlppVisitor;
+
+public class SqlppPrintVisitor extends QueryPrintVisitor implements ISqlppVisitor<Void, Integer> {
+
+    private final PrintWriter out;
+
+    public SqlppPrintVisitor() {
+        super();
+        out = new PrintWriter(System.out);
+    }
+
+    public SqlppPrintVisitor(PrintWriter out) {
+        super(out);
+        this.out = out;
+    }
+
+    @Override
+    public Void visit(FromClause fromClause, Integer step) throws AsterixException {
+        out.print(skip(step) + "FROM [");
+        int index = 0;
+        for (FromTerm fromTerm : fromClause.getFromTerms()) {
+            if (index > 0) {
+                out.println(",");
+            }
+            fromTerm.accept(this, step + 1);
+            ++index;
+        }
+        out.println(skip(step) + "]");
+        return null;
+    }
+
+    @Override
+    public Void visit(FromTerm fromTerm, Integer step) throws AsterixException {
+        fromTerm.getLeftExpression().accept(this, step);
+        out.println(skip(step) + "AS");
+        fromTerm.getLeftVariable().accept(this, step);
+        if (fromTerm.hasPositionalVariable()) {
+            out.println(skip(step) + "AT");
+            fromTerm.getPositionalVariable().accept(this, step);
+        }
+        if (fromTerm.hasCorrelateClauses()) {
+            for (AbstractBinaryCorrelateClause correlateClause : fromTerm.getCorrelateClauses()) {
+                correlateClause.accept(this, step);
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public Void visit(JoinClause joinClause, Integer step) throws AsterixException {
+        out.println(skip(step) + joinClause.getJoinType() + " JOIN");
+        joinClause.getRightExpression().accept(this, step + 1);
+        out.println(skip(step + 1) + "AS");
+        joinClause.getRightVariable().accept(this, step + 1);
+        if (joinClause.hasPositionalVariable()) {
+            out.println(skip(step + 1) + "AT");
+            joinClause.getPositionalVariable().accept(this, step + 1);
+        }
+        joinClause.getConditionExpression().accept(this, step + 1);
+        return null;
+    }
+
+    @Override
+    public Void visit(NestClause nestClause, Integer step) throws AsterixException {
+        out.println(skip(step) + nestClause.getJoinType() + " NEST");
+        nestClause.getRightExpression().accept(this, step + 1);
+        out.println(skip(step + 1) + "AS");
+        nestClause.getRightVariable().accept(this, step + 1);
+        if (nestClause.hasPositionalVariable()) {
+            out.println(skip(step + 1) + "AT");
+            nestClause.getPositionalVariable().accept(this, step + 1);
+        }
+        nestClause.getConditionExpression().accept(this, step + 1);
+        return null;
+    }
+
+    @Override
+    public Void visit(Projection projection, Integer step) throws AsterixException {
+        projection.getExpression().accept(this, step);
+        out.println(skip(step) + projection.getName());
+        return null;
+    }
+
+    @Override
+    public Void visit(SelectBlock selectBlock, Integer step) throws AsterixException {
+        selectBlock.getSelectClause().accept(this, step);
+        if (selectBlock.hasFromClause()) {
+            selectBlock.getFromClause().accept(this, step);
+        }
+        if (selectBlock.hasLetClauses()) {
+            for (LetClause letClause : selectBlock.getLetList()) {
+                letClause.accept(this, step);
+            }
+        }
+        if (selectBlock.hasWhereClause()) {
+            selectBlock.getWhereClause().accept(this, step);
+        }
+        if (selectBlock.hasGroupbyClause()) {
+            selectBlock.getGroupbyClause().accept(this, step);
+            if (selectBlock.hasLetClausesAfterGroupby()) {
+                for (LetClause letClause : selectBlock.getLetListAfterGroupby()) {
+                    letClause.accept(this, step);
+                }
+            }
+        }
+        if (selectBlock.hasHavingClause()) {
+            selectBlock.getHavingClause().accept(this, step);
+        }
+        return null;
+    }
+
+    @Override
+    public Void visit(SelectClause selectClause, Integer step) throws AsterixException {
+        if (selectClause.selectRegular()) {
+            selectClause.getSelectRegular().accept(this, step);
+        }
+        if (selectClause.selectElement()) {
+            selectClause.getSelectElement().accept(this, step);
+        }
+        return null;
+    }
+
+    @Override
+    public Void visit(SelectElement selectElement, Integer step) throws AsterixException {
+        out.println(skip(step) + "SELECT ELEMENT [");
+        selectElement.getExpression().accept(this, step);
+        out.println(skip(step) + "]");
+        return null;
+    }
+
+    @Override
+    public Void visit(SelectRegular selectRegular, Integer step) throws AsterixException {
+        out.println(skip(step) + "SELECT [");
+        for (Projection projection : selectRegular.getProjections()) {
+            projection.accept(this, step);
+        }
+        out.println(skip(step) + "]");
+        return null;
+    }
+
+    @Override
+    public Void visit(SelectSetOperation selectSetOperation, Integer step) throws AsterixException {
+        selectSetOperation.getLeftInput().accept(this, step);
+        if (selectSetOperation.hasRightInputs()) {
+            for (SetOperationRight right : selectSetOperation.getRightInputs()) {
+                String all = right.setSemantics() ? " ALL " : "";
+                out.println(skip(step) + right.getSetOpType() + all);
+                right.getSetOperationRightInput().accept(this, step + 1);
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public Void visit(SelectExpression selectStatement, Integer step) throws AsterixException {
+        if (selectStatement.isSubquery()) {
+            out.println(skip(step) + "(");
+        }
+        int selectStep = selectStatement.isSubquery() ? step + 1 : step;
+        if (selectStatement.hasLetClauses()) {
+            for (LetClause letClause : selectStatement.getLetList()) {
+                letClause.accept(this, selectStep);
+            }
+        }
+        selectStatement.getSelectSetOperation().accept(this, selectStep);
+        if (selectStatement.hasOrderby()) {
+            selectStatement.getOrderbyClause().accept(this, selectStep);
+        }
+        if (selectStatement.hasLimit()) {
+            selectStatement.getLimitClause().accept(this, selectStep);
+        }
+        if (selectStatement.isSubquery()) {
+            out.println(skip(step) + ")");
+        }
+        return null;
+    }
+
+    @Override
+    public Void visit(UnnestClause unnestClause, Integer step) throws AsterixException {
+        out.println(skip(step) + unnestClause.getJoinType() + " UNNEST");
+        unnestClause.getRightExpression().accept(this, step + 1);
+        out.println(skip(step + 1) + " AS");
+        unnestClause.getRightVariable().accept(this, step + 1);
+        if (unnestClause.hasPositionalVariable()) {
+            out.println(skip(step + 1) + " AT");
+            unnestClause.getPositionalVariable().accept(this, step + 1);
+        }
+        return null;
+    }
+
+    @Override
+    public Void visit(HavingClause havingClause, Integer step) throws AsterixException {
+        out.println(skip(step) + " HAVING");
+        havingClause.getFilterExpression().accept(this, step + 1);
+        return null;
+    }
+
+}
diff --git a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppSubstituteVariablesVisitor.java b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppSubstituteVariablesVisitor.java
new file mode 100644
index 0000000..b00afe0
--- /dev/null
+++ b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppSubstituteVariablesVisitor.java
@@ -0,0 +1,45 @@
+/*
+ * 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 org.apache.asterix.lang.common.base.Expression;
+import org.apache.asterix.lang.common.expression.VariableExpr;
+import org.apache.asterix.lang.common.rewrites.LangRewritingContext;
+import org.apache.asterix.lang.common.rewrites.VariableSubstitutionEnvironment;
+
+public class SqlppSubstituteVariablesVisitor extends SqlppCloneAndSubstituteVariablesVisitor {
+
+    public SqlppSubstituteVariablesVisitor(LangRewritingContext context) {
+        super(context);
+    }
+
+    @Override
+    public Expression rewriteVariableExpr(VariableExpr expr, VariableSubstitutionEnvironment env) {
+        if (env.constainsOldVar(expr)) {
+            return env.findSubstituion(expr);
+        }
+        return expr;
+    }
+
+    @Override
+    public VariableExpr generateNewVariable(LangRewritingContext context, VariableExpr varExpr) {
+        return varExpr;
+    }
+
+}
\ No newline at end of file
diff --git a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/VariableCheckAndRewriteVisitor.java b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/VariableCheckAndRewriteVisitor.java
new file mode 100644
index 0000000..9dd223d
--- /dev/null
+++ b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/VariableCheckAndRewriteVisitor.java
@@ -0,0 +1,481 @@
+/*
+ * 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.Iterator;
+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.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.context.Scope;
+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.literal.StringLiteral;
+import org.apache.asterix.lang.common.parser.ScopeChecker;
+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.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.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.SqlppFormatPrintUtil;
+import org.apache.asterix.lang.sqlpp.visitor.base.AbstractSqlppQueryExpressionVisitor;
+import org.apache.asterix.metadata.bootstrap.MetadataConstants;
+
+public class VariableCheckAndRewriteVisitor extends AbstractSqlppQueryExpressionVisitor<Expression, Void> {
+
+    private final ScopeChecker scopeChecker = new ScopeChecker();
+
+    @Override
+    public Expression visit(FromClause fromClause, Void arg) throws AsterixException {
+        scopeChecker.extendCurrentScope();
+        for (FromTerm fromTerm : fromClause.getFromTerms()) {
+            fromTerm.accept(this, arg);
+        }
+        return null;
+    }
+
+    @Override
+    public Expression visit(FromTerm fromTerm, Void arg) throws AsterixException {
+        // Visit the left expression of a from term.
+        fromTerm.setLeftExpression(fromTerm.getLeftExpression().accept(this, arg));
+
+        // Registers the data item variable
+        VariableExpr leftVar = fromTerm.getLeftVariable();
+        scopeChecker.getCurrentScope().addNewVarSymbolToScope(leftVar.getVar());
+
+        // Registers the positional variable
+        if (fromTerm.hasPositionalVariable()) {
+            VariableExpr posVar = fromTerm.getPositionalVariable();
+            scopeChecker.getCurrentScope().addNewVarSymbolToScope(posVar.getVar());
+        }
+
+        // Visits join/unnest/nest clauses.
+        for (AbstractBinaryCorrelateClause correlateClause : fromTerm.getCorrelateClauses()) {
+            correlateClause.accept(this, arg);
+        }
+        return null;
+    }
+
+    @Override
+    public Expression visit(JoinClause joinClause, Void arg) throws AsterixException {
+        // NOTE: the two join branches cannot be correlated, instead of checking
+        // the correlation here,
+        // we defer the check to the query optimizer.
+        joinClause.setRightExpression(joinClause.getRightExpression().accept(this, arg));
+
+        // Registers the data item variable.
+        VariableExpr rightVar = joinClause.getRightVariable();
+        scopeChecker.getCurrentScope().addNewVarSymbolToScope(rightVar.getVar());
+
+        if (joinClause.hasPositionalVariable()) {
+            // Registers the positional variable.
+            VariableExpr posVar = joinClause.getPositionalVariable();
+            scopeChecker.getCurrentScope().addNewVarSymbolToScope(posVar.getVar());
+        }
+
+        // The condition expression can refer to the just registered variables
+        // for the right branch.
+        joinClause.setConditionExpression(joinClause.getConditionExpression().accept(this, arg));
+        return null;
+    }
+
+    @Override
+    public Expression visit(NestClause nestClause, Void arg) throws AsterixException {
+        // NOTE: the two branches of a NEST cannot be correlated, instead of
+        // checking the correlation here, we defer the check to the query
+        // optimizer.
+        nestClause.setRightExpression(nestClause.getRightExpression().accept(this, arg));
+
+        // Registers the data item variable.
+        VariableExpr rightVar = nestClause.getRightVariable();
+        scopeChecker.getCurrentScope().addNewVarSymbolToScope(rightVar.getVar());
+
+        if (nestClause.hasPositionalVariable()) {
+            // Registers the positional variable.
+            VariableExpr posVar = nestClause.getPositionalVariable();
+            scopeChecker.getCurrentScope().addNewVarSymbolToScope(posVar.getVar());
+        }
+
+        // The condition expression can refer to the just registered variables
+        // for the right branch.
+        nestClause.setConditionExpression(nestClause.getConditionExpression().accept(this, arg));
+        return null;
+    }
+
+    @Override
+    public Expression visit(UnnestClause unnestClause, Void arg) throws AsterixException {
+        unnestClause.setRightExpression(unnestClause.getRightExpression().accept(this, arg));
+
+        // register the data item variable
+        VariableExpr rightVar = unnestClause.getRightVariable();
+        scopeChecker.getCurrentScope().addNewVarSymbolToScope(rightVar.getVar());
+
+        if (unnestClause.hasPositionalVariable()) {
+            // register the positional variable
+            VariableExpr posVar = unnestClause.getPositionalVariable();
+            scopeChecker.getCurrentScope().addNewVarSymbolToScope(posVar.getVar());
+        }
+        return null;
+    }
+
+    @Override
+    public Expression visit(Projection projection, Void arg) throws AsterixException {
+        projection.setExpression(projection.getExpression().accept(this, arg));
+        return null;
+    }
+
+    @Override
+    public Expression visit(SelectBlock selectBlock, Void arg) throws AsterixException {
+        // Traverses the select block in the order of "from", "let"s, "where",
+        // "group by", "let"s, "having" and "select".
+        if (selectBlock.hasFromClause()) {
+            selectBlock.getFromClause().accept(this, arg);
+        }
+        if (selectBlock.hasLetClauses()) {
+            List<LetClause> letList = selectBlock.getLetList();
+            for (LetClause letClause : letList) {
+                letClause.accept(this, arg);
+            }
+        }
+        if (selectBlock.hasWhereClause()) {
+            selectBlock.getWhereClause().accept(this, arg);
+        }
+        if (selectBlock.hasGroupbyClause()) {
+            selectBlock.getGroupbyClause().accept(this, arg);
+        }
+        if (selectBlock.hasLetClausesAfterGroupby()) {
+            List<LetClause> letListAfterGby = selectBlock.getLetListAfterGroupby();
+            for (LetClause letClauseAfterGby : letListAfterGby) {
+                letClauseAfterGby.accept(this, arg);
+            }
+        }
+        if (selectBlock.hasHavingClause()) {
+            selectBlock.getHavingClause().accept(this, arg);
+        }
+        selectBlock.getSelectClause().accept(this, arg);
+        return null;
+    }
+
+    @Override
+    public Expression visit(SelectClause selectClause, Void arg) throws AsterixException {
+        if (selectClause.selectElement()) {
+            selectClause.getSelectElement().accept(this, arg);
+        }
+        if (selectClause.selectRegular()) {
+            selectClause.getSelectRegular().accept(this, arg);
+        }
+        return null;
+    }
+
+    @Override
+    public Expression visit(SelectElement selectElement, Void arg) throws AsterixException {
+        selectElement.setExpression(selectElement.getExpression().accept(this, arg));
+        return null;
+    }
+
+    @Override
+    public Expression visit(SelectRegular selectRegular, Void arg) throws AsterixException {
+        for (Projection projection : selectRegular.getProjections()) {
+            projection.accept(this, arg);
+        }
+        return null;
+    }
+
+    @Override
+    public Expression visit(SelectSetOperation selectSetOperation, Void arg) throws AsterixException {
+        selectSetOperation.getLeftInput().accept(this, arg);
+        for (SetOperationRight right : selectSetOperation.getRightInputs()) {
+            scopeChecker.createNewScope();
+            right.getSetOperationRightInput().accept(this, arg);
+        }
+        return null;
+    }
+
+    @Override
+    public Expression visit(HavingClause havingClause, Void arg) throws AsterixException {
+        havingClause.setFilterExpression(havingClause.getFilterExpression().accept(this, arg));
+        return null;
+    }
+
+    @Override
+    public Expression visit(Query q, Void arg) throws AsterixException {
+        q.setBody(q.getBody().accept(this, arg));
+        return null;
+    }
+
+    @Override
+    public Expression visit(FunctionDecl fd, Void arg) throws AsterixException {
+        scopeChecker.createNewScope();
+        fd.setFuncBody(fd.getFuncBody().accept(this, arg));
+        scopeChecker.removeCurrentScope();
+        return null;
+    }
+
+    @Override
+    public Expression visit(WhereClause whereClause, Void arg) throws AsterixException {
+        whereClause.setWhereExpr(whereClause.getWhereExpr().accept(this, arg));
+        return null;
+    }
+
+    @Override
+    public Expression visit(OrderbyClause oc, Void arg) throws AsterixException {
+        List<Expression> newOrderbyList = new ArrayList<Expression>();
+        for (Expression orderExpr : oc.getOrderbyList()) {
+            newOrderbyList.add(orderExpr.accept(this, arg));
+        }
+        oc.setOrderbyList(newOrderbyList);
+        return null;
+    }
+
+    @Override
+    public Expression visit(GroupbyClause gc, Void arg) throws AsterixException {
+        Scope newScope = scopeChecker.extendCurrentScopeNoPush(true);
+        // Puts all group-by variables into the symbol set of the new scope.
+        for (GbyVariableExpressionPair gbyVarExpr : gc.getGbyPairList()) {
+            gbyVarExpr.setExpr(gbyVarExpr.getExpr().accept(this, arg));
+            VariableExpr gbyVar = gbyVarExpr.getVar();
+            if (gbyVar != null) {
+                newScope.addNewVarSymbolToScope(gbyVarExpr.getVar().getVar());
+            }
+        }
+        // Puts all live variables into withVarList.
+        List<VariableExpr> withVarList = new ArrayList<VariableExpr>();
+        Iterator<Identifier> varIterator = scopeChecker.getCurrentScope().liveSymbols();
+        while (varIterator.hasNext()) {
+            Identifier ident = varIterator.next();
+            VariableExpr varExpr = new VariableExpr();
+            if (ident instanceof VarIdentifier) {
+                varExpr.setIsNewVar(false);
+                varExpr.setVar((VarIdentifier) ident);
+                withVarList.add(varExpr);
+                newScope.addNewVarSymbolToScope((VarIdentifier) ident);
+            }
+        }
+        gc.setWithVarList(withVarList);
+        scopeChecker.replaceCurrentScope(newScope);
+        return null;
+    }
+
+    @Override
+    public Expression visit(LimitClause limitClause, Void arg) throws AsterixException {
+        scopeChecker.pushForbiddenScope(scopeChecker.getCurrentScope());
+        limitClause.setLimitExpr(limitClause.getLimitExpr().accept(this, arg));
+        scopeChecker.popForbiddenScope();
+        return null;
+    }
+
+    @Override
+    public Expression visit(LetClause letClause, Void arg) throws AsterixException {
+        scopeChecker.extendCurrentScope();
+        scopeChecker.getCurrentScope().addNewVarSymbolToScope(letClause.getVarExpr().getVar());
+        letClause.setBindingExpr(letClause.getBindingExpr().accept(this, arg));
+        return null;
+    }
+
+    @Override
+    public Expression visit(SelectExpression selectExpression, Void arg) throws AsterixException {
+        Scope scopeBeforeSelectExpression = scopeChecker.getCurrentScope();
+        scopeChecker.createNewScope();
+
+        // visit let list
+        if (selectExpression.hasLetClauses()) {
+            for (LetClause letClause : selectExpression.getLetList()) {
+                letClause.accept(this, arg);
+            }
+        }
+
+        // visit the main select.
+        selectExpression.getSelectSetOperation().accept(this, arg);
+
+        // visit order by
+        if (selectExpression.hasOrderby()) {
+            for (Expression orderExpr : selectExpression.getOrderbyClause().getOrderbyList()) {
+                orderExpr.accept(this, arg);
+            }
+        }
+
+        // visit limit
+        if (selectExpression.hasLimit()) {
+            selectExpression.getLimitClause().accept(this, arg);
+        }
+
+        // Exit scopes that were entered within this select expression
+        while (scopeChecker.getCurrentScope() != scopeBeforeSelectExpression) {
+            scopeChecker.removeCurrentScope();
+        }
+        return selectExpression;
+    }
+
+    @Override
+    public Expression visit(LiteralExpr l, Void arg) throws AsterixException {
+        return l;
+    }
+
+    @Override
+    public Expression visit(ListConstructor lc, Void arg) throws AsterixException {
+        List<Expression> newExprList = new ArrayList<Expression>();
+        for (Expression expr : lc.getExprList()) {
+            newExprList.add(expr.accept(this, arg));
+        }
+        lc.setExprList(newExprList);
+        return lc;
+    }
+
+    @Override
+    public Expression visit(RecordConstructor rc, Void arg) throws AsterixException {
+        for (FieldBinding binding : rc.getFbList()) {
+            binding.setLeftExpr(binding.getLeftExpr().accept(this, arg));
+            binding.setRightExpr(binding.getRightExpr().accept(this, arg));
+        }
+        return rc;
+    }
+
+    @Override
+    public Expression visit(OperatorExpr operatorExpr, Void arg) throws AsterixException {
+        List<Expression> newExprList = new ArrayList<Expression>();
+        for (Expression expr : operatorExpr.getExprList()) {
+            newExprList.add(expr.accept(this, arg));
+        }
+        operatorExpr.setExprList(newExprList);
+        return operatorExpr;
+    }
+
+    @Override
+    public Expression visit(IfExpr ifExpr, Void arg) throws AsterixException {
+        ifExpr.setCondExpr(ifExpr.getCondExpr().accept(this, arg));
+        ifExpr.setThenExpr(ifExpr.getThenExpr().accept(this, arg));
+        ifExpr.setElseExpr(ifExpr.getElseExpr().accept(this, arg));
+        return ifExpr;
+    }
+
+    @Override
+    public Expression visit(QuantifiedExpression qe, Void arg) throws AsterixException {
+        scopeChecker.createNewScope();
+        for (QuantifiedPair pair : qe.getQuantifiedList()) {
+            scopeChecker.getCurrentScope().addNewVarSymbolToScope(pair.getVarExpr().getVar());
+            pair.setExpr(pair.getExpr().accept(this, arg));
+        }
+        qe.setSatisfiesExpr(qe.getSatisfiesExpr().accept(this, arg));
+        scopeChecker.removeCurrentScope();
+        return qe;
+    }
+
+    @Override
+    public Expression visit(CallExpr callExpr, Void arg) throws AsterixException {
+        List<Expression> newExprList = new ArrayList<Expression>();
+        for (Expression expr : callExpr.getExprList()) {
+            newExprList.add(expr.accept(this, arg));
+        }
+        callExpr.setExprList(newExprList);
+        return callExpr;
+    }
+
+    @Override
+    public Expression visit(VariableExpr varExpr, Void 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;
+        }
+    }
+
+    @Override
+    public Expression visit(UnaryExpr u, Void arg) throws AsterixException {
+        u.setExpr(u.getExpr().accept(this, arg));
+        return u;
+    }
+
+    @Override
+    public Expression visit(FieldAccessor fa, Void arg) throws AsterixException {
+        fa.setExpr(fa.getExpr().accept(this, arg));
+        return fa;
+    }
+
+    @Override
+    public Expression visit(IndexAccessor ia, Void arg) throws AsterixException {
+        ia.setExpr(ia.getExpr().accept(this, arg));
+        if (ia.getIndexExpr() != null) {
+            ia.setIndexExpr(ia.getExpr());
+        }
+        return ia;
+    }
+
+    // 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(Expression expr) throws AsterixException {
+        String funcName = "dataset";
+        String dataverse = MetadataConstants.METADATA_DATAVERSE_NAME;
+        FunctionSignature signature = new FunctionSignature(dataverse, funcName, 1);
+        List<Expression> argList = new ArrayList<Expression>();
+        argList.add(new LiteralExpr(new StringLiteral(SqlppFormatPrintUtil.toString(expr))));
+        return new CallExpr(signature, argList);
+    }
+}
diff --git a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppQueryExpressionVisitor.java b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppQueryExpressionVisitor.java
new file mode 100644
index 0000000..dda25eb
--- /dev/null
+++ b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppQueryExpressionVisitor.java
@@ -0,0 +1,26 @@
+/*
+ * 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.base;
+
+import org.apache.asterix.lang.common.visitor.base.AbstractQueryExpressionVisitor;
+
+public abstract class AbstractSqlppQueryExpressionVisitor<R, T> extends AbstractQueryExpressionVisitor<R, T>
+        implements ISqlppVisitor<R, T> {
+
+}
diff --git a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/ISqlppVisitor.java b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/ISqlppVisitor.java
new file mode 100644
index 0000000..538d9c3
--- /dev/null
+++ b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/ISqlppVisitor.java
@@ -0,0 +1,64 @@
+/*
+ * 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.base;
+
+import org.apache.asterix.common.exceptions.AsterixException;
+import org.apache.asterix.lang.common.visitor.base.ILangVisitor;
+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;
+
+public interface ISqlppVisitor<R, T> extends ILangVisitor<R, T> {
+
+    R visit(FromClause fromClause, T arg) throws AsterixException;
+
+    R visit(FromTerm fromTerm, T arg) throws AsterixException;
+
+    R visit(JoinClause joinClause, T arg) throws AsterixException;
+
+    R visit(NestClause nestClause, T arg) throws AsterixException;
+
+    R visit(Projection projection, T arg) throws AsterixException;
+
+    R visit(SelectBlock selectBlock, T arg) throws AsterixException;
+
+    R visit(SelectClause selectClause, T arg) throws AsterixException;
+
+    R visit(SelectElement selectElement, T arg) throws AsterixException;
+
+    R visit(SelectRegular selectRegular, T arg) throws AsterixException;
+
+    R visit(SelectSetOperation selectSetOperation, T arg) throws AsterixException;
+
+    R visit(SelectExpression selectStatement, T arg) throws AsterixException;
+
+    R visit(UnnestClause unnestClause, T arg) throws AsterixException;
+
+    R visit(HavingClause havingClause, T arg) throws AsterixException;
+}
diff --git a/asterix-lang-sqlpp/src/main/javacc/SQLPP.html b/asterix-lang-sqlpp/src/main/javacc/SQLPP.html
new file mode 100644
index 0000000..4bb64d2
--- /dev/null
+++ b/asterix-lang-sqlpp/src/main/javacc/SQLPP.html
@@ -0,0 +1,895 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<HTML>
+<HEAD>
+<TITLE>BNF for SQLPP.jj</TITLE>
+</HEAD>
+<BODY>
+<H1 ALIGN=CENTER>BNF for SQLPP.jj</H1>
+<H2 ALIGN=CENTER>TOKENS</H2>
+<TABLE>
+ <!-- Token -->
+ <TR>
+  <TD>
+   <PRE>
+&lt;DEFAULT,IN_DBL_BRACE&gt; TOKEN [IGNORE_CASE] : {
+&lt;ALL: "all"&gt;
+| &lt;AND: "and"&gt;
+| &lt;APPLY: "apply"&gt;
+| &lt;AS: "as"&gt;
+| &lt;ASC: "asc"&gt;
+| &lt;AT: "at"&gt;
+| &lt;AUTOGENERATED: "autogenerated"&gt;
+| &lt;BTREE: "btree"&gt;
+| &lt;BY: "by"&gt;
+| &lt;CASE: "case"&gt;
+| &lt;CLOSED: "closed"&gt;
+| &lt;CREATE: "create"&gt;
+| &lt;COMPACTION: "compaction"&gt;
+| &lt;COMPACT: "compact"&gt;
+| &lt;CONNECT: "connect"&gt;
+| &lt;CORRELATE: "correlate"&gt;
+| &lt;DATASET: "table"&gt;
+| &lt;DATAVERSE: "database"&gt;
+| &lt;DECLARE: "declare"&gt;
+| &lt;DEFINITION: "definition"&gt;
+| &lt;DELETE: "delete"&gt;
+| &lt;DESC: "desc"&gt;
+| &lt;DISCONNECT: "disconnect"&gt;
+| &lt;DISTINCT: "distinct"&gt;
+| &lt;DROP: "drop"&gt;
+| &lt;ELEMENT: "element"&gt;
+| &lt;ELSE: "else"&gt;
+| &lt;ENFORCED: "enforced"&gt;
+| &lt;EVERY: "every"&gt;
+| &lt;EXCEPT: "except"&gt;
+| &lt;EXISTS: "exists"&gt;
+| &lt;EXTERNAL: "external"&gt;
+| &lt;FEED: "feed"&gt;
+| &lt;FILTER: "filter"&gt;
+| &lt;FLATTEN: "flatten"&gt;
+| &lt;FOR: "for"&gt;
+| &lt;FORMAT: "format"&gt;
+| &lt;FROM: "from"&gt;
+| &lt;FULL: "full"&gt;
+| &lt;FUNCTION: "function"&gt;
+| &lt;GROUP: "group"&gt;
+| &lt;HAVING: "having"&gt;
+| &lt;HINTS: "hints"&gt;
+| &lt;IF: "if"&gt;
+| &lt;INTO: "into"&gt;
+| &lt;IN: "in"&gt;
+| &lt;INDEX: "index"&gt;
+| &lt;INGESTION: "ingestion"&gt;
+| &lt;INNER: "inner"&gt;
+| &lt;INSERT: "insert"&gt;
+| &lt;INTERNAL: "internal"&gt;
+| &lt;INTERSECT: "intersect"&gt;
+| &lt;JOIN: "join"&gt;
+| &lt;KEYWORD: "keyword"&gt;
+| &lt;KEY: "key"&gt;
+| &lt;LEFT: "left"&gt;
+| &lt;LETTING: "letting"&gt;
+| &lt;LET: "let"&gt;
+| &lt;LIMIT: "limit"&gt;
+| &lt;LOAD: "load"&gt;
+| &lt;NEST: "nest"&gt;
+| &lt;NODEGROUP: "nodegroup"&gt;
+| &lt;NGRAM: "ngram"&gt;
+| &lt;OFFSET: "offset"&gt;
+| &lt;ON: "on"&gt;
+| &lt;OPEN: "open"&gt;
+| &lt;OR: "or"&gt;
+| &lt;ORDER: "order"&gt;
+| &lt;OUTER: "outer"&gt;
+| &lt;OUTPUT: "output"&gt;
+| &lt;PATH: "path"&gt;
+| &lt;POLICY: "policy"&gt;
+| &lt;PRESORTED: "pre-sorted"&gt;
+| &lt;PRIMARY: "primary"&gt;
+| &lt;RAW: "raw"&gt;
+| &lt;REFRESH: "refresh"&gt;
+| &lt;RETURN: "return"&gt;
+| &lt;RTREE: "rtree"&gt;
+| &lt;RUN: "run"&gt;
+| &lt;SATISFIES: "satisfies"&gt;
+| &lt;SECONDARY: "secondary"&gt;
+| &lt;SELECT: "select"&gt;
+| &lt;SET: "set"&gt;
+| &lt;SOME: "some"&gt;
+| &lt;TEMPORARY: "temporary"&gt;
+| &lt;THEN: "then"&gt;
+| &lt;TYPE: "type"&gt;
+| &lt;TO: "to"&gt;
+| &lt;UNION: "union"&gt;
+| &lt;UNNEST: "unnest"&gt;
+| &lt;VALUE: "value"&gt;
+| &lt;WHEN: "when"&gt;
+| &lt;WHERE: "where"&gt;
+| &lt;WITH: "with"&gt;
+| &lt;WRITE: "write"&gt;
+| &lt;UPDATE: "update"&gt;
+| &lt;USE: "use"&gt;
+| &lt;USING: "using"&gt;
+}
+
+   </PRE>
+  </TD>
+ </TR>
+ <!-- Token -->
+ <TR>
+  <TD>
+   <PRE>
+&lt;DEFAULT,IN_DBL_BRACE&gt; TOKEN : {
+&lt;CARET: "^"&gt;
+| &lt;DIV: "/"&gt;
+| &lt;IDIV: "idiv"&gt;
+| &lt;MINUS: "-"&gt;
+| &lt;MOD: "%"&gt;
+| &lt;MUL: "*"&gt;
+| &lt;PLUS: "+"&gt;
+| &lt;LEFTPAREN: "("&gt;
+| &lt;RIGHTPAREN: ")"&gt;
+| &lt;LEFTBRACKET: "["&gt;
+| &lt;RIGHTBRACKET: "]"&gt;
+| &lt;ATT: "@"&gt;
+| &lt;COLON: ":"&gt;
+| &lt;COMMA: ","&gt;
+| &lt;DOT: "."&gt;
+| &lt;QUES: "?"&gt;
+| &lt;SEMICOLON: ";"&gt;
+| &lt;SHARP: "#"&gt;
+| &lt;LT: "&lt;"&gt;
+| &lt;GT: "&gt;"&gt;
+| &lt;LE: "&lt;="&gt;
+| &lt;GE: "&gt;="&gt;
+| &lt;EQ: "="&gt;
+| &lt;NE: "!="&gt;
+| &lt;SIMILAR: "~="&gt;
+}
+
+   </PRE>
+  </TD>
+ </TR>
+ <!-- Token -->
+ <TR>
+  <TD>
+   <PRE>
+&lt;DEFAULT,IN_DBL_BRACE&gt; TOKEN : {
+&lt;LEFTBRACE: "{"&gt; : DEFAULT
+}
+
+   </PRE>
+  </TD>
+ </TR>
+ <!-- Token -->
+ <TR>
+  <TD>
+   <PRE>
+&lt;DEFAULT&gt; TOKEN : {
+&lt;RIGHTBRACE: "}"&gt; : {
+}
+
+   </PRE>
+  </TD>
+ </TR>
+ <!-- Token -->
+ <TR>
+  <TD>
+   <PRE>
+&lt;DEFAULT,IN_DBL_BRACE&gt; TOKEN : {
+&lt;LEFTDBLBRACE: "{{"&gt; : IN_DBL_BRACE
+}
+
+   </PRE>
+  </TD>
+ </TR>
+ <!-- Token -->
+ <TR>
+  <TD>
+   <PRE>
+&lt;IN_DBL_BRACE&gt; TOKEN : {
+&lt;RIGHTDBLBRACE: "}}"&gt; : {
+}
+
+   </PRE>
+  </TD>
+ </TR>
+ <!-- Token -->
+ <TR>
+  <TD>
+   <PRE>
+&lt;DEFAULT,IN_DBL_BRACE&gt; TOKEN : {
+&lt;INTEGER_LITERAL: (&lt;DIGIT&gt;)+&gt;
+}
+
+   </PRE>
+  </TD>
+ </TR>
+ <!-- Token -->
+ <TR>
+  <TD>
+   <PRE>
+&lt;DEFAULT,IN_DBL_BRACE&gt; TOKEN : {
+&lt;NULL: "null"&gt;
+| &lt;TRUE: "true"&gt;
+| &lt;FALSE: "false"&gt;
+}
+
+   </PRE>
+  </TD>
+ </TR>
+ <!-- Token -->
+ <TR>
+  <TD>
+   <PRE>
+&lt;DEFAULT,IN_DBL_BRACE&gt; TOKEN : {
+&lt;#DIGIT: ["0"-"9"]&gt;
+}
+
+   </PRE>
+  </TD>
+ </TR>
+ <!-- Token -->
+ <TR>
+  <TD>
+   <PRE>
+&lt;DEFAULT,IN_DBL_BRACE&gt; TOKEN : {
+&lt;DOUBLE_LITERAL: &lt;DIGITS&gt; | &lt;DIGITS&gt; ("." &lt;DIGITS&gt;)? | "." &lt;DIGITS&gt;&gt;
+| &lt;FLOAT_LITERAL: &lt;DIGITS&gt; ("f" | "F") | &lt;DIGITS&gt; ("." &lt;DIGITS&gt; ("f" | "F"))? | "." &lt;DIGITS&gt; ("f" | "F")&gt;
+| &lt;DIGITS: (&lt;DIGIT&gt;)+&gt;
+}
+
+   </PRE>
+  </TD>
+ </TR>
+ <!-- Token -->
+ <TR>
+  <TD>
+   <PRE>
+&lt;DEFAULT,IN_DBL_BRACE&gt; TOKEN : {
+&lt;#LETTER: ["A"-"Z","a"-"z"]&gt;
+| &lt;SPECIALCHARS: ["$","_"]&gt;
+}
+
+   </PRE>
+  </TD>
+ </TR>
+ <!-- Token -->
+ <TR>
+  <TD>
+   <PRE>
+&lt;DEFAULT,IN_DBL_BRACE&gt; TOKEN : {
+&lt;QUOTED_STRING: "\"" (&lt;EscapeQuot&gt; | &lt;EscapeBslash&gt; | &lt;EscapeSlash&gt; | &lt;EscapeBspace&gt; | &lt;EscapeFormf&gt; | &lt;EscapeNl&gt; | &lt;EscapeCr&gt; | &lt;EscapeTab&gt; | ~["\"","\\"])* "\""&gt;
+| &lt;STRING_LITERAL: "\'" (&lt;EscapeQuot&gt; | &lt;EscapeApos&gt; | &lt;EscapeBslash&gt; | &lt;EscapeSlash&gt; | &lt;EscapeBspace&gt; | &lt;EscapeFormf&gt; | &lt;EscapeNl&gt; | &lt;EscapeCr&gt; | &lt;EscapeTab&gt; | ~["\'","\\"])* "\'"&gt;
+| &lt;#EscapeQuot: "\\\""&gt;
+| &lt;#EscapeApos: "\\\'"&gt;
+| &lt;#EscapeBslash: "\\\\"&gt;
+| &lt;#EscapeSlash: "\\/"&gt;
+| &lt;#EscapeBspace: "\\b"&gt;
+| &lt;#EscapeFormf: "\\f"&gt;
+| &lt;#EscapeNl: "\\n"&gt;
+| &lt;#EscapeCr: "\\r"&gt;
+| &lt;#EscapeTab: "\\t"&gt;
+}
+
+   </PRE>
+  </TD>
+ </TR>
+ <!-- Token -->
+ <TR>
+  <TD>
+   <PRE>
+&lt;DEFAULT,IN_DBL_BRACE&gt; TOKEN : {
+&lt;IDENTIFIER: &lt;LETTER&gt; (&lt;LETTER&gt; | &lt;DIGIT&gt; | &lt;SPECIALCHARS&gt;)*&gt;
+}
+
+   </PRE>
+  </TD>
+ </TR>
+ <!-- Token -->
+ <TR>
+  <TD>
+   <PRE>
+&lt;DEFAULT,IN_DBL_BRACE&gt; SKIP : {
+" "
+| "\t"
+| "\r"
+| "\n"
+}
+
+   </PRE>
+  </TD>
+ </TR>
+ <!-- Token -->
+ <TR>
+  <TD>
+   <PRE>
+&lt;DEFAULT,IN_DBL_BRACE&gt; SKIP : {
+&lt;"//" (~["\n"])* "\n"&gt;
+}
+
+   </PRE>
+  </TD>
+ </TR>
+ <!-- Token -->
+ <TR>
+  <TD>
+   <PRE>
+&lt;DEFAULT,IN_DBL_BRACE&gt; SKIP : {
+&lt;"//" (~["\n","\r"])* ("\n" | "\r" | "\r\n")?&gt;
+}
+
+   </PRE>
+  </TD>
+ </TR>
+ <!-- Token -->
+ <TR>
+  <TD>
+   <PRE>
+&lt;DEFAULT,IN_DBL_BRACE&gt; SKIP : {
+"/*" : INSIDE_COMMENT
+}
+
+   </PRE>
+  </TD>
+ </TR>
+ <!-- Token -->
+ <TR>
+  <TD>
+   <PRE>
+&lt;INSIDE_COMMENT&gt; SPECIAL : {
+&lt;"+" (" ")* (~["*"])*&gt;
+}
+
+   </PRE>
+  </TD>
+ </TR>
+ <!-- Token -->
+ <TR>
+  <TD>
+   <PRE>
+&lt;INSIDE_COMMENT&gt; SKIP : {
+"/*" : {
+}
+
+   </PRE>
+  </TD>
+ </TR>
+ <!-- Token -->
+ <TR>
+  <TD>
+   <PRE>
+&lt;INSIDE_COMMENT&gt; SKIP : {
+"*/" : {
+| &lt;~[]&gt;
+}
+
+   </PRE>
+  </TD>
+ </TR>
+</TABLE>
+<H2 ALIGN=CENTER>NON-TERMINALS</H2>
+<TABLE>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod1">Statement</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>( <A HREF="#prod2">SingleStatement</A> ( &lt;SEMICOLON&gt; )* )* &lt;EOF&gt;</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod2">SingleStatement</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>( <A HREF="#prod3">DataverseDeclaration</A> | <A HREF="#prod4">FunctionDeclaration</A> | <A HREF="#prod5">CreateStatement</A> | <A HREF="#prod6">LoadStatement</A> | <A HREF="#prod7">DropStatement</A> | <A HREF="#prod8">WriteStatement</A> | <A HREF="#prod9">SetStatement</A> | <A HREF="#prod10">InsertStatement</A> | <A HREF="#prod11">DeleteStatement</A> | <A HREF="#prod12">UpdateStatement</A> | <A HREF="#prod13">FeedStatement</A> | <A HREF="#prod14">CompactStatement</A> | <A HREF="#prod15">Query</A> &lt;SEMICOLON&gt; | <A HREF="#prod16">RefreshExternalDatasetStatement</A> | <A HREF="#prod17">RunStatement</A> )</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod3">DataverseDeclaration</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>&lt;USE&gt; <A HREF="#prod18">Identifier</A></TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod5">CreateStatement</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>&lt;CREATE&gt; ( <A HREF="#prod19">TypeSpecification</A> | <A HREF="#prod20">NodegroupSpecification</A> | <A HREF="#prod21">DatasetSpecification</A> | <A HREF="#prod22">IndexSpecification</A> | <A HREF="#prod23">DataverseSpecification</A> | <A HREF="#prod24">FunctionSpecification</A> | <A HREF="#prod25">FeedSpecification</A> | <A HREF="#prod26">FeedPolicySpecification</A> )</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod19">TypeSpecification</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>&lt;TYPE&gt; <A HREF="#prod27">TypeName</A> <A HREF="#prod28">IfNotExists</A> &lt;AS&gt; <A HREF="#prod29">TypeExpr</A></TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod20">NodegroupSpecification</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>&lt;NODEGROUP&gt; <A HREF="#prod18">Identifier</A> <A HREF="#prod28">IfNotExists</A> &lt;ON&gt; <A HREF="#prod18">Identifier</A> ( &lt;COMMA&gt; <A HREF="#prod18">Identifier</A> )*</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod21">DatasetSpecification</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>( &lt;EXTERNAL&gt; &lt;DATASET&gt; <A HREF="#prod30">QualifiedName</A> &lt;LEFTPAREN&gt; <A HREF="#prod18">Identifier</A> &lt;RIGHTPAREN&gt; <A HREF="#prod28">IfNotExists</A> &lt;USING&gt; <A HREF="#prod31">AdapterName</A> <A HREF="#prod32">Configuration</A> ( &lt;ON&gt; <A HREF="#prod18">Identifier</A> )? ( &lt;HINTS&gt; <A HREF="#prod33">Properties</A> )? ( &lt;USING&gt; &lt;COMPACTION&gt; &lt;POLICY&gt; <A HREF="#prod34">CompactionPolicy</A> ( <A HREF="#prod32">Configuration</A> )? )? | ( &lt;INTERNAL&gt; | &lt;TEMPORARY&gt; )? &lt;DATASET&gt; <A HREF="#prod30">QualifiedName</A> &lt;LEFTPAREN&gt; <A HREF="#prod18">Identifier</A> &lt;RIGHTPAREN&gt; <A HREF="#prod28">IfNotExists</A> <A HREF="#prod35">PrimaryKey</A> ( &lt;AUTOGENERATED&gt; )? ( &lt;ON&gt; <A HREF="#prod18">Identifier</A> )? ( &lt;HINTS&gt; <A HREF="#prod33">Properties</A> )? ( &lt;USING&gt; &lt;COMPACTION&gt; &lt;POLICY&gt; <A HREF="#prod34">CompactionPolicy</A> ( <A HREF="#prod32">Configuration</A> )? )? ( &lt;WITH&gt; &lt;FILTER&gt; &lt;ON&gt; <A HREF="#prod36">NestedField</A> )? )</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod16">RefreshExternalDatasetStatement</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>&lt;REFRESH&gt; &lt;EXTERNAL&gt; &lt;DATASET&gt; <A HREF="#prod30">QualifiedName</A></TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod17">RunStatement</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>&lt;RUN&gt; <A HREF="#prod18">Identifier</A> &lt;LEFTPAREN&gt; ( <A HREF="#prod18">Identifier</A> ( &lt;COMMA&gt; )? )* &lt;RIGHTPAREN&gt; &lt;FROM&gt; &lt;DATASET&gt; <A HREF="#prod30">QualifiedName</A> &lt;TO&gt; &lt;DATASET&gt; <A HREF="#prod30">QualifiedName</A></TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod22">IndexSpecification</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>&lt;INDEX&gt; <A HREF="#prod18">Identifier</A> <A HREF="#prod28">IfNotExists</A> &lt;ON&gt; <A HREF="#prod30">QualifiedName</A> &lt;LEFTPAREN&gt; ( <A HREF="#prod37">OpenField</A> ) ( &lt;COMMA&gt; <A HREF="#prod37">OpenField</A> )* &lt;RIGHTPAREN&gt; ( &lt;TYPE&gt; <A HREF="#prod38">IndexType</A> )? ( &lt;ENFORCED&gt; )?</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod34">CompactionPolicy</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE><A HREF="#prod18">Identifier</A></TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod39">FilterField</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE><A HREF="#prod18">Identifier</A></TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod38">IndexType</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>( &lt;BTREE&gt; | &lt;RTREE&gt; | &lt;KEYWORD&gt; | &lt;NGRAM&gt; &lt;LEFTPAREN&gt; &lt;INTEGER_LITERAL&gt; &lt;RIGHTPAREN&gt; )</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod23">DataverseSpecification</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>&lt;DATAVERSE&gt; <A HREF="#prod18">Identifier</A> <A HREF="#prod28">IfNotExists</A> ( &lt;WITH&gt; &lt;FORMAT&gt; <A HREF="#prod40">QuotedString</A> )?</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod24">FunctionSpecification</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>&lt;FUNCTION&gt; <A HREF="#prod41">FunctionName</A> <A HREF="#prod28">IfNotExists</A> <A HREF="#prod42">ParameterList</A> &lt;LEFTBRACE&gt; <A HREF="#prod43">Expression</A> &lt;RIGHTBRACE&gt;</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod25">FeedSpecification</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>( &lt;SECONDARY&gt; &lt;FEED&gt; <A HREF="#prod30">QualifiedName</A> <A HREF="#prod28">IfNotExists</A> &lt;FROM&gt; &lt;FEED&gt; <A HREF="#prod30">QualifiedName</A> ( <A HREF="#prod44">ApplyFunction</A> )? | ( &lt;PRIMARY&gt; )? &lt;FEED&gt; <A HREF="#prod30">QualifiedName</A> <A HREF="#prod28">IfNotExists</A> &lt;USING&gt; <A HREF="#prod31">AdapterName</A> <A HREF="#prod32">Configuration</A> ( <A HREF="#prod44">ApplyFunction</A> )? )</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod26">FeedPolicySpecification</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>( &lt;INGESTION&gt; &lt;POLICY&gt; <A HREF="#prod18">Identifier</A> <A HREF="#prod28">IfNotExists</A> &lt;FROM&gt; ( &lt;POLICY&gt; <A HREF="#prod18">Identifier</A> <A HREF="#prod32">Configuration</A> ( &lt;DEFINITION&gt; <A HREF="#prod40">QuotedString</A> )? | &lt;PATH&gt; <A HREF="#prod18">Identifier</A> ( &lt;DEFINITION&gt; <A HREF="#prod40">QuotedString</A> )? ) )</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod42">ParameterList</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>&lt;LEFTPAREN&gt; ( &lt;IDENTIFIER&gt; ( &lt;COMMA&gt; &lt;IDENTIFIER&gt; )* )? &lt;RIGHTPAREN&gt;</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod28">IfNotExists</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>( &lt;IF&gt; ( "not exists" | "NOT EXISTS" ) )?</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod44">ApplyFunction</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>&lt;APPLY&gt; &lt;FUNCTION&gt; <A HREF="#prod41">FunctionName</A></TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod45">GetPolicy</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>&lt;USING&gt; &lt;POLICY&gt; <A HREF="#prod18">Identifier</A></TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod46">FunctionSignature</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE><A HREF="#prod41">FunctionName</A> &lt;ATT&gt; &lt;INTEGER_LITERAL&gt;</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod35">PrimaryKey</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>&lt;PRIMARY&gt; &lt;KEY&gt; <A HREF="#prod36">NestedField</A> ( &lt;COMMA&gt; <A HREF="#prod36">NestedField</A> )*</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod7">DropStatement</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>&lt;DROP&gt; ( &lt;DATASET&gt; <A HREF="#prod30">QualifiedName</A> <A HREF="#prod47">IfExists</A> | &lt;INDEX&gt; <A HREF="#prod48">DoubleQualifiedName</A> <A HREF="#prod47">IfExists</A> | &lt;NODEGROUP&gt; <A HREF="#prod18">Identifier</A> <A HREF="#prod47">IfExists</A> | &lt;TYPE&gt; <A HREF="#prod27">TypeName</A> <A HREF="#prod47">IfExists</A> | &lt;DATAVERSE&gt; <A HREF="#prod18">Identifier</A> <A HREF="#prod47">IfExists</A> | &lt;FUNCTION&gt; <A HREF="#prod46">FunctionSignature</A> <A HREF="#prod47">IfExists</A> | &lt;FEED&gt; <A HREF="#prod30">QualifiedName</A> <A HREF="#prod47">IfExists</A> )</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod47">IfExists</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>( &lt;IF&gt; &lt;EXISTS&gt; )?</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod10">InsertStatement</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>&lt;INSERT&gt; &lt;INTO&gt; <A HREF="#prod30">QualifiedName</A> <A HREF="#prod15">Query</A></TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod11">DeleteStatement</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>&lt;DELETE&gt; <A HREF="#prod49">Variable</A> &lt;FROM&gt; <A HREF="#prod30">QualifiedName</A> ( &lt;WHERE&gt; <A HREF="#prod43">Expression</A> )?</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod12">UpdateStatement</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>&lt;UPDATE&gt; <A HREF="#prod49">Variable</A> &lt;IN&gt; <A HREF="#prod43">Expression</A> &lt;WHERE&gt; <A HREF="#prod43">Expression</A> &lt;LEFTPAREN&gt; ( <A HREF="#prod50">UpdateClause</A> ( &lt;COMMA&gt; <A HREF="#prod50">UpdateClause</A> )* ) &lt;RIGHTPAREN&gt;</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod50">UpdateClause</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>( &lt;SET&gt; <A HREF="#prod43">Expression</A> &lt;EQ&gt; <A HREF="#prod43">Expression</A> | <A HREF="#prod10">InsertStatement</A> | <A HREF="#prod11">DeleteStatement</A> | <A HREF="#prod12">UpdateStatement</A> | &lt;IF&gt; &lt;LEFTPAREN&gt; <A HREF="#prod43">Expression</A> &lt;RIGHTPAREN&gt; &lt;THEN&gt; <A HREF="#prod50">UpdateClause</A> ( &lt;ELSE&gt; <A HREF="#prod50">UpdateClause</A> )? )</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod9">SetStatement</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>&lt;SET&gt; <A HREF="#prod18">Identifier</A> <A HREF="#prod40">QuotedString</A></TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod8">WriteStatement</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>&lt;WRITE&gt; &lt;OUTPUT&gt; &lt;TO&gt; <A HREF="#prod18">Identifier</A> &lt;COLON&gt; <A HREF="#prod40">QuotedString</A> ( &lt;USING&gt; <A HREF="#prod40">QuotedString</A> )?</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod6">LoadStatement</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>&lt;LOAD&gt; &lt;DATASET&gt; <A HREF="#prod30">QualifiedName</A> &lt;USING&gt; <A HREF="#prod31">AdapterName</A> <A HREF="#prod32">Configuration</A> ( &lt;PRESORTED&gt; )?</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod31">AdapterName</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE><A HREF="#prod18">Identifier</A></TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod14">CompactStatement</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>&lt;COMPACT&gt; &lt;DATASET&gt; <A HREF="#prod30">QualifiedName</A></TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod13">FeedStatement</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>( &lt;CONNECT&gt; &lt;FEED&gt; <A HREF="#prod30">QualifiedName</A> &lt;TO&gt; &lt;DATASET&gt; <A HREF="#prod30">QualifiedName</A> ( <A HREF="#prod45">GetPolicy</A> )? | &lt;DISCONNECT&gt; &lt;FEED&gt; <A HREF="#prod30">QualifiedName</A> &lt;FROM&gt; &lt;DATASET&gt; <A HREF="#prod30">QualifiedName</A> )</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod32">Configuration</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>&lt;LEFTPAREN&gt; ( <A HREF="#prod51">KeyValuePair</A> ( &lt;COMMA&gt; <A HREF="#prod51">KeyValuePair</A> )* )? &lt;RIGHTPAREN&gt;</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod51">KeyValuePair</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>&lt;LEFTPAREN&gt; <A HREF="#prod40">QuotedString</A> &lt;EQ&gt; <A HREF="#prod40">QuotedString</A> &lt;RIGHTPAREN&gt;</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod33">Properties</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>( &lt;LEFTPAREN&gt; <A HREF="#prod52">Property</A> ( &lt;COMMA&gt; <A HREF="#prod52">Property</A> )* &lt;RIGHTPAREN&gt; )?</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod52">Property</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE><A HREF="#prod18">Identifier</A> &lt;EQ&gt; ( <A HREF="#prod40">QuotedString</A> | &lt;INTEGER_LITERAL&gt; )</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod53">IndexedTypeExpr</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>( <A HREF="#prod54">TypeReference</A> | <A HREF="#prod55">OrderedListTypeDef</A> | <A HREF="#prod56">UnorderedListTypeDef</A> )</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod29">TypeExpr</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>( <A HREF="#prod57">RecordTypeDef</A> | <A HREF="#prod54">TypeReference</A> | <A HREF="#prod55">OrderedListTypeDef</A> | <A HREF="#prod56">UnorderedListTypeDef</A> )</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod57">RecordTypeDef</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>( &lt;CLOSED&gt; | &lt;OPEN&gt; )? &lt;LEFTBRACE&gt; ( <A HREF="#prod58">RecordField</A> ( &lt;COMMA&gt; <A HREF="#prod58">RecordField</A> )* )? &lt;RIGHTBRACE&gt;</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod58">RecordField</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE><A HREF="#prod18">Identifier</A> &lt;COLON&gt; <A HREF="#prod29">TypeExpr</A> ( &lt;QUES&gt; )?</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod54">TypeReference</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE><A HREF="#prod18">Identifier</A></TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod55">OrderedListTypeDef</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>&lt;LEFTBRACKET&gt; ( <A HREF="#prod29">TypeExpr</A> ) &lt;RIGHTBRACKET&gt;</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod56">UnorderedListTypeDef</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>&lt;LEFTDBLBRACE&gt; ( <A HREF="#prod29">TypeExpr</A> ) &lt;RIGHTDBLBRACE&gt;</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod41">FunctionName</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE><A HREF="#prod18">Identifier</A> ( &lt;DOT&gt; <A HREF="#prod18">Identifier</A> ( &lt;SHARP&gt; <A HREF="#prod18">Identifier</A> )? | &lt;SHARP&gt; <A HREF="#prod18">Identifier</A> )?</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod27">TypeName</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE><A HREF="#prod30">QualifiedName</A></TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod18">Identifier</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>( &lt;IDENTIFIER&gt; | <A HREF="#prod40">QuotedString</A> )</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod37">OpenField</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE><A HREF="#prod36">NestedField</A> ( &lt;COLON&gt; <A HREF="#prod53">IndexedTypeExpr</A> )?</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod36">NestedField</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE><A HREF="#prod18">Identifier</A> ( &lt;DOT&gt; <A HREF="#prod18">Identifier</A> )*</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod40">QuotedString</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>&lt;QUOTED_STRING&gt;</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod59">StringLiteral</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>&lt;STRING_LITERAL&gt;</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod30">QualifiedName</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE><A HREF="#prod18">Identifier</A> ( &lt;DOT&gt; <A HREF="#prod18">Identifier</A> )?</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod48">DoubleQualifiedName</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE><A HREF="#prod18">Identifier</A> &lt;DOT&gt; <A HREF="#prod18">Identifier</A> ( &lt;DOT&gt; <A HREF="#prod18">Identifier</A> )?</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod4">FunctionDeclaration</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>&lt;DECLARE&gt; &lt;FUNCTION&gt; <A HREF="#prod18">Identifier</A> <A HREF="#prod42">ParameterList</A> &lt;LEFTBRACE&gt; <A HREF="#prod43">Expression</A> &lt;RIGHTBRACE&gt;</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod15">Query</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>( <A HREF="#prod43">Expression</A> | <A HREF="#prod60">SelectExpression</A> )</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod43">Expression</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>( <A HREF="#prod61">OperatorExpr</A> | <A HREF="#prod62">IfThenElse</A> | <A HREF="#prod63">QuantifiedExpression</A> )</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod61">OperatorExpr</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE><A HREF="#prod64">AndExpr</A> ( &lt;OR&gt; <A HREF="#prod64">AndExpr</A> )*</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod64">AndExpr</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE><A HREF="#prod65">RelExpr</A> ( &lt;AND&gt; <A HREF="#prod65">RelExpr</A> )*</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod65">RelExpr</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE><A HREF="#prod66">AddExpr</A> ( ( &lt;LT&gt; | &lt;GT&gt; | &lt;LE&gt; | &lt;GE&gt; | &lt;EQ&gt; | &lt;NE&gt; | &lt;SIMILAR&gt; ) <A HREF="#prod66">AddExpr</A> )?</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod66">AddExpr</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE><A HREF="#prod67">MultExpr</A> ( ( &lt;PLUS&gt; | &lt;MINUS&gt; ) <A HREF="#prod67">MultExpr</A> )*</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod67">MultExpr</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE><A HREF="#prod68">UnaryExpr</A> ( ( &lt;MUL&gt; | &lt;DIV&gt; | &lt;MOD&gt; | &lt;CARET&gt; | &lt;IDIV&gt; ) <A HREF="#prod68">UnaryExpr</A> )*</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod68">UnaryExpr</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>( ( &lt;PLUS&gt; | &lt;MINUS&gt; ) )? <A HREF="#prod69">ValueExpr</A></TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod69">ValueExpr</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE><A HREF="#prod70">PrimaryExpr</A> ( <A HREF="#prod71">Field</A> | <A HREF="#prod72">Index</A> )*</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod71">Field</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>&lt;DOT&gt; <A HREF="#prod18">Identifier</A></TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod72">Index</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>&lt;LEFTBRACKET&gt; ( <A HREF="#prod43">Expression</A> | &lt;QUES&gt; ) &lt;RIGHTBRACKET&gt;</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod70">PrimaryExpr</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>( <A HREF="#prod73">FunctionCallExpr</A> | <A HREF="#prod74">Literal</A> | <A HREF="#prod75">VariableRef</A> | <A HREF="#prod76">ListConstructor</A> | <A HREF="#prod77">RecordConstructor</A> | <A HREF="#prod78">ParenthesizedExpression</A> )</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod74">Literal</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>( <A HREF="#prod59">StringLiteral</A> | &lt;INTEGER_LITERAL&gt; | &lt;FLOAT_LITERAL&gt; | &lt;DOUBLE_LITERAL&gt; | &lt;NULL&gt; | &lt;TRUE&gt; | &lt;FALSE&gt; )</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod75">VariableRef</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>( &lt;IDENTIFIER&gt; | <A HREF="#prod40">QuotedString</A> )</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod49">Variable</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>( &lt;IDENTIFIER&gt; | <A HREF="#prod40">QuotedString</A> )</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod76">ListConstructor</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>( <A HREF="#prod79">OrderedListConstructor</A> | <A HREF="#prod80">UnorderedListConstructor</A> )</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod79">OrderedListConstructor</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>&lt;LEFTBRACKET&gt; <A HREF="#prod81">ExpressionList</A> &lt;RIGHTBRACKET&gt;</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod80">UnorderedListConstructor</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>&lt;LEFTDBLBRACE&gt; <A HREF="#prod81">ExpressionList</A> &lt;RIGHTDBLBRACE&gt;</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod81">ExpressionList</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>( <A HREF="#prod43">Expression</A> ( &lt;COMMA&gt; <A HREF="#prod81">ExpressionList</A> )? )? ( <A HREF="#prod82">Comma</A> )?</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod82">Comma</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>&lt;COMMA&gt;</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod77">RecordConstructor</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>&lt;LEFTBRACE&gt; ( <A HREF="#prod83">FieldBinding</A> ( &lt;COMMA&gt; <A HREF="#prod83">FieldBinding</A> )* )? &lt;RIGHTBRACE&gt;</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod83">FieldBinding</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE><A HREF="#prod43">Expression</A> &lt;COLON&gt; <A HREF="#prod43">Expression</A></TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod73">FunctionCallExpr</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE><A HREF="#prod41">FunctionName</A> &lt;LEFTPAREN&gt; ( <A HREF="#prod43">Expression</A> ( &lt;COMMA&gt; <A HREF="#prod43">Expression</A> )* )? &lt;RIGHTPAREN&gt;</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod78">ParenthesizedExpression</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>( &lt;LEFTPAREN&gt; <A HREF="#prod43">Expression</A> &lt;RIGHTPAREN&gt; | <A HREF="#prod84">Subquery</A> )</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod62">IfThenElse</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>&lt;IF&gt; &lt;LEFTPAREN&gt; <A HREF="#prod43">Expression</A> &lt;RIGHTPAREN&gt; &lt;THEN&gt; <A HREF="#prod43">Expression</A> &lt;ELSE&gt; <A HREF="#prod43">Expression</A></TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod60">SelectExpression</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>( <A HREF="#prod85">LetClause</A> )? <A HREF="#prod86">SelectSetOperation</A> ( <A HREF="#prod87">OrderbyClause</A> )? ( <A HREF="#prod88">LimitClause</A> )?</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod86">SelectSetOperation</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE><A HREF="#prod89">SelectBlock</A> ( ( &lt;UNION&gt; | &lt;INTERSECT&gt; | &lt;EXCEPT&gt; ) ( &lt;ALL&gt; )? ( <A HREF="#prod89">SelectBlock</A> | <A HREF="#prod84">Subquery</A> ) )*</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod84">Subquery</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>&lt;LEFTPAREN&gt; <A HREF="#prod60">SelectExpression</A> &lt;RIGHTPAREN&gt;</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod89">SelectBlock</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>( <A HREF="#prod90">SelectClause</A> ( <A HREF="#prod91">FromClause</A> ( <A HREF="#prod85">LetClause</A> )? )? ( <A HREF="#prod92">WhereClause</A> )? ( <A HREF="#prod93">GroupbyClause</A> ( <A HREF="#prod85">LetClause</A> )? ( <A HREF="#prod94">HavingClause</A> )? )? | <A HREF="#prod91">FromClause</A> ( <A HREF="#prod85">LetClause</A> )? ( <A HREF="#prod92">WhereClause</A> )? ( <A HREF="#prod93">GroupbyClause</A> ( <A HREF="#prod85">LetClause</A> )? ( <A HREF="#prod94">HavingClause</A> )? )? <A HREF="#prod90">SelectClause</A> )</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod90">SelectClause</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>&lt;SELECT&gt; ( &lt;ALL&gt; | &lt;DISTINCT&gt; )? ( <A HREF="#prod95">SelectRegular</A> | <A HREF="#prod96">SelectElement</A> )</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod95">SelectRegular</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE><A HREF="#prod97">Projection</A> ( &lt;COMMA&gt; <A HREF="#prod97">Projection</A> )*</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod96">SelectElement</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>( &lt;RAW&gt; | &lt;ELEMENT&gt; | &lt;VALUE&gt; ) <A HREF="#prod43">Expression</A></TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod97">Projection</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>( <A HREF="#prod43">Expression</A> ( &lt;AS&gt; )? <A HREF="#prod18">Identifier</A> | <A HREF="#prod43">Expression</A> &lt;DOT&gt; &lt;MUL&gt; | &lt;MUL&gt; )</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod91">FromClause</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>&lt;FROM&gt; <A HREF="#prod98">FromTerm</A> ( &lt;COMMA&gt; <A HREF="#prod98">FromTerm</A> )*</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod98">FromTerm</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE><A HREF="#prod43">Expression</A> ( &lt;AS&gt; )? <A HREF="#prod49">Variable</A> ( &lt;AT&gt; <A HREF="#prod49">Variable</A> )? ( ( <A HREF="#prod99">JoinType</A> )? ( <A HREF="#prod100">JoinClause</A> | <A HREF="#prod101">NestClause</A> | <A HREF="#prod102">UnnestClause</A> ) )*</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod100">JoinClause</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>&lt;JOIN&gt; <A HREF="#prod43">Expression</A> ( &lt;AS&gt; )? <A HREF="#prod49">Variable</A> ( &lt;AT&gt; <A HREF="#prod49">Variable</A> )? &lt;ON&gt; <A HREF="#prod43">Expression</A></TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod101">NestClause</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>&lt;NEST&gt; <A HREF="#prod43">Expression</A> ( &lt;AS&gt; )? <A HREF="#prod49">Variable</A> ( &lt;AT&gt; <A HREF="#prod49">Variable</A> )? &lt;ON&gt; <A HREF="#prod43">Expression</A></TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod102">UnnestClause</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>( &lt;UNNEST&gt; | &lt;CORRELATE&gt; | &lt;FLATTEN&gt; ) <A HREF="#prod43">Expression</A> ( &lt;AS&gt; )? <A HREF="#prod49">Variable</A> ( &lt;AT&gt; <A HREF="#prod49">Variable</A> )?</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod99">JoinType</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>( &lt;INNER&gt; | &lt;LEFT&gt; ( &lt;OUTER&gt; )? )</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod85">LetClause</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>( ( &lt;LET&gt; | &lt;LETTING&gt; ) <A HREF="#prod103">LetElement</A> ( &lt;COMMA&gt; <A HREF="#prod103">LetElement</A> )* | &lt;WITH&gt; <A HREF="#prod104">WithElement</A> ( &lt;COMMA&gt; <A HREF="#prod104">WithElement</A> )* )</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod92">WhereClause</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>&lt;WHERE&gt; <A HREF="#prod43">Expression</A></TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod87">OrderbyClause</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>&lt;ORDER&gt; &lt;BY&gt; <A HREF="#prod43">Expression</A> ( ( &lt;ASC&gt; ) | ( &lt;DESC&gt; ) )? ( &lt;COMMA&gt; <A HREF="#prod43">Expression</A> ( ( &lt;ASC&gt; ) | ( &lt;DESC&gt; ) )? )*</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod93">GroupbyClause</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>&lt;GROUP&gt; &lt;BY&gt; ( <A HREF="#prod43">Expression</A> ( ( &lt;AS&gt; )? <A HREF="#prod49">Variable</A> )? ( &lt;COMMA&gt; <A HREF="#prod43">Expression</A> ( ( &lt;AS&gt; )? <A HREF="#prod49">Variable</A> )? )* )</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod94">HavingClause</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>&lt;HAVING&gt; <A HREF="#prod43">Expression</A></TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod88">LimitClause</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>&lt;LIMIT&gt; <A HREF="#prod43">Expression</A> ( &lt;OFFSET&gt; <A HREF="#prod43">Expression</A> )?</TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod63">QuantifiedExpression</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE>( ( &lt;SOME&gt; ) | ( &lt;EVERY&gt; ) ) <A HREF="#prod49">Variable</A> &lt;IN&gt; <A HREF="#prod43">Expression</A> ( &lt;COMMA&gt; <A HREF="#prod49">Variable</A> &lt;IN&gt; <A HREF="#prod43">Expression</A> )* &lt;SATISFIES&gt; <A HREF="#prod43">Expression</A></TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod103">LetElement</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE><A HREF="#prod49">Variable</A> &lt;EQ&gt; <A HREF="#prod43">Expression</A></TD>
+</TR>
+<TR>
+<TD ALIGN=RIGHT VALIGN=BASELINE><A NAME="prod104">WithElement</A></TD>
+<TD ALIGN=CENTER VALIGN=BASELINE>::=</TD>
+<TD ALIGN=LEFT VALIGN=BASELINE><A HREF="#prod49">Variable</A> &lt;AS&gt; <A HREF="#prod43">Expression</A></TD>
+</TR>
+</TABLE>
+</BODY>
+</HTML>
diff --git a/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj b/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
new file mode 100644
index 0000000..62899d6
--- /dev/null
+++ b/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
@@ -0,0 +1,2910 @@
+options {
+
+
+       STATIC = false;
+
+}
+
+
+PARSER_BEGIN(SQLPPParser)
+
+package org.apache.asterix.lang.sqlpp.parser;
+
+// For SQL++ ParserTokenManager
+import org.apache.xerces.util.IntStack;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.asterix.common.annotations.AutoDataGen;
+import org.apache.asterix.common.annotations.DateBetweenYearsDataGen;
+import org.apache.asterix.common.annotations.DatetimeAddRandHoursDataGen;
+import org.apache.asterix.common.annotations.DatetimeBetweenYearsDataGen;
+import org.apache.asterix.common.annotations.FieldIntervalDataGen;
+import org.apache.asterix.common.annotations.FieldValFileDataGen;
+import org.apache.asterix.common.annotations.FieldValFileSameIndexDataGen;
+import org.apache.asterix.common.annotations.IRecordFieldDataGen;
+import org.apache.asterix.common.annotations.InsertRandIntDataGen;
+import org.apache.asterix.common.annotations.ListDataGen;
+import org.apache.asterix.common.annotations.ListValFileDataGen;
+import org.apache.asterix.common.annotations.SkipSecondaryIndexSearchExpressionAnnotation;
+import org.apache.asterix.common.annotations.TypeDataGen;
+import org.apache.asterix.common.annotations.UndeclaredFieldsDataGen;
+import org.apache.asterix.common.config.DatasetConfig.DatasetType;
+import org.apache.asterix.common.config.DatasetConfig.IndexType;
+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.Literal;
+import org.apache.asterix.lang.common.base.IParser;
+import org.apache.asterix.lang.common.base.Statement;
+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.UpdateClause;
+import org.apache.asterix.lang.common.clause.WhereClause;
+import org.apache.asterix.lang.common.context.RootScopeFactory;
+import org.apache.asterix.lang.common.context.Scope;
+import org.apache.asterix.lang.common.expression.AbstractAccessor;
+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.OrderedListTypeDefinition;
+import org.apache.asterix.lang.common.expression.QuantifiedExpression;
+import org.apache.asterix.lang.common.expression.RecordConstructor;
+import org.apache.asterix.lang.common.expression.RecordTypeDefinition;
+import org.apache.asterix.lang.common.expression.TypeExpression;
+import org.apache.asterix.lang.common.expression.TypeReferenceExpression;
+import org.apache.asterix.lang.common.expression.UnaryExpr;
+import org.apache.asterix.lang.common.expression.UnaryExpr.Sign;
+import org.apache.asterix.lang.common.expression.UnorderedListTypeDefinition;
+import org.apache.asterix.lang.common.expression.VariableExpr;
+import org.apache.asterix.lang.common.literal.DoubleLiteral;
+import org.apache.asterix.lang.common.literal.FalseLiteral;
+import org.apache.asterix.lang.common.literal.FloatLiteral;
+import org.apache.asterix.lang.common.literal.LongIntegerLiteral;
+import org.apache.asterix.lang.common.literal.NullLiteral;
+import org.apache.asterix.lang.common.literal.StringLiteral;
+import org.apache.asterix.lang.common.literal.TrueLiteral;
+import org.apache.asterix.lang.common.parser.ScopeChecker;
+import org.apache.asterix.lang.common.statement.CompactStatement;
+import org.apache.asterix.lang.common.statement.ConnectFeedStatement;
+import org.apache.asterix.lang.common.statement.CreateDataverseStatement;
+import org.apache.asterix.lang.common.statement.CreateFeedPolicyStatement;
+import org.apache.asterix.lang.common.statement.CreateFeedStatement;
+import org.apache.asterix.lang.common.statement.CreateFunctionStatement;
+import org.apache.asterix.lang.common.statement.CreateIndexStatement;
+import org.apache.asterix.lang.common.statement.CreatePrimaryFeedStatement;
+import org.apache.asterix.lang.common.statement.CreateSecondaryFeedStatement;
+import org.apache.asterix.lang.common.statement.DatasetDecl;
+import org.apache.asterix.lang.common.statement.DataverseDecl;
+import org.apache.asterix.lang.common.statement.DataverseDropStatement;
+import org.apache.asterix.lang.common.statement.DeleteStatement;
+import org.apache.asterix.lang.common.statement.DisconnectFeedStatement;
+import org.apache.asterix.lang.common.statement.DropStatement;
+import org.apache.asterix.lang.common.statement.ExternalDetailsDecl;
+import org.apache.asterix.lang.common.statement.FeedDropStatement;
+import org.apache.asterix.lang.common.statement.FunctionDecl;
+import org.apache.asterix.lang.common.statement.FunctionDropStatement;
+import org.apache.asterix.lang.common.statement.IndexDropStatement;
+import org.apache.asterix.lang.common.statement.InsertStatement;
+import org.apache.asterix.lang.common.statement.InternalDetailsDecl;
+import org.apache.asterix.lang.common.statement.LoadStatement;
+import org.apache.asterix.lang.common.statement.NodeGroupDropStatement;
+import org.apache.asterix.lang.common.statement.NodegroupDecl;
+import org.apache.asterix.lang.common.statement.Query;
+import org.apache.asterix.lang.common.statement.RefreshExternalDatasetStatement;
+import org.apache.asterix.lang.common.statement.RunStatement;
+import org.apache.asterix.lang.common.statement.SetStatement;
+import org.apache.asterix.lang.common.statement.TypeDecl;
+import org.apache.asterix.lang.common.statement.TypeDropStatement;
+import org.apache.asterix.lang.common.statement.UpdateStatement;
+import org.apache.asterix.lang.common.statement.WriteStatement;
+import org.apache.asterix.lang.common.struct.Identifier;
+import org.apache.asterix.lang.common.struct.QuantifiedPair;
+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.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.optype.JoinType;
+import org.apache.asterix.lang.sqlpp.optype.SetOpType;
+import org.apache.asterix.lang.sqlpp.struct.SetOperationInput;
+import org.apache.asterix.lang.sqlpp.struct.SetOperationRight;
+import org.apache.asterix.metadata.bootstrap.MetadataConstants;
+import org.apache.hyracks.algebricks.common.utils.Pair;
+import org.apache.hyracks.algebricks.common.utils.Triple;
+import org.apache.hyracks.algebricks.core.algebra.expressions.IExpressionAnnotation;
+import org.apache.hyracks.algebricks.core.algebra.expressions.IndexedNLJoinExpressionAnnotation;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+
+
+
+public class SQLPPParser extends ScopeChecker implements IParser {
+
+    // optimizer hints
+    private static final String AUTO_HINT = "auto";
+    private static final String BROADCAST_JOIN_HINT = "bcast";
+    private static final String COMPOSE_VAL_FILES_HINT = "compose-val-files";
+    private static final String DATE_BETWEEN_YEARS_HINT = "date-between-years";
+    private static final String DATETIME_ADD_RAND_HOURS_HINT = "datetime-add-rand-hours";
+    private static final String DATETIME_BETWEEN_YEARS_HINT = "datetime-between-years";
+    private static final String HASH_GROUP_BY_HINT = "hash";
+    private static final String INDEXED_NESTED_LOOP_JOIN_HINT = "indexnl";
+    private static final String INMEMORY_HINT = "inmem";
+    private static final String INSERT_RAND_INT_HINT = "insert-rand-int";
+    private static final String INTERVAL_HINT = "interval";
+    private static final String LIST_HINT = "list";
+    private static final String LIST_VAL_FILE_HINT = "list-val-file";
+    private static final String RANGE_HINT = "range";
+    private static final String SKIP_SECONDARY_INDEX_SEARCH_HINT = "skip-index";
+    private static final String VAL_FILE_HINT = "val-files";
+    private static final String VAL_FILE_SAME_INDEX_HINT = "val-file-same-idx";
+
+    private static final String GEN_FIELDS_HINT = "gen-fields";
+
+    // data generator hints
+    private static final String DGEN_HINT = "dgen";
+
+    private static class IndexParams {
+      public IndexType type;
+      public int gramLength;
+
+      public IndexParams(IndexType type, int gramLength) {
+        this.type = type;
+        this.gramLength = gramLength;
+      }
+    };
+
+    private static class FunctionName {
+       public String dataverse = null;
+       public String library = null;
+       public String function = null;
+       public String hint = null;
+    }
+
+    private static String getHint(Token t) {
+        if (t.specialToken == null) {
+            return null;
+        }
+        String s = t.specialToken.image;
+        int n = s.length();
+        if (n < 2) {
+            return null;
+        }
+        return s.substring(1).trim();
+    }
+
+    private static IRecordFieldDataGen parseFieldDataGen(String hint) throws ParseException {
+      IRecordFieldDataGen rfdg = null;
+      String splits[] = hint.split(" +");
+      if (splits[0].equals(VAL_FILE_HINT)) {
+        File[] valFiles = new File[splits.length - 1];
+        for (int k=1; k<splits.length; k++) {
+          valFiles[k-1] = new File(splits[k]);
+        }
+        rfdg = new FieldValFileDataGen(valFiles);
+      } else if (splits[0].equals(VAL_FILE_SAME_INDEX_HINT)) {
+        rfdg = new FieldValFileSameIndexDataGen(new File(splits[1]), splits[2]);
+      } else if (splits[0].equals(LIST_VAL_FILE_HINT)) {
+        rfdg = new ListValFileDataGen(new File(splits[1]), Integer.parseInt(splits[2]), Integer.parseInt(splits[3]));
+      } else if (splits[0].equals(LIST_HINT)) {
+        rfdg = new ListDataGen(Integer.parseInt(splits[1]), Integer.parseInt(splits[2]));
+      } else if (splits[0].equals(INTERVAL_HINT)) {
+        FieldIntervalDataGen.ValueType vt;
+        if (splits[1].equals("int")) {
+          vt = FieldIntervalDataGen.ValueType.INT;
+        } else if (splits[1].equals("long")) {
+          vt = FieldIntervalDataGen.ValueType.LONG;
+        } else if (splits[1].equals("float")) {
+          vt = FieldIntervalDataGen.ValueType.FLOAT;
+        } else if (splits[1].equals("double")) {
+          vt = FieldIntervalDataGen.ValueType.DOUBLE;
+        } else {
+          throw new ParseException("Unknown type for interval data gen: " + splits[1]);
+        }
+        rfdg = new FieldIntervalDataGen(vt, splits[2], splits[3]);
+      } else if (splits[0].equals(INSERT_RAND_INT_HINT)) {
+        rfdg = new InsertRandIntDataGen(splits[1], splits[2]);
+      } else if (splits[0].equals(DATE_BETWEEN_YEARS_HINT)) {
+        rfdg = new DateBetweenYearsDataGen(Integer.parseInt(splits[1]), Integer.parseInt(splits[2]));
+      } else if (splits[0].equals(DATETIME_BETWEEN_YEARS_HINT)) {
+        rfdg = new DatetimeBetweenYearsDataGen(Integer.parseInt(splits[1]), Integer.parseInt(splits[2]));
+      } else if (splits[0].equals(DATETIME_ADD_RAND_HOURS_HINT)) {
+        rfdg = new DatetimeAddRandHoursDataGen(Integer.parseInt(splits[1]), Integer.parseInt(splits[2]), splits[3]);
+      } else if (splits[0].equals(AUTO_HINT)) {
+        rfdg = new AutoDataGen(splits[1]);
+      }
+      return rfdg;
+    }
+
+    public SQLPPParser(String s){
+        this(new StringReader(s));
+        super.setInput(s);
+    }
+
+    public static void main(String args[]) throws ParseException, TokenMgrError, IOException, FileNotFoundException, AsterixException {
+        File file = new File(args[0]);
+        Reader fis = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"));
+        SQLPPParser parser = new SQLPPParser(fis);
+        List<Statement> st = parser.parse();
+        //st.accept(new SQLPPPrintVisitor(), 0);
+    }
+
+    public List<Statement> parse() throws AsterixException {
+        try {
+            return Statement();
+        } catch (Error e) {
+            // this is here as the JavaCharStream that's below the lexer somtimes throws Errors that are not handled
+            // by the ANTLR-generated lexer or parser (e.g it does this for invalid backslash u + 4 hex digits escapes)
+            throw new AsterixException(new ParseException(e.getMessage()));
+        } catch (ParseException e) {
+            throw new AsterixException(e.getMessage());
+        }
+    }
+}
+
+PARSER_END(SQLPPParser)
+
+
+List<Statement> Statement() throws ParseException:
+{
+  scopeStack.push(RootScopeFactory.createRootScope(this));
+  List<Statement> decls = new ArrayList<Statement>();
+  Statement stmt = null;
+}
+{
+  ( stmt = SingleStatement() (<SEMICOLON>)*
+    {
+      decls.add(stmt);
+    }
+  )*
+  <EOF>
+  {
+    return decls;
+  }
+}
+
+Statement SingleStatement() throws ParseException:
+{
+  Statement stmt = null;
+}
+{
+  (
+    stmt = DataverseDeclaration()
+    | stmt = FunctionDeclaration()
+    | stmt = CreateStatement()
+    | stmt = LoadStatement()
+    | stmt = DropStatement()
+    | stmt = WriteStatement()
+    | stmt = SetStatement()
+    | stmt = InsertStatement()
+    | stmt = DeleteStatement()
+    | stmt = UpdateStatement()
+    | stmt = FeedStatement()
+    | stmt = CompactStatement()
+    | stmt = Query() <SEMICOLON>
+    | stmt = RefreshExternalDatasetStatement()
+    | stmt = RunStatement()
+  )
+  {
+    return stmt;
+  }
+}
+
+DataverseDecl DataverseDeclaration() throws ParseException:
+{
+  String dvName = null;
+}
+{
+  <USE> dvName = Identifier()
+    {
+      defaultDataverse = dvName;
+      return new DataverseDecl(new Identifier(dvName));
+    }
+}
+
+Statement CreateStatement() throws ParseException:
+{
+  String hint = null;
+  boolean dgen = false;
+  Statement stmt = null;
+}
+{
+  <CREATE>
+  (
+    {
+      hint = getHint(token);
+      if (hint != null && hint.startsWith(DGEN_HINT)) {
+        dgen = true;
+      }
+    }
+    stmt = TypeSpecification(hint, dgen)
+    | stmt = NodegroupSpecification()
+    | stmt = DatasetSpecification()
+    | stmt = IndexSpecification()
+    | stmt = DataverseSpecification()
+    | stmt = FunctionSpecification()
+    | stmt = FeedSpecification()
+    | stmt = FeedPolicySpecification()
+  )
+  {
+    return stmt;
+  }
+}
+
+TypeDecl TypeSpecification(String hint, boolean dgen) throws ParseException:
+{
+  Pair<Identifier,Identifier> nameComponents = null;
+  boolean ifNotExists = false;
+  TypeExpression typeExpr = null;
+}
+{
+  <TYPE> nameComponents = TypeName() ifNotExists = IfNotExists()
+  <AS> typeExpr = TypeExpr()
+    {
+      long numValues = -1;
+      String filename = null;
+      if (dgen) {
+        String splits[] = hint.split(" +");
+        if (splits.length != 3) {
+          throw new ParseException("Expecting /*+ dgen <filename> <numberOfItems> */");
+        }
+        filename = splits[1];
+        numValues = Long.parseLong(splits[2]);
+      }
+      TypeDataGen tddg = new TypeDataGen(dgen, filename, numValues);
+      return new TypeDecl(nameComponents.first, nameComponents.second, typeExpr, tddg, ifNotExists);
+    }
+}
+
+
+NodegroupDecl NodegroupSpecification() throws ParseException:
+{
+  String name = null;
+  String tmp = null;
+  boolean ifNotExists = false;
+  List<Identifier>ncNames = null;
+}
+{
+  <NODEGROUP> name = Identifier()
+  ifNotExists = IfNotExists() <ON> tmp = Identifier()
+    {
+      ncNames = new ArrayList<Identifier>();
+      ncNames.add(new Identifier(tmp));
+    }
+  ( <COMMA> tmp = Identifier()
+    {
+      ncNames.add(new Identifier(tmp));
+    }
+  )*
+    {
+      return new NodegroupDecl(new Identifier(name), ncNames, ifNotExists);
+    }
+}
+
+DatasetDecl DatasetSpecification() throws ParseException:
+{
+  Pair<Identifier,Identifier> nameComponents = null;
+  boolean ifNotExists = false;
+  String typeName = null;
+  String adapterName = null;
+  Map<String,String> properties = null;
+  Map<String,String> compactionPolicyProperties = null;
+  FunctionSignature appliedFunction = null;
+  List<List<String>> primaryKeyFields = null;
+  String nodeGroupName = null;
+  Map<String,String> hints = new HashMap<String,String>();
+  DatasetDecl dsetDecl = null;
+  boolean autogenerated = false;
+  String compactionPolicy = null;
+  boolean temp = false;
+  List<String> filterField = null;
+}
+{
+  (
+    <EXTERNAL> <DATASET> nameComponents = QualifiedName()
+    <LEFTPAREN> typeName = Identifier() <RIGHTPAREN>
+    ifNotExists = IfNotExists()
+    <USING> adapterName = AdapterName() properties = Configuration()
+    (<ON> nodeGroupName = Identifier() )?
+    ( <HINTS> hints = Properties() )?
+    ( <USING> <COMPACTION> <POLICY> compactionPolicy = CompactionPolicy() (LOOKAHEAD(1) compactionPolicyProperties = Configuration())? )?
+      {
+        ExternalDetailsDecl edd = new ExternalDetailsDecl();
+        edd.setAdapter(adapterName);
+        edd.setProperties(properties);
+        dsetDecl = new DatasetDecl(nameComponents.first,
+                                   nameComponents.second,
+                                   new Identifier(typeName),
+                                   nodeGroupName != null? new Identifier(nodeGroupName): null,
+                                   compactionPolicy,
+                                   compactionPolicyProperties,
+                                   hints,
+                                   DatasetType.EXTERNAL,
+                                   edd,
+                                   ifNotExists);
+      }
+
+    | (<INTERNAL> | <TEMPORARY> { temp = true; })? 
+    <DATASET> nameComponents = QualifiedName()
+    <LEFTPAREN> typeName = Identifier() <RIGHTPAREN>
+    ifNotExists = IfNotExists()
+    primaryKeyFields = PrimaryKey()
+    (<AUTOGENERATED> { autogenerated = true; } )?
+    (<ON> nodeGroupName = Identifier() )?
+    ( <HINTS> hints = Properties() )?
+    ( <USING> <COMPACTION> <POLICY> compactionPolicy = CompactionPolicy() (LOOKAHEAD(1) compactionPolicyProperties = Configuration())? )?
+    ( LOOKAHEAD(2) <WITH> <FILTER> <ON>  filterField = NestedField() )?
+      {
+        InternalDetailsDecl idd = new InternalDetailsDecl(primaryKeyFields,
+                                                          autogenerated,
+                                                          filterField,
+                                                          temp);
+        dsetDecl = new DatasetDecl(nameComponents.first,
+                                   nameComponents.second,
+                                   new Identifier(typeName),
+                                   nodeGroupName != null ? new Identifier(nodeGroupName) : null,
+                                   compactionPolicy,
+                                   compactionPolicyProperties,
+                                   hints,
+                                   DatasetType.INTERNAL,
+                                   idd,
+                                   ifNotExists);
+      }
+  )
+    {
+      return dsetDecl;
+    }
+}
+
+RefreshExternalDatasetStatement RefreshExternalDatasetStatement() throws ParseException:
+{
+  RefreshExternalDatasetStatement redss = new RefreshExternalDatasetStatement();
+  Pair<Identifier,Identifier> nameComponents = null;
+  String datasetName = null;
+}
+{
+    <REFRESH> <EXTERNAL> <DATASET> nameComponents = QualifiedName()
+    {
+    redss.setDataverseName(nameComponents.first);
+    redss.setDatasetName(nameComponents.second);
+    return redss;
+    }
+}
+
+RunStatement RunStatement() throws ParseException:
+{
+  String system = null;
+  String tmp;
+  ArrayList<String> parameters = new  ArrayList<String>();
+  Pair<Identifier,Identifier> nameComponentsFrom = null;
+  Pair<Identifier,Identifier> nameComponentsTo = null;
+}
+{
+  <RUN> system = Identifier()<LEFTPAREN> ( tmp = Identifier() [<COMMA>]
+    {
+      parameters.add(tmp);
+    }
+  )*<RIGHTPAREN>
+  <FROM> <DATASET> nameComponentsFrom  = QualifiedName()
+  <TO> <DATASET> nameComponentsTo  = QualifiedName()
+    {
+      return new RunStatement(system, parameters, nameComponentsFrom.first, nameComponentsFrom.second, nameComponentsTo.first, nameComponentsTo.second);
+    }
+}
+
+CreateIndexStatement IndexSpecification() throws ParseException:
+{
+  CreateIndexStatement cis = new CreateIndexStatement();
+  String indexName = null;
+  boolean ifNotExists = false;
+  Pair<Identifier,Identifier> nameComponents = null;
+  Pair<List<String>, TypeExpression> fieldPair = null;
+  IndexParams indexType = null;
+  boolean enforced = false;
+}
+{
+  <INDEX> indexName = Identifier()
+  ifNotExists = IfNotExists()
+  <ON> nameComponents = QualifiedName()
+  <LEFTPAREN> ( fieldPair = OpenField()
+    {
+      cis.addFieldExprPair(fieldPair);
+    }
+  ) (<COMMA> fieldPair = OpenField()
+    {
+      cis.addFieldExprPair(fieldPair);
+    }
+  )* <RIGHTPAREN> ( <TYPE> indexType = IndexType() )? ( <ENFORCED> { enforced = true; } )?
+    {
+      cis.setIndexName(new Identifier(indexName));
+      cis.setIfNotExists(ifNotExists);
+      cis.setDataverseName(nameComponents.first);
+      cis.setDatasetName(nameComponents.second);
+      if (indexType != null) {
+        cis.setIndexType(indexType.type);
+        cis.setGramLength(indexType.gramLength);
+      }
+      cis.setEnforced(enforced);
+      return cis;
+    }
+}
+
+String CompactionPolicy() throws ParseException :
+{
+  String compactionPolicy = null;
+}
+{
+  compactionPolicy = Identifier()
+    {
+      return compactionPolicy;
+    }
+}
+
+String FilterField() throws ParseException :
+{
+  String filterField = null;
+}
+{
+  filterField = Identifier()
+    {
+      return filterField;
+    }
+}
+
+IndexParams IndexType() throws ParseException:
+{
+  IndexType type = null;
+  int gramLength = 0;
+}
+{
+  (<BTREE>
+    {
+      type = IndexType.BTREE;
+    }
+  | <RTREE>
+    {
+      type = IndexType.RTREE;
+    }
+  | <KEYWORD>
+    {
+      type = IndexType.LENGTH_PARTITIONED_WORD_INVIX;
+    }
+  | <NGRAM> <LEFTPAREN> <INTEGER_LITERAL>
+    {
+      type = IndexType.LENGTH_PARTITIONED_NGRAM_INVIX;
+      gramLength = Integer.valueOf(token.image);
+    }
+  <RIGHTPAREN>)
+    {
+      return new IndexParams(type, gramLength);
+    }
+}
+
+CreateDataverseStatement DataverseSpecification() throws ParseException :
+{
+  String dvName = null;
+  boolean ifNotExists = false;
+  String format = null;
+}
+{
+  <DATAVERSE> dvName = Identifier()
+  ifNotExists = IfNotExists()
+  ( LOOKAHEAD(1) <WITH> <FORMAT> format = QuotedString() )?
+    {
+      return new CreateDataverseStatement(new Identifier(dvName), format, ifNotExists);
+    }
+}
+
+CreateFunctionStatement FunctionSpecification() throws ParseException:
+{
+  FunctionSignature signature;
+  boolean ifNotExists = false;
+  List<VarIdentifier> paramList = new ArrayList<VarIdentifier>();
+  String functionBody;
+  VarIdentifier var = null;
+  Expression functionBodyExpr;
+  Token beginPos;
+  Token endPos;
+  FunctionName fctName = null;
+
+  createNewScope();
+}
+{
+  <FUNCTION> fctName = FunctionName()
+  ifNotExists = IfNotExists()
+  paramList = ParameterList()
+  <LEFTBRACE>
+  {
+     beginPos = token;
+  }
+  functionBodyExpr = Expression() <RIGHTBRACE>
+    {
+      endPos = token;
+      functionBody = extractFragment(beginPos.beginLine, beginPos.beginColumn, endPos.beginLine, endPos.beginColumn);
+      // TODO use fctName.library
+      signature = new FunctionSignature(fctName.dataverse, fctName.function, paramList.size());
+      getCurrentScope().addFunctionDescriptor(signature, false);
+      removeCurrentScope();
+      return new CreateFunctionStatement(signature, paramList, functionBody, ifNotExists);
+    }
+}
+
+CreateFeedStatement FeedSpecification() throws ParseException:
+{
+  Pair<Identifier,Identifier> nameComponents = null;
+  boolean ifNotExists = false;
+  String adapterName = null;
+  Map<String,String> properties = null;
+  FunctionSignature appliedFunction = null;
+  CreateFeedStatement cfs = null;
+  Pair<Identifier,Identifier> sourceNameComponents = null;
+  
+}
+{
+  (
+    <SECONDARY> <FEED>  nameComponents = QualifiedName() ifNotExists = IfNotExists()
+      <FROM> <FEED> sourceNameComponents = QualifiedName() (appliedFunction = ApplyFunction())?
+      {
+        cfs = new CreateSecondaryFeedStatement(nameComponents,
+                                   sourceNameComponents, appliedFunction, ifNotExists);
+      }
+     |
+     (<PRIMARY>)? <FEED> nameComponents = QualifiedName() ifNotExists = IfNotExists()
+      <USING> adapterName = AdapterName() properties = Configuration() (appliedFunction = ApplyFunction())?  
+       {
+        cfs = new CreatePrimaryFeedStatement(nameComponents,
+                                    adapterName, properties, appliedFunction, ifNotExists);
+       }
+  )
+    {
+      return cfs;
+    }
+}
+
+CreateFeedPolicyStatement FeedPolicySpecification() throws ParseException:
+{
+  String policyName = null;  
+  String basePolicyName = null; 
+  String sourcePolicyFile = null;
+  String definition = null;
+  boolean ifNotExists = false;
+  Map<String,String> properties = null;
+  CreateFeedPolicyStatement cfps = null;
+}
+{
+  (
+    <INGESTION> <POLICY>  policyName = Identifier() ifNotExists = IfNotExists()
+      <FROM> 
+      (<POLICY> basePolicyName = Identifier() properties = Configuration() (<DEFINITION> definition = QuotedString())?  
+      {
+        cfps = new CreateFeedPolicyStatement(policyName,
+                                   basePolicyName, properties, definition, ifNotExists);
+      }
+     | <PATH> sourcePolicyFile = Identifier() (<DEFINITION> definition = QuotedString())?  
+       {
+        cfps = new CreateFeedPolicyStatement(policyName, sourcePolicyFile, definition, ifNotExists);
+       }
+     ) 
+       
+  )
+    {
+      return cfps;
+    }
+}
+
+
+
+List<VarIdentifier> ParameterList() throws ParseException:
+{
+  List<VarIdentifier> paramList = new ArrayList<VarIdentifier>();
+  VarIdentifier var = null;
+}
+{
+  <LEFTPAREN> (<IDENTIFIER>
+    {
+      var = new VarIdentifier();
+      var.setValue(token.image);
+      paramList.add(var);
+      getCurrentScope().addNewVarSymbolToScope(var);
+    }
+  (<COMMA> <IDENTIFIER>
+    {
+      var = new VarIdentifier();
+      var.setValue(token.image);
+      paramList.add(var);
+      getCurrentScope().addNewVarSymbolToScope(var);
+    }
+  )*)? <RIGHTPAREN>
+    {
+      return paramList;
+    }
+}
+
+boolean IfNotExists() throws ParseException:
+{
+}
+{
+  ( LOOKAHEAD(1) <IF> ("not exists"|"NOT EXISTS")
+    {
+      return true;
+    }
+  )?
+    {
+      return false;
+    }
+}
+
+FunctionSignature ApplyFunction() throws ParseException:
+{
+  FunctionName functioName = null;
+  FunctionSignature funcSig = null;
+}
+{
+  <APPLY> <FUNCTION> functioName = FunctionName()
+    {
+       String fqFunctionName = functioName.library == null ? functioName.function : functioName.library + "#" + functioName.function;
+       return new FunctionSignature(functioName.dataverse, fqFunctionName, 1);
+    }
+}
+
+String GetPolicy() throws ParseException:
+{
+  String policy = null;
+}
+{
+   <USING> <POLICY> policy = Identifier()
+   {
+     return policy;
+   }
+
+}
+
+FunctionSignature FunctionSignature() throws ParseException:
+{
+  FunctionName fctName = null;
+  int arity = 0;
+}
+{
+  fctName = FunctionName() <ATT> <INTEGER_LITERAL>
+    {
+      arity = new Integer(token.image);
+      if (arity < 0 && arity != FunctionIdentifier.VARARGS) {
+        throw new ParseException(" invalid arity:" + arity);
+      }
+
+      // TODO use fctName.library
+      String fqFunctionName = fctName.library == null ? fctName.function : fctName.library + "#" + fctName.function;
+      return new FunctionSignature(fctName.dataverse, fqFunctionName, arity);
+    }
+}
+
+List<List<String>> PrimaryKey() throws ParseException:
+{
+  List<String> tmp = null;
+  List<List<String>> primaryKeyFields = new ArrayList<List<String>>();
+}
+{
+  <PRIMARY> <KEY> tmp = NestedField()
+    {
+      primaryKeyFields.add(tmp);
+    }
+  ( <COMMA> tmp = NestedField()
+    {
+      primaryKeyFields.add(tmp);
+    }
+  )*
+    {
+      return primaryKeyFields;
+    }
+}
+
+Statement DropStatement() throws ParseException:
+{
+  String id = null;
+  Pair<Identifier,Identifier> pairId = null;
+  Triple<Identifier,Identifier,Identifier> tripleId = null;
+  FunctionSignature funcSig = null;
+  boolean ifExists = false;
+  Statement stmt = null;
+}
+{
+  <DROP>
+  (
+    <DATASET> pairId = QualifiedName() ifExists = IfExists()
+      {
+        stmt = new DropStatement(pairId.first, pairId.second, ifExists);
+      }
+    | <INDEX> tripleId = DoubleQualifiedName() ifExists = IfExists()
+      {
+        stmt = new IndexDropStatement(tripleId.first, tripleId.second, tripleId.third, ifExists);
+      }
+    | <NODEGROUP> id = Identifier() ifExists = IfExists()
+      {
+        stmt = new NodeGroupDropStatement(new Identifier(id), ifExists);
+      }
+    | <TYPE> pairId = TypeName() ifExists = IfExists()
+      {
+        stmt = new TypeDropStatement(pairId.first, pairId.second, ifExists);
+      }
+    | <DATAVERSE> id = Identifier() ifExists = IfExists()
+      {
+        stmt = new DataverseDropStatement(new Identifier(id), ifExists);
+      }
+    | <FUNCTION> funcSig = FunctionSignature() ifExists = IfExists()
+      {
+        stmt = new FunctionDropStatement(funcSig, ifExists);
+      }
+    | <FEED> pairId = QualifiedName() ifExists = IfExists()
+      {
+        stmt = new FeedDropStatement(pairId.first, pairId.second, ifExists);
+      }
+  )
+  {
+    return stmt;
+  }
+}
+
+boolean IfExists() throws ParseException :
+{
+}
+{
+  ( LOOKAHEAD(1) <IF> <EXISTS>
+    {
+      return true;
+    }
+  )?
+    {
+      return false;
+    }
+}
+
+InsertStatement InsertStatement() throws ParseException:
+{
+  Pair<Identifier,Identifier> nameComponents = null;
+  Query query;
+}
+{
+  <INSERT> <INTO> nameComponents = QualifiedName() query = Query()
+    {
+      query.setTopLevel(false);
+      return new InsertStatement(nameComponents.first, nameComponents.second, query, getVarCounter());
+    }
+}
+
+DeleteStatement DeleteStatement() throws ParseException:
+{
+  VariableExpr var = null;
+  Expression condition = null;
+  Pair<Identifier, Identifier> nameComponents;
+  // This is related to the new metadata lock management
+  setDataverses(new ArrayList<String>());
+  setDatasets(new ArrayList<String>());
+
+}
+{
+  <DELETE> var = Variable()
+    {
+      getCurrentScope().addNewVarSymbolToScope(var.getVar());
+    }
+  <FROM> nameComponents  = QualifiedName()
+  (<WHERE> condition = Expression())?
+    {
+      // First we get the dataverses and datasets that we want to lock
+      List<String> dataverses = getDataverses();
+      List<String> datasets = getDatasets();
+      // we remove the pointer to the dataverses and datasets
+      setDataverses(null);
+      setDatasets(null);
+      return new DeleteStatement(var, nameComponents.first, nameComponents.second,
+          condition, getVarCounter(), dataverses, datasets);
+    }
+}
+
+UpdateStatement UpdateStatement() throws ParseException:
+{
+  VariableExpr vars;
+  Expression target;
+  Expression condition;
+  UpdateClause uc;
+  List<UpdateClause> ucs = new ArrayList<UpdateClause>();
+}
+{
+  <UPDATE> vars = Variable() <IN> target = Expression()
+  <WHERE> condition = Expression()
+  <LEFTPAREN> (uc = UpdateClause()
+    {
+      ucs.add(uc);
+    }
+  (<COMMA> uc = UpdateClause()
+    {
+      ucs.add(uc);
+    }
+  )*) <RIGHTPAREN>
+    {
+      return new UpdateStatement(vars, target, condition, ucs);
+    }
+}
+
+UpdateClause UpdateClause() throws ParseException:
+{
+  Expression target = null;
+  Expression value = null ;
+  InsertStatement is = null;
+  DeleteStatement ds = null;
+  UpdateStatement us = null;
+  Expression condition = null;
+  UpdateClause ifbranch = null;
+  UpdateClause elsebranch = null;
+}
+{
+   (<SET> target = Expression() <EQ> value = Expression()
+   | is = InsertStatement()
+   | ds = DeleteStatement()
+   | us = UpdateStatement()
+   | <IF> <LEFTPAREN> condition = Expression() <RIGHTPAREN>
+     <THEN> ifbranch = UpdateClause()
+     [LOOKAHEAD(1) <ELSE> elsebranch = UpdateClause()]
+     {
+       return new UpdateClause(target, value, is, ds, us, condition, ifbranch, elsebranch);
+     }
+   )
+}
+
+Statement SetStatement() throws ParseException:
+{
+  String pn = null;
+  String pv = null;
+}
+{
+  <SET> pn = Identifier() pv = QuotedString()
+    {
+      return new SetStatement(pn, pv);
+    }
+}
+
+Statement WriteStatement() throws ParseException:
+{
+  String nodeName = null;
+  String fileName = null;
+  Query query;
+  String writerClass = null;
+  Pair<Identifier,Identifier> nameComponents = null;
+}
+{
+  <WRITE> <OUTPUT> <TO> nodeName = Identifier() <COLON> fileName = QuotedString()
+    ( <USING> writerClass = QuotedString() )?
+    {
+      return new WriteStatement(new Identifier(nodeName), fileName, writerClass);
+    }
+}
+
+LoadStatement LoadStatement() throws ParseException:
+{
+  Identifier dataverseName = null;
+  Identifier datasetName = null;
+  boolean alreadySorted = false;
+  String adapterName;
+  Map<String,String> properties;
+  Pair<Identifier,Identifier> nameComponents = null;
+}
+{
+  <LOAD> <DATASET> nameComponents = QualifiedName()
+    {
+      dataverseName = nameComponents.first;
+      datasetName = nameComponents.second;
+    }
+  <USING> adapterName = AdapterName() properties = Configuration()
+  (<PRESORTED>
+    {
+      alreadySorted = true;
+    }
+  )?
+    {
+      return new LoadStatement(dataverseName, datasetName, adapterName, properties, alreadySorted);
+    }
+}
+
+
+String AdapterName() throws ParseException :
+{
+  String adapterName = null;
+}
+{
+  adapterName = Identifier()
+    {
+      return adapterName;
+    }
+}
+
+Statement CompactStatement() throws ParseException:
+{
+  Pair<Identifier,Identifier> nameComponents = null;
+  Statement stmt = null;
+}
+{
+  <COMPACT> <DATASET> nameComponents = QualifiedName()
+    {
+      stmt = new CompactStatement(nameComponents.first, nameComponents.second);
+    }
+    {
+      return stmt;
+    }
+}
+
+Statement FeedStatement() throws ParseException:
+{
+  Pair<Identifier,Identifier> feedNameComponents = null;
+  Pair<Identifier,Identifier> datasetNameComponents = null;
+
+  Map<String,String> configuration = null;
+  Statement stmt = null;
+  String policy = null;
+}
+{
+  (
+    <CONNECT> <FEED> feedNameComponents = QualifiedName() <TO> <DATASET> datasetNameComponents = QualifiedName() (policy = GetPolicy())?
+      {
+        stmt = new ConnectFeedStatement(feedNameComponents, datasetNameComponents, policy, getVarCounter());
+      }
+    | <DISCONNECT> <FEED> feedNameComponents = QualifiedName() <FROM> <DATASET> datasetNameComponents = QualifiedName()
+      {
+        stmt = new DisconnectFeedStatement(feedNameComponents, datasetNameComponents);
+      }
+  )
+    {
+      return stmt;
+    }
+}
+
+Map<String,String> Configuration()  throws ParseException :
+{
+    Map<String,String> configuration = new LinkedHashMap<String,String>();
+    Pair<String, String> keyValuePair = null;
+}
+{
+  <LEFTPAREN> ( keyValuePair = KeyValuePair()
+    {
+      configuration.put(keyValuePair.first, keyValuePair.second);
+    }
+  ( <COMMA> keyValuePair = KeyValuePair()
+    {
+      configuration.put(keyValuePair.first, keyValuePair.second);
+    }
+  )* )? <RIGHTPAREN>
+    {
+      return configuration;
+    }
+}
+
+Pair<String, String> KeyValuePair() throws ParseException:
+{
+  String key;
+  String value;
+}
+{
+  <LEFTPAREN> key = QuotedString() <EQ> value = QuotedString() <RIGHTPAREN>
+    {
+      return new Pair<String, String>(key, value);
+    }
+}
+
+Map<String,String> Properties() throws ParseException:
+{
+  Map<String,String> properties = new HashMap<String,String>();
+  Pair<String, String> property;
+}
+{
+  (LOOKAHEAD(1) <LEFTPAREN> property = Property()
+    {
+      properties.put(property.first, property.second);
+    }
+  ( <COMMA> property = Property()
+    {
+      properties.put(property.first, property.second);
+    }
+  )* <RIGHTPAREN> )?
+    {
+      return properties;
+    }
+}
+
+Pair<String, String> Property() throws ParseException:
+{
+  String key;
+  String value;
+}
+{
+  key = Identifier() <EQ> ( value = QuotedString() | <INTEGER_LITERAL>
+    {
+      try {
+        value = "" + Long.valueOf(token.image);
+      } catch (NumberFormatException nfe) {
+        throw new ParseException("inapproriate value: " + token.image);
+      }
+    }
+  )
+    {
+      return new Pair<String, String>(key.toUpperCase(), value);
+    }
+}
+
+TypeExpression IndexedTypeExpr() throws ParseException:
+{
+  TypeExpression typeExpr = null;
+}
+{
+  (
+      typeExpr = TypeReference()
+    | typeExpr = OrderedListTypeDef()
+    | typeExpr = UnorderedListTypeDef()
+  )
+  {
+    return typeExpr;
+  }
+}
+
+TypeExpression TypeExpr() throws ParseException:
+{
+  TypeExpression typeExpr = null;
+}
+{
+  (
+      typeExpr = RecordTypeDef()
+    | typeExpr = TypeReference()
+    | typeExpr = OrderedListTypeDef()
+    | typeExpr = UnorderedListTypeDef()
+  )
+  {
+    return typeExpr;
+  }
+}
+
+RecordTypeDefinition RecordTypeDef() throws ParseException:
+{
+  RecordTypeDefinition recType = new RecordTypeDefinition();
+  RecordTypeDefinition.RecordKind recordKind = null;
+}
+{
+  ( <CLOSED> { recordKind = RecordTypeDefinition.RecordKind.CLOSED; }
+    | <OPEN> { recordKind = RecordTypeDefinition.RecordKind.OPEN; } )?
+   <LEFTBRACE>
+    {
+      String hint = getHint(token);
+      if (hint != null) {
+        String splits[] = hint.split(" +");
+        if (splits[0].equals(GEN_FIELDS_HINT)) {
+          if (splits.length != 5) {
+            throw new ParseException("Expecting: /*+ gen-fields <type> <min> <max> <prefix>*/");
+          }
+          if (!splits[1].equals("int")) {
+            throw new ParseException("The only supported type for gen-fields is int.");
+          }
+          UndeclaredFieldsDataGen ufdg = new UndeclaredFieldsDataGen(UndeclaredFieldsDataGen.Type.INT,
+             Integer.parseInt(splits[2]), Integer.parseInt(splits[3]), splits[4]);
+          recType.setUndeclaredFieldsDataGen(ufdg);
+        }
+      }
+
+    }
+        (
+          RecordField(recType)
+          ( <COMMA>  RecordField(recType) )*
+        )?
+   <RIGHTBRACE>
+   {
+      if (recordKind == null) {
+        recordKind = RecordTypeDefinition.RecordKind.OPEN;
+      }
+      recType.setRecordKind(recordKind);
+      return recType;
+   }
+}
+
+void RecordField(RecordTypeDefinition recType) throws ParseException:
+{
+  String fieldName;
+  TypeExpression type = null;
+  boolean nullable = false;
+}
+{
+  fieldName = Identifier()
+    {
+      String hint = getHint(token);
+      IRecordFieldDataGen rfdg = hint != null ? parseFieldDataGen(hint) : null;
+    }
+  <COLON> type =  TypeExpr() (<QUES> { nullable = true; } )?
+    {
+      recType.addField(fieldName, type, nullable, rfdg);
+    }
+}
+
+TypeReferenceExpression TypeReference() throws ParseException:
+{
+  String id = null;
+}
+{
+ id = Identifier()
+   {
+     if (id.equalsIgnoreCase("int")) {
+        id = "int64";
+     }
+
+     return new TypeReferenceExpression(new Identifier(id));
+   }
+}
+
+OrderedListTypeDefinition OrderedListTypeDef() throws ParseException:
+{
+  TypeExpression type = null;
+}
+{
+  <LEFTBRACKET>
+    ( type =  TypeExpr() )
+  <RIGHTBRACKET>
+  {
+    return new OrderedListTypeDefinition(type);
+  }
+}
+
+
+UnorderedListTypeDefinition UnorderedListTypeDef() throws ParseException:
+{
+  TypeExpression type = null;
+}
+{
+  <LEFTDBLBRACE>
+    ( type =  TypeExpr() )
+  <RIGHTDBLBRACE>
+  {
+    return new UnorderedListTypeDefinition(type);
+  }
+}
+
+FunctionName FunctionName() throws ParseException:
+{
+  String first = null;
+  String second = null;
+  String third = null;
+  boolean secondAfterDot = false;
+}
+{
+  first = Identifier() 
+  {
+    FunctionName result = new FunctionName();
+    result.hint = getHint(token);
+  } 
+  ( <DOT> second = Identifier()
+    {
+      secondAfterDot = true;
+    }
+  (<SHARP> third = Identifier())? | <SHARP> second = Identifier() )?
+    {
+      if (second == null) {
+        result.dataverse = defaultDataverse;
+        result.library = null;
+        result.function = first;
+      } else if (third == null) {
+        if (secondAfterDot) {
+          result.dataverse = first;
+          result.library   = null;
+          result.function = second;
+        } else {
+          result.dataverse = defaultDataverse;
+          result.library   = first;
+          result.function = second;
+        }
+      } else {
+        result.dataverse = first;
+        result.library   = second;
+        result.function  = third;
+      }
+
+      if (result.function.equalsIgnoreCase("int")) {
+            result.function = "int64";
+      }
+      return result;
+    }
+}
+
+Pair<Identifier,Identifier> TypeName() throws ParseException:
+{
+  Pair<Identifier,Identifier> name = null;
+}
+{
+  name = QualifiedName()
+    {
+      if (name.first == null) {
+        name.first = new Identifier(defaultDataverse);
+      }
+      return name;
+    }
+}
+
+String Identifier() throws ParseException:
+{
+  String lit = null;
+}
+{
+  (<IDENTIFIER>
+    {
+      return token.image;
+    }
+  | lit = QuotedString()
+    {
+      return lit;
+    }
+  )
+}
+
+Pair<List<String>, TypeExpression> OpenField() throws ParseException:
+{
+  TypeExpression fieldType = null;
+  List<String> fieldList = null;
+}
+{
+  fieldList = NestedField()
+  ( <COLON> fieldType =  IndexedTypeExpr() )?
+  {
+    return new Pair<List<String>, TypeExpression>(fieldList, fieldType);
+  }
+}
+
+List<String> NestedField() throws ParseException:
+{
+  List<String> exprList = new ArrayList<String>();
+  String lit = null;
+}
+{
+  lit = Identifier()
+  {
+    exprList.add(lit);
+  }
+  (<DOT>
+    lit = Identifier()
+    {
+      exprList.add(lit);
+    }
+  )*
+  {
+    return exprList;
+  }
+}
+
+
+String QuotedString() throws ParseException:
+{
+}
+{
+  <QUOTED_STRING>
+    {
+      return removeQuotesAndEscapes(token.image);
+    }
+}
+
+
+String StringLiteral() throws ParseException:
+{
+}
+{
+  <STRING_LITERAL>
+    {
+      return removeQuotesAndEscapes(token.image);
+    }
+}
+
+Pair<Identifier,Identifier> QualifiedName() throws ParseException:
+{
+  String first = null;
+  String second = null;
+}
+{
+  first = Identifier() (<DOT> second = Identifier())?
+  {
+    Identifier id1 = null;
+    Identifier id2 = null;
+    if (second == null) {
+      id2 = new Identifier(first);
+    } else
+    {
+      id1 = new Identifier(first);
+      id2 = new Identifier(second);
+    }
+    return new Pair<Identifier,Identifier>(id1, id2);
+  }
+}
+
+Triple<Identifier,Identifier,Identifier> DoubleQualifiedName() throws ParseException:
+{
+  String first = null;
+  String second = null;
+  String third = null;
+}
+{
+  first = Identifier() <DOT> second = Identifier() (<DOT> third = Identifier())?
+  {
+    Identifier id1 = null;
+    Identifier id2 = null;
+    Identifier id3 = null;
+    if (third == null) {
+      id2 = new Identifier(first);
+      id3 = new Identifier(second);
+    } else {
+      id1 = new Identifier(first);
+      id2 = new Identifier(second);
+      id3 = new Identifier(third);
+    }
+    return new Triple<Identifier,Identifier,Identifier>(id1, id2, id3);
+  }
+}
+
+FunctionDecl FunctionDeclaration() throws ParseException:
+{
+  FunctionDecl funcDecl;
+  FunctionSignature signature;
+  String functionName;
+  List<VarIdentifier> paramList = new ArrayList<VarIdentifier>();
+  Expression funcBody;
+  createNewScope();
+}
+{
+  <DECLARE> <FUNCTION> functionName = Identifier()
+  paramList = ParameterList()
+  <LEFTBRACE> funcBody = Expression() <RIGHTBRACE>
+    {
+      signature = new FunctionSignature(defaultDataverse, functionName, paramList.size());
+      getCurrentScope().addFunctionDescriptor(signature, false);
+      funcDecl = new FunctionDecl(signature, paramList, funcBody);
+      removeCurrentScope();
+      return funcDecl;
+    }
+}
+
+
+Query Query() throws ParseException:
+{
+  Query query = new Query();
+  // we set the pointers to the dataverses and datasets lists to fill them with entities to be locked
+  setDataverses(query.getDataverses());
+  setDatasets(query.getDatasets());
+  Expression expr;
+}
+{
+  (
+  expr = Expression()
+  |
+  expr = SelectExpression(false)
+  )
+  {
+    query.setBody(expr);
+    query.setVarCounter(getVarCounter());
+    // we remove the pointers to the locked entities before we return the query object
+    setDataverses(null);
+    setDatasets(null);
+    return query;
+  }
+}
+
+
+
+Expression Expression():
+{
+  Expression expr = null;
+  Expression exprP = null;
+}
+{
+(
+    LOOKAHEAD(2)
+    expr = OperatorExpr()
+    | expr = IfThenElse()
+    | expr = QuantifiedExpression()
+)
+    {
+      return (exprP==null) ? expr : exprP;
+    }
+}
+
+
+
+Expression OperatorExpr()throws ParseException:
+{
+  OperatorExpr op = null;
+  Expression operand = null;
+}
+{
+    operand = AndExpr()
+    (
+
+      <OR>
+      {
+        if (op == null) {
+          op = new OperatorExpr();
+          op.addOperand(operand);
+        op.setCurrentop(true);
+        }
+      op.addOperator(token.image);
+    }
+
+    operand = AndExpr()
+    {
+      op.addOperand(operand);
+    }
+
+    )*
+
+    {
+      return op==null? operand: op;
+    }
+}
+
+Expression AndExpr()throws ParseException:
+{
+  OperatorExpr op = null;
+  Expression operand = null;
+}
+{
+    operand = RelExpr()
+    (
+
+      <AND>
+      {
+        if (op == null) {
+          op = new OperatorExpr();
+          op.addOperand(operand);
+        op.setCurrentop(true);
+        }
+      op.addOperator(token.image);
+    }
+
+    operand = RelExpr()
+    {
+      op.addOperand(operand);
+    }
+
+    )*
+
+    {
+      return op==null? operand: op;
+    }
+}
+
+
+
+Expression RelExpr()throws ParseException:
+{
+  OperatorExpr op = null;
+  Expression operand = null;
+  boolean broadcast = false;
+  IExpressionAnnotation annotation = null;
+}
+{
+    operand = AddExpr()
+    {
+      if (operand instanceof VariableExpr) {
+        String hint = getHint(token);
+        if (hint != null && hint.equals(BROADCAST_JOIN_HINT)) {
+          broadcast = true;
+        }
+      }
+    }
+
+    (
+      LOOKAHEAD(2)( <LT> | <GT> | <LE> | <GE> | <EQ> | <NE> |<SIMILAR>)
+        {
+          String mhint = getHint(token);
+          if (mhint != null) {
+            if (mhint.equals(INDEXED_NESTED_LOOP_JOIN_HINT)) {
+            annotation = IndexedNLJoinExpressionAnnotation.INSTANCE;
+          } else if (mhint.equals(SKIP_SECONDARY_INDEX_SEARCH_HINT)) {
+            annotation = SkipSecondaryIndexSearchExpressionAnnotation.INSTANCE;
+          }
+        }
+          if (op == null) {
+            op = new OperatorExpr();
+            op.addOperand(operand, broadcast);
+          op.setCurrentop(true);
+          broadcast = false;
+          }
+        op.addOperator(token.image);
+      }
+
+       operand = AddExpr()
+      {
+         broadcast = false;
+         if (operand instanceof VariableExpr) {
+           String hint = getHint(token);
+           if (hint != null && hint.equals(BROADCAST_JOIN_HINT)) {
+             broadcast = true;
+           }
+         }
+         op.addOperand(operand, broadcast);
+      }
+    )?
+
+     {
+       if (annotation != null) {
+         op.addHint(annotation);
+       }
+       return op==null? operand: op;
+     }
+}
+
+Expression AddExpr()throws ParseException:
+{
+  OperatorExpr op = null;
+  Expression operand = null;
+}
+{
+    operand = MultExpr()
+    ( 
+       LOOKAHEAD(1)
+      (<PLUS> | <MINUS>)
+      {
+        if (op == null) {
+          op = new OperatorExpr();
+        op.addOperand(operand);
+        op.setCurrentop(true);
+        }
+      ((OperatorExpr)op).addOperator(token.image);
+    }
+
+    operand = MultExpr()
+    {
+      op.addOperand(operand);
+    }
+    )*
+
+    {
+       return op==null? operand: op;
+    }
+}
+
+Expression MultExpr()throws ParseException:
+{
+  OperatorExpr op = null;
+  Expression operand = null;
+}
+{
+    operand = UnaryExpr()
+
+    (( <MUL> | <DIV> | <MOD> | <CARET> | <IDIV>)
+      {
+        if (op == null) {
+          op = new OperatorExpr();
+        op.addOperand(operand);
+        op.setCurrentop(true);
+        }
+      op.addOperator(token.image);
+    }
+    operand = UnaryExpr()
+    {
+       op.addOperand(operand);
+    }
+    )*
+
+     {
+       return op==null?operand:op;
+     }
+}
+
+Expression UnaryExpr() throws ParseException:
+{
+    Expression uexpr = null;
+    Expression expr = null;
+}
+{   
+    ( (<PLUS> | <MINUS>)
+    {
+        uexpr = new UnaryExpr();
+        if("+".equals(token.image))
+            ((UnaryExpr)uexpr).setSign(Sign.POSITIVE);
+        else if("-".equals(token.image))
+            ((UnaryExpr)uexpr).setSign(Sign.NEGATIVE);
+        else
+            throw new ParseException();
+    }
+    )?
+
+    expr = ValueExpr()
+    {
+        if(uexpr!=null){
+            ((UnaryExpr)uexpr).setExpr(expr);
+            return uexpr;
+        }
+        else{
+            return expr;
+        }
+    }
+}
+
+Expression ValueExpr()throws ParseException:
+{
+  Expression expr = null;
+  Identifier ident = null;
+  AbstractAccessor fa = null;
+  Expression indexExpr = null;
+}
+{
+  expr = PrimaryExpr() (
+     ident = Field()
+     {
+      fa = (fa == null ? new FieldAccessor(expr, ident)
+                       : new FieldAccessor(fa, ident));
+     }
+     | indexExpr = Index()
+     {
+      fa = (fa == null ? new IndexAccessor(expr, indexExpr)
+                       : new IndexAccessor(fa, indexExpr));
+     }
+    )*
+    {
+      return fa == null ? expr : fa;
+    }
+}
+
+Identifier Field() throws ParseException:
+{
+  String ident = null;
+}
+{
+   <DOT> ident = Identifier()
+    {
+      return new Identifier(ident);
+    }
+}
+
+Expression Index() throws ParseException:
+{
+    Expression expr = null;
+}
+{
+  <LEFTBRACKET> ( expr = Expression()
+    {
+        if(expr.getKind() == Expression.Kind.LITERAL_EXPRESSION)
+        {
+            Literal lit = ((LiteralExpr)expr).getValue();
+            if(lit.getLiteralType() != Literal.Type.INTEGER &&
+               lit.getLiteralType() != Literal.Type.LONG) {
+                throw new ParseException("Index should be an INTEGER");
+            }
+        }
+    }
+
+      | <QUES> // ANY
+
+      )
+
+   <RIGHTBRACKET>
+    {
+      return expr;
+    }
+}
+
+
+Expression PrimaryExpr()throws ParseException:
+{
+  Expression expr = null;
+}
+{
+  ( LOOKAHEAD(4)
+    expr = FunctionCallExpr()
+  | expr = Literal()
+  | expr = VariableRef()
+  | expr = ListConstructor()
+  | expr = RecordConstructor()
+  | expr = ParenthesizedExpression()
+  )
+    {
+      return expr;
+    }
+}
+
+Expression Literal() throws ParseException:
+{
+  LiteralExpr lit = new LiteralExpr();
+  String str = null;
+}
+{
+  ( str = StringLiteral()
+    {
+      lit.setValue(new StringLiteral(str));
+    }
+  | <INTEGER_LITERAL>
+    {
+      lit.setValue(new LongIntegerLiteral(new Long(token.image)));
+    }
+  | <FLOAT_LITERAL>
+    {
+      lit.setValue(new FloatLiteral(new Float(token.image)));
+    }
+  | <DOUBLE_LITERAL>
+    {
+      lit.setValue(new DoubleLiteral(new Double(token.image)));
+    }
+  | <NULL>
+    {
+      lit.setValue(NullLiteral.INSTANCE);
+    }
+  | <TRUE>
+    {
+      lit.setValue(TrueLiteral.INSTANCE);
+    }
+  | <FALSE>
+    {
+      lit.setValue(FalseLiteral.INSTANCE);
+    }
+  )
+    {
+      return lit;
+    }
+}
+
+
+VariableExpr VariableRef() throws ParseException:
+{
+    VariableExpr varExp = new VariableExpr();
+    VarIdentifier var = new VarIdentifier();
+}
+{
+    { String id = null; }
+    (<IDENTIFIER> { id = token.image; } | id = QuotedString())
+    {
+     Identifier ident = lookupSymbol(id);
+     if (isInForbiddenScopes(id)) {
+       throw new ParseException("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(ident != null) { // exist such ident
+       varExp.setIsNewVar(false);
+       varExp.setVar((VarIdentifier)ident);
+     } else {
+       varExp.setVar(var);
+     }
+     var.setValue(id);
+     return varExp;
+    }
+}
+
+
+VariableExpr Variable() throws ParseException:
+{
+    VariableExpr varExp = new VariableExpr();
+    VarIdentifier var = new VarIdentifier();
+}
+{
+    { String id = null; }
+    (<IDENTIFIER> { id = token.image; } | id = QuotedString())
+    {
+     Identifier ident = lookupSymbol(id);
+     if(ident != null) { // exist such ident
+       varExp.setIsNewVar(false);
+     }
+     varExp.setVar(var);
+     var.setValue(token.image);
+     return varExp;
+    }
+}
+
+Expression ListConstructor() throws ParseException:
+{
+    Expression expr = null;
+}
+{
+    (
+        expr = OrderedListConstructor() | expr = UnorderedListConstructor()
+    )
+
+    {
+      return expr;
+    }
+}
+
+
+ListConstructor OrderedListConstructor() throws ParseException:
+{
+      ListConstructor expr = new ListConstructor();
+      List<Expression> exprList = null;
+      expr.setType(ListConstructor.Type.ORDERED_LIST_CONSTRUCTOR);
+}
+{
+    <LEFTBRACKET> exprList = ExpressionList() <RIGHTBRACKET>
+    {
+      expr.setExprList(exprList);
+      return expr;
+    }
+}
+
+ListConstructor UnorderedListConstructor() throws ParseException:
+{
+      ListConstructor expr = new ListConstructor();
+      List<Expression> exprList = null;
+      expr.setType(ListConstructor.Type.UNORDERED_LIST_CONSTRUCTOR);
+}
+{
+    <LEFTDBLBRACE> exprList = ExpressionList() <RIGHTDBLBRACE>
+    {
+      expr.setExprList(exprList);
+      return expr;
+    }
+}
+
+List<Expression> ExpressionList() throws ParseException:
+{
+      Expression expr = null;
+      List<Expression> list = null;
+      List<Expression> exprList = new ArrayList<Expression>();
+}
+{
+    (
+      expr = Expression() { exprList.add(expr); }
+      (LOOKAHEAD(1) <COMMA> list = ExpressionList() { exprList.addAll(list); })?
+    )?
+    (LOOKAHEAD(1) Comma())?
+    {
+        return exprList;
+    }
+}
+
+void Comma():
+{}
+{
+   <COMMA>
+}
+
+RecordConstructor RecordConstructor() throws ParseException:
+{
+      RecordConstructor expr = new RecordConstructor();
+      FieldBinding tmp = null;
+      List<FieldBinding> fbList = new ArrayList<FieldBinding>();
+}
+{
+    <LEFTBRACE> (tmp = FieldBinding()
+    {
+      fbList.add(tmp);
+    }
+    (<COMMA> tmp = FieldBinding() { fbList.add(tmp);  })*)? <RIGHTBRACE>
+    {
+      expr.setFbList(fbList);
+      return expr;
+    }
+}
+
+FieldBinding FieldBinding() throws ParseException:
+{
+    FieldBinding fb = new FieldBinding();
+    Expression left, right;
+}
+{
+    left = Expression() <COLON> right = Expression()
+    {
+      fb.setLeftExpr(left);
+      fb.setRightExpr(right);
+      return fb;
+    }
+}
+
+
+Expression FunctionCallExpr() throws ParseException:
+{
+  CallExpr callExpr;
+  List<Expression> argList = new ArrayList<Expression>();
+  Expression tmp;
+  int arity = 0;
+  FunctionName funcName = null;
+  String hint = null;
+}
+{
+  funcName = FunctionName()
+    {
+      hint = funcName.hint;
+    }
+  <LEFTPAREN> (tmp = Expression()
+    {
+      argList.add(tmp);
+      arity ++;
+    }
+  (<COMMA> tmp = Expression()
+    {
+      argList.add(tmp);
+      arity++;
+    }
+  )*)? <RIGHTPAREN>
+    {
+      // TODO use funcName.library
+      String fqFunctionName = funcName.library == null ? funcName.function : funcName.library + "#" + funcName.function;
+      FunctionSignature signature
+        = lookupFunctionSignature(funcName.dataverse, fqFunctionName, arity);
+      if (signature == null) {
+        signature = new FunctionSignature(funcName.dataverse, fqFunctionName, arity);
+      }
+      callExpr = new CallExpr(signature,argList);
+      if (hint != null) {
+        if (hint.startsWith(INDEXED_NESTED_LOOP_JOIN_HINT)) {
+          callExpr.addHint(IndexedNLJoinExpressionAnnotation.INSTANCE);
+        } else if (hint.startsWith(SKIP_SECONDARY_INDEX_SEARCH_HINT)) {
+          callExpr.addHint(SkipSecondaryIndexSearchExpressionAnnotation.INSTANCE);
+        }
+      }
+      return callExpr;
+    }
+}
+
+Expression ParenthesizedExpression() throws ParseException:
+{
+  Expression expr;
+}
+{
+    (
+    LOOKAHEAD(2)
+    <LEFTPAREN> expr = Expression() <RIGHTPAREN>
+    |
+    expr = Subquery()
+    )
+    {
+      return expr;
+    }
+}
+
+Expression IfThenElse() throws ParseException:
+{
+  Expression condExpr;
+  Expression thenExpr;
+  Expression elseExpr;
+  IfExpr ifExpr = new IfExpr();
+}
+{
+    <IF> <LEFTPAREN> condExpr = Expression() <RIGHTPAREN> <THEN> thenExpr = Expression() <ELSE> elseExpr = Expression()
+
+    {
+      ifExpr.setCondExpr(condExpr);
+      ifExpr.setThenExpr(thenExpr);
+      ifExpr.setElseExpr(elseExpr);
+      return ifExpr;
+    }
+}
+
+SelectExpression SelectExpression(boolean subquery) throws ParseException: {
+  List<LetClause> letClauses = new ArrayList<LetClause>();
+  SelectSetOperation selectSetOperation;
+  OrderbyClause orderbyClause = null;
+  LimitClause limitClause = null;
+  createNewScope();
+} {
+    ( letClauses = LetClause() )?
+    selectSetOperation = SelectSetOperation()
+    (orderbyClause = OrderbyClause() {})?
+    (limitClause = LimitClause() {})?
+    {
+      return new SelectExpression(letClauses, selectSetOperation, orderbyClause, limitClause, subquery);
+    }
+}
+
+SelectSetOperation SelectSetOperation() throws ParseException: {
+  SetOperationInput setOperationInputLeft;
+  List<SetOperationRight> setOperationRights = new ArrayList<SetOperationRight>();
+}
+{
+  {
+      SelectBlock selectBlockLeft = null;
+      SelectExpression subqueryLeft = null;
+      Expression expr = null;
+  }
+  selectBlockLeft = SelectBlock()
+  {
+     setOperationInputLeft = new SetOperationInput(selectBlockLeft, subqueryLeft);
+  }
+  (
+    {
+      SetOpType opType = SetOpType.UNION;
+      boolean setSemantics = true;
+      SelectBlock selectBlockRight = null;
+      SelectExpression subqueryRight = null;
+    }
+    (<UNION> {opType = SetOpType.UNION;} |<INTERSECT> {opType = SetOpType.INTERSECT;} |<EXCEPT> {opType = SetOpType.EXCEPT;}) (<ALL> {setSemantics = false;} )?
+    (selectBlockRight = SelectBlock()| subqueryRight = Subquery())
+    {
+        setOperationRights.add(new SetOperationRight(opType, setSemantics, new SetOperationInput(selectBlockRight, subqueryRight)));
+    }
+  )*
+  {
+    return new SelectSetOperation(setOperationInputLeft, setOperationRights);
+  }
+}
+
+SelectExpression Subquery() throws ParseException: {
+   SelectExpression selectExpr = null;
+}
+{
+  <LEFTPAREN> selectExpr = SelectExpression(true) {} <RIGHTPAREN>
+  {
+    return selectExpr;
+  }
+}
+
+SelectBlock SelectBlock() throws ParseException: {
+  SelectClause selectClause = null;
+  FromClause fromClause = null;
+  List<LetClause> fromLetClauses = null;
+  WhereClause whereClause = null;
+  GroupbyClause groupbyClause = null;
+  List<LetClause> gbyLetClauses = null;
+  HavingClause havingClause = null;
+}
+{
+  (
+     selectClause = SelectClause()
+     (
+        LOOKAHEAD(1)
+        fromClause = FromClause()
+        (
+            LOOKAHEAD(1)
+            fromLetClauses = LetClause()
+        )?
+     )?
+     (whereClause = WhereClause())?
+     (
+        groupbyClause = GroupbyClause()
+        (
+            LOOKAHEAD(1)
+            gbyLetClauses = LetClause()
+        )?
+        (havingClause = HavingClause())?
+     )?
+    |
+     fromClause = FromClause()
+     (
+        LOOKAHEAD(1)
+        fromLetClauses = LetClause()
+     )?
+     (whereClause = WhereClause())?
+     (
+        groupbyClause = GroupbyClause()
+        (
+            gbyLetClauses = LetClause()
+        )?
+        (havingClause = HavingClause())?
+     )?
+     selectClause = SelectClause()
+  )
+  {
+    return new SelectBlock(selectClause, fromClause, fromLetClauses, whereClause, groupbyClause, gbyLetClauses, havingClause);
+  }
+}
+
+SelectClause SelectClause() throws ParseException: {
+  SelectRegular selectRegular = null;
+  SelectElement selectElement = null;
+  boolean distinct = false;
+}
+{
+  <SELECT> (<ALL>|<DISTINCT> {distinct = true; } )? 
+  (
+    selectRegular = SelectRegular()
+    | 
+    selectElement = SelectElement()
+  )
+  {
+    return new SelectClause(selectElement, selectRegular, distinct);
+  }
+}
+
+SelectRegular SelectRegular() throws ParseException: {
+  List<Projection> projections = new ArrayList<Projection>(); 
+}
+{
+   {
+      Projection projection = null;
+   }
+   projection = Projection() { projections.add(projection); } 
+   ( LOOKAHEAD(2) <COMMA> 
+      projection = Projection() {projections.add(projection);}
+   )*
+  {
+    return new SelectRegular(projections);
+  }
+}
+
+SelectElement SelectElement() throws ParseException: {
+  Expression expr = null;
+  String name = null;
+}
+{
+  (<RAW>|<ELEMENT>|<VALUE>) expr = Expression()
+  {
+    return new SelectElement(expr);
+  }
+}
+
+Projection Projection() throws ParseException: {
+  Expression expr = null;
+  Identifier identifier = null;
+  String name = null;
+  boolean star = false;
+  boolean exprStar = false;
+}
+{
+  (
+    LOOKAHEAD(2)
+    expr= Expression() (<AS>)? name = Identifier()
+    | expr = Expression() <DOT> <MUL> {exprStar = true; }
+    | <MUL> {star = true; }
+  )
+  {
+    return new Projection(expr, name, star, exprStar);
+  }
+}
+
+FromClause FromClause() throws ParseException :
+{
+  List<FromTerm> fromTerms = new ArrayList<FromTerm>();
+  extendCurrentScope();
+}
+{
+  {
+    FromTerm fromTerm = null;
+  }
+    <FROM> fromTerm = FromTerm() { fromTerms.add(fromTerm); } 
+    (LOOKAHEAD(2) <COMMA> fromTerm = FromTerm() { fromTerms.add(fromTerm); } )*
+  {
+    return new FromClause(fromTerms);
+  }
+}
+
+FromTerm FromTerm() throws ParseException :
+{
+  Expression leftExpr = null;
+  VariableExpr leftVar = null; 
+  VariableExpr posVar = null;
+  List<AbstractBinaryCorrelateClause> correlateClauses = new ArrayList<AbstractBinaryCorrelateClause>();
+}
+{
+  leftExpr = Expression() (<AS>)? leftVar = Variable() (<AT> posVar = Variable())?
+  (
+     {JoinType joinType = JoinType.INNER; }
+     (joinType = JoinType())?
+     {
+       AbstractBinaryCorrelateClause correlateClause = null;
+     }
+     (correlateClause = JoinClause(joinType)
+      |
+      correlateClause = NestClause(joinType)
+      |
+      correlateClause = UnnestClause(joinType)
+     )
+     {
+        correlateClauses.add(correlateClause);
+     }
+  )*
+  {
+    return new FromTerm(leftExpr, leftVar, posVar, correlateClauses);
+  }
+}
+
+JoinClause JoinClause(JoinType joinType) throws ParseException :
+{
+    Expression rightExpr = null;
+    VariableExpr rightVar = null;
+    VariableExpr posVar = null;
+    Expression conditionExpr = null;
+}
+{
+  <JOIN> rightExpr = Expression() (<AS>)? rightVar = Variable() (<AT> posVar = Variable())? <ON> conditionExpr = Expression()
+  {
+    return new JoinClause(joinType, rightExpr, rightVar, posVar, conditionExpr);
+  }
+}
+
+NestClause NestClause(JoinType joinType) throws ParseException :
+{
+    Expression rightExpr = null;
+    VariableExpr rightVar = null;
+    VariableExpr posVar = null;
+    Expression conditionExpr = null;
+}
+{
+  <NEST> rightExpr = Expression() (<AS>)? rightVar = Variable() (<AT> posVar = Variable())?  <ON> conditionExpr = Expression()
+  {
+    return new NestClause(joinType, rightExpr, rightVar, posVar, conditionExpr);
+  }
+}
+
+UnnestClause UnnestClause(JoinType joinType) throws ParseException :
+{
+    Expression rightExpr;
+    VariableExpr rightVar;
+    VariableExpr posVar = null;
+}
+{
+  (<UNNEST>|<CORRELATE>|<FLATTEN>) rightExpr = Expression() (<AS>)? rightVar = Variable() (<AT> posVar = Variable())?
+  {
+    return new UnnestClause(joinType, rightExpr, rightVar, posVar);
+  }
+}
+
+
+JoinType JoinType() throws ParseException :
+{
+   JoinType joinType = JoinType.INNER;
+}
+{
+     (<INNER>|<LEFT> (<OUTER>)? {joinType = JoinType.LEFTOUTER; })
+     {
+       return joinType;
+     }
+}
+
+List<LetClause> LetClause() throws ParseException:
+{
+    List<LetClause> letList = new ArrayList<LetClause>();
+    LetClause letClause;
+}
+{
+    (
+     (<LET>|<LETTING>) letClause = LetElement() { letList.add(letClause); } (LOOKAHEAD(1) <COMMA> letClause = LetElement() { letList.add(letClause); })*
+     |
+     <WITH> letClause = WithElement() { letList.add(letClause); } (LOOKAHEAD(1) <COMMA> letClause = WithElement() { letList.add(letClause); })*
+    )
+    {
+      return letList;
+    }
+}
+
+WhereClause WhereClause()throws ParseException :
+{
+  WhereClause wc = new WhereClause();
+  Expression whereExpr;
+}
+{
+    <WHERE> whereExpr = Expression()
+    {
+      wc.setWhereExpr(whereExpr);
+      return wc;
+    }
+}
+
+OrderbyClause OrderbyClause()throws ParseException :
+{
+    OrderbyClause oc = new OrderbyClause();
+    Expression orderbyExpr;
+    List<Expression> orderbyList = new ArrayList<Expression>();
+    List<OrderbyClause.OrderModifier> modifierList = new ArrayList<OrderbyClause.OrderModifier >();
+    int numOfOrderby = 0;
+}
+{
+    <ORDER>
+      {
+        String hint = getHint(token);
+        if (hint != null) {
+          if (hint.startsWith(INMEMORY_HINT)) {
+            String splits[] = hint.split(" +");
+            int numFrames = Integer.parseInt(splits[1]);
+            int numTuples = Integer.parseInt(splits[2]);
+            oc.setNumFrames(numFrames);
+            oc.setNumTuples(numTuples);
+          }
+        }
+      }
+    <BY> orderbyExpr = Expression()
+    {
+      orderbyList.add(orderbyExpr);
+      OrderbyClause.OrderModifier modif = OrderbyClause.OrderModifier.ASC;
+    }
+    ( (<ASC> { modif = OrderbyClause.OrderModifier.ASC; })
+    | (<DESC> { modif = OrderbyClause.OrderModifier.DESC; }))?
+    {
+      modifierList.add(modif);
+    }
+
+    (LOOKAHEAD(2) <COMMA> orderbyExpr = Expression()
+    {
+      orderbyList.add(orderbyExpr);
+      modif = OrderbyClause.OrderModifier.ASC;
+    }
+    ( (<ASC> { modif = OrderbyClause.OrderModifier.ASC; })
+    | (<DESC> { modif = OrderbyClause.OrderModifier.DESC; }))?
+    {
+      modifierList.add(modif);
+    }
+    )*
+
+    {
+      oc.setModifierList(modifierList);
+      oc.setOrderbyList(orderbyList);
+      return oc;
+    }
+}
+
+GroupbyClause GroupbyClause()throws ParseException :
+{
+    GroupbyClause gbc = new GroupbyClause();
+    List<GbyVariableExpressionPair> vePairList = new ArrayList<GbyVariableExpressionPair>();
+    VariableExpr var = null;
+    VariableExpr withVar = null;
+    Expression expr = null;
+    VariableExpr decorVar = null;
+    Expression decorExpr = null;
+}
+{
+      {
+        Scope newScope = extendCurrentScopeNoPush(true);
+        // extendCurrentScope(true);
+      }
+    <GROUP>
+      {
+         String hint = getHint(token);
+         if (hint != null && hint.equals(HASH_GROUP_BY_HINT)) {
+           gbc.setHashGroupByHint(true);
+         }
+      }
+    <BY> (
+       expr = Expression()
+       (LOOKAHEAD(1) (<AS>)?
+        var = Variable()
+        {
+            newScope.addNewVarSymbolToScope(var.getVar());
+        })?
+        {
+            GbyVariableExpressionPair pair1 = new GbyVariableExpressionPair(var, expr);
+            vePairList.add(pair1);
+        }
+       ( LOOKAHEAD(1) <COMMA>
+         expr = Expression()
+         (LOOKAHEAD(1)  (<AS>)?
+         var = Variable()
+         {
+             newScope.addNewVarSymbolToScope(var.getVar());
+         })?
+         {
+             GbyVariableExpressionPair pair2 = new GbyVariableExpressionPair(var, expr);
+             vePairList.add(pair2);
+         }
+        )*
+    )
+    {
+      gbc.setGbyPairList(vePairList);
+      gbc.setDecorPairList(new ArrayList<GbyVariableExpressionPair>());
+      gbc.setWithVarList(new ArrayList<VariableExpr>());
+      replaceCurrentScope(newScope);
+      return gbc;
+    }
+}
+
+HavingClause HavingClause() throws ParseException:
+{
+   Expression filterExpr = null;
+}
+{
+    <HAVING> filterExpr = Expression()
+    {
+       return new HavingClause(filterExpr);
+    }
+}
+
+LimitClause LimitClause() throws ParseException:
+{
+    LimitClause lc = new LimitClause();
+    Expression expr;
+    pushForbiddenScope(getCurrentScope());
+}
+{
+    <LIMIT> expr = Expression()    { lc.setLimitExpr(expr);    }
+    (<OFFSET> expr = Expression() { lc.setOffset(expr);    })?
+
+  {
+    popForbiddenScope();
+    return lc;
+  }
+}
+
+QuantifiedExpression QuantifiedExpression()throws ParseException:
+{
+  QuantifiedExpression qc = new QuantifiedExpression();
+  List<QuantifiedPair> quantifiedList = new ArrayList<QuantifiedPair>();
+  Expression satisfiesExpr;
+  VariableExpr var;
+  Expression inExpr;
+  QuantifiedPair pair;
+}
+{
+  {
+    createNewScope();
+  }
+
+   (      (<SOME>  {  qc.setQuantifier(QuantifiedExpression.Quantifier.SOME);	})
+        | (<EVERY> {  qc.setQuantifier(QuantifiedExpression.Quantifier.EVERY);	}))
+    var = Variable() <IN> inExpr = Expression()
+    {
+      pair = new QuantifiedPair(var, inExpr);
+      getCurrentScope().addNewVarSymbolToScope(var.getVar());
+      quantifiedList.add(pair);
+    }
+    (
+    <COMMA> var = Variable() <IN> inExpr = Expression()
+    {
+      pair = new QuantifiedPair(var, inExpr);
+      getCurrentScope().addNewVarSymbolToScope(var.getVar());
+      quantifiedList.add(pair);
+    }
+    )*
+     <SATISFIES> satisfiesExpr = Expression()
+     {
+       qc.setSatisfiesExpr(satisfiesExpr);
+       qc.setQuantifiedList(quantifiedList);
+       removeCurrentScope();
+       return qc;
+     }
+}
+
+LetClause LetElement() throws ParseException:
+{
+    LetClause lc = new LetClause();
+    VariableExpr varExp;
+    Expression beExp;
+    extendCurrentScope();
+}
+{
+    varExp = Variable() <EQ> beExp = Expression()
+    {
+      getCurrentScope().addNewVarSymbolToScope(varExp.getVar());
+      lc.setVarExpr(varExp);
+      lc.setBindingExpr(beExp);
+      return lc;
+    }
+}
+
+LetClause WithElement() throws ParseException:
+{
+    LetClause lc = new LetClause();
+    VariableExpr varExp;
+    Expression beExp;
+    extendCurrentScope();
+}
+{
+    varExp = Variable() <AS> beExp = Expression()
+    {
+      getCurrentScope().addNewVarSymbolToScope(varExp.getVar());
+      lc.setVarExpr(varExp);
+      lc.setBindingExpr(beExp);
+      return lc;
+    }
+}
+
+TOKEN_MGR_DECLS:
+{
+    public int commentDepth = 0;
+    public IntStack lexerStateStack = new IntStack();
+
+    public void pushState() {
+      lexerStateStack.push( curLexState );
+    }
+
+    public void popState(String token) {
+      if (lexerStateStack.size() > 0) {
+         SwitchTo( lexerStateStack.pop() );
+      } else {
+         int errorLine = input_stream.getEndLine();
+         int errorColumn = input_stream.getEndColumn();
+         String msg = "Lexical error at line " + errorLine + ", column " + errorColumn + ". Encountered \"" + token
+             + "\" but state stack is empty.";
+         throw new TokenMgrError(msg, -1);
+      }
+    }
+}
+
+<DEFAULT,IN_DBL_BRACE>
+TOKEN [IGNORE_CASE]:
+{
+  <ALL : "all">
+  | <AND : "and">
+  | <APPLY : "apply">
+  | <AS : "as">
+  | <ASC : "asc">
+  | <AT : "at">
+  | <AUTOGENERATED : "autogenerated">
+  | <BTREE : "btree">
+  | <BY : "by">
+  | <CASE : "case">
+  | <CLOSED : "closed">
+  | <CREATE : "create">
+  | <COMPACTION : "compaction">
+  | <COMPACT : "compact">
+  | <CONNECT : "connect">
+  | <CORRELATE : "correlate">
+  | <DATASET : "table">
+  | <DATAVERSE : "database">
+  | <DECLARE : "declare">
+  | <DEFINITION : "definition">
+  | <DELETE : "delete">
+  | <DESC : "desc">
+  | <DISCONNECT : "disconnect">
+  | <DISTINCT : "distinct">
+  | <DROP : "drop">
+  | <ELEMENT : "element">
+  | <ELSE : "else">
+  | <ENFORCED : "enforced">
+  | <EVERY : "every">
+  | <EXCEPT : "except">
+  | <EXISTS : "exists">
+  | <EXTERNAL : "external">
+  | <FEED : "feed">
+  | <FILTER : "filter">
+  | <FLATTEN : "flatten">
+  | <FOR : "for">
+  | <FORMAT : "format">
+  | <FROM : "from">
+  | <FULL : "full">
+  | <FUNCTION : "function">
+  | <GROUP : "group">
+  | <HAVING : "having">
+  | <HINTS : "hints">
+  | <IF : "if">
+  | <INTO : "into">
+  | <IN : "in">
+  | <INDEX : "index">
+  | <INGESTION : "ingestion">
+  | <INNER : "inner">
+  | <INSERT : "insert">
+  | <INTERNAL : "internal">
+  | <INTERSECT : "intersect">
+  | <JOIN : "join">
+  | <KEYWORD : "keyword">
+  | <KEY : "key">
+  | <LEFT : "left">
+  | <LETTING : "letting">
+  | <LET : "let">
+  | <LIMIT : "limit">
+  | <LOAD : "load">
+  | <NEST : "nest">
+  | <NODEGROUP : "nodegroup">
+  | <NGRAM : "ngram">
+  | <OFFSET : "offset">
+  | <ON : "on">
+  | <OPEN : "open">
+  | <OR : "or">
+  | <ORDER : "order">
+  | <OUTER : "outer">
+  | <OUTPUT : "output">
+  | <PATH : "path">
+  | <POLICY : "policy">
+  | <PRESORTED : "pre-sorted">
+  | <PRIMARY : "primary">
+  | <RAW : "raw">
+  | <REFRESH : "refresh">
+  | <RETURN : "return">
+  | <RTREE : "rtree">
+  | <RUN : "run">
+  | <SATISFIES : "satisfies">
+  | <SECONDARY : "secondary">
+  | <SELECT : "select">
+  | <SET : "set">
+  | <SOME : "some">
+  | <TEMPORARY : "temporary">
+  | <THEN : "then">
+  | <TYPE : "type">
+  | <TO : "to">
+  | <UNION : "union">
+  | <UNNEST : "unnest">
+  | <VALUE : "value">
+  | <WHEN : "when">
+  | <WHERE : "where">
+  | <WITH : "with">
+  | <WRITE : "write">
+  | <UPDATE : "update">
+  | <USE : "use">
+  | <USING : "using">
+}
+
+<DEFAULT,IN_DBL_BRACE>
+TOKEN :
+{
+    <CARET : "^">
+  | <DIV : "/">
+  | <IDIV : "idiv">
+  | <MINUS : "-">
+  | <MOD : "%">
+  | <MUL : "*">
+  | <PLUS : "+">
+
+  | <LEFTPAREN : "(">
+  | <RIGHTPAREN : ")">
+  | <LEFTBRACKET : "[">
+  | <RIGHTBRACKET : "]">
+
+  | <ATT : "@">
+  | <COLON : ":">
+  | <COMMA : ",">
+  | <DOT : ".">
+  | <QUES : "?">
+  | <SEMICOLON : ";">
+  | <SHARP : "#">
+
+  | <LT : "<">
+  | <GT : ">">
+  | <LE : "<=">
+  | <GE : ">=">
+  | <EQ : "=">
+  | <NE : "!=">
+  | <SIMILAR : "~=">
+}
+
+<DEFAULT,IN_DBL_BRACE>
+TOKEN :
+{
+    <LEFTBRACE : "{"> { pushState(); } : DEFAULT
+}
+
+<DEFAULT>
+TOKEN :
+{
+    <RIGHTBRACE : "}"> { popState("}"); }
+}
+
+<DEFAULT,IN_DBL_BRACE>
+TOKEN :
+{
+    <LEFTDBLBRACE : "{{"> { pushState(); } : IN_DBL_BRACE
+}
+
+<IN_DBL_BRACE>
+TOKEN :
+{
+    <RIGHTDBLBRACE : "}}"> { popState("}}"); }
+}
+
+<DEFAULT,IN_DBL_BRACE>
+TOKEN :
+{
+    <INTEGER_LITERAL : (<DIGIT>)+ >
+}
+
+<DEFAULT,IN_DBL_BRACE>
+TOKEN :
+{
+    <NULL : "null">
+  | <TRUE : "true">
+  | <FALSE : "false">
+}
+
+<DEFAULT,IN_DBL_BRACE>
+TOKEN :
+{
+    <#DIGIT : ["0" - "9"]>
+}
+
+<DEFAULT,IN_DBL_BRACE>
+TOKEN:
+{
+    < DOUBLE_LITERAL: <DIGITS>
+        | <DIGITS> ( "." <DIGITS> )?
+        | "." <DIGITS>
+    >
+  | < FLOAT_LITERAL: <DIGITS> ( "f" | "F" )
+        | <DIGITS> ( "." <DIGITS> ( "f" | "F" ) )?
+        | "." <DIGITS> ( "f" | "F" )
+    >
+  | <DIGITS : (<DIGIT>)+ >
+}
+
+<DEFAULT,IN_DBL_BRACE>
+TOKEN :
+{
+    <#LETTER : ["A" - "Z", "a" - "z"]>
+  | <SPECIALCHARS : ["$", "_"]>
+}
+
+<DEFAULT,IN_DBL_BRACE>
+TOKEN :
+{
+    // backslash u + 4 hex digits escapes are handled in the underlying JavaCharStream
+    <QUOTED_STRING : "\"" (
+          <EscapeQuot>
+        | <EscapeBslash>
+        | <EscapeSlash>
+        | <EscapeBspace>
+        | <EscapeFormf>
+        | <EscapeNl>
+        | <EscapeCr>
+        | <EscapeTab>
+        | ~["\"","\\"])* "\"">
+  | <STRING_LITERAL : "\'" (
+          <EscapeQuot>
+        | <EscapeApos>
+        | <EscapeBslash>
+        | <EscapeSlash>
+        | <EscapeBspace>
+        | <EscapeFormf>
+        | <EscapeNl>
+        | <EscapeCr>
+        | <EscapeTab>
+        | ~["\'","\\"])* "\'">
+  | < #EscapeQuot: "\\\"" >
+  | < #EscapeApos: "\\\'" >
+  | < #EscapeBslash: "\\\\" >
+  | < #EscapeSlash: "\\/" >
+  | < #EscapeBspace: "\\b" >
+  | < #EscapeFormf: "\\f" >
+  | < #EscapeNl: "\\n" >
+  | < #EscapeCr: "\\r" >
+  | < #EscapeTab: "\\t" >
+}
+
+<DEFAULT,IN_DBL_BRACE>
+TOKEN :
+{
+    <IDENTIFIER : <LETTER> (<LETTER> | <DIGIT> | <SPECIALCHARS>)*>
+}
+
+<DEFAULT,IN_DBL_BRACE>
+SKIP:
+{
+    " "
+  | "\t"
+  | "\r"
+  | "\n"
+}
+
+<DEFAULT,IN_DBL_BRACE>
+SKIP:
+{
+    <"//" (~["\n"])* "\n">
+}
+
+<DEFAULT,IN_DBL_BRACE>
+SKIP:
+{
+    <"//" (~["\n","\r"])* ("\n"|"\r"|"\r\n")?>
+}
+
+<DEFAULT,IN_DBL_BRACE>
+SKIP:
+{
+    <"/*"> { pushState(); } : INSIDE_COMMENT
+}
+
+<INSIDE_COMMENT>
+SPECIAL_TOKEN:
+{
+    <"+"(" ")*(~["*"])*>
+}
+
+<INSIDE_COMMENT>
+SKIP:
+{
+    <"/*"> { pushState(); }
+}
+
+<INSIDE_COMMENT>
+SKIP:
+{
+    <"*/"> { popState("*/"); }
+  | <~[]>
+}