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:43 UTC

[27/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/expression/function/ToNumberFunction.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ToNumberFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ToNumberFunction.java
new file mode 100644
index 0000000..84cc2b4
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ToNumberFunction.java
@@ -0,0 +1,176 @@
+/*
+ * 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.expression.function;
+
+import static org.apache.phoenix.util.ByteUtil.EMPTY_BYTE_ARRAY;
+
+import java.io.*;
+import java.math.BigDecimal;
+import java.sql.SQLException;
+import java.text.*;
+import java.util.Date;
+import java.util.List;
+
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+import org.apache.hadoop.io.WritableUtils;
+
+import com.google.common.base.Preconditions;
+import org.apache.phoenix.expression.Expression;
+import org.apache.phoenix.parse.FunctionParseNode.Argument;
+import org.apache.phoenix.parse.FunctionParseNode.BuiltInFunction;
+import org.apache.phoenix.parse.*;
+import org.apache.phoenix.schema.PDataType;
+import org.apache.phoenix.schema.tuple.Tuple;
+
+/**
+ * 
+ * Implementation of TO_NUMBER(<string>/<date>/<timestamp>, [<pattern-string>]) built-in function.  The format for the optional
+ * <code>pattern_string</code> param is specified in {@link DecimalFormat}.
+ *
+ * @author elevine
+ * @since 0.1
+ */
+@BuiltInFunction(name=ToNumberFunction.NAME,  nodeClass=ToNumberParseNode.class, args= {
+        @Argument(allowedTypes={PDataType.VARCHAR, PDataType.TIMESTAMP}),
+        @Argument(allowedTypes={PDataType.VARCHAR}, isConstant=true, defaultValue="null")} )
+public class ToNumberFunction extends ScalarFunction {
+	public static final String NAME = "TO_NUMBER";
+    
+    private String formatString = null;
+    private Format format = null;
+	private FunctionArgumentType type;
+    
+    public ToNumberFunction() {}
+
+    public ToNumberFunction(List<Expression> children, FunctionArgumentType type, String formatString, Format formatter) throws SQLException {
+        super(children.subList(0, 1));
+        Preconditions.checkNotNull(type);
+        this.type = type;
+        this.formatString = formatString;
+        this.format = formatter;
+    }
+
+    @Override
+    public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) {
+        Expression expression = getExpression();
+        if (!expression.evaluate(tuple, ptr)) {
+            return false;
+        } else if (ptr.getLength() == 0) {
+            return true;
+        }
+
+        PDataType type = expression.getDataType();
+        if (type.isCoercibleTo(PDataType.TIMESTAMP)) {
+        	Date date = (Date) type.toObject(ptr, expression.getColumnModifier());
+        	BigDecimal time = new BigDecimal(date.getTime());
+            byte[] byteValue = getDataType().toBytes(time);
+            ptr.set(byteValue);
+            return true;
+        }
+        
+        String stringValue = (String)type.toObject(ptr, expression.getColumnModifier());
+        if (stringValue == null) {
+            ptr.set(EMPTY_BYTE_ARRAY);
+            return true;
+        }
+        stringValue = stringValue.trim();
+        BigDecimal decimalValue;
+        if (format == null) {
+            decimalValue = (BigDecimal) getDataType().toObject(stringValue);
+        } else {
+            ParsePosition parsePosition = new ParsePosition(0);
+            Number number = ((DecimalFormat) format).parse(stringValue, parsePosition);
+            if (parsePosition.getErrorIndex() > -1) {
+                ptr.set(EMPTY_BYTE_ARRAY);
+                return true;
+            }
+            
+            if (number instanceof BigDecimal) { 
+                // since we set DecimalFormat.setParseBigDecimal(true) we are guaranteeing result to be 
+                // of type BigDecimal in most cases.  see java.text.DecimalFormat.parse() JavaDoc.
+                decimalValue = (BigDecimal)number;
+            } else {
+                ptr.set(EMPTY_BYTE_ARRAY);
+                return true;
+            }
+        }
+        byte[] byteValue = getDataType().toBytes(decimalValue);
+        ptr.set(byteValue);
+        return true;
+    }
+
+    @Override
+    public PDataType getDataType() {
+    	return PDataType.DECIMAL;
+    }
+    
+    @Override
+    public boolean isNullable() {
+        return getExpression().isNullable();
+    }
+
+    private Expression getExpression() {
+        return children.get(0);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+    
+    @Override
+    public void readFields(DataInput input) throws IOException {
+        super.readFields(input);
+        formatString = WritableUtils.readString(input);
+        type = WritableUtils.readEnum(input, FunctionArgumentType.class);
+        if (formatString != null) {
+        	format = type.getFormatter(formatString);
+        }
+    }
+
+    @Override
+    public void write(DataOutput output) throws IOException {
+        super.write(output);
+        WritableUtils.writeString(output, formatString);
+        WritableUtils.writeEnum(output, type);
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = super.hashCode();
+        result = prime * result + ((formatString == null) ? 0 : formatString.hashCode());
+        result = prime * result + getExpression().hashCode();
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) return true;
+        if (!super.equals(obj)) return false;
+        if (getClass() != obj.getClass()) return false;
+        ToNumberFunction other = (ToNumberFunction)obj;
+        if (formatString == null) {
+            if (other.formatString != null) return false;
+        } else if (!formatString.equals(other.formatString)) return false;
+        if (!getExpression().equals(other.getExpression())) return false;
+        return true;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/50d523f6/phoenix-core/src/main/java/org/apache/phoenix/expression/function/TrimFunction.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/TrimFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/TrimFunction.java
new file mode 100644
index 0000000..b8c06cd
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/TrimFunction.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.expression.function;
+
+import java.sql.SQLException;
+import java.util.List;
+
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+
+import org.apache.phoenix.expression.Expression;
+import org.apache.phoenix.parse.FunctionParseNode.Argument;
+import org.apache.phoenix.parse.FunctionParseNode.BuiltInFunction;
+import org.apache.phoenix.schema.ColumnModifier;
+import org.apache.phoenix.schema.PDataType;
+import org.apache.phoenix.schema.tuple.Tuple;
+import org.apache.phoenix.util.ByteUtil;
+import org.apache.phoenix.util.StringUtil;
+
+
+/**
+ * Implementation of the Trim(<string>) build-in function. It removes from both end of <string>
+ * space character and other function bytes in single byte utf8 characters set.
+ * 
+ * @author zhuang
+ * @since 0.1
+ */
+@BuiltInFunction(name=TrimFunction.NAME, args={
+    @Argument(allowedTypes={PDataType.VARCHAR})} )
+public class TrimFunction extends ScalarFunction {
+    public static final String NAME = "TRIM";
+
+    private Integer byteSize;
+
+    public TrimFunction() { }
+
+    public TrimFunction(List<Expression> children) throws SQLException {
+        super(children);
+        if (getStringExpression().getDataType().isFixedWidth()) {
+            byteSize = getStringExpression().getByteSize();
+        }
+    }
+
+    private Expression getStringExpression() {
+        return children.get(0);
+    }
+
+    @Override
+    public ColumnModifier getColumnModifier() {
+        return children.get(0).getColumnModifier();
+    }    
+
+    @Override
+    public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) {
+        if (!getStringExpression().evaluate(tuple, ptr)) {
+            return false;
+        }
+        if (ptr.getLength() == 0) {
+            ptr.set(ByteUtil.EMPTY_BYTE_ARRAY);
+            return true;
+        }
+        byte[] string = ptr.get();
+        int offset = ptr.getOffset();
+        int length = ptr.getLength();
+        
+        ColumnModifier columnModifier = getColumnModifier();
+        int end = StringUtil.getFirstNonBlankCharIdxFromEnd(string, offset, length, columnModifier);
+        if (end == offset - 1) {
+            ptr.set(ByteUtil.EMPTY_BYTE_ARRAY);
+            return true; 
+        }
+        int head = StringUtil.getFirstNonBlankCharIdxFromStart(string, offset, length, columnModifier);
+        ptr.set(string, head, end - head + 1);
+        return true;
+    }
+
+    @Override
+    public Integer getByteSize() {
+        return byteSize;
+    }
+
+    @Override
+    public PDataType getDataType() {
+        return PDataType.VARCHAR;
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/50d523f6/phoenix-core/src/main/java/org/apache/phoenix/expression/function/TruncFunction.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/TruncFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/TruncFunction.java
new file mode 100644
index 0000000..26a75ee
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/TruncFunction.java
@@ -0,0 +1,65 @@
+/*
+ * 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.expression.function;
+
+import java.sql.SQLException;
+import java.util.List;
+
+import org.apache.phoenix.expression.Expression;
+import org.apache.phoenix.parse.FloorParseNode;
+import org.apache.phoenix.parse.FunctionParseNode.Argument;
+import org.apache.phoenix.parse.FunctionParseNode.BuiltInFunction;
+import org.apache.phoenix.schema.PDataType;
+
+
+/**
+ * 
+ * Function used to bucketize date/time values by truncating them to
+ * an even increment.  Usage:
+ * TRUNC(<date/time col ref>,<'day'|'hour'|'minute'|'second'|'millisecond'>,[<optional integer multiplier>])
+ * The integer multiplier is optional and is used to do rollups to a partial time unit (i.e. 10 minute rollup)
+ * The function returns a {@link org.apache.phoenix.schema.PDataType#DATE}
+ *
+ * @author jtaylor
+ * @since 0.1
+ */
+@BuiltInFunction(name = TruncFunction.NAME,
+nodeClass = FloorParseNode.class,
+args = {
+       @Argument(allowedTypes={PDataType.TIMESTAMP, PDataType.DECIMAL}),
+       @Argument(allowedTypes={PDataType.VARCHAR, PDataType.INTEGER}, defaultValue = "null", isConstant=true),
+       @Argument(allowedTypes={PDataType.INTEGER}, defaultValue="1", isConstant=true)
+       } 
+)
+public abstract class TruncFunction extends ScalarFunction {
+    
+    public static final String NAME = "TRUNC";
+    
+    public TruncFunction(List<Expression> children) throws SQLException {
+        super(children);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+    
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/50d523f6/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/BaseExpressionVisitor.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/BaseExpressionVisitor.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/BaseExpressionVisitor.java
new file mode 100644
index 0000000..a524617
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/BaseExpressionVisitor.java
@@ -0,0 +1,264 @@
+/*
+ * 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.expression.visitor;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.phoenix.expression.AddExpression;
+import org.apache.phoenix.expression.AndExpression;
+import org.apache.phoenix.expression.ArrayConstructorExpression;
+import org.apache.phoenix.expression.CaseExpression;
+import org.apache.phoenix.expression.CoerceExpression;
+import org.apache.phoenix.expression.ComparisonExpression;
+import org.apache.phoenix.expression.DivideExpression;
+import org.apache.phoenix.expression.Expression;
+import org.apache.phoenix.expression.InListExpression;
+import org.apache.phoenix.expression.IsNullExpression;
+import org.apache.phoenix.expression.KeyValueColumnExpression;
+import org.apache.phoenix.expression.LikeExpression;
+import org.apache.phoenix.expression.LiteralExpression;
+import org.apache.phoenix.expression.MultiplyExpression;
+import org.apache.phoenix.expression.NotExpression;
+import org.apache.phoenix.expression.OrExpression;
+import org.apache.phoenix.expression.ProjectedColumnExpression;
+import org.apache.phoenix.expression.RowKeyColumnExpression;
+import org.apache.phoenix.expression.RowValueConstructorExpression;
+import org.apache.phoenix.expression.StringConcatExpression;
+import org.apache.phoenix.expression.SubtractExpression;
+import org.apache.phoenix.expression.function.ScalarFunction;
+import org.apache.phoenix.expression.function.SingleAggregateFunction;
+
+
+public abstract class BaseExpressionVisitor<E> implements ExpressionVisitor<E> {
+    @Override
+    public E visit(Expression node) {
+        return null;
+    }
+
+    @Override
+    public Iterator<Expression> visitEnter(Expression node) {
+        return null;
+    }
+
+    @Override
+    public E visitLeave(Expression node, List<E> l) {
+        return null;
+    }
+
+    @Override
+    public E defaultReturn(Expression node, List<E> l) {
+        return null;
+    }
+
+    @Override
+    public Iterator<Expression> visitEnter(AndExpression node) {
+        return null;
+    }
+
+    @Override
+    public E visitLeave(AndExpression node, List<E> l) {
+        return null;
+    }
+
+    @Override
+    public Iterator<Expression> visitEnter(OrExpression node) {
+        return null;
+    }
+
+    @Override
+    public E visitLeave(OrExpression node, List<E> l) {
+        return null;
+    }
+
+    @Override
+    public Iterator<Expression> visitEnter(ScalarFunction node) {
+        return null;
+    }
+
+    @Override
+    public E visitLeave(ScalarFunction node, List<E> l) {
+        return null;
+    }
+
+    @Override
+    public Iterator<Expression> visitEnter(ComparisonExpression node) {
+        return null;
+    }
+
+    @Override
+    public E visitLeave(ComparisonExpression node, List<E> l) {
+        return null;
+    }
+
+    @Override
+    public Iterator<Expression> visitEnter(LikeExpression node) {
+        return null;
+    }
+
+    @Override
+    public E visitLeave(LikeExpression node, List<E> l) {
+        return null;
+    }
+
+    @Override
+    public Iterator<Expression> visitEnter(SingleAggregateFunction node) {
+        return null;
+    }
+
+    @Override
+    public E visitLeave(SingleAggregateFunction node, List<E> l) {
+        return null;
+    }
+
+    @Override
+    public Iterator<Expression> visitEnter(CaseExpression node) {
+        return null;
+    }
+
+    @Override
+    public E visitLeave(CaseExpression node, List<E> l) {
+        return null;
+    }
+
+    @Override
+    public Iterator<Expression> visitEnter(NotExpression node) {
+        return null;
+    }
+
+    @Override
+    public E visitLeave(NotExpression node, List<E> l) {
+        return null;
+    }
+
+    @Override
+    public Iterator<Expression> visitEnter(IsNullExpression node) {
+        return null;
+    }
+
+    @Override
+    public E visitLeave(IsNullExpression node, List<E> l) {
+        return null;
+    }
+
+    @Override
+    public Iterator<Expression> visitEnter(InListExpression node) {
+        return null;
+    }
+
+    @Override
+    public E visitLeave(InListExpression node, List<E> l) {
+        return null;
+    }
+
+    @Override
+    public E visit(LiteralExpression node) {
+        return null;
+    }
+
+    @Override
+    public E visit(RowKeyColumnExpression node) {
+        return null;
+    }
+
+    @Override
+    public E visit(KeyValueColumnExpression node) {
+        return null;
+    }
+    
+    @Override
+    public E visit(ProjectedColumnExpression node) {
+        return null;
+    }
+
+    @Override
+    public Iterator<Expression> visitEnter(SubtractExpression node) {
+        return null;
+    }
+
+    @Override
+    public E visitLeave(SubtractExpression node, List<E> l) {
+        return null;
+    }
+
+    @Override
+    public Iterator<Expression> visitEnter(AddExpression node) {
+        return null;
+    }
+    @Override
+    public E visitLeave(AddExpression node, List<E> l) {
+        return null;
+    }
+
+    @Override
+    public Iterator<Expression> visitEnter(MultiplyExpression node) {
+        return null;
+    }
+    @Override
+    public E visitLeave(MultiplyExpression node, List<E> l) {
+        return null;
+    }
+
+    @Override
+    public Iterator<Expression> visitEnter(DivideExpression node) {
+        return null;
+    }
+    @Override
+    public E visitLeave(DivideExpression node, List<E> l) {
+        return null;
+    }
+    
+    @Override
+    public Iterator<Expression> visitEnter(StringConcatExpression node) {
+        return null;
+    }
+    @Override
+    public E visitLeave(StringConcatExpression node, List<E> l) {
+        return null;
+    }
+    
+    @Override
+    public Iterator<Expression> visitEnter(RowValueConstructorExpression node) {
+        return null;
+    }
+    @Override
+    public E visitLeave(RowValueConstructorExpression node, List<E> l) {
+        return null;
+    }
+    
+    @Override
+    public Iterator<Expression> visitEnter(CoerceExpression node) {
+        return null;
+    }
+    
+    @Override
+    public E visitLeave(CoerceExpression node, List<E> l) {
+        return null;
+    }
+
+    @Override
+    public Iterator<Expression> visitEnter(ArrayConstructorExpression node) {
+        return null;
+    }
+    @Override
+    public E visitLeave(ArrayConstructorExpression node, List<E> l) {
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/50d523f6/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/ExpressionVisitor.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/ExpressionVisitor.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/ExpressionVisitor.java
new file mode 100644
index 0000000..efde2a4
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/ExpressionVisitor.java
@@ -0,0 +1,142 @@
+/*
+ * 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.expression.visitor;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.phoenix.expression.AddExpression;
+import org.apache.phoenix.expression.AndExpression;
+import org.apache.phoenix.expression.ArrayConstructorExpression;
+import org.apache.phoenix.expression.CaseExpression;
+import org.apache.phoenix.expression.CoerceExpression;
+import org.apache.phoenix.expression.ComparisonExpression;
+import org.apache.phoenix.expression.DivideExpression;
+import org.apache.phoenix.expression.Expression;
+import org.apache.phoenix.expression.InListExpression;
+import org.apache.phoenix.expression.IsNullExpression;
+import org.apache.phoenix.expression.KeyValueColumnExpression;
+import org.apache.phoenix.expression.LikeExpression;
+import org.apache.phoenix.expression.LiteralExpression;
+import org.apache.phoenix.expression.MultiplyExpression;
+import org.apache.phoenix.expression.NotExpression;
+import org.apache.phoenix.expression.OrExpression;
+import org.apache.phoenix.expression.ProjectedColumnExpression;
+import org.apache.phoenix.expression.RowKeyColumnExpression;
+import org.apache.phoenix.expression.RowValueConstructorExpression;
+import org.apache.phoenix.expression.StringConcatExpression;
+import org.apache.phoenix.expression.SubtractExpression;
+import org.apache.phoenix.expression.function.ScalarFunction;
+import org.apache.phoenix.expression.function.SingleAggregateFunction;
+
+
+/**
+ * 
+ * Visitor for an expression (which may contain other nested expressions)
+ *
+ * @author jtaylor
+ * @since 0.1
+ */
+public interface ExpressionVisitor<E> {
+    /**
+     * Default visit method when an expression subclass doesn't
+     * define an accept method of its own. This will end up calling
+     * the {@link #defaultIterator(Expression)} to iterate over the
+     * children calling accept on them
+     */
+    public E visit(Expression node);
+    /**
+     * Default visitEnter method when an expression subclass doesn't
+     * define an accept method of its own. This will end up calling
+     * the {@link #defaultIterator(Expression)} to iterate over the
+     * children calling accept on them
+     */
+    public Iterator<Expression> visitEnter(Expression node);
+    /**
+     * Default visitLeave method when an expression subclass doesn't
+     * define an accept method of its own.  This will end up calling
+     * the {@link #defaultReturn(Expression, List)} with the list from
+     * the iteration over the children.
+     */
+    public E visitLeave(Expression node, List<E> l);
+
+    public E defaultReturn(Expression node, List<E> l);
+    public Iterator<Expression> defaultIterator(Expression node);
+    
+    public Iterator<Expression> visitEnter(AndExpression node);
+    public E visitLeave(AndExpression node, List<E> l);
+    
+    public Iterator<Expression> visitEnter(OrExpression node);
+    public E visitLeave(OrExpression node, List<E> l);
+    
+    public Iterator<Expression> visitEnter(ScalarFunction node);
+    public E visitLeave(ScalarFunction node, List<E> l);
+    
+    public Iterator<Expression> visitEnter(ComparisonExpression node);
+    public E visitLeave(ComparisonExpression node, List<E> l);
+
+    public Iterator<Expression> visitEnter(LikeExpression node);
+    public E visitLeave(LikeExpression node, List<E> l);
+    
+    public Iterator<Expression> visitEnter(SingleAggregateFunction node);
+    public E visitLeave(SingleAggregateFunction node, List<E> l);
+    
+    public Iterator<Expression> visitEnter(CaseExpression node);
+    public E visitLeave(CaseExpression node, List<E> l);
+    
+    public Iterator<Expression> visitEnter(NotExpression node);
+    public E visitLeave(NotExpression node, List<E> l);
+
+    public Iterator<Expression> visitEnter(InListExpression node);
+    public E visitLeave(InListExpression node, List<E> l);
+
+    public Iterator<Expression> visitEnter(IsNullExpression node);
+    public E visitLeave(IsNullExpression node, List<E> l);
+
+    public Iterator<Expression> visitEnter(SubtractExpression node);
+    public E visitLeave(SubtractExpression node, List<E> l);
+    
+    public Iterator<Expression> visitEnter(MultiplyExpression node);
+    public E visitLeave(MultiplyExpression node, List<E> l);
+    
+    public Iterator<Expression> visitEnter(AddExpression node);
+    public E visitLeave(AddExpression node, List<E> l);
+    
+    public Iterator<Expression> visitEnter(DivideExpression node);
+    public E visitLeave(DivideExpression node, List<E> l);
+    
+    public Iterator<Expression> visitEnter(CoerceExpression node);
+    public E visitLeave(CoerceExpression node, List<E> l);
+
+    public Iterator<Expression> visitEnter(ArrayConstructorExpression node);
+    public E visitLeave(ArrayConstructorExpression node, List<E> l);
+    
+    public E visit(LiteralExpression node);
+    public E visit(RowKeyColumnExpression node);
+    public E visit(KeyValueColumnExpression node);
+    public E visit(ProjectedColumnExpression node);
+    
+	public Iterator<Expression> visitEnter(StringConcatExpression node);
+	public E visitLeave(StringConcatExpression node, List<E> l);
+	
+	public Iterator<Expression> visitEnter(RowValueConstructorExpression node);
+    public E visitLeave(RowValueConstructorExpression node, List<E> l);
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/50d523f6/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/KeyValueExpressionVisitor.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/KeyValueExpressionVisitor.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/KeyValueExpressionVisitor.java
new file mode 100644
index 0000000..838bb0b
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/KeyValueExpressionVisitor.java
@@ -0,0 +1,38 @@
+/*
+ * 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.expression.visitor;
+
+import org.apache.phoenix.expression.KeyValueColumnExpression;
+
+
+
+
+/**
+ * 
+ * Implementation of ExpressionVisitor where only KeyValueDataAccessor
+ * is being visited
+ *
+ * @author jtaylor
+ * @since 0.1
+ */
+public abstract class KeyValueExpressionVisitor extends TraverseAllExpressionVisitor<Void> {
+    @Override
+    abstract public Void visit(KeyValueColumnExpression node);
+}

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/50d523f6/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/SingleAggregateFunctionVisitor.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/SingleAggregateFunctionVisitor.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/SingleAggregateFunctionVisitor.java
new file mode 100644
index 0000000..63313a7
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/SingleAggregateFunctionVisitor.java
@@ -0,0 +1,40 @@
+/*
+ * 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.expression.visitor;
+
+import java.util.Iterator;
+
+import org.apache.phoenix.expression.Expression;
+import org.apache.phoenix.expression.function.SingleAggregateFunction;
+
+
+
+/**
+ * 
+ * Implementation of ExpressionVisitor where only SingleAggregateFunction
+ * instances are visited
+ *
+ * @author jtaylor
+ * @since 0.1
+ */
+public abstract class SingleAggregateFunctionVisitor extends TraverseAllExpressionVisitor<Void> {
+    @Override
+    abstract public Iterator<Expression> visitEnter(SingleAggregateFunction node);
+}

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/50d523f6/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/TraverseAllExpressionVisitor.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/TraverseAllExpressionVisitor.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/TraverseAllExpressionVisitor.java
new file mode 100644
index 0000000..39e7a3d
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/TraverseAllExpressionVisitor.java
@@ -0,0 +1,57 @@
+/*
+ * 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.expression.visitor;
+
+import java.util.*;
+
+import org.apache.phoenix.expression.Expression;
+
+
+
+
+public class TraverseAllExpressionVisitor<E> extends BaseExpressionVisitor<E> {
+
+    @Override
+    public Iterator<Expression> defaultIterator(Expression node) {
+        final List<Expression> children = node.getChildren();
+        return new Iterator<Expression>() {
+            private int position;
+            
+            @Override
+            public final boolean hasNext() {
+                return position < children.size();
+            }
+
+            @Override
+            public final Expression next() {
+                if (!hasNext()) {
+                    throw new NoSuchElementException();
+                }
+                return children.get(position++);
+            }
+
+            @Override
+            public final void remove() {
+                throw new UnsupportedOperationException();
+            }
+        };
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/50d523f6/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/TraverseNoExpressionVisitor.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/TraverseNoExpressionVisitor.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/TraverseNoExpressionVisitor.java
new file mode 100644
index 0000000..fe1d1f5
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/TraverseNoExpressionVisitor.java
@@ -0,0 +1,35 @@
+/*
+ * 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.expression.visitor;
+
+import java.util.Iterator;
+
+
+import com.google.common.collect.Iterators;
+import org.apache.phoenix.expression.Expression;
+
+public class TraverseNoExpressionVisitor<E> extends BaseExpressionVisitor<E> {
+
+    @Override
+    public Iterator<Expression> defaultIterator(Expression node) {
+        return Iterators.emptyIterator();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/50d523f6/phoenix-core/src/main/java/org/apache/phoenix/filter/BooleanExpressionFilter.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/filter/BooleanExpressionFilter.java b/phoenix-core/src/main/java/org/apache/phoenix/filter/BooleanExpressionFilter.java
new file mode 100644
index 0000000..d7b2243
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/filter/BooleanExpressionFilter.java
@@ -0,0 +1,125 @@
+/*
+ * 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.filter;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+
+import org.apache.hadoop.hbase.filter.FilterBase;
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+import org.apache.hadoop.io.WritableUtils;
+import org.apache.phoenix.expression.Expression;
+import org.apache.phoenix.expression.ExpressionType;
+import org.apache.phoenix.schema.IllegalDataException;
+import org.apache.phoenix.schema.tuple.Tuple;
+import org.apache.phoenix.util.ServerUtil;
+
+
+/**
+ * 
+ * Base class for filter that evaluates a WHERE clause expression.
+ *
+ * @author jtaylor
+ * @since 0.1
+ */
+abstract public class BooleanExpressionFilter extends FilterBase {
+
+    protected Expression expression;
+    protected boolean evaluateOnCompletion;
+    private ImmutableBytesWritable tempPtr = new ImmutableBytesWritable();
+    
+    public BooleanExpressionFilter() {
+    }
+
+    public BooleanExpressionFilter(Expression expression) {
+        this.expression = expression;
+    }
+
+    protected void setEvaluateOnCompletion(boolean evaluateOnCompletion) {
+        this.evaluateOnCompletion = evaluateOnCompletion;
+    }
+    
+    protected boolean evaluateOnCompletion() {
+        return evaluateOnCompletion;
+    }
+    
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + expression.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;
+        BooleanExpressionFilter other = (BooleanExpressionFilter)obj;
+        if (!expression.equals(other.expression)) return false;
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return expression.toString();
+    }
+
+    @edu.umd.cs.findbugs.annotations.SuppressWarnings(
+            value="NP_BOOLEAN_RETURN_NULL",
+            justification="Returns null by design.")
+    protected Boolean evaluate(Tuple input) {
+        try {
+            if (!expression.evaluate(input, tempPtr)) {
+                return null;
+            }
+        } catch (IllegalDataException e) {
+            return Boolean.FALSE;
+        }
+        return (Boolean)expression.getDataType().toObject(tempPtr);
+    }
+
+    @Override
+    public void readFields(DataInput input) throws IOException {
+        try {
+            expression = ExpressionType.values()[WritableUtils.readVInt(input)].newInstance();
+            expression.readFields(input);
+        } catch (Throwable t) { // Catches incompatibilities during reading/writing and doesn't retry
+            ServerUtil.throwIOException("BooleanExpressionFilter failed during reading", t);
+        }
+    }
+
+    @Override
+    public void write(DataOutput output) throws IOException {
+        try {
+            WritableUtils.writeVInt(output, ExpressionType.valueOf(expression).ordinal());
+            expression.write(output);
+        } catch (Throwable t) { // Catches incompatibilities during reading/writing and doesn't retry
+            ServerUtil.throwIOException("BooleanExpressionFilter failed during writing", t);
+        }
+    }
+    
+    @Override
+    public void reset() {
+        expression.reset();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/50d523f6/phoenix-core/src/main/java/org/apache/phoenix/filter/EvaluateOnCompletionVisitor.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/filter/EvaluateOnCompletionVisitor.java b/phoenix-core/src/main/java/org/apache/phoenix/filter/EvaluateOnCompletionVisitor.java
new file mode 100644
index 0000000..7dcc954
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/filter/EvaluateOnCompletionVisitor.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.filter;
+
+import java.util.Iterator;
+
+import org.apache.phoenix.expression.ArrayConstructorExpression;
+import org.apache.phoenix.expression.CaseExpression;
+import org.apache.phoenix.expression.Expression;
+import org.apache.phoenix.expression.IsNullExpression;
+import org.apache.phoenix.expression.RowKeyColumnExpression;
+import org.apache.phoenix.expression.RowValueConstructorExpression;
+import org.apache.phoenix.expression.visitor.TraverseAllExpressionVisitor;
+
+
+/**
+ * 
+ * Implementation of ExpressionVisitor for the expression used by the
+ * BooleanExpressionFilter that looks for expressions that need to be
+ * evaluated upon completion. Examples include:
+ * - CaseExpression with an else clause, since upon completion, the
+ * else clause would apply if the when clauses could not be evaluated
+ * due to the absense of a value.
+ * - IsNullExpression that's not negated, since upon completion, we
+ * know definitively that a column value was not found.
+ * - row key columns are used, since we may never have encountered a
+ * key value column of interest, but the expression may evaluate to true
+ * just based on the row key columns.
+ * @author jtaylor
+ * @since 0.1
+ */
+public class EvaluateOnCompletionVisitor extends TraverseAllExpressionVisitor<Void> {
+    private boolean evaluateOnCompletion = false;
+    
+    public boolean evaluateOnCompletion() {
+        return evaluateOnCompletion;
+    }
+    
+    @Override
+    public Iterator<Expression> visitEnter(IsNullExpression node) {
+        evaluateOnCompletion |= !node.isNegate();
+        return null;
+    }
+    @Override
+    public Iterator<Expression> visitEnter(CaseExpression node) {
+        evaluateOnCompletion |= node.hasElse();
+        return null;
+    }
+    @Override
+    public Void visit(RowKeyColumnExpression node) {
+        evaluateOnCompletion = true;
+        return null;
+    }
+    @Override
+    public Iterator<Expression> visitEnter(RowValueConstructorExpression node) {
+        evaluateOnCompletion = true;
+        return null;
+    }
+    
+    @Override
+    public Iterator<Expression> visitEnter(ArrayConstructorExpression node) {
+        evaluateOnCompletion = true;
+        return null;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/50d523f6/phoenix-core/src/main/java/org/apache/phoenix/filter/MultiCFCQKeyValueComparisonFilter.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/filter/MultiCFCQKeyValueComparisonFilter.java b/phoenix-core/src/main/java/org/apache/phoenix/filter/MultiCFCQKeyValueComparisonFilter.java
new file mode 100644
index 0000000..27a356b
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/filter/MultiCFCQKeyValueComparisonFilter.java
@@ -0,0 +1,134 @@
+/*
+ * 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.filter;
+
+import java.util.TreeSet;
+
+import org.apache.hadoop.hbase.util.Bytes;
+
+import org.apache.phoenix.expression.Expression;
+
+
+/**
+ *
+ * Filter that evaluates WHERE clause expression, used in the case where there
+ * are references to multiple column qualifiers over multiple column families.
+ *
+ * @author jtaylor
+ * @since 0.1
+ */
+public class MultiCFCQKeyValueComparisonFilter extends MultiKeyValueComparisonFilter {
+    private final ImmutablePairBytesPtr ptr = new ImmutablePairBytesPtr();
+    private TreeSet<byte[]> cfSet;
+
+    public MultiCFCQKeyValueComparisonFilter() {
+    }
+
+    public MultiCFCQKeyValueComparisonFilter(Expression expression) {
+        super(expression);
+    }
+
+    @Override
+    protected void init() {
+        cfSet = new TreeSet<byte[]>(Bytes.BYTES_COMPARATOR);
+        super.init();
+    }
+
+    @Override
+    protected Object setColumnKey(byte[] cf, int cfOffset, int cfLength,
+            byte[] cq, int cqOffset, int cqLength) {
+        ptr.set(cf, cfOffset, cfLength, cq, cqOffset, cqLength);
+        return ptr;
+    }
+
+    @Override
+    protected Object newColumnKey(byte[] cf, int cfOffset, int cfLength,
+            byte[] cq, int cqOffset, int cqLength) {
+
+        byte[] cfKey;
+        if (cfOffset == 0 && cf.length == cfLength) {
+            cfKey = cf;
+        } else {
+            // Copy bytes here, but figure cf names are typically a few bytes at most,
+            // so this will be better than creating an ImmutableBytesPtr
+            cfKey = new byte[cfLength];
+            System.arraycopy(cf, cfOffset, cfKey, 0, cfLength);
+        }
+        cfSet.add(cfKey);
+        return new ImmutablePairBytesPtr(cf, cfOffset, cfLength, cq, cqOffset, cqLength);
+    }
+
+    private static class ImmutablePairBytesPtr {
+        private byte[] bytes1;
+        private int offset1;
+        private int length1;
+        private byte[] bytes2;
+        private int offset2;
+        private int length2;
+        private int hashCode;
+
+        private ImmutablePairBytesPtr() {
+        }
+
+        private ImmutablePairBytesPtr(byte[] bytes1, int offset1, int length1, byte[] bytes2, int offset2, int length2) {
+            set(bytes1, offset1, length1, bytes2, offset2, length2);
+        }
+
+        @Override
+        public int hashCode() {
+            return hashCode;
+        }
+
+        public void set(byte[] bytes1, int offset1, int length1, byte[] bytes2, int offset2, int length2) {
+            this.bytes1 = bytes1;
+            this.offset1 = offset1;
+            this.length1 = length1;
+            this.bytes2 = bytes2;
+            this.offset2 = offset2;
+            this.length2 = length2;
+            int hash = 1;
+            for (int i = offset1; i < offset1 + length1; i++)
+                hash = (31 * hash) + bytes1[i];
+            for (int i = offset2; i < offset2 + length2; i++)
+                hash = (31 * hash) + bytes2[i];
+            hashCode = hash;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) return true;
+            if (obj == null) return false;
+            if (getClass() != obj.getClass()) return false;
+            ImmutablePairBytesPtr that = (ImmutablePairBytesPtr)obj;
+            if (this.hashCode != that.hashCode) return false;
+            if (Bytes.compareTo(this.bytes2, this.offset2, this.length2, that.bytes2, that.offset2, that.length2) != 0) return false;
+            if (Bytes.compareTo(this.bytes1, this.offset1, this.length1, that.bytes1, that.offset1, that.length1) != 0) return false;
+            return true;
+        }
+    }
+
+
+    @SuppressWarnings("all") // suppressing missing @Override since this doesn't exist for HBase 0.94.4
+    public boolean isFamilyEssential(byte[] name) {
+        // Only the column families involved in the expression are essential.
+        // The others are for columns projected in the select expression.
+        return cfSet.contains(name);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/50d523f6/phoenix-core/src/main/java/org/apache/phoenix/filter/MultiCQKeyValueComparisonFilter.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/filter/MultiCQKeyValueComparisonFilter.java b/phoenix-core/src/main/java/org/apache/phoenix/filter/MultiCQKeyValueComparisonFilter.java
new file mode 100644
index 0000000..c66b788
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/filter/MultiCQKeyValueComparisonFilter.java
@@ -0,0 +1,70 @@
+/*
+ * 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.filter;
+
+import org.apache.hadoop.hbase.util.Bytes;
+
+import org.apache.hadoop.hbase.index.util.ImmutableBytesPtr;
+import org.apache.phoenix.expression.Expression;
+
+/**
+ *
+ * Filter that evaluates WHERE clause expression, used in the case where there
+ * are references to multiple column qualifiers over a single column family.
+ *
+ * @author jtaylor
+ * @since 0.1
+ */
+public class MultiCQKeyValueComparisonFilter extends MultiKeyValueComparisonFilter {
+    private ImmutableBytesPtr ptr = new ImmutableBytesPtr();
+    private byte[] cf;
+
+    public MultiCQKeyValueComparisonFilter() {
+    }
+
+    public MultiCQKeyValueComparisonFilter(Expression expression) {
+        super(expression);
+    }
+
+    @Override
+    protected Object setColumnKey(byte[] cf, int cfOffset, int cfLength, byte[] cq, int cqOffset,
+            int cqLength) {
+        ptr.set(cq, cqOffset, cqLength);
+        return ptr;
+    }
+
+    @Override
+    protected Object newColumnKey(byte[] cf, int cfOffset, int cfLength, byte[] cq, int cqOffset,
+            int cqLength) {
+        if (cfOffset == 0 && cf.length == cfLength) {
+            this.cf = cf;
+        } else {
+            this.cf = new byte[cfLength];
+            System.arraycopy(cf, cfOffset, this.cf, 0, cfLength);
+        }
+        return new ImmutableBytesPtr(cq, cqOffset, cqLength);
+    }
+
+
+    @SuppressWarnings("all") // suppressing missing @Override since this doesn't exist for HBase 0.94.4
+    public boolean isFamilyEssential(byte[] name) {
+        return Bytes.compareTo(cf, name) == 0;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/50d523f6/phoenix-core/src/main/java/org/apache/phoenix/filter/MultiKeyValueComparisonFilter.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/filter/MultiKeyValueComparisonFilter.java b/phoenix-core/src/main/java/org/apache/phoenix/filter/MultiKeyValueComparisonFilter.java
new file mode 100644
index 0000000..ab8e1c0
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/filter/MultiKeyValueComparisonFilter.java
@@ -0,0 +1,247 @@
+/*
+ * 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.filter;
+
+import java.io.DataInput;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.hadoop.hbase.KeyValue;
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+import org.apache.hadoop.hbase.util.Bytes;
+
+import org.apache.phoenix.expression.Expression;
+import org.apache.phoenix.expression.KeyValueColumnExpression;
+import org.apache.phoenix.schema.tuple.Tuple;
+
+
+/**
+ * 
+ * Modeled after {@link org.apache.hadoop.hbase.filter.SingleColumnValueFilter},
+ * but for general expression evaluation in the case where multiple KeyValue
+ * columns are referenced in the expression.
+ *
+ * @author jtaylor
+ * @since 0.1
+ */
+public abstract class MultiKeyValueComparisonFilter extends BooleanExpressionFilter {
+    private static final byte[] UNITIALIZED_KEY_BUFFER = new byte[0];
+
+    private Boolean matchedColumn;
+    protected final IncrementalResultTuple inputTuple = new IncrementalResultTuple();
+
+    public MultiKeyValueComparisonFilter() {
+    }
+
+    public MultiKeyValueComparisonFilter(Expression expression) {
+        super(expression);
+        init();
+    }
+
+    private static final class KeyValueRef {
+        public KeyValue keyValue;
+        
+        @Override
+        public String toString() {
+            if(keyValue != null) {
+                return keyValue.toString() + " value = " + Bytes.toStringBinary(keyValue.getValue());
+            } else {
+                return super.toString();
+            }
+        }
+    }
+    
+    protected abstract Object setColumnKey(byte[] cf, int cfOffset, int cfLength, byte[] cq, int cqOffset, int cqLength);
+    protected abstract Object newColumnKey(byte[] cf, int cfOffset, int cfLength, byte[] cq, int cqOffset, int cqLength);
+    
+    private final class IncrementalResultTuple implements Tuple {
+        private int refCount;
+        private final ImmutableBytesWritable keyPtr = new ImmutableBytesWritable(UNITIALIZED_KEY_BUFFER);
+        private final Map<Object,KeyValueRef> foundColumns = new HashMap<Object,KeyValueRef>(5);
+        
+        public void reset() {
+            refCount = 0;
+            keyPtr.set(UNITIALIZED_KEY_BUFFER);
+            for (KeyValueRef ref : foundColumns.values()) {
+                ref.keyValue = null;
+            }
+        }
+        
+        @Override
+        public boolean isImmutable() {
+            return refCount == foundColumns.size();
+        }
+        
+        public void setImmutable() {
+            refCount = foundColumns.size();
+        }
+        
+        public ReturnCode resolveColumn(KeyValue value) {
+            // Always set key, in case we never find a key value column of interest,
+            // and our expression uses row key columns.
+            setKey(value);
+            byte[] buf = value.getBuffer();
+            Object ptr = setColumnKey(buf, value.getFamilyOffset(), value.getFamilyLength(), buf, value.getQualifierOffset(), value.getQualifierLength());
+            KeyValueRef ref = foundColumns.get(ptr);
+            if (ref == null) {
+                // Return INCLUDE here. Although this filter doesn't need this KV
+                // it should still be projected into the Result
+                return ReturnCode.INCLUDE;
+            }
+            // Since we only look at the latest key value for a given column,
+            // we are not interested in older versions
+            // TODO: test with older versions to confirm this doesn't get tripped
+            // This shouldn't be necessary, because a scan only looks at the latest
+            // version
+            if (ref.keyValue != null) {
+                // Can't do NEXT_ROW, because then we don't match the other columns
+                // SKIP, INCLUDE, and NEXT_COL seem to all act the same
+                return ReturnCode.NEXT_COL;
+            }
+            ref.keyValue = value;
+            refCount++;
+            return null;
+        }
+        
+        public void addColumn(byte[] cf, byte[] cq) {
+            Object ptr = MultiKeyValueComparisonFilter.this.newColumnKey(cf, 0, cf.length, cq, 0, cq.length);
+            foundColumns.put(ptr, new KeyValueRef());
+        }
+        
+        public void setKey(KeyValue value) {
+            keyPtr.set(value.getBuffer(), value.getRowOffset(), value.getRowLength());
+        }
+        
+        @Override
+        public void getKey(ImmutableBytesWritable ptr) {
+            ptr.set(keyPtr.get(),keyPtr.getOffset(),keyPtr.getLength());
+        }
+        
+        @Override
+        public KeyValue getValue(byte[] cf, byte[] cq) {
+            Object ptr = setColumnKey(cf, 0, cf.length, cq, 0, cq.length);
+            KeyValueRef ref = foundColumns.get(ptr);
+            return ref == null ? null : ref.keyValue;
+        }
+        
+        @Override
+        public String toString() {
+            return foundColumns.toString();
+        }
+
+        @Override
+        public int size() {
+            return refCount;
+        }
+
+        @Override
+        public KeyValue getValue(int index) {
+            // This won't perform very well, but it's not
+            // currently used anyway
+            for (KeyValueRef ref : foundColumns.values()) {
+                if (ref.keyValue == null) {
+                    continue;
+                }
+                if (index == 0) {
+                    return ref.keyValue;
+                }
+                index--;
+            }
+            throw new IndexOutOfBoundsException(Integer.toString(index));
+        }
+
+        @Override
+        public boolean getValue(byte[] family, byte[] qualifier,
+                ImmutableBytesWritable ptr) {
+            KeyValue kv = getValue(family, qualifier);
+            if (kv == null)
+                return false;
+            ptr.set(kv.getBuffer(), kv.getValueOffset(), kv.getValueLength());
+            return true;
+        }
+    }
+    
+    protected void init() {
+        EvaluateOnCompletionVisitor visitor = new EvaluateOnCompletionVisitor() {
+            @Override
+            public Void visit(KeyValueColumnExpression expression) {
+                inputTuple.addColumn(expression.getColumnFamily(), expression.getColumnName());
+                return null;
+            }
+        };
+        expression.accept(visitor);
+        this.evaluateOnCompletion = visitor.evaluateOnCompletion();
+        expression.reset();
+    }
+    
+    @Override
+    public ReturnCode filterKeyValue(KeyValue keyValue) {
+        if (Boolean.TRUE.equals(this.matchedColumn)) {
+          // We already found and matched the single column, all keys now pass
+          return ReturnCode.INCLUDE;
+        }
+        if (Boolean.FALSE.equals(this.matchedColumn)) {
+          // We found all the columns, but did not match the expression, so skip to next row
+          return ReturnCode.NEXT_ROW;
+        }
+        // This is a key value we're not interested in (TODO: why INCLUDE here instead of NEXT_COL?)
+        ReturnCode code = inputTuple.resolveColumn(keyValue);
+        if (code != null) {
+            return code;
+        }
+        
+        // We found a new column, so we can re-evaluate
+        // TODO: if we have row key columns in our expression, should
+        // we always evaluate or just wait until the end?
+        this.matchedColumn = this.evaluate(inputTuple);
+        if (this.matchedColumn == null) {
+            if (inputTuple.isImmutable()) {
+                this.matchedColumn = Boolean.FALSE;
+            } else {
+                return ReturnCode.INCLUDE;
+            }
+        }
+        return this.matchedColumn ? ReturnCode.INCLUDE : ReturnCode.NEXT_ROW;
+    }
+
+    @Override
+    public boolean filterRow() {
+        if (this.matchedColumn == null && !inputTuple.isImmutable() && evaluateOnCompletion()) {
+            inputTuple.setImmutable();
+            this.matchedColumn = this.evaluate(inputTuple);
+        }
+        
+        return ! (Boolean.TRUE.equals(this.matchedColumn));
+    }
+
+      @Override
+    public void reset() {
+        matchedColumn = null;
+        inputTuple.reset();
+        super.reset();
+    }
+
+    @Override
+    public void readFields(DataInput input) throws IOException {
+        super.readFields(input);
+        init();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/50d523f6/phoenix-core/src/main/java/org/apache/phoenix/filter/RowKeyComparisonFilter.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/filter/RowKeyComparisonFilter.java b/phoenix-core/src/main/java/org/apache/phoenix/filter/RowKeyComparisonFilter.java
new file mode 100644
index 0000000..bd47a8c
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/filter/RowKeyComparisonFilter.java
@@ -0,0 +1,156 @@
+/*
+ * 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.filter;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+
+import org.apache.hadoop.hbase.KeyValue;
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.hadoop.io.WritableUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.phoenix.expression.Expression;
+import org.apache.phoenix.schema.tuple.Tuple;
+
+
+/**
+ *
+ * Filter for use when expressions only reference row key columns
+ *
+ * @author jtaylor
+ * @since 0.1
+ */
+public class RowKeyComparisonFilter extends BooleanExpressionFilter {
+    private static final Logger logger = LoggerFactory.getLogger(RowKeyComparisonFilter.class);
+
+    private boolean evaluate = true;
+    private boolean keepRow = false;
+    private RowKeyTuple inputTuple = new RowKeyTuple();
+    private byte[] essentialCF;
+
+    public RowKeyComparisonFilter() {
+    }
+
+    public RowKeyComparisonFilter(Expression expression, byte[] essentialCF) {
+        super(expression);
+        this.essentialCF = essentialCF;
+    }
+
+    @Override
+    public void reset() {
+        this.keepRow = false;
+        this.evaluate = true;
+        super.reset();
+    }
+
+    /**
+     * Evaluate in filterKeyValue instead of filterRowKey, because HBASE-6562 causes filterRowKey
+     * to be called with deleted or partial row keys.
+     */
+    @Override
+    public ReturnCode filterKeyValue(KeyValue v) {
+        if (evaluate) {
+            inputTuple.setKey(v.getBuffer(), v.getRowOffset(), v.getRowLength());
+            this.keepRow = Boolean.TRUE.equals(evaluate(inputTuple));
+            if (logger.isDebugEnabled()) {
+                logger.debug("RowKeyComparisonFilter: " + (this.keepRow ? "KEEP" : "FILTER")  + " row " + inputTuple);
+            }
+            evaluate = false;
+        }
+        return keepRow ? ReturnCode.INCLUDE : ReturnCode.NEXT_ROW;
+    }
+
+    private final class RowKeyTuple implements Tuple {
+        private byte[] buf;
+        private int offset;
+        private int length;
+
+        public void setKey(byte[] buf, int offset, int length) {
+            this.buf = buf;
+            this.offset = offset;
+            this.length = length;
+        }
+
+        @Override
+        public void getKey(ImmutableBytesWritable ptr) {
+            ptr.set(buf, offset, length);
+        }
+
+        @Override
+        public KeyValue getValue(byte[] cf, byte[] cq) {
+            return null;
+        }
+
+        @Override
+        public boolean isImmutable() {
+            return true;
+        }
+
+        @Override
+        public String toString() {
+            return Bytes.toStringBinary(buf, offset, length);
+        }
+
+        @Override
+        public int size() {
+            return 0;
+        }
+
+        @Override
+        public KeyValue getValue(int index) {
+            throw new IndexOutOfBoundsException(Integer.toString(index));
+        }
+
+        @Override
+        public boolean getValue(byte[] family, byte[] qualifier,
+                ImmutableBytesWritable ptr) {
+            return false;
+        }
+    }
+
+    @Override
+    public boolean filterRow() {
+        return !this.keepRow;
+    }
+
+    @SuppressWarnings("all") // suppressing missing @Override since this doesn't exist for HBase 0.94.4
+    public boolean isFamilyEssential(byte[] name) {
+        // We only need our "guaranteed to have a key value" column family,
+        // which we pass in and serialize through. In the case of a VIEW where
+        // we don't have this, we have to say that all families are essential.
+        return this.essentialCF.length == 0 ? true : Bytes.compareTo(this.essentialCF, name) == 0;
+    }
+
+    @Override
+    public void readFields(DataInput input) throws IOException {
+        super.readFields(input);
+        this.essentialCF = WritableUtils.readCompressedByteArray(input);
+    }
+
+    @Override
+    public void write(DataOutput output) throws IOException {
+        super.write(output);
+        WritableUtils.writeCompressedByteArray(output, this.essentialCF);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/50d523f6/phoenix-core/src/main/java/org/apache/phoenix/filter/SingleCFCQKeyValueComparisonFilter.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/filter/SingleCFCQKeyValueComparisonFilter.java b/phoenix-core/src/main/java/org/apache/phoenix/filter/SingleCFCQKeyValueComparisonFilter.java
new file mode 100644
index 0000000..5b8a5f0
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/filter/SingleCFCQKeyValueComparisonFilter.java
@@ -0,0 +1,51 @@
+/*
+ * 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.filter;
+
+import org.apache.hadoop.hbase.util.Bytes;
+
+import org.apache.phoenix.expression.Expression;
+
+
+/**
+ * 
+ * SingleKeyValueComparisonFilter that needs to compare both the column family and
+ * column qualifier parts of the key value to disambiguate with another similarly
+ * named column qualifier in a different column family.
+ *
+ * @author jtaylor
+ * @since 0.1
+ */
+public class SingleCFCQKeyValueComparisonFilter extends SingleKeyValueComparisonFilter {
+    public SingleCFCQKeyValueComparisonFilter() {
+    }
+
+    public SingleCFCQKeyValueComparisonFilter(Expression expression) {
+        super(expression);
+    }
+
+    @Override
+    protected final int compare(byte[] cfBuf, int cfOffset, int cfLength, byte[] cqBuf, int cqOffset, int cqLength) {
+        int c = Bytes.compareTo(cf, 0, cf.length, cfBuf, cfOffset, cfLength);
+        if (c != 0) return c;
+        return Bytes.compareTo(cq, 0, cq.length, cqBuf, cqOffset, cqLength);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/50d523f6/phoenix-core/src/main/java/org/apache/phoenix/filter/SingleCQKeyValueComparisonFilter.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/filter/SingleCQKeyValueComparisonFilter.java b/phoenix-core/src/main/java/org/apache/phoenix/filter/SingleCQKeyValueComparisonFilter.java
new file mode 100644
index 0000000..425839a
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/filter/SingleCQKeyValueComparisonFilter.java
@@ -0,0 +1,49 @@
+/*
+ * 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.filter;
+
+import org.apache.hadoop.hbase.util.Bytes;
+
+import org.apache.phoenix.expression.Expression;
+
+
+/**
+ * 
+ * SingleKeyValueComparisonFilter that needs to only compare the column qualifier
+ * part of the key value since the column qualifier is unique across all column
+ * families.
+ *
+ * @author jtaylor
+ * @since 0.1
+ */
+public class SingleCQKeyValueComparisonFilter extends SingleKeyValueComparisonFilter {
+    public SingleCQKeyValueComparisonFilter() {
+    }
+
+    public SingleCQKeyValueComparisonFilter(Expression expression) {
+        super(expression);
+    }
+
+    @Override
+    protected final int compare(byte[] cfBuf, int cfOffset, int cfLength, byte[] cqBuf, int cqOffset, int cqLength) {
+        return Bytes.compareTo(cq, 0, cq.length, cqBuf, cqOffset, cqLength);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/50d523f6/phoenix-core/src/main/java/org/apache/phoenix/filter/SingleKeyValueComparisonFilter.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/filter/SingleKeyValueComparisonFilter.java b/phoenix-core/src/main/java/org/apache/phoenix/filter/SingleKeyValueComparisonFilter.java
new file mode 100644
index 0000000..1caa332
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/filter/SingleKeyValueComparisonFilter.java
@@ -0,0 +1,147 @@
+/*
+ * 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.filter;
+
+import java.io.DataInput;
+import java.io.IOException;
+
+import org.apache.hadoop.hbase.KeyValue;
+import org.apache.hadoop.hbase.util.Bytes;
+
+import org.apache.phoenix.expression.Expression;
+import org.apache.phoenix.expression.KeyValueColumnExpression;
+import org.apache.phoenix.schema.tuple.SingleKeyValueTuple;
+
+
+
+/**
+ *
+ * Modeled after {@link org.apache.hadoop.hbase.filter.SingleColumnValueFilter},
+ * but for general expression evaluation in the case where only a single KeyValue
+ * column is referenced in the expression.
+ *
+ * @author jtaylor
+ * @since 0.1
+ */
+public abstract class SingleKeyValueComparisonFilter extends BooleanExpressionFilter {
+    private final SingleKeyValueTuple inputTuple = new SingleKeyValueTuple();
+    private boolean matchedColumn;
+    protected byte[] cf;
+    protected byte[] cq;
+
+    public SingleKeyValueComparisonFilter() {
+    }
+
+    public SingleKeyValueComparisonFilter(Expression expression) {
+        super(expression);
+        init();
+    }
+
+    protected abstract int compare(byte[] cfBuf, int cfOffset, int cfLength, byte[] cqBuf, int cqOffset, int cqLength);
+
+    private void init() {
+        EvaluateOnCompletionVisitor visitor = new EvaluateOnCompletionVisitor() {
+            @Override
+            public Void visit(KeyValueColumnExpression expression) {
+                cf = expression.getColumnFamily();
+                cq = expression.getColumnName();
+                return null;
+            }
+        };
+        expression.accept(visitor);
+        this.evaluateOnCompletion = visitor.evaluateOnCompletion();
+    }
+
+    private boolean foundColumn() {
+        return inputTuple.size() > 0;
+    }
+
+    @Override
+    public ReturnCode filterKeyValue(KeyValue keyValue) {
+        if (this.matchedColumn) {
+          // We already found and matched the single column, all keys now pass
+          // TODO: why won't this cause earlier versions of a kv to be included?
+          return ReturnCode.INCLUDE;
+        }
+        if (this.foundColumn()) {
+          // We found all the columns, but did not match the expression, so skip to next row
+          return ReturnCode.NEXT_ROW;
+        }
+        byte[] buf = keyValue.getBuffer();
+        if (compare(buf, keyValue.getFamilyOffset(), keyValue.getFamilyLength(), buf, keyValue.getQualifierOffset(), keyValue.getQualifierLength()) != 0) {
+            // Remember the key in case this is the only key value we see.
+            // We'll need it if we have row key columns too.
+            inputTuple.setKey(keyValue);
+            // This is a key value we're not interested in
+            // TODO: use NEXT_COL when bug fix comes through that includes the row still
+            return ReturnCode.INCLUDE;
+        }
+        inputTuple.setKeyValue(keyValue);
+
+        // We have the columns, so evaluate here
+        if (!Boolean.TRUE.equals(evaluate(inputTuple))) {
+            return ReturnCode.NEXT_ROW;
+        }
+        this.matchedColumn = true;
+        return ReturnCode.INCLUDE;
+    }
+
+    @Override
+    public boolean filterRow() {
+        // If column was found, return false if it was matched, true if it was not.
+        if (foundColumn()) {
+            return !this.matchedColumn;
+        }
+        // If column was not found, evaluate the expression here upon completion.
+        // This is required with certain expressions, for example, with IS NULL
+        // expressions where they'll evaluate to TRUE when the column being
+        // tested wasn't found.
+        // Since the filter is called also to position the scan initially, we have
+        // to guard against this by checking whether or not we've filtered in
+        // the key value (i.e. filterKeyValue was called and we found the keyValue
+        // for which we're looking).
+        if (inputTuple.hasKey() && evaluateOnCompletion()) {
+            return !Boolean.TRUE.equals(evaluate(inputTuple));
+        }
+        // Finally, if we have no values, and we're not required to re-evaluate it
+        // just filter the row
+        return true;
+    }
+
+      @Override
+    public void reset() {
+        inputTuple.reset();
+        matchedColumn = false;
+        super.reset();
+    }
+
+    @Override
+    public void readFields(DataInput input) throws IOException {
+        super.readFields(input);
+        init();
+    }
+
+    @SuppressWarnings("all") // suppressing missing @Override since this doesn't exist for HBase 0.94.4
+    public boolean isFamilyEssential(byte[] name) {
+        // Only the column families involved in the expression are essential.
+        // The others are for columns projected in the select expression
+        return Bytes.compareTo(cf, name) == 0;
+    }
+}