You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@phoenix.apache.org by ja...@apache.org on 2014/01/27 23:15:33 UTC

[17/51] [partial] Initial commit of master branch from github

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/50d523f6/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseContext.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseContext.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseContext.java
new file mode 100644
index 0000000..275ceb2
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseContext.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2010 The Apache Software Foundation
+ *
+ * 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.phoenix.parse;
+
+import java.util.List;
+
+import com.google.common.collect.Lists;
+
+public class ParseContext {
+    private boolean isAggregate;
+    
+    public ParseContext() {
+    }
+
+    public boolean isAggregate() {
+        return isAggregate;
+    }
+
+    public void setAggregate(boolean isAggregate) {
+        this.isAggregate |= isAggregate;
+    }
+
+    public static class Stack {
+        private final List<ParseContext> stack = Lists.newArrayListWithExpectedSize(5);
+        
+        public void push(ParseContext context) {
+            stack.add(context);
+        }
+        
+        public ParseContext pop() {
+            return stack.remove(stack.size()-1);
+        }
+        
+        public ParseContext peek() {
+            return stack.get(stack.size()-1);
+        }
+        
+        public boolean isEmpty() {
+            return stack.isEmpty();
+        }
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/50d523f6/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseException.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseException.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseException.java
new file mode 100644
index 0000000..010dd88
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseException.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2010 The Apache Software Foundation
+ *
+ * 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.phoenix.parse;
+
+/**
+ * 
+ * RuntimeException for exceptions occurring during parsing,
+ * since ANTLR doesn't handle typed exceptions well.
+ *
+ * @author jtaylor
+ * @since 2.0
+ */
+public class ParseException extends RuntimeException {
+
+    public ParseException() {
+    }
+
+    public ParseException(String msg) {
+        super(msg);
+    }
+
+    public ParseException(Throwable t) {
+        super(t);
+    }
+
+    public ParseException(String msg, Throwable t) {
+        super(msg, t);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/50d523f6/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNode.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNode.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNode.java
new file mode 100644
index 0000000..46a385c
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNode.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2010 The Apache Software Foundation
+ *
+ * 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.phoenix.parse;
+
+import java.sql.SQLException;
+import java.util.List;
+
+
+
+
+/**
+ * 
+ * Abstract base class for a parse node in SQL
+ *
+ * @author jtaylor
+ * @since 0.1
+ */
+public abstract class ParseNode {
+    public abstract List<ParseNode> getChildren();
+    public abstract <T> T accept(ParseNodeVisitor<T> visitor) throws SQLException;
+    
+    public boolean isStateless() {
+        return false;
+    }
+    
+    /**
+     * Allows node to override what the alias is for a given node.
+     * Useful for a column reference, as JDBC says that the alias
+     * name for "a.b" should be "b"
+     * @return the alias to use for this node or null for no alias
+     */
+    public String getAlias() {
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/50d523f6/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java
new file mode 100644
index 0000000..20082a2
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java
@@ -0,0 +1,564 @@
+/*
+ * Copyright 2010 The Apache Software Foundation
+ *
+ * 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.phoenix.parse;
+
+import java.lang.reflect.Constructor;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp;
+import org.apache.hadoop.hbase.util.Pair;
+
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.Maps;
+import org.apache.phoenix.exception.UnknownFunctionException;
+import org.apache.phoenix.expression.Expression;
+import org.apache.phoenix.expression.ExpressionType;
+import org.apache.phoenix.expression.function.AvgAggregateFunction;
+import org.apache.phoenix.expression.function.CountAggregateFunction;
+import org.apache.phoenix.expression.function.CurrentDateFunction;
+import org.apache.phoenix.expression.function.CurrentTimeFunction;
+import org.apache.phoenix.expression.function.DistinctCountAggregateFunction;
+import org.apache.phoenix.expression.function.FunctionExpression;
+import org.apache.phoenix.parse.FunctionParseNode.BuiltInFunction;
+import org.apache.phoenix.parse.FunctionParseNode.BuiltInFunctionInfo;
+import org.apache.phoenix.parse.JoinTableNode.JoinType;
+import org.apache.phoenix.schema.ColumnModifier;
+import org.apache.phoenix.schema.PDataType;
+import org.apache.phoenix.schema.PIndexState;
+import org.apache.phoenix.schema.PTableType;
+import org.apache.phoenix.schema.TypeMismatchException;
+import org.apache.phoenix.util.SchemaUtil;
+
+
+/**
+ *
+ * Factory used by parser to construct object model while parsing a SQL statement
+ *
+ * @author jtaylor
+ * @since 0.1
+ */
+public class ParseNodeFactory {
+    private static final String ARRAY_ELEM = "ARRAY_ELEM";
+	// TODO: Use Google's Reflection library instead to find aggregate functions
+    @SuppressWarnings("unchecked")
+    private static final List<Class<? extends FunctionExpression>> CLIENT_SIDE_BUILT_IN_FUNCTIONS = Arrays.<Class<? extends FunctionExpression>>asList(
+        CurrentDateFunction.class,
+        CurrentTimeFunction.class,
+        AvgAggregateFunction.class
+        );
+    private static final Map<BuiltInFunctionKey, BuiltInFunctionInfo> BUILT_IN_FUNCTION_MAP = Maps.newHashMap();
+
+    /**
+     *
+     * Key used to look up a built-in function using the combination of
+     * the lowercase name and the number of arguments. This disambiguates
+     * the aggregate MAX(<col>) from the non aggregate MAX(<col1>,<col2>).
+     *
+     * @author jtaylor
+     * @since 0.1
+     */
+    private static class BuiltInFunctionKey {
+        private final String upperName;
+        private final int argCount;
+
+        private BuiltInFunctionKey(String lowerName, int argCount) {
+            this.upperName = lowerName;
+            this.argCount = argCount;
+        }
+        
+        @Override
+        public String toString() {
+            return upperName;
+        }
+        
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + argCount;
+            result = prime * result + ((upperName == null) ? 0 : upperName.hashCode());
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) return true;
+            if (obj == null) return false;
+            if (getClass() != obj.getClass()) return false;
+            BuiltInFunctionKey other = (BuiltInFunctionKey)obj;
+            if (argCount != other.argCount) return false;
+            if (!upperName.equals(other.upperName)) return false;
+            return true;
+        }
+    }
+
+    private static void addBuiltInFunction(Class<? extends FunctionExpression> f) throws Exception {
+        BuiltInFunction d = f.getAnnotation(BuiltInFunction.class);
+        if (d == null) {
+            return;
+        }
+        int nArgs = d.args().length;
+        BuiltInFunctionInfo value = new BuiltInFunctionInfo(f, d);
+        do {
+            // Add function to function map, throwing if conflicts found
+            // Add entry for each possible version of function based on arguments that are not required to be present (i.e. arg with default value)
+            BuiltInFunctionKey key = new BuiltInFunctionKey(value.getName(), nArgs);
+            if (BUILT_IN_FUNCTION_MAP.put(key, value) != null) {
+                throw new IllegalStateException("Multiple " + value.getName() + " functions with " + nArgs + " arguments");
+            }
+        } while (--nArgs >= 0 && d.args()[nArgs].defaultValue().length() > 0);
+
+        // Look for default values that aren't at the end and throw
+        while (--nArgs >= 0) {
+            if (d.args()[nArgs].defaultValue().length() > 0) {
+                throw new IllegalStateException("Function " + value.getName() + " has non trailing default value of '" + d.args()[nArgs].defaultValue() + "'. Only trailing arguments may have default values");
+            }
+        }
+    }
+    /**
+     * Reflect this class and populate static structures from it.
+     * Don't initialize in static block because we have a circular dependency
+     */
+    private synchronized static void initBuiltInFunctionMap() {
+        if (!BUILT_IN_FUNCTION_MAP.isEmpty()) {
+            return;
+        }
+        Class<? extends FunctionExpression> f = null;
+        try {
+            // Reflection based parsing which yields direct explicit function evaluation at runtime
+            for (int i = 0; i < CLIENT_SIDE_BUILT_IN_FUNCTIONS.size(); i++) {
+                f = CLIENT_SIDE_BUILT_IN_FUNCTIONS.get(i);
+                addBuiltInFunction(f);
+            }
+            for (ExpressionType et : ExpressionType.values()) {
+                Class<? extends Expression> ec = et.getExpressionClass();
+                if (FunctionExpression.class.isAssignableFrom(ec)) {
+                    @SuppressWarnings("unchecked")
+                    Class<? extends FunctionExpression> c = (Class<? extends FunctionExpression>)ec;
+                    addBuiltInFunction(f = c);
+                }
+            }
+        } catch (Exception e) {
+            throw new RuntimeException("Failed initialization of built-in functions at class '" + f + "'", e);
+        }
+    }
+
+    private static BuiltInFunctionInfo getInfo(String name, List<ParseNode> children) {
+        return get(SchemaUtil.normalizeIdentifier(name), children);
+    }
+
+    public static BuiltInFunctionInfo get(String normalizedName, List<ParseNode> children) {
+        initBuiltInFunctionMap();
+        BuiltInFunctionInfo info = BUILT_IN_FUNCTION_MAP.get(new BuiltInFunctionKey(normalizedName,children.size()));
+        if (info == null) {
+            throw new UnknownFunctionException(normalizedName);
+        }
+        return info;
+    }
+
+    public ParseNodeFactory() {
+    }
+
+    public ExplainStatement explain(BindableStatement statement) {
+        return new ExplainStatement(statement);
+    }
+
+    public AliasedNode aliasedNode(String alias, ParseNode expression) {
+    	return new AliasedNode(alias, expression);
+    }
+    
+    public AddParseNode add(List<ParseNode> children) {
+        return new AddParseNode(children);
+    }
+
+    public SubtractParseNode subtract(List<ParseNode> children) {
+        return new SubtractParseNode(children);
+    }
+
+    public MultiplyParseNode multiply(List<ParseNode> children) {
+        return new MultiplyParseNode(children);
+    }
+
+    public AndParseNode and(List<ParseNode> children) {
+        return new AndParseNode(children);
+    }
+    
+    public FamilyWildcardParseNode family(String familyName){
+    	    return new FamilyWildcardParseNode(familyName, false);
+    }
+
+    public WildcardParseNode wildcard() {
+        return WildcardParseNode.INSTANCE;
+    }
+
+    public BetweenParseNode between(ParseNode l, ParseNode r1, ParseNode r2, boolean negate) {
+        return new BetweenParseNode(l, r1, r2, negate);
+    }
+
+    public BindParseNode bind(String bind) {
+        return new BindParseNode(bind);
+    }
+
+    public StringConcatParseNode concat(List<ParseNode> children) {
+        return new StringConcatParseNode(children);
+    }
+
+    public ColumnParseNode column(TableName tableName, String name, String alias) {
+        return new ColumnParseNode(tableName,name,alias);
+    }
+    
+    public ColumnName columnName(String columnName) {
+        return new ColumnName(columnName);
+    }
+
+    public ColumnName columnName(String familyName, String columnName) {
+        return new ColumnName(familyName, columnName);
+    }
+
+    public PropertyName propertyName(String propertyName) {
+        return new PropertyName(propertyName);
+    }
+
+    public PropertyName propertyName(String familyName, String propertyName) {
+        return new PropertyName(familyName, propertyName);
+    }
+
+    public ColumnDef columnDef(ColumnName columnDefName, String sqlTypeName, boolean isNull, Integer maxLength, Integer scale, boolean isPK, ColumnModifier columnModifier) {
+        return new ColumnDef(columnDefName, sqlTypeName, isNull, maxLength, scale, isPK, columnModifier);
+    }
+    
+    public ColumnDef columnDef(ColumnName columnDefName, String sqlTypeName, boolean isArray, Integer arrSize, boolean isNull, Integer maxLength, Integer scale, boolean isPK, 
+        	ColumnModifier columnModifier) {
+        return new ColumnDef(columnDefName, sqlTypeName, isArray, arrSize, isNull, maxLength, scale, isPK, columnModifier);
+    }
+
+    public PrimaryKeyConstraint primaryKey(String name, List<Pair<ColumnName, ColumnModifier>> columnNameAndModifier) {
+        return new PrimaryKeyConstraint(name, columnNameAndModifier);
+    }
+    
+    public CreateTableStatement createTable(TableName tableName, ListMultimap<String,Pair<String,Object>> props, List<ColumnDef> columns, PrimaryKeyConstraint pkConstraint, List<ParseNode> splits, PTableType tableType, boolean ifNotExists, TableName baseTableName, ParseNode tableTypeIdNode, int bindCount) {
+        return new CreateTableStatement(tableName, props, columns, pkConstraint, splits, tableType, ifNotExists, baseTableName, tableTypeIdNode, bindCount);
+    }
+    
+    public CreateIndexStatement createIndex(NamedNode indexName, NamedTableNode dataTable, PrimaryKeyConstraint pkConstraint, List<ColumnName> includeColumns, List<ParseNode> splits, ListMultimap<String,Pair<String,Object>> props, boolean ifNotExists, int bindCount) {
+        return new CreateIndexStatement(indexName, dataTable, pkConstraint, includeColumns, splits, props, ifNotExists, bindCount);
+    }
+    
+    public CreateSequenceStatement createSequence(TableName tableName, ParseNode startsWith, ParseNode incrementBy, ParseNode cacheSize, boolean ifNotExits, int bindCount){
+    	return new CreateSequenceStatement(tableName, startsWith, incrementBy, cacheSize, ifNotExits, bindCount);
+    } 
+    
+    public DropSequenceStatement dropSequence(TableName tableName, boolean ifExits, int bindCount){
+        return new DropSequenceStatement(tableName, ifExits, bindCount);
+    }
+    
+	public SequenceValueParseNode currentValueFor(TableName tableName) {
+		return new SequenceValueParseNode(tableName, SequenceValueParseNode.Op.CURRENT_VALUE);
+	}
+    
+    public SequenceValueParseNode nextValueFor(TableName tableName) {
+        return new SequenceValueParseNode(tableName, SequenceValueParseNode.Op.NEXT_VALUE);
+    }
+    
+    public AddColumnStatement addColumn(NamedTableNode table,  PTableType tableType, List<ColumnDef> columnDefs, boolean ifNotExists, Map<String,Object> props) {
+        return new AddColumnStatement(table, tableType, columnDefs, ifNotExists, props);
+    }
+    
+    public DropColumnStatement dropColumn(NamedTableNode table,  PTableType tableType, List<ColumnName> columnNodes, boolean ifExists) {
+        return new DropColumnStatement(table, tableType, columnNodes, ifExists);
+    }
+    
+    public DropTableStatement dropTable(TableName tableName, PTableType tableType, boolean ifExists) {
+        return new DropTableStatement(tableName, tableType, ifExists);
+    }
+    
+    public DropIndexStatement dropIndex(NamedNode indexName, TableName tableName, boolean ifExists) {
+        return new DropIndexStatement(indexName, tableName, ifExists);
+    }
+    
+    public AlterIndexStatement alterIndex(NamedTableNode indexTableNode, String dataTableName, boolean ifExists, PIndexState state) {
+        return new AlterIndexStatement(indexTableNode, dataTableName, ifExists, state);
+    }
+    
+    public TableName table(String schemaName, String tableName) {
+        return TableName.createNormalized(schemaName,tableName);
+    }
+
+    public NamedNode indexName(String name) {
+        return new NamedNode(name);
+    }
+
+    public NamedTableNode namedTable(String alias, TableName name) {
+        return new NamedTableNode(alias, name);
+    }
+
+    public NamedTableNode namedTable(String alias, TableName name ,List<ColumnDef> dyn_columns) {
+        return new NamedTableNode(alias, name,dyn_columns);
+    }
+
+    public BindTableNode bindTable(String alias, TableName name) {
+        return new BindTableNode(alias, name);
+    }
+
+    public CaseParseNode caseWhen(List<ParseNode> children) {
+        return new CaseParseNode(children);
+    }
+
+    public DivideParseNode divide(List<ParseNode> children) {
+        return new DivideParseNode(children);
+    }
+
+
+    public FunctionParseNode functionDistinct(String name, List<ParseNode> args) {
+        if (CountAggregateFunction.NAME.equals(SchemaUtil.normalizeIdentifier(name))) {
+            BuiltInFunctionInfo info = getInfo(
+                    SchemaUtil.normalizeIdentifier(DistinctCountAggregateFunction.NAME), args);
+            return new DistinctCountParseNode(name, args, info);
+        } else {
+            throw new UnsupportedOperationException("DISTINCT not supported with " + name);
+        }
+    }
+    
+    public FunctionParseNode arrayElemRef(List<ParseNode> args) {
+    	return function(ARRAY_ELEM, args);
+    }
+
+    public FunctionParseNode function(String name, List<ParseNode> args) {
+        BuiltInFunctionInfo info = getInfo(name, args);
+        Constructor<? extends FunctionParseNode> ctor = info.getNodeCtor();
+        if (ctor == null) {
+            return info.isAggregate()
+            ? new AggregateFunctionParseNode(name, args, info)
+            : new FunctionParseNode(name, args, info);
+        } else {
+            try {
+                return ctor.newInstance(name, args, info);
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    public FunctionParseNode function(String name, List<ParseNode> valueNodes,
+            List<ParseNode> columnNodes, boolean isAscending) {
+        // Right now we support PERCENT functions on only one column
+        if (valueNodes.size() != 1 || columnNodes.size() != 1) {
+            throw new UnsupportedOperationException(name + " not supported on multiple columns");
+        }
+        List<ParseNode> children = new ArrayList<ParseNode>(3);
+        children.add(columnNodes.get(0));
+        children.add(new LiteralParseNode(Boolean.valueOf(isAscending)));
+        children.add(valueNodes.get(0));
+        return function(name, children);
+    }
+    
+
+    public HintNode hint(String hint) {
+        return new HintNode(hint);
+    }
+
+    public InListParseNode inList(List<ParseNode> children, boolean negate) {
+        return new InListParseNode(children, negate);
+    }
+
+    public ExistsParseNode exists(ParseNode l, ParseNode r, boolean negate) {
+        return new ExistsParseNode(l, r, negate);
+    }
+
+    public InParseNode in(ParseNode l, ParseNode r, boolean negate) {
+        return new InParseNode(l, r, negate);
+    }
+
+    public IsNullParseNode isNull(ParseNode child, boolean negate) {
+        return new IsNullParseNode(child, negate);
+    }
+
+    public JoinTableNode join (JoinType type, ParseNode on, TableNode table) {
+        return new JoinTableNode(type, on, table);
+    }
+
+    public DerivedTableNode derivedTable (String alias, SelectStatement select) {
+        return new DerivedTableNode(alias, select);
+    }
+
+    public LikeParseNode like(ParseNode lhs, ParseNode rhs, boolean negate) {
+        return new LikeParseNode(lhs, rhs, negate);
+    }
+
+
+    public LiteralParseNode literal(Object value) {
+        return new LiteralParseNode(value);
+    }
+    
+    public CastParseNode cast(ParseNode expression, String dataType) {
+    	return new CastParseNode(expression, dataType);
+    }
+    
+    public CastParseNode cast(ParseNode expression, PDataType dataType) {
+    	return new CastParseNode(expression, dataType);
+    }
+    
+    public ParseNode rowValueConstructor(List<ParseNode> l) {
+        return new RowValueConstructorParseNode(l);
+    }
+    
+    private void checkTypeMatch (PDataType expectedType, PDataType actualType) throws SQLException {
+        if (!expectedType.isCoercibleTo(actualType)) {
+            throw TypeMismatchException.newException(expectedType, actualType);
+        }
+    }
+
+    public LiteralParseNode literal(Object value, PDataType expectedType) throws SQLException {
+        PDataType actualType = PDataType.fromLiteral(value);
+        if (actualType != null && actualType != expectedType) {
+            checkTypeMatch(expectedType, actualType);
+            value = expectedType.toObject(value, actualType);
+        }
+        return new LiteralParseNode(value);
+    }
+
+    public LiteralParseNode coerce(LiteralParseNode literalNode, PDataType expectedType) throws SQLException {
+        PDataType actualType = literalNode.getType();
+        if (actualType != null) {
+            Object before = literalNode.getValue();
+            checkTypeMatch(expectedType, actualType);
+            Object after = expectedType.toObject(before, actualType);
+            if (before != after) {
+                literalNode = literal(after);
+            }
+        }
+        return literalNode;
+    }
+
+    public ComparisonParseNode comparison(CompareOp op, ParseNode lhs, ParseNode rhs) {
+        switch (op){
+        case LESS:
+            return lt(lhs,rhs);
+        case LESS_OR_EQUAL:
+            return lte(lhs,rhs);
+        case EQUAL:
+            return equal(lhs,rhs);
+        case NOT_EQUAL:
+            return notEqual(lhs,rhs);
+        case GREATER_OR_EQUAL:
+            return gte(lhs,rhs);
+        case GREATER:
+            return gt(lhs,rhs);
+        default:
+            throw new IllegalArgumentException("Unexpcted CompareOp of " + op);
+        }
+    }
+
+    public GreaterThanParseNode gt(ParseNode lhs, ParseNode rhs) {
+        return new GreaterThanParseNode(lhs, rhs);
+    }
+
+
+    public GreaterThanOrEqualParseNode gte(ParseNode lhs, ParseNode rhs) {
+        return new GreaterThanOrEqualParseNode(lhs, rhs);
+    }
+
+    public LessThanParseNode lt(ParseNode lhs, ParseNode rhs) {
+        return new LessThanParseNode(lhs, rhs);
+    }
+
+
+    public LessThanOrEqualParseNode lte(ParseNode lhs, ParseNode rhs) {
+        return new LessThanOrEqualParseNode(lhs, rhs);
+    }
+
+    public EqualParseNode equal(ParseNode lhs, ParseNode rhs) {
+        return new EqualParseNode(lhs, rhs);
+    }
+
+    public ArrayConstructorNode upsertStmtArrayNode(List<ParseNode> upsertStmtArray) {
+    	return new ArrayConstructorNode(upsertStmtArray);
+    }
+
+    public MultiplyParseNode negate(ParseNode child) {
+        return new MultiplyParseNode(Arrays.asList(child,this.literal(-1)));
+    }
+
+    public NotEqualParseNode notEqual(ParseNode lhs, ParseNode rhs) {
+        return new NotEqualParseNode(lhs, rhs);
+    }
+
+    public NotParseNode not(ParseNode child) {
+        return new NotParseNode(child);
+    }
+
+
+    public OrParseNode or(List<ParseNode> children) {
+        return new OrParseNode(children);
+    }
+
+
+    public OrderByNode orderBy(ParseNode expression, boolean nullsLast, boolean orderAscending) {
+        return new OrderByNode(expression, nullsLast, orderAscending);
+    }
+
+
+    public OuterJoinParseNode outer(ParseNode node) {
+        return new OuterJoinParseNode(node);
+    }
+    
+    public SelectStatement select(List<? extends TableNode> from, HintNode hint, boolean isDistinct, List<AliasedNode> select, ParseNode where,
+            List<ParseNode> groupBy, ParseNode having, List<OrderByNode> orderBy, LimitNode limit, int bindCount, boolean isAggregate) {
+
+        return new SelectStatement(from, hint, isDistinct, select, where, groupBy == null ? Collections.<ParseNode>emptyList() : groupBy, having, orderBy == null ? Collections.<OrderByNode>emptyList() : orderBy, limit, bindCount, isAggregate);
+    }
+    
+    public UpsertStatement upsert(NamedTableNode table, HintNode hint, List<ColumnName> columns, List<ParseNode> values, SelectStatement select, int bindCount) {
+        return new UpsertStatement(table, hint, columns, values, select, bindCount);
+    }
+    
+    public DeleteStatement delete(NamedTableNode table, HintNode hint, ParseNode node, List<OrderByNode> orderBy, LimitNode limit, int bindCount) {
+        return new DeleteStatement(table, hint, node, orderBy, limit, bindCount);
+    }
+
+    public SelectStatement select(SelectStatement statement, ParseNode where, ParseNode having) {
+        return select(statement.getFrom(), statement.getHint(), statement.isDistinct(), statement.getSelect(), where, statement.getGroupBy(), having, statement.getOrderBy(), statement.getLimit(), statement.getBindCount(), statement.isAggregate());
+    }
+
+    public SelectStatement select(SelectStatement statement, List<? extends TableNode> tables) {
+        return select(tables, statement.getHint(), statement.isDistinct(), statement.getSelect(), statement.getWhere(), statement.getGroupBy(), statement.getHaving(), statement.getOrderBy(), statement.getLimit(), statement.getBindCount(), statement.isAggregate());
+    }
+
+    public SelectStatement select(SelectStatement statement, HintNode hint) {
+        return hint == null || hint.isEmpty() ? statement : select(statement.getFrom(), hint, statement.isDistinct(), statement.getSelect(), statement.getWhere(), statement.getGroupBy(), statement.getHaving(), statement.getOrderBy(), statement.getLimit(), statement.getBindCount(), statement.isAggregate());
+    }
+
+    public SubqueryParseNode subquery(SelectStatement select) {
+        return new SubqueryParseNode(select);
+    }
+
+    public LimitNode limit(BindParseNode b) {
+        return new LimitNode(b);
+    }
+
+    public LimitNode limit(LiteralParseNode l) {
+        return new LimitNode(l);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/50d523f6/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeRewriter.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeRewriter.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeRewriter.java
new file mode 100644
index 0000000..504b496
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeRewriter.java
@@ -0,0 +1,553 @@
+/*
+ * Copyright 2010 The Apache Software Foundation
+ *
+ * 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.phoenix.parse;
+
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import org.apache.phoenix.compile.ColumnResolver;
+import org.apache.phoenix.schema.AmbiguousColumnException;
+import org.apache.phoenix.schema.ColumnNotFoundException;
+
+/**
+ * 
+ * Base class for visitors that rewrite the expression node hierarchy
+ *
+ * @author jtaylor 
+ * @since 0.1
+ */
+public class ParseNodeRewriter extends TraverseAllParseNodeVisitor<ParseNode> {
+    
+    protected static final ParseNodeFactory NODE_FACTORY = new ParseNodeFactory();
+
+    public static ParseNode rewrite(ParseNode where, ParseNodeRewriter rewriter) throws SQLException {
+        if (where == null) {
+            return null;
+        }
+        rewriter.reset();
+        return where.accept(rewriter);
+    }
+    
+    /**
+     * Rewrite the select statement by switching any constants to the right hand side
+     * of the expression.
+     * @param statement the select statement
+     * @return new select statement
+     * @throws SQLException 
+     */
+    public static SelectStatement rewrite(SelectStatement statement, ParseNodeRewriter rewriter) throws SQLException {
+        Map<String,ParseNode> aliasMap = rewriter.getAliasMap();
+        List<TableNode> from = statement.getFrom();
+        List<TableNode> normFrom = from;
+        if (from.size() > 1) {
+        	for (int i = 1; i < from.size(); i++) {
+        		TableNode tableNode = from.get(i);
+        		if (tableNode instanceof JoinTableNode) {
+        			JoinTableNode joinTableNode = (JoinTableNode) tableNode;
+        			ParseNode onNode = joinTableNode.getOnNode();
+        			rewriter.reset();
+        			ParseNode normOnNode = onNode.accept(rewriter);
+        			if (onNode == normOnNode) {
+        				if (from != normFrom) {
+        					normFrom.add(tableNode);
+        				}
+        				continue;
+        			}
+        			if (from == normFrom) {
+        				normFrom = Lists.newArrayList(from.subList(0, i));
+        			}
+        			TableNode normTableNode = NODE_FACTORY.join(joinTableNode.getType(), normOnNode, joinTableNode.getTable());
+        			normFrom.add(normTableNode);
+        		} else if (from != normFrom) {
+					normFrom.add(tableNode);
+				}
+        	}
+        }
+        ParseNode where = statement.getWhere();
+        ParseNode normWhere = where;
+        if (where != null) {
+            rewriter.reset();
+            normWhere = where.accept(rewriter);
+        }
+        ParseNode having = statement.getHaving();
+        ParseNode normHaving= having;
+        if (having != null) {
+            rewriter.reset();
+            normHaving = having.accept(rewriter);
+        }
+        List<AliasedNode> selectNodes = statement.getSelect();
+        List<AliasedNode> normSelectNodes = selectNodes;
+        for (int i = 0; i < selectNodes.size(); i++) {
+            AliasedNode aliasedNode = selectNodes.get(i);
+            ParseNode selectNode = aliasedNode.getNode();
+            rewriter.reset();
+            ParseNode normSelectNode = selectNode.accept(rewriter);
+            if (selectNode == normSelectNode) {
+                if (selectNodes != normSelectNodes) {
+                    normSelectNodes.add(aliasedNode);
+                }
+                continue;
+            }
+            if (selectNodes == normSelectNodes) {
+                normSelectNodes = Lists.newArrayList(selectNodes.subList(0, i));
+            }
+            AliasedNode normAliasedNode = NODE_FACTORY.aliasedNode(aliasedNode.isCaseSensitve() ? '"' + aliasedNode.getAlias() + '"' : aliasedNode.getAlias(), normSelectNode);
+            normSelectNodes.add(normAliasedNode);
+        }
+        // Add to map in separate pass so that we don't try to use aliases
+        // while processing the select expressions
+        if (aliasMap != null) {
+            for (int i = 0; i < normSelectNodes.size(); i++) {
+                AliasedNode aliasedNode = normSelectNodes.get(i);
+                ParseNode selectNode = aliasedNode.getNode();
+                String alias = aliasedNode.getAlias();
+                if (alias != null) {
+                    aliasMap.put(alias, selectNode);
+                }
+            }
+        }
+        
+        List<ParseNode> groupByNodes = statement.getGroupBy();
+        List<ParseNode> normGroupByNodes = groupByNodes;
+        for (int i = 0; i < groupByNodes.size(); i++) {
+            ParseNode groupByNode = groupByNodes.get(i);
+            rewriter.reset();
+            ParseNode normGroupByNode = groupByNode.accept(rewriter);
+            if (groupByNode == normGroupByNode) {
+                if (groupByNodes != normGroupByNodes) {
+                    normGroupByNodes.add(groupByNode);
+                }
+                continue;
+            }
+            if (groupByNodes == normGroupByNodes) {
+                normGroupByNodes = Lists.newArrayList(groupByNodes.subList(0, i));
+            }
+            normGroupByNodes.add(normGroupByNode);
+        }
+        List<OrderByNode> orderByNodes = statement.getOrderBy();
+        List<OrderByNode> normOrderByNodes = orderByNodes;
+        for (int i = 0; i < orderByNodes.size(); i++) {
+            OrderByNode orderByNode = orderByNodes.get(i);
+            ParseNode node = orderByNode.getNode();
+            rewriter.reset();
+            ParseNode normNode = node.accept(rewriter);
+            if (node == normNode) {
+                if (orderByNodes != normOrderByNodes) {
+                    normOrderByNodes.add(orderByNode);
+                }
+                continue;
+            }
+            if (orderByNodes == normOrderByNodes) {
+                normOrderByNodes = Lists.newArrayList(orderByNodes.subList(0, i));
+            }
+            normOrderByNodes.add(NODE_FACTORY.orderBy(normNode, orderByNode.isNullsLast(), orderByNode.isAscending()));
+        }
+        
+        // Return new SELECT statement with updated WHERE clause
+        if (normFrom == from && 
+        		normWhere == where && 
+                normHaving == having && 
+                selectNodes == normSelectNodes && 
+                groupByNodes == normGroupByNodes &&
+                orderByNodes == normOrderByNodes) {
+            return statement;
+        }
+        return NODE_FACTORY.select(normFrom, statement.getHint(), statement.isDistinct(),
+                normSelectNodes, normWhere, normGroupByNodes, normHaving, normOrderByNodes,
+                statement.getLimit(), statement.getBindCount(), statement.isAggregate());
+    }
+    
+    private Map<String, ParseNode> getAliasMap() {
+        return aliasMap;
+    }
+
+    private final ColumnResolver resolver;
+    private final Map<String, ParseNode> aliasMap;
+    private int nodeCount;
+    
+    public boolean isTopLevel() {
+        return nodeCount == 0;
+    }
+    
+    protected ParseNodeRewriter() {
+        this.resolver = null;
+        this.aliasMap = null;
+    }
+    
+    protected ParseNodeRewriter(ColumnResolver resolver) {
+        this.resolver = resolver;
+        this.aliasMap = null;
+    }
+    
+    protected ParseNodeRewriter(ColumnResolver resolver, int maxAliasCount) {
+        this.resolver = resolver;
+        this.aliasMap = Maps.newHashMapWithExpectedSize(maxAliasCount);
+    }
+    
+    protected ColumnResolver getResolver() {
+        return resolver;
+    }
+    
+    protected void reset() {
+        this.nodeCount = 0;
+    }
+    
+    private static interface CompoundNodeFactory {
+        ParseNode createNode(List<ParseNode> children);
+    }
+    
+    private ParseNode leaveCompoundNode(CompoundParseNode node, List<ParseNode> children, CompoundNodeFactory factory) {
+        if (children.equals(node.getChildren())) {
+            return node;
+        } else { // Child nodes have been inverted (because a literal was found on LHS)
+            return factory.createNode(children);
+        }
+    }
+    
+    @Override
+    public ParseNode visitLeave(AndParseNode node, List<ParseNode> nodes) throws SQLException {
+        return leaveCompoundNode(node, nodes, new CompoundNodeFactory() {
+            @Override
+            public ParseNode createNode(List<ParseNode> children) {
+                return NODE_FACTORY.and(children);
+            }
+        });
+    }
+
+    @Override
+    public ParseNode visitLeave(OrParseNode node, List<ParseNode> nodes) throws SQLException {
+        return leaveCompoundNode(node, nodes, new CompoundNodeFactory() {
+            @Override
+            public ParseNode createNode(List<ParseNode> children) {
+                return NODE_FACTORY.or(children);
+            }
+        });
+    }
+    
+    @Override
+    public ParseNode visitLeave(SubtractParseNode node, List<ParseNode> nodes) throws SQLException {
+        return leaveCompoundNode(node, nodes, new CompoundNodeFactory() {
+            @Override
+            public ParseNode createNode(List<ParseNode> children) {
+                return NODE_FACTORY.subtract(children);
+            }
+        });
+    }
+    
+    @Override
+    public ParseNode visitLeave(AddParseNode node, List<ParseNode> nodes) throws SQLException {
+        return leaveCompoundNode(node, nodes, new CompoundNodeFactory() {
+            @Override
+            public ParseNode createNode(List<ParseNode> children) {
+                return NODE_FACTORY.add(children);
+            }
+        });
+    }
+    
+    @Override
+    public ParseNode visitLeave(MultiplyParseNode node, List<ParseNode> nodes) throws SQLException {
+        return leaveCompoundNode(node, nodes, new CompoundNodeFactory() {
+            @Override
+            public ParseNode createNode(List<ParseNode> children) {
+                return NODE_FACTORY.multiply(children);
+            }
+        });
+    }
+    
+    @Override
+    public ParseNode visitLeave(DivideParseNode node, List<ParseNode> nodes) throws SQLException {
+        return leaveCompoundNode(node, nodes, new CompoundNodeFactory() {
+            @Override
+            public ParseNode createNode(List<ParseNode> children) {
+                return NODE_FACTORY.divide(children);
+            }
+        });
+    }
+    
+    @Override
+    public ParseNode visitLeave(final FunctionParseNode node, List<ParseNode> nodes) throws SQLException {
+        return leaveCompoundNode(node, nodes, new CompoundNodeFactory() {
+            @Override
+            public ParseNode createNode(List<ParseNode> children) {
+                return NODE_FACTORY.function(node.getName(),children);
+            }
+        });
+    }
+    
+    @Override
+    public ParseNode visitLeave(CaseParseNode node, List<ParseNode> nodes) throws SQLException {
+        return leaveCompoundNode(node, nodes, new CompoundNodeFactory() {
+            @Override
+            public ParseNode createNode(List<ParseNode> children) {
+                return NODE_FACTORY.caseWhen(children);
+            }
+        });
+    }
+
+    @Override
+    public ParseNode visitLeave(final LikeParseNode node, List<ParseNode> nodes) throws SQLException {
+        return leaveCompoundNode(node, nodes, new CompoundNodeFactory() {
+            @Override
+            public ParseNode createNode(List<ParseNode> children) {
+                return NODE_FACTORY.like(children.get(0),children.get(1),node.isNegate());
+            }
+        });
+    }
+    
+    @Override
+    public ParseNode visitLeave(NotParseNode node, List<ParseNode> nodes) throws SQLException {
+        return leaveCompoundNode(node, nodes, new CompoundNodeFactory() {
+            @Override
+            public ParseNode createNode(List<ParseNode> children) {
+                return NODE_FACTORY.not(children.get(0));
+            }
+        });
+    }
+    
+    @Override
+    public ParseNode visitLeave(final CastParseNode node, List<ParseNode> nodes) throws SQLException {
+        return leaveCompoundNode(node, nodes, new CompoundNodeFactory() {
+            @Override
+            public ParseNode createNode(List<ParseNode> children) {
+                return NODE_FACTORY.cast(children.get(0), node.getDataType());
+            }
+        });
+    }
+    
+    @Override
+    public ParseNode visitLeave(final InListParseNode node, List<ParseNode> nodes) throws SQLException {
+        return leaveCompoundNode(node, nodes, new CompoundNodeFactory() {
+            @Override
+            public ParseNode createNode(List<ParseNode> children) {
+                return NODE_FACTORY.inList(children, node.isNegate());
+            }
+        });
+    }
+    
+    @Override
+    public ParseNode visitLeave(final IsNullParseNode node, List<ParseNode> nodes) throws SQLException {
+        return leaveCompoundNode(node, nodes, new CompoundNodeFactory() {
+            @Override
+            public ParseNode createNode(List<ParseNode> children) {
+                return NODE_FACTORY.isNull(children.get(0), node.isNegate());
+            }
+        });
+    }
+    
+    /**
+     * Rewrites expressions of the form (a, b, c) = (1, 2) as a = 1 and b = 2 and c is null
+     * as this is equivalent and already optimized
+     * @param lhs
+     * @param rhs
+     * @param andNodes
+     * @throws SQLException 
+     */
+    private void rewriteRowValueConstuctorEqualityComparison(ParseNode lhs, ParseNode rhs, List<ParseNode> andNodes) throws SQLException {
+        if (lhs instanceof RowValueConstructorParseNode && rhs instanceof RowValueConstructorParseNode) {
+            int i = 0;
+            for (; i < Math.min(lhs.getChildren().size(),rhs.getChildren().size()); i++) {
+                rewriteRowValueConstuctorEqualityComparison(lhs.getChildren().get(i), rhs.getChildren().get(i), andNodes);
+            }
+            for (; i < lhs.getChildren().size(); i++) {
+                rewriteRowValueConstuctorEqualityComparison(lhs.getChildren().get(i), null, andNodes);
+            }
+            for (; i < rhs.getChildren().size(); i++) {
+                rewriteRowValueConstuctorEqualityComparison(null, rhs.getChildren().get(i), andNodes);
+            }
+        } else if (lhs instanceof RowValueConstructorParseNode) {
+            rewriteRowValueConstuctorEqualityComparison(lhs.getChildren().get(0), rhs, andNodes);
+            for (int i = 1; i < lhs.getChildren().size(); i++) {
+                rewriteRowValueConstuctorEqualityComparison(lhs.getChildren().get(i), null, andNodes);
+            }
+        } else if (rhs instanceof RowValueConstructorParseNode) {
+            rewriteRowValueConstuctorEqualityComparison(lhs, rhs.getChildren().get(0), andNodes);
+            for (int i = 1; i < rhs.getChildren().size(); i++) {
+                rewriteRowValueConstuctorEqualityComparison(null, rhs.getChildren().get(i), andNodes);
+            }
+        } else if (lhs == null && rhs == null) { // null == null will end up making the query degenerate
+            andNodes.add(NODE_FACTORY.comparison(CompareOp.EQUAL, null, null).accept(this));
+        } else if (lhs == null) { // AND rhs IS NULL
+            andNodes.add(NODE_FACTORY.isNull(rhs, false).accept(this));
+        } else if (rhs == null) { // AND lhs IS NULL
+            andNodes.add(NODE_FACTORY.isNull(lhs, false).accept(this));
+        } else { // AND lhs = rhs
+            andNodes.add(NODE_FACTORY.comparison(CompareOp.EQUAL, lhs, rhs).accept(this));
+        }
+    }
+    
+    @Override
+    public ParseNode visitLeave(final ComparisonParseNode node, List<ParseNode> nodes) throws SQLException {
+        ParseNode normNode = leaveCompoundNode(node, nodes, new CompoundNodeFactory() {
+            @Override
+            public ParseNode createNode(List<ParseNode> children) {
+                return NODE_FACTORY.comparison(node.getFilterOp(), children.get(0), children.get(1));
+            }
+        });
+        
+        CompareOp op = node.getFilterOp();
+        if (op == CompareOp.EQUAL || op == CompareOp.NOT_EQUAL) {
+            // Rewrite row value constructor in = or != expression, as this is the same as if it was
+            // used in an equality expression for each individual part.
+            ParseNode lhs = normNode.getChildren().get(0);
+            ParseNode rhs = normNode.getChildren().get(1);
+            if (lhs instanceof RowValueConstructorParseNode || rhs instanceof RowValueConstructorParseNode) {
+                List<ParseNode> andNodes = Lists.newArrayListWithExpectedSize(Math.max(lhs.getChildren().size(), rhs.getChildren().size()));
+                rewriteRowValueConstuctorEqualityComparison(lhs,rhs,andNodes);
+                normNode = NODE_FACTORY.and(andNodes);
+                if (op == CompareOp.NOT_EQUAL) {
+                    normNode = NODE_FACTORY.not(normNode);
+                }
+            }
+        }
+        return normNode;
+    }
+    
+    @Override
+    public ParseNode visitLeave(final BetweenParseNode node, List<ParseNode> nodes) throws SQLException {
+        return leaveCompoundNode(node, nodes, new CompoundNodeFactory() {
+            @Override
+            public ParseNode createNode(List<ParseNode> children) {
+                if(node.isNegate()) {
+                    return NODE_FACTORY.not(NODE_FACTORY.and(children));
+                } else {
+                    return NODE_FACTORY.and(children);
+                }
+            }
+        });
+    }
+    
+    @Override
+    public ParseNode visit(ColumnParseNode node) throws SQLException {
+        // If we're resolving aliases and we have an unqualified ColumnParseNode,
+        // check if we find the name in our alias map.
+        if (aliasMap != null && node.getTableName() == null) {
+            ParseNode aliasedNode = aliasMap.get(node.getName());
+            // If we found something, then try to resolve it unless the two nodes are the same
+            if (aliasedNode != null && !node.equals(aliasedNode)) {
+                try {
+                    // If we're able to resolve it, that means we have a conflict
+                    resolver.resolveColumn(node.getSchemaName(), node.getTableName(), node.getName());
+                    throw new AmbiguousColumnException(node.getName());
+                } catch (ColumnNotFoundException e) {
+                    // Not able to resolve alias as a column name as well, so we use the alias
+                    return aliasedNode;
+                }
+            }
+        }
+        return node;
+    }
+
+    @Override
+    public ParseNode visit(LiteralParseNode node) throws SQLException {
+        return node;
+    }
+    
+    @Override
+    public ParseNode visit(BindParseNode node) throws SQLException {
+        return node;
+    }
+    
+    @Override
+    public ParseNode visit(WildcardParseNode node) throws SQLException {
+        return node;
+    }
+    
+    @Override
+    public ParseNode visit(FamilyWildcardParseNode node) throws SQLException {
+        return node;
+    }
+    
+    @Override
+    public List<ParseNode> newElementList(int size) {
+        nodeCount += size;
+        return new ArrayList<ParseNode>(size);
+    }
+    
+    @Override
+    public ParseNode visitLeave(StringConcatParseNode node, List<ParseNode> l) throws SQLException {
+        return node;
+    }
+
+    @Override
+    public void addElement(List<ParseNode> l, ParseNode element) {
+        nodeCount--;
+        if (element != null) {
+            l.add(element);
+        }
+    }
+
+    @Override
+    public ParseNode visitLeave(RowValueConstructorParseNode node, List<ParseNode> children) throws SQLException {
+        // Strip trailing nulls from rvc as they have no meaning
+        if (children.get(children.size()-1) == null) {
+            children = Lists.newArrayList(children);
+            do {
+                children.remove(children.size()-1);
+            } while (children.size() > 0 && children.get(children.size()-1) == null);
+            // If we're down to a single child, it's not a rvc anymore
+            if (children.size() == 0) {
+                return null;
+            }
+            if (children.size() == 1) {
+                return children.get(0);
+            }
+        }
+        // Flatten nested row value constructors, as this makes little sense and adds no information
+        List<ParseNode> flattenedChildren = children;
+        for (int i = 0; i < children.size(); i++) {
+            ParseNode child = children.get(i);
+            if (child instanceof RowValueConstructorParseNode) {
+                if (flattenedChildren == children) {
+                    flattenedChildren = Lists.newArrayListWithExpectedSize(children.size() + child.getChildren().size());
+                    flattenedChildren.addAll(children.subList(0, i));
+                }
+                flattenedChildren.addAll(child.getChildren());
+            }
+        }
+        
+        return leaveCompoundNode(node, flattenedChildren, new CompoundNodeFactory() {
+            @Override
+            public ParseNode createNode(List<ParseNode> children) {
+                return NODE_FACTORY.rowValueConstructor(children);
+            }
+        });
+    }
+
+	@Override
+	public ParseNode visit(SequenceValueParseNode node) throws SQLException {		
+		return node;
+	}
+
+	@Override
+	public ParseNode visitLeave(ArrayConstructorNode node, List<ParseNode> nodes) throws SQLException {
+	    return leaveCompoundNode(node, nodes, new CompoundNodeFactory() {
+            @Override
+            public ParseNode createNode(List<ParseNode> children) {
+                return NODE_FACTORY.upsertStmtArrayNode(children);
+            }
+        });
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/50d523f6/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeVisitor.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeVisitor.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeVisitor.java
new file mode 100644
index 0000000..f300173
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeVisitor.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2010 The Apache Software Foundation
+ *
+ * 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.phoenix.parse;
+
+import java.sql.SQLException;
+import java.util.List;
+
+
+
+/**
+ * 
+ * Visitor for ParseNode in the node tree. Uses composite
+ * visitor pattern with enter/leave calls for any
+ * compound expression node. Only supported SQL constructs
+ * have visit methods.  Unsupported constructs fall through
+ * to {@link #visitEnter(CompoundParseNode)} for
+ * compound parse nodes and {@link #visit(ParseNode)}
+ * for terminal parse nodes.
+ * 
+ * @author jtaylor
+ * @since 0.1
+ */
+public interface ParseNodeVisitor<E> {
+    public List<E> newElementList(int size);
+    public void addElement(List<E> a, E element);
+    
+    public boolean visitEnter(LikeParseNode node) throws SQLException;
+    public E visitLeave(LikeParseNode node, List<E> l) throws SQLException;
+    
+    public boolean visitEnter(AndParseNode node) throws SQLException;
+    public E visitLeave(AndParseNode node, List<E> l) throws SQLException;
+    
+    public boolean visitEnter(OrParseNode node) throws SQLException;
+    public E visitLeave(OrParseNode node, List<E> l) throws SQLException;
+    
+    public boolean visitEnter(FunctionParseNode node) throws SQLException;
+    public E visitLeave(FunctionParseNode node, List<E> l) throws SQLException;
+    
+    public boolean visitEnter(ComparisonParseNode node) throws SQLException;
+    public E visitLeave(ComparisonParseNode node, List<E> l) throws SQLException;
+
+    public boolean visitEnter(CaseParseNode node) throws SQLException;
+    public E visitLeave(CaseParseNode node, List<E> l) throws SQLException;
+    
+    public boolean visitEnter(CompoundParseNode node) throws SQLException;
+    public E visitLeave(CompoundParseNode node, List<E> l) throws SQLException;
+    
+    public boolean visitEnter(AddParseNode node) throws SQLException;
+    public E visitLeave(AddParseNode node, List<E> l) throws SQLException;
+    
+    public boolean visitEnter(MultiplyParseNode node) throws SQLException;
+    public E visitLeave(MultiplyParseNode node, List<E> l) throws SQLException;
+    
+    public boolean visitEnter(DivideParseNode node) throws SQLException;
+    public E visitLeave(DivideParseNode node, List<E> l) throws SQLException;
+    
+    public boolean visitEnter(SubtractParseNode node) throws SQLException;
+    public E visitLeave(SubtractParseNode node, List<E> l) throws SQLException;
+    
+    public boolean visitEnter(NotParseNode node) throws SQLException;
+    public E visitLeave(NotParseNode node, List<E> l) throws SQLException;
+    
+    public boolean visitEnter(InListParseNode node) throws SQLException;
+    public E visitLeave(InListParseNode node, List<E> l) throws SQLException;
+    
+    public boolean visitEnter(IsNullParseNode node) throws SQLException;
+    public E visitLeave(IsNullParseNode node, List<E> l) throws SQLException;
+    
+    public E visit(ColumnParseNode node) throws SQLException;
+    public E visit(LiteralParseNode node) throws SQLException;
+    public E visit(BindParseNode node) throws SQLException;
+    public E visit(WildcardParseNode node) throws SQLException;  
+    public E visit(FamilyWildcardParseNode node) throws SQLException;  
+    public E visit(ParseNode node) throws SQLException;  
+    
+    public boolean visitEnter(StringConcatParseNode node) throws SQLException;
+    public E visitLeave(StringConcatParseNode node, List<E> l) throws SQLException;
+	
+    public boolean visitEnter(BetweenParseNode node) throws SQLException;
+    public E visitLeave(BetweenParseNode node, List<E> l) throws SQLException;
+    
+    public boolean visitEnter(CastParseNode node) throws SQLException;
+    public E visitLeave(CastParseNode node, List<E> l) throws SQLException;
+    
+    public boolean visitEnter(RowValueConstructorParseNode node) throws SQLException;
+    public E visitLeave(RowValueConstructorParseNode node, List<E> l) throws SQLException;
+    
+    public boolean visitEnter(ArrayConstructorNode node) throws SQLException;
+    public E visitLeave(ArrayConstructorNode node, List<E> l) throws SQLException;
+    public E visit(SequenceValueParseNode node) throws SQLException;
+}

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/50d523f6/phoenix-core/src/main/java/org/apache/phoenix/parse/PrimaryKeyConstraint.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/PrimaryKeyConstraint.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/PrimaryKeyConstraint.java
new file mode 100644
index 0000000..97c99cb
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/PrimaryKeyConstraint.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2010 The Apache Software Foundation
+ *
+ * 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.phoenix.parse;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+
+import org.apache.hadoop.hbase.util.Pair;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Maps;
+import org.apache.phoenix.schema.ColumnModifier;
+
+public class PrimaryKeyConstraint extends NamedNode {
+    public static final PrimaryKeyConstraint EMPTY = new PrimaryKeyConstraint(null, Collections.<Pair<ColumnName, ColumnModifier>>emptyList());
+
+    private final List<Pair<ColumnName, ColumnModifier>> columns;
+    private final HashMap<ColumnName, Pair<ColumnName, ColumnModifier>> columnNameToModifier;
+    
+    PrimaryKeyConstraint(String name, List<Pair<ColumnName, ColumnModifier>> columns) {
+        super(name);
+        this.columns = columns == null ? Collections.<Pair<ColumnName, ColumnModifier>>emptyList() : ImmutableList.copyOf(columns);
+        this.columnNameToModifier = Maps.newHashMapWithExpectedSize(this.columns.size());
+        for (Pair<ColumnName, ColumnModifier> p : this.columns) {
+            this.columnNameToModifier.put(p.getFirst(), p);
+        }
+    }
+
+    public List<Pair<ColumnName, ColumnModifier>> getColumnNames() {
+        return columns;
+    }
+    
+    public Pair<ColumnName, ColumnModifier> getColumn(ColumnName columnName) {
+    	return columnNameToModifier.get(columnName);
+    }
+    
+    public boolean contains(ColumnName columnName) {
+        return columnNameToModifier.containsKey(columnName);
+    }
+    
+    @Override
+    public int hashCode() {
+        return super.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        return super.equals(obj);
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/50d523f6/phoenix-core/src/main/java/org/apache/phoenix/parse/PropertyName.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/PropertyName.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/PropertyName.java
new file mode 100644
index 0000000..167b323
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/PropertyName.java
@@ -0,0 +1,25 @@
+package org.apache.phoenix.parse;
+
+import org.apache.phoenix.util.SchemaUtil;
+
+public class PropertyName {
+    private final NamedNode familyName;
+    private final String propertyName;
+    
+    PropertyName(String familyName, String propertyName) {
+        this.familyName = familyName == null ? null : new NamedNode(familyName);
+        this.propertyName = SchemaUtil.normalizeIdentifier(propertyName);;
+    }
+
+    PropertyName(String columnName) {
+        this(null, columnName);
+    }
+
+    public String getFamilyName() {
+        return familyName == null ? "" : familyName.getName();
+    }
+
+    public String getPropertyName() {
+        return propertyName;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/50d523f6/phoenix-core/src/main/java/org/apache/phoenix/parse/RoundParseNode.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/RoundParseNode.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/RoundParseNode.java
new file mode 100644
index 0000000..b7e2c0c
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/RoundParseNode.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2010 The Apache Software Foundation
+ *
+ * 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.phoenix.parse;
+
+import java.sql.SQLException;
+import java.util.List;
+
+import org.apache.phoenix.compile.StatementContext;
+import org.apache.phoenix.expression.Expression;
+import org.apache.phoenix.expression.function.RoundDateExpression;
+import org.apache.phoenix.expression.function.RoundDecimalExpression;
+import org.apache.phoenix.expression.function.RoundFunction;
+import org.apache.phoenix.expression.function.RoundTimestampExpression;
+import org.apache.phoenix.schema.PDataType;
+import org.apache.phoenix.schema.TypeMismatchException;
+
+/**
+ * 
+ * Parse node corresponding to {@link RoundFunction}. 
+ * It also acts as a factory for creating the right kind of
+ * round expression according to the data type of the 
+ * first child.
+ *
+ * @author samarth.jain
+ * @since 3.0.0
+ */
+public class RoundParseNode extends FunctionParseNode {
+
+    RoundParseNode(String name, List<ParseNode> children, BuiltInFunctionInfo info) {
+        super(name, children, info);
+    }
+
+    @Override
+    public Expression create(List<Expression> children, StatementContext context) throws SQLException {
+        return getRoundExpression(children);
+    }
+
+    public static Expression getRoundExpression(List<Expression> children) throws SQLException {
+        final Expression firstChild = children.get(0);
+        final PDataType firstChildDataType = firstChild.getDataType();
+        
+        if(firstChildDataType.isCoercibleTo(PDataType.DATE)) {
+            return RoundDateExpression.create(children); // FIXME: remove cast
+        } else if (firstChildDataType.isCoercibleTo(PDataType.TIMESTAMP)) {
+            return RoundTimestampExpression.create(children); // FIXME: remove cast
+        } else if(firstChildDataType.isCoercibleTo(PDataType.DECIMAL)) {
+            return new RoundDecimalExpression(children);
+        } else {
+            throw TypeMismatchException.newException(firstChildDataType, "1");
+        }
+    }
+    
+    /**
+     * When rounding off decimals, user need not specify the scale. In such cases, 
+     * we need to prevent the function from getting evaluated as null. This is really
+     * a hack. A better way would have been if {@link org.apache.phoenix.parse.FunctionParseNode.BuiltInFunctionInfo} provided a 
+     * way of associating default values for each permissible data type.
+     * Something like: @ Argument(allowedTypes={PDataType.VARCHAR, PDataType.INTEGER}, defaultValues = {"null", "1"} isConstant=true)
+     * Till then, this will have to do.
+     */
+    @Override
+    public boolean evalToNullIfParamIsNull(StatementContext context, int index) throws SQLException {
+        return index == 0;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/50d523f6/phoenix-core/src/main/java/org/apache/phoenix/parse/RowValueConstructorParseNode.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/RowValueConstructorParseNode.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/RowValueConstructorParseNode.java
new file mode 100644
index 0000000..e35646c
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/RowValueConstructorParseNode.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2010 The Apache Software Foundation
+ *
+ * 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.phoenix.parse;
+
+import java.sql.SQLException;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * 
+ * Node representing a row value constructor in SQL.  
+ *
+ * @author samarth.jain
+ * @since 0.1
+ */
+public class RowValueConstructorParseNode extends CompoundParseNode {
+    
+    public RowValueConstructorParseNode(List<ParseNode> l) {
+        super(l);
+    }
+
+    @Override
+    public <T> T accept(ParseNodeVisitor<T> visitor) throws SQLException {
+        List<T> l = Collections.emptyList();
+        if (visitor.visitEnter(this)) {
+            l = acceptChildren(visitor);
+        }
+        return visitor.visitLeave(this, l);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/50d523f6/phoenix-core/src/main/java/org/apache/phoenix/parse/SQLParser.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/SQLParser.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/SQLParser.java
new file mode 100644
index 0000000..4a0139e
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/SQLParser.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright 2010 The Apache Software Foundation
+ *
+ * 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.phoenix.parse;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
+
+import org.antlr.runtime.ANTLRReaderStream;
+import org.antlr.runtime.CharStream;
+import org.antlr.runtime.CommonTokenStream;
+import org.antlr.runtime.RecognitionException;
+
+import org.apache.phoenix.exception.PhoenixParserException;
+
+/**
+ * 
+ * SQL Parser for Phoenix
+ *
+ * @author jtaylor
+ * @since 0.1
+ */
+public class SQLParser {
+    private static final ParseNodeFactory DEFAULT_NODE_FACTORY = new ParseNodeFactory();
+
+    private final PhoenixSQLParser parser;
+
+    public static ParseNode parseCondition(String expression) throws SQLException {
+        if (expression == null) return null;
+        SQLParser parser = new SQLParser(expression);
+        return parser.parseCondition();
+    }
+    
+    public SQLParser(String query) {
+        this(query,DEFAULT_NODE_FACTORY);
+    }
+
+    public SQLParser(String query, ParseNodeFactory factory) {
+        PhoenixSQLLexer lexer;
+        try {
+            lexer = new PhoenixSQLLexer(new CaseInsensitiveReaderStream(new StringReader(query)));
+        } catch (IOException e) {
+            throw new RuntimeException(e); // Impossible
+        }
+        CommonTokenStream cts = new CommonTokenStream(lexer);
+        parser = new PhoenixSQLParser(cts);
+        parser.setParseNodeFactory(factory);
+    }
+
+    public SQLParser(Reader queryReader, ParseNodeFactory factory) throws IOException {
+        PhoenixSQLLexer lexer = new PhoenixSQLLexer(new CaseInsensitiveReaderStream(queryReader));
+        CommonTokenStream cts = new CommonTokenStream(lexer);
+        parser = new PhoenixSQLParser(cts);
+        parser.setParseNodeFactory(factory);
+    }
+
+    public SQLParser(Reader queryReader) throws IOException {
+        PhoenixSQLLexer lexer = new PhoenixSQLLexer(new CaseInsensitiveReaderStream(queryReader));
+        CommonTokenStream cts = new CommonTokenStream(lexer);
+        parser = new PhoenixSQLParser(cts);
+        parser.setParseNodeFactory(DEFAULT_NODE_FACTORY);
+    }
+
+    /**
+     * Parses the input as a series of semicolon-terminated SQL statements.
+     * @throws SQLException 
+     */
+    public BindableStatement nextStatement(ParseNodeFactory factory) throws SQLException {
+        try {
+            parser.resetBindCount();
+            parser.setParseNodeFactory(factory);
+            BindableStatement statement = parser.nextStatement();
+            return statement;
+        } catch (RecognitionException e) {
+            throw PhoenixParserException.newException(e, parser.getTokenNames());
+        } catch (UnsupportedOperationException e) {
+            throw new SQLFeatureNotSupportedException(e);
+        } catch (RuntimeException e) {
+            if (e.getCause() instanceof SQLException) {
+                throw (SQLException) e.getCause();
+            }
+            throw PhoenixParserException.newException(e, parser.getTokenNames());
+        }
+    }
+
+    /**
+     * Parses the input as a SQL select or upsert statement.
+     * @throws SQLException 
+     */
+    public BindableStatement parseStatement() throws SQLException {
+        try {
+            BindableStatement statement = parser.statement();
+            return statement;
+        } catch (RecognitionException e) {
+            throw PhoenixParserException.newException(e, parser.getTokenNames());
+        } catch (UnsupportedOperationException e) {
+            throw new SQLFeatureNotSupportedException(e);
+        } catch (RuntimeException e) {
+            if (e.getCause() instanceof SQLException) {
+                throw (SQLException) e.getCause();
+            }
+            throw PhoenixParserException.newException(e, parser.getTokenNames());
+        }
+    }
+
+    /**
+     * Parses the input as a SQL select statement.
+     * Used only in tests
+     * @throws SQLException 
+     */
+    public SelectStatement parseQuery() throws SQLException {
+        try {
+            SelectStatement statement = parser.query();
+            return statement;
+        } catch (RecognitionException e) {
+            throw PhoenixParserException.newException(e, parser.getTokenNames());
+        } catch (RuntimeException e) {
+            if (e.getCause() instanceof SQLException) {
+                throw (SQLException) e.getCause();
+            }
+            throw PhoenixParserException.newException(e, parser.getTokenNames());
+        }
+    }
+
+    /**
+     * Parses the input as a SQL select statement.
+     * Used only in tests
+     * @throws SQLException 
+     */
+    public ParseNode parseCondition() throws SQLException {
+        try {
+            ParseNode node = parser.condition();
+            return node;
+        } catch (RecognitionException e) {
+            throw PhoenixParserException.newException(e, parser.getTokenNames());
+        } catch (RuntimeException e) {
+            if (e.getCause() instanceof SQLException) {
+                throw (SQLException) e.getCause();
+            }
+            throw PhoenixParserException.newException(e, parser.getTokenNames());
+        }
+    }
+
+    /**
+     * Parses the input as a SQL literal
+     * @throws SQLException 
+     */
+    public LiteralParseNode parseLiteral() throws SQLException {
+        try {
+            LiteralParseNode literalNode = parser.literal();
+            return literalNode;
+        } catch (RecognitionException e) {
+            throw PhoenixParserException.newException(e, parser.getTokenNames());
+        } catch (RuntimeException e) {
+            if (e.getCause() instanceof SQLException) {
+                throw (SQLException) e.getCause();
+            }
+            throw PhoenixParserException.newException(e, parser.getTokenNames());
+        }
+    }
+
+    private static class CaseInsensitiveReaderStream extends ANTLRReaderStream {
+        CaseInsensitiveReaderStream(Reader script) throws IOException {
+            super(script);
+        }
+
+        @Override
+        public int LA(int i) {
+            if (i == 0) { return 0; // undefined
+            }
+            if (i < 0) {
+                i++; // e.g., translate LA(-1) to use offset 0
+            }
+
+            if ((p + i - 1) >= n) { return CharStream.EOF; }
+            return Character.toLowerCase(data[p + i - 1]);
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/50d523f6/phoenix-core/src/main/java/org/apache/phoenix/parse/SelectStatement.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/SelectStatement.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/SelectStatement.java
new file mode 100644
index 0000000..1f2be75
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/SelectStatement.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2010 The Apache Software Foundation
+ *
+ * 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.phoenix.parse;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.phoenix.expression.function.CountAggregateFunction;
+import org.apache.phoenix.parse.FunctionParseNode.BuiltInFunction;
+import org.apache.phoenix.parse.FunctionParseNode.BuiltInFunctionInfo;
+
+/**
+ * 
+ * Top level node representing a SQL statement
+ *
+ * @author jtaylor
+ * @since 0.1
+ */
+public class SelectStatement implements FilterableStatement {
+    public static final SelectStatement SELECT_ONE =
+            new SelectStatement(
+                    Collections.<TableNode>emptyList(), null, false, 
+                    Collections.<AliasedNode>singletonList(new AliasedNode(null,new LiteralParseNode(1))),
+                    null, Collections.<ParseNode>emptyList(),
+                    null, Collections.<OrderByNode>emptyList(),
+                    null, 0, false);
+    public static final SelectStatement COUNT_ONE =
+            new SelectStatement(
+                    Collections.<TableNode>emptyList(), null, false,
+                    Collections.<AliasedNode>singletonList(
+                    new AliasedNode(null, 
+                        new AggregateFunctionParseNode(
+                                CountAggregateFunction.NORMALIZED_NAME, 
+                                LiteralParseNode.STAR, 
+                                new BuiltInFunctionInfo(CountAggregateFunction.class, CountAggregateFunction.class.getAnnotation(BuiltInFunction.class))))),
+                    null, Collections.<ParseNode>emptyList(), 
+                    null, Collections.<OrderByNode>emptyList(), 
+                    null, 0, true);
+    public static SelectStatement create(SelectStatement select, HintNode hint) {
+        if (select.getHint() == hint || hint.isEmpty()) {
+            return select;
+        }
+        return new SelectStatement(select.getFrom(), hint, select.isDistinct(), 
+                select.getSelect(), select.getWhere(), select.getGroupBy(), select.getHaving(), 
+                select.getOrderBy(), select.getLimit(), select.getBindCount(), select.isAggregate());
+    }
+    
+    public SelectStatement combine(ParseNode where) {
+        if (where == null) {
+            return this;
+        }
+        if (this.getWhere() != null) {
+            where = new AndParseNode(Arrays.asList(this.getWhere(), where));
+        }
+        return new SelectStatement(this.getFrom(), this.getHint(), this.isDistinct(), 
+                this.getSelect(), where, this.getGroupBy(), this.getHaving(), 
+                this.getOrderBy(), this.getLimit(), this.getBindCount(), this.isAggregate());
+    }
+    
+    public static SelectStatement create(SelectStatement select, List<AliasedNode> selects) {
+        return new SelectStatement(select.getFrom(), select.getHint(), select.isDistinct(), 
+                selects, select.getWhere(), select.getGroupBy(), select.getHaving(), 
+                select.getOrderBy(), select.getLimit(), select.getBindCount(), select.isAggregate());
+    }
+    
+    private final List<TableNode> fromTable;
+    private final HintNode hint;
+    private final boolean isDistinct;
+    private final List<AliasedNode> select;
+    private final ParseNode where;
+    private final List<ParseNode> groupBy;
+    private final ParseNode having;
+    private final List<OrderByNode> orderBy;
+    private final LimitNode limit;
+    private final int bindCount;
+    private final boolean isAggregate;
+    
+    // Count constant expressions
+    private static int countConstants(List<ParseNode> nodes) {
+        int count = 0;
+        for (int i = 0; i < nodes.size(); i++) {
+            if (nodes.get(i).isStateless()) {
+                count++;
+            }
+        }
+        return count;
+    }
+    
+    protected SelectStatement(List<? extends TableNode> from, HintNode hint, boolean isDistinct, List<AliasedNode> select, ParseNode where, List<ParseNode> groupBy, ParseNode having, List<OrderByNode> orderBy, LimitNode limit, int bindCount, boolean isAggregate) {
+        this.fromTable = Collections.unmodifiableList(from);
+        this.hint = hint == null ? HintNode.EMPTY_HINT_NODE : hint;
+        this.isDistinct = isDistinct;
+        this.select = Collections.unmodifiableList(select);
+        this.where = where;
+        this.groupBy = Collections.unmodifiableList(groupBy);
+        this.having = having;
+        this.orderBy = Collections.unmodifiableList(orderBy);
+        this.limit = limit;
+        this.bindCount = bindCount;
+        this.isAggregate = isAggregate || groupBy.size() != countConstants(groupBy) || this.having != null;
+    }
+    
+    @Override
+    public boolean isDistinct() {
+        return isDistinct;
+    }
+    
+    @Override
+    public LimitNode getLimit() {
+        return limit;
+    }
+    
+    @Override
+    public int getBindCount() {
+        return bindCount;
+    }
+    
+    public List<TableNode> getFrom() {
+        return fromTable;
+    }
+    
+    @Override
+    public HintNode getHint() {
+        return hint;
+    }
+    
+    public List<AliasedNode> getSelect() {
+        return select;
+    }
+    /**
+     * Gets the where condition, or null if none.
+     */
+    @Override
+    public ParseNode getWhere() {
+        return where;
+    }
+    
+    /**
+     * Gets the group-by, containing at least 1 element, or null, if none.
+     */
+    public List<ParseNode> getGroupBy() {
+        return groupBy;
+    }
+    
+    public ParseNode getHaving() {
+        return having;
+    }
+    
+    /**
+     * Gets the order-by, containing at least 1 element, or null, if none.
+     */
+    @Override
+    public List<OrderByNode> getOrderBy() {
+        return orderBy;
+    }
+
+    @Override
+    public boolean isAggregate() {
+        return isAggregate;
+    }
+}