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 20:23:33 UTC
[32/51] [partial] Initial commit
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/c5b80246/src/main/java/org/apache/phoenix/expression/LikeExpression.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/phoenix/expression/LikeExpression.java b/src/main/java/org/apache/phoenix/expression/LikeExpression.java
new file mode 100644
index 0000000..038f705
--- /dev/null
+++ b/src/main/java/org/apache/phoenix/expression/LikeExpression.java
@@ -0,0 +1,302 @@
+/*
+ * 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;
+
+import java.io.DataInput;
+import java.io.IOException;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.phoenix.expression.visitor.ExpressionVisitor;
+import org.apache.phoenix.schema.PDataType;
+import org.apache.phoenix.schema.tuple.Tuple;
+import org.apache.phoenix.util.StringUtil;
+
+
+/**
+ *
+ * Implementation for LIKE operation where the first child expression is the string
+ * and the second is the pattern. The pattern supports '_' character for single
+ * character wildcard match and '%' for zero or more character match. where these
+ * characters may be escaped by preceding them with a '\'.
+ *
+ * Example: foo LIKE 'ab%' will match a row in which foo starts with 'ab'
+ *
+ * @author jtaylor
+ * @since 0.1
+ */
+public class LikeExpression extends BaseCompoundExpression {
+ private static final Logger logger = LoggerFactory.getLogger(LikeExpression.class);
+
+ private static final String ZERO_OR_MORE = "\\E.*\\Q";
+ private static final String ANY_ONE = "\\E.\\Q";
+
+ public static String unescapeLike(String s) {
+ return StringUtil.replace(s, StringUtil.LIKE_ESCAPE_SEQS, StringUtil.LIKE_UNESCAPED_SEQS);
+ }
+
+ /**
+ * @return the substring of s for which we have a literal string
+ * that we can potentially use to set the start/end key, or null
+ * if there is none.
+ */
+ public static String getStartsWithPrefix(String s) {
+ int i = indexOfWildcard(s);
+ return i == -1 ? s : s.substring(0,i);
+ }
+
+ public static boolean hasWildcards(String s) {
+ return indexOfWildcard(s) != -1;
+ }
+
+ /**
+ * Replace unescaped '*' and '?' in s with '%' and '_' respectively
+ * such that the returned string may be used in a LIKE expression.
+ * Provides an alternate way of expressing a LIKE pattern which is
+ * more friendly for wildcard matching when the source string is
+ * likely to contain an '%' or '_' character.
+ * @param s wildcard pattern that may use '*' for multi character
+ * match and '?' for single character match, escaped by the backslash
+ * character
+ * @return replaced
+ */
+ public static String wildCardToLike(String s) {
+ s = StringUtil.escapeLike(s);
+ StringBuilder buf = new StringBuilder();
+ // Look for another unprotected * or ? in the middle
+ int i = 0;
+ int j = 0;
+ while (true) {
+ int pctPos = s.indexOf(StringUtil.MULTI_CHAR_WILDCARD, i);
+ int underPos = s.indexOf(StringUtil.SINGLE_CHAR_WILDCARD, i);
+ if (pctPos == -1 && underPos == -1) {
+ return i == 0 ? s : buf.append(s.substring(i)).toString();
+ }
+ i = pctPos;
+ if (underPos != -1 && (i == -1 || underPos < i)) {
+ i = underPos;
+ }
+
+ if (i > 0 && s.charAt(i - 1) == '\\') {
+ // If we found protection then keep looking
+ buf.append(s.substring(j,i-1));
+ buf.append(s.charAt(i));
+ } else {
+ // We found an unprotected % or _ in the middle
+ buf.append(s.substring(j,i));
+ buf.append(s.charAt(i) == StringUtil.MULTI_CHAR_WILDCARD ? StringUtil.MULTI_CHAR_LIKE : StringUtil.SINGLE_CHAR_LIKE);
+ }
+ j = ++i;
+ }
+ }
+
+ public static int indexOfWildcard(String s) {
+ // Look for another unprotected % or _ in the middle
+ if (s == null) {
+ return -1;
+ }
+ int i = 0;
+ while (true) {
+ int pctPos = s.indexOf(StringUtil.MULTI_CHAR_LIKE, i);
+ int underPos = s.indexOf(StringUtil.SINGLE_CHAR_LIKE, i);
+ if (pctPos == -1 && underPos == -1) {
+ return -1;
+ }
+ i = pctPos;
+ if (underPos != -1 && (i == -1 || underPos < i)) {
+ i = underPos;
+ }
+
+ if (i > 0 && s.charAt(i - 1) == '\\') {
+ // If we found protection then keep looking
+ i++;
+ } else {
+ // We found an unprotected % or _ in the middle
+ return i;
+ }
+ }
+ }
+
+ private static String toPattern(String s) {
+ StringBuilder sb = new StringBuilder(s.length());
+
+ // From the JDK doc: \Q and \E protect everything between them
+ sb.append("\\Q");
+ boolean wasSlash = false;
+ for (int i = 0; i < s.length(); i++) {
+ char c = s.charAt(i);
+ if (wasSlash) {
+ sb.append(c);
+ wasSlash = false;
+ } else if (c == StringUtil.SINGLE_CHAR_LIKE) {
+ sb.append(ANY_ONE);
+ } else if (c == StringUtil.MULTI_CHAR_LIKE) {
+ sb.append(ZERO_OR_MORE);
+ } else if (c == '\\') {
+ wasSlash = true;
+ } else {
+ sb.append(c);
+ }
+ }
+ sb.append("\\E");
+ // Found nothing interesting
+ return sb.toString();
+ }
+
+// private static String fromPattern(String s) {
+// StringBuilder sb = new StringBuilder(s.length());
+//
+// for (int i = 0; i < s.length(); i++) {
+// if (s.substring(i).startsWith("\\Q")) {
+// while (s.substring(i + "\\Q".length()).startsWith("\\E")) {
+// sb.append(s.charAt(i++ + "\\Q".length()));
+// }
+// i+= "\\E".length();
+// }
+// if (s.charAt(i) == '.') {
+// if (s.charAt(i+1) == '*') {
+// sb.append('%');
+// i+=2;
+// } else {
+// sb.append('_');
+// i++;
+// }
+// }
+// }
+// return sb.toString();
+// }
+
+ private Pattern pattern;
+
+ public LikeExpression() {
+ }
+
+ public LikeExpression(List<Expression> children) {
+ super(children);
+ init();
+ }
+
+ public boolean startsWithWildcard() {
+ return pattern != null && pattern.pattern().startsWith("\\Q\\E");
+ }
+
+ private void init() {
+ Expression e = getPatternExpression();
+ if (e instanceof LiteralExpression) {
+ LiteralExpression patternExpression = (LiteralExpression)e;
+ String value = (String)patternExpression.getValue();
+ pattern = Pattern.compile(toPattern(value));
+ }
+ }
+
+ private Expression getStrExpression() {
+ return children.get(0);
+ }
+
+ private Expression getPatternExpression() {
+ return children.get(1);
+ }
+
+ @Override
+ public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) {
+ Pattern pattern = this.pattern;
+ if (pattern == null) { // TODO: don't allow? this is going to be slooowwww
+ if (!getPatternExpression().evaluate(tuple, ptr)) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("LIKE is FALSE: pattern is null");
+ }
+ return false;
+ }
+ String value = (String)PDataType.VARCHAR.toObject(ptr, getPatternExpression().getColumnModifier());
+ pattern = Pattern.compile(toPattern(value));
+ if (logger.isDebugEnabled()) {
+ logger.debug("LIKE pattern is expression: " + pattern.pattern());
+ }
+ }
+
+ if (!getStrExpression().evaluate(tuple, ptr)) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("LIKE is FALSE: child expression is null");
+ }
+ return false;
+ }
+ if (ptr.getLength() == 0) {
+ return true;
+ }
+
+ String value = (String)PDataType.VARCHAR.toObject(ptr, getStrExpression().getColumnModifier());
+ boolean matched = pattern.matcher(value).matches();
+ ptr.set(matched ? PDataType.TRUE_BYTES : PDataType.FALSE_BYTES);
+ if (logger.isDebugEnabled()) {
+ logger.debug("LIKE(value='" + value + "'pattern='" + pattern.pattern() + "' is " + matched);
+ }
+ return true;
+ }
+
+ @Override
+ public void readFields(DataInput input) throws IOException {
+ super.readFields(input);
+ init();
+ }
+
+ @Override
+ public PDataType getDataType() {
+ return PDataType.BOOLEAN;
+ }
+
+ @Override
+ public final <T> T accept(ExpressionVisitor<T> visitor) {
+ List<T> l = acceptChildren(visitor, visitor.visitEnter(this));
+ T t = visitor.visitLeave(this, l);
+ if (t == null) {
+ t = visitor.defaultReturn(this, l);
+ }
+ return t;
+ }
+
+ public String getLiteralPrefix() {
+ if (pattern == null) {
+ return "";
+ }
+ String pattern = this.pattern.pattern();
+ int fromIndex = "\\Q".length();
+ return pattern.substring(fromIndex, pattern.indexOf("\\E", fromIndex));
+ }
+
+ public boolean endsWithOnlyWildcard() {
+ if (pattern == null) {
+ return false;
+ }
+ String pattern = this.pattern.pattern();
+ String endsWith = ZERO_OR_MORE + "\\E";
+ return pattern.endsWith(endsWith) &&
+ pattern.lastIndexOf(ANY_ONE, pattern.length() - endsWith.length() - 1) == -1 &&
+ pattern.lastIndexOf(ZERO_OR_MORE, pattern.length() - endsWith.length() - 1) == -1;
+ }
+
+ @Override
+ public String toString() {
+ return (children.get(0) + " LIKE " + children.get(1));
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/c5b80246/src/main/java/org/apache/phoenix/expression/LiteralExpression.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/phoenix/expression/LiteralExpression.java b/src/main/java/org/apache/phoenix/expression/LiteralExpression.java
new file mode 100644
index 0000000..c86387d
--- /dev/null
+++ b/src/main/java/org/apache/phoenix/expression/LiteralExpression.java
@@ -0,0 +1,260 @@
+/*
+ * 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;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.sql.SQLException;
+
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.hadoop.io.WritableUtils;
+
+import org.apache.phoenix.exception.SQLExceptionCode;
+import org.apache.phoenix.exception.SQLExceptionInfo;
+import org.apache.phoenix.expression.visitor.ExpressionVisitor;
+import org.apache.phoenix.schema.ColumnModifier;
+import org.apache.phoenix.schema.IllegalDataException;
+import org.apache.phoenix.schema.PDataType;
+import org.apache.phoenix.schema.TypeMismatchException;
+import org.apache.phoenix.schema.tuple.Tuple;
+import org.apache.phoenix.util.ByteUtil;
+import org.apache.phoenix.util.StringUtil;
+
+
+
+/**
+ *
+ * Accessor for a literal value.
+ *
+ * @author jtaylor
+ * @since 0.1
+ */
+public class LiteralExpression extends BaseTerminalExpression {
+ public static final LiteralExpression NULL_EXPRESSION = new LiteralExpression(null);
+ private static final LiteralExpression[] TYPED_NULL_EXPRESSIONS = new LiteralExpression[PDataType.values().length];
+ static {
+ for (int i = 0; i < TYPED_NULL_EXPRESSIONS.length; i++) {
+ TYPED_NULL_EXPRESSIONS[i] = new LiteralExpression(PDataType.values()[i]);
+ }
+ }
+ public static final LiteralExpression FALSE_EXPRESSION = new LiteralExpression(Boolean.FALSE, PDataType.BOOLEAN, PDataType.BOOLEAN.toBytes(Boolean.FALSE));
+ public static final LiteralExpression TRUE_EXPRESSION = new LiteralExpression(Boolean.TRUE, PDataType.BOOLEAN, PDataType.BOOLEAN.toBytes(Boolean.TRUE));
+
+ private Object value;
+ private PDataType type;
+ private byte[] byteValue;
+ private Integer byteSize;
+ private Integer maxLength;
+ private Integer scale;
+ private ColumnModifier columnModifier;
+
+ // TODO: cache?
+ public static LiteralExpression newConstant(Object value) {
+ if (Boolean.FALSE.equals(value)) {
+ return FALSE_EXPRESSION;
+ }
+ if (Boolean.TRUE.equals(value)) {
+ return TRUE_EXPRESSION;
+ }
+ if (value == null) {
+ return NULL_EXPRESSION;
+ }
+ PDataType type = PDataType.fromLiteral(value);
+ byte[] b = type.toBytes(value);
+ if (b.length == 0) {
+ return TYPED_NULL_EXPRESSIONS[type.ordinal()];
+ }
+ if (type == PDataType.VARCHAR) {
+ String s = (String) value;
+ if (s.length() == b.length) { // single byte characters only
+ type = PDataType.CHAR;
+ }
+ }
+ return new LiteralExpression(value, type, b);
+ }
+
+ public static LiteralExpression newConstant(Object value, PDataType type) throws SQLException {
+ return newConstant(value, type, null);
+ }
+
+ public static LiteralExpression newConstant(Object value, PDataType type, ColumnModifier columnModifier) throws SQLException {
+ return newConstant(value, type, null, null, columnModifier);
+ }
+
+ public static LiteralExpression newConstant(Object value, PDataType type, Integer maxLength, Integer scale) throws SQLException { // remove?
+ return newConstant(value, type, maxLength, scale, null);
+ }
+
+ // TODO: cache?
+ public static LiteralExpression newConstant(Object value, PDataType type, Integer maxLength, Integer scale, ColumnModifier columnModifier)
+ throws SQLException {
+ if (value == null) {
+ if (type == null) {
+ return NULL_EXPRESSION;
+ }
+ return TYPED_NULL_EXPRESSIONS[type.ordinal()];
+ }
+ PDataType actualType = PDataType.fromLiteral(value);
+ if (!actualType.isCoercibleTo(type, value)) {
+ throw new TypeMismatchException(type, actualType, value.toString());
+ }
+ value = type.toObject(value, actualType);
+ try {
+ byte[] b = type.toBytes(value, columnModifier);
+ if (type == PDataType.VARCHAR || type == PDataType.CHAR) {
+ if (type == PDataType.CHAR && maxLength != null && b.length < maxLength) {
+ b = StringUtil.padChar(b, maxLength);
+ } else if (value != null) {
+ maxLength = ((String)value).length();
+ }
+ }
+ if (b.length == 0) {
+ return TYPED_NULL_EXPRESSIONS[type.ordinal()];
+ }
+ return new LiteralExpression(value, type, b, maxLength, scale, columnModifier);
+ } catch (IllegalDataException e) {
+ throw new SQLExceptionInfo.Builder(SQLExceptionCode.ILLEGAL_DATA).setRootCause(e).build().buildException();
+ }
+ }
+
+ public LiteralExpression() {
+ }
+
+ private LiteralExpression(PDataType type) {
+ this(null, type, ByteUtil.EMPTY_BYTE_ARRAY);
+ }
+
+ private LiteralExpression(Object value, PDataType type, byte[] byteValue) {
+ this(value, type, byteValue, type == null? null : type.getMaxLength(value), type == null? null : type.getScale(value), null);
+ }
+
+ private LiteralExpression(Object value, PDataType type, byte[] byteValue,
+ Integer maxLength, Integer scale, ColumnModifier columnModifier) {
+ this.value = value;
+ this.type = type;
+ this.byteValue = byteValue;
+ this.byteSize = byteValue.length;
+ this.maxLength = maxLength;
+ this.scale = scale;
+ this.columnModifier = columnModifier;
+ }
+
+ @Override
+ public String toString() {
+ return type != null && type.isCoercibleTo(PDataType.VARCHAR) ? "'" + value + "'" : "" + value;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((value == null) ? 0 : value.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;
+ LiteralExpression other = (LiteralExpression)obj;
+ if (value == null) {
+ if (other.value != null) return false;
+ } else if (!value.equals(other.value)) return false;
+ return true;
+ }
+
+ @Override
+ public void readFields(DataInput input) throws IOException {
+ this.byteValue = Bytes.readByteArray(input);
+ columnModifier = ColumnModifier.fromSystemValue(WritableUtils.readVInt(input));
+ if (this.byteValue.length > 0) {
+ this.type = PDataType.values()[WritableUtils.readVInt(input)];
+ this.value = this.type.toObject(byteValue, 0, byteValue.length, this.type, columnModifier);
+ }
+ byteSize = this.byteValue.length;
+ }
+
+ @Override
+ public void write(DataOutput output) throws IOException {
+ Bytes.writeByteArray(output, byteValue);
+ WritableUtils.writeVInt(output, ColumnModifier.toSystemValue(columnModifier));
+ if (this.byteValue.length > 0) {
+ WritableUtils.writeVInt(output, this.type.ordinal());
+ }
+ }
+
+ @Override
+ public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) {
+ // Literal always evaluates, even when it returns null
+ ptr.set(byteValue);
+ return true;
+ }
+
+ @Override
+ public PDataType getDataType() {
+ return type;
+ }
+
+ @Override
+ public Integer getByteSize() {
+ return byteSize;
+ }
+
+ @Override
+ public Integer getMaxLength() {
+ return maxLength;
+ }
+
+ @Override
+ public Integer getScale() {
+ return scale;
+ }
+
+ @Override
+ public ColumnModifier getColumnModifier() {
+ return columnModifier;
+ }
+
+ @Override
+ public boolean isNullable() {
+ return value == null;
+ }
+
+ public Object getValue() {
+ return value;
+ }
+
+ public byte[] getBytes() {
+ return byteValue;
+ }
+
+ @Override
+ public final <T> T accept(ExpressionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
+ @Override
+ public boolean isConstant() {
+ return true;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/c5b80246/src/main/java/org/apache/phoenix/expression/LongAddExpression.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/phoenix/expression/LongAddExpression.java b/src/main/java/org/apache/phoenix/expression/LongAddExpression.java
new file mode 100644
index 0000000..a14286d
--- /dev/null
+++ b/src/main/java/org/apache/phoenix/expression/LongAddExpression.java
@@ -0,0 +1,62 @@
+/*
+ * 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;
+
+import java.util.List;
+
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+
+import org.apache.phoenix.schema.PDataType;
+import org.apache.phoenix.schema.tuple.Tuple;
+
+
+public class LongAddExpression extends AddExpression {
+
+ public LongAddExpression() {
+ }
+
+ public LongAddExpression(List<Expression> children) {
+ super(children);
+ }
+
+ @Override
+ public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) {
+ long finalResult=0;
+
+ for(int i=0;i<children.size();i++) {
+ Expression child = children.get(i);
+ if (!child.evaluate(tuple, ptr) || ptr.getLength() == 0) {
+ return false;
+ }
+ long childvalue = child.getDataType().getCodec().decodeLong(ptr, child.getColumnModifier());
+ finalResult += childvalue;
+ }
+ byte[] resultPtr=new byte[PDataType.LONG.getByteSize()];
+ ptr.set(resultPtr);
+ getDataType().getCodec().encodeLong(finalResult, ptr);
+ return true;
+ }
+
+ @Override
+ public final PDataType getDataType() {
+ return PDataType.LONG;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/c5b80246/src/main/java/org/apache/phoenix/expression/LongDivideExpression.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/phoenix/expression/LongDivideExpression.java b/src/main/java/org/apache/phoenix/expression/LongDivideExpression.java
new file mode 100644
index 0000000..68560a6
--- /dev/null
+++ b/src/main/java/org/apache/phoenix/expression/LongDivideExpression.java
@@ -0,0 +1,66 @@
+/*
+ * 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;
+
+import java.util.List;
+
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+
+import org.apache.phoenix.schema.PDataType;
+import org.apache.phoenix.schema.tuple.Tuple;
+
+
+public class LongDivideExpression extends DivideExpression {
+
+ public LongDivideExpression() {
+ }
+
+ public LongDivideExpression(List<Expression> children) {
+ super(children);
+ }
+
+ @Override
+ public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) {
+ long finalResult=0;
+
+ for(int i=0;i<children.size();i++) {
+ Expression child = children.get(i);
+ if (!child.evaluate(tuple, ptr) || ptr.getLength() == 0) {
+ return false;
+ }
+ long childvalue = child.getDataType().getCodec().decodeLong(ptr, child.getColumnModifier());
+ if (i == 0) {
+ finalResult = childvalue;
+ } else {
+ finalResult /= childvalue;
+ }
+ }
+ byte[] resultPtr=new byte[PDataType.LONG.getByteSize()];
+ ptr.set(resultPtr);
+ getDataType().getCodec().encodeLong(finalResult, ptr);
+ return true;
+ }
+
+ @Override
+ public final PDataType getDataType() {
+ return PDataType.LONG;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/c5b80246/src/main/java/org/apache/phoenix/expression/LongMultiplyExpression.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/phoenix/expression/LongMultiplyExpression.java b/src/main/java/org/apache/phoenix/expression/LongMultiplyExpression.java
new file mode 100644
index 0000000..508d33c
--- /dev/null
+++ b/src/main/java/org/apache/phoenix/expression/LongMultiplyExpression.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;
+
+import java.util.List;
+
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+
+import org.apache.phoenix.schema.PDataType;
+import org.apache.phoenix.schema.tuple.Tuple;
+
+
+public class LongMultiplyExpression extends MultiplyExpression {
+
+ public LongMultiplyExpression() {
+ }
+
+ public LongMultiplyExpression(List<Expression> children) {
+ super(children);
+ }
+
+ @Override
+ public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) {
+ long finalResult=1;
+
+ for(int i=0;i<children.size();i++) {
+ Expression child = children.get(i);
+ if (!child.evaluate(tuple, ptr)) {
+ return false;
+ }
+ if (ptr.getLength() == 0) {
+ return false;
+ }
+ long childvalue = child.getDataType().getCodec().decodeLong(ptr, child.getColumnModifier());
+ finalResult *= childvalue;
+ }
+ byte[] resultPtr=new byte[getDataType().getByteSize()];
+ ptr.set(resultPtr);
+ getDataType().getCodec().encodeLong(finalResult, ptr);
+ return true;
+ }
+
+ @Override
+ public final PDataType getDataType() {
+ return PDataType.LONG;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/c5b80246/src/main/java/org/apache/phoenix/expression/LongSubtractExpression.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/phoenix/expression/LongSubtractExpression.java b/src/main/java/org/apache/phoenix/expression/LongSubtractExpression.java
new file mode 100644
index 0000000..56317d6
--- /dev/null
+++ b/src/main/java/org/apache/phoenix/expression/LongSubtractExpression.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.expression;
+
+import java.util.List;
+
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+
+import org.apache.phoenix.query.QueryConstants;
+import org.apache.phoenix.schema.PDataType;
+import org.apache.phoenix.schema.tuple.Tuple;
+
+
+
+/**
+ *
+ * Subtract expression implementation
+ *
+ * @author kmahadik
+ * @since 0.1
+ */
+public class LongSubtractExpression extends SubtractExpression {
+ public LongSubtractExpression() {
+ }
+
+ public LongSubtractExpression(List<Expression> children) {
+ super(children);
+ }
+
+ @Override
+ public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) {
+ long finalResult=0;
+
+ for(int i=0;i<children.size();i++) {
+ Expression child = children.get(i);
+ if (!child.evaluate(tuple, ptr) || ptr.getLength() == 0) {
+ return false;
+ }
+ PDataType childType = child.getDataType();
+ boolean isDate = childType.isCoercibleTo(PDataType.DATE);
+ long childvalue = childType.getCodec().decodeLong(ptr, child.getColumnModifier());
+ if (i == 0) {
+ finalResult = childvalue;
+ } else {
+ finalResult -= childvalue;
+ /*
+ * Special case for date subtraction - note that only first two expression may be dates.
+ * We need to convert the date to a unit of "days" because that's what sql expects.
+ */
+ if (isDate) {
+ finalResult /= QueryConstants.MILLIS_IN_DAY;
+ }
+ }
+ }
+ byte[] resultPtr=new byte[getDataType().getByteSize()];
+ ptr.set(resultPtr);
+ getDataType().getCodec().encodeLong(finalResult, ptr);
+ return true;
+ }
+
+ @Override
+ public final PDataType getDataType() {
+ return PDataType.LONG;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/c5b80246/src/main/java/org/apache/phoenix/expression/MultiplyExpression.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/phoenix/expression/MultiplyExpression.java b/src/main/java/org/apache/phoenix/expression/MultiplyExpression.java
new file mode 100644
index 0000000..fa815a2
--- /dev/null
+++ b/src/main/java/org/apache/phoenix/expression/MultiplyExpression.java
@@ -0,0 +1,98 @@
+/*
+ * 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;
+
+import java.util.List;
+
+import org.apache.phoenix.expression.visitor.ExpressionVisitor;
+import org.apache.phoenix.schema.PDataType;
+
+
+/**
+ *
+ * Subtract expression implementation
+ *
+ * @author jtaylor
+ * @since 0.1
+ */
+public abstract class MultiplyExpression extends ArithmeticExpression {
+ private Integer maxLength;
+ private Integer scale;
+
+ public MultiplyExpression() {
+ }
+
+ public MultiplyExpression(List<Expression> children) {
+ super(children);
+ for (int i=0; i<children.size(); i++) {
+ Expression childExpr = children.get(i);
+ if (i == 0) {
+ maxLength = childExpr.getMaxLength();
+ scale = childExpr.getScale();
+ } else {
+ maxLength = getPrecision(maxLength, childExpr.getMaxLength(), scale, childExpr.getScale());
+ scale = getScale(maxLength, childExpr.getMaxLength(), scale, childExpr.getScale());
+ }
+ }
+ }
+
+ @Override
+ public final <T> T accept(ExpressionVisitor<T> visitor) {
+ List<T> l = acceptChildren(visitor, visitor.visitEnter(this));
+ T t = visitor.visitLeave(this, l);
+ if (t == null) {
+ t = visitor.defaultReturn(this, l);
+ }
+ return t;
+ }
+
+ @Override
+ public String getOperatorString() {
+ return " * ";
+ }
+
+ private static Integer getPrecision(Integer lp, Integer rp, Integer ls, Integer rs) {
+ if (ls == null || rs == null) {
+ return PDataType.MAX_PRECISION;
+ }
+ int val = lp + rp;
+ return Math.min(PDataType.MAX_PRECISION, val);
+ }
+
+ private static Integer getScale(Integer lp, Integer rp, Integer ls, Integer rs) {
+ // If we are adding a decimal with scale and precision to a decimal
+ // with no precision nor scale, the scale system does not apply.
+ if (ls == null || rs == null) {
+ return null;
+ }
+ int val = ls + rs;
+ return Math.min(PDataType.MAX_PRECISION, val);
+ }
+
+ @Override
+ public Integer getScale() {
+ return scale;
+ }
+
+ @Override
+ public Integer getMaxLength() {
+ return maxLength;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/c5b80246/src/main/java/org/apache/phoenix/expression/NotExpression.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/phoenix/expression/NotExpression.java b/src/main/java/org/apache/phoenix/expression/NotExpression.java
new file mode 100644
index 0000000..c3a07c9
--- /dev/null
+++ b/src/main/java/org/apache/phoenix/expression/NotExpression.java
@@ -0,0 +1,86 @@
+/*
+ * 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;
+
+import java.util.List;
+
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+
+import org.apache.phoenix.expression.visitor.ExpressionVisitor;
+import org.apache.phoenix.schema.PDataType;
+import org.apache.phoenix.schema.tuple.Tuple;
+
+
+/**
+ *
+ * Implementation of the NOT operator that negates it's
+ * single boolean child expression.
+ *
+ * @author jtaylor
+ * @since 0.1
+ */
+public class NotExpression extends BaseSingleExpression {
+
+ public NotExpression() {
+ }
+
+ public NotExpression(Expression expression) {
+ super(expression);
+ if (expression == null || expression.getDataType() != PDataType.BOOLEAN) {
+ throw new IllegalArgumentException("NOT expectes a single BOOLEAN expression, but got " + expression);
+ }
+ }
+
+ @Override
+ public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) {
+ if (!getChild().evaluate(tuple, ptr)) {
+ return false;
+ }
+ if (ptr.getLength() == 0) {
+ return true;
+ }
+
+ ptr.set(Boolean.TRUE.equals(PDataType.BOOLEAN.toObject(ptr)) ? PDataType.FALSE_BYTES : PDataType.TRUE_BYTES);
+ return true;
+ }
+
+ @Override
+ public PDataType getDataType() {
+ return PDataType.BOOLEAN;
+ }
+
+ @Override
+ public final <T> T accept(ExpressionVisitor<T> visitor) {
+ List<T> l = acceptChildren(visitor, visitor.visitEnter(this));
+ T t = visitor.visitLeave(this, l);
+ if (t == null) {
+ t = visitor.defaultReturn(this, l);
+ }
+ return t;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder buf = new StringBuilder("NOT (");
+ buf.append(children.get(0).toString());
+ buf.append(")");
+ return buf.toString();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/c5b80246/src/main/java/org/apache/phoenix/expression/OrExpression.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/phoenix/expression/OrExpression.java b/src/main/java/org/apache/phoenix/expression/OrExpression.java
new file mode 100644
index 0000000..7339852
--- /dev/null
+++ b/src/main/java/org/apache/phoenix/expression/OrExpression.java
@@ -0,0 +1,67 @@
+/*
+ * 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;
+
+import java.util.List;
+
+import org.apache.phoenix.expression.visitor.ExpressionVisitor;
+
+
+/**
+ *
+ * OR expression implementation
+ *
+ * @author jtaylor
+ * @since 0.1
+ */
+public class OrExpression extends AndOrExpression {
+ public OrExpression() {
+ }
+
+ public OrExpression(List<Expression> children) {
+ super(children);
+ }
+
+ @Override
+ protected boolean getStopValue() {
+ return Boolean.TRUE;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder buf = new StringBuilder("(");
+ for (int i = 0; i < children.size() - 1; i++) {
+ buf.append(children.get(i) + " OR ");
+ }
+ buf.append(children.get(children.size()-1));
+ buf.append(')');
+ return buf.toString();
+ }
+
+ @Override
+ public final <T> T accept(ExpressionVisitor<T> visitor) {
+ List<T> l = acceptChildren(visitor, visitor.visitEnter(this));
+ T t = visitor.visitLeave(this, l);
+ if (t == null) {
+ t = visitor.defaultReturn(this, l);
+ }
+ return t;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/c5b80246/src/main/java/org/apache/phoenix/expression/OrderByExpression.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/phoenix/expression/OrderByExpression.java b/src/main/java/org/apache/phoenix/expression/OrderByExpression.java
new file mode 100644
index 0000000..67d7e99
--- /dev/null
+++ b/src/main/java/org/apache/phoenix/expression/OrderByExpression.java
@@ -0,0 +1,87 @@
+package org.apache.phoenix.expression;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+
+import org.apache.hadoop.io.Writable;
+import org.apache.hadoop.io.WritableUtils;
+
+/**
+ * A container for a column that appears in ORDER BY clause.
+ */
+public class OrderByExpression implements Writable {
+ private Expression expression;
+ private boolean isNullsLast;
+ private boolean isAscending;
+
+ public OrderByExpression() {
+ }
+
+ public OrderByExpression(Expression expression, boolean isNullsLast, boolean isAcending) {
+ checkNotNull(expression);
+ this.expression = expression;
+ this.isNullsLast = isNullsLast;
+ this.isAscending = isAcending;
+ }
+
+ public Expression getExpression() {
+ return expression;
+ }
+
+ public boolean isNullsLast() {
+ return isNullsLast;
+ }
+
+ public boolean isAscending() {
+ return isAscending;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o != null && this.getClass() == o.getClass()) {
+ OrderByExpression that = (OrderByExpression)o;
+ return isNullsLast == that.isNullsLast
+ && isAscending == that.isAscending
+ && expression.equals(that.expression);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + (isNullsLast ? 0 : 1);
+ result = prime * result + (isAscending ? 0 : 1);
+ result = prime * result + expression.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return this.getExpression() + (isAscending ? "" : " DESC") + (isNullsLast ? " NULLS LAST" : "");
+ }
+
+ @Override
+ public void readFields(DataInput input) throws IOException {
+ this.isNullsLast = input.readBoolean();
+ this.isAscending = input.readBoolean();
+ expression = ExpressionType.values()[WritableUtils.readVInt(input)].newInstance();
+ expression.readFields(input);
+ }
+
+ @Override
+ public void write(DataOutput output) throws IOException {
+ output.writeBoolean(isNullsLast);
+ output.writeBoolean(isAscending);
+ WritableUtils.writeVInt(output, ExpressionType.valueOf(expression).ordinal());
+ expression.write(output);
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/c5b80246/src/main/java/org/apache/phoenix/expression/RowKeyColumnExpression.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/phoenix/expression/RowKeyColumnExpression.java b/src/main/java/org/apache/phoenix/expression/RowKeyColumnExpression.java
new file mode 100644
index 0000000..383ef2e
--- /dev/null
+++ b/src/main/java/org/apache/phoenix/expression/RowKeyColumnExpression.java
@@ -0,0 +1,150 @@
+/*
+ * 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;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+
+import org.apache.phoenix.expression.visitor.ExpressionVisitor;
+import org.apache.phoenix.schema.PDataType;
+import org.apache.phoenix.schema.PDatum;
+import org.apache.phoenix.schema.RowKeyValueAccessor;
+import org.apache.phoenix.schema.tuple.Tuple;
+import org.apache.phoenix.util.ByteUtil;
+
+
+/**
+ *
+ * Class to access a value stored in the row key
+ *
+ * @author jtaylor
+ * @since 0.1
+ */
+public class RowKeyColumnExpression extends ColumnExpression {
+ private PDataType fromType;
+ private RowKeyValueAccessor accessor;
+ protected final String name;
+
+ public RowKeyColumnExpression() {
+ name = null; // Only on client
+ }
+
+ private RowKeyColumnExpression(PDatum datum, RowKeyValueAccessor accessor, PDataType fromType, String name) {
+ super(datum);
+ this.accessor = accessor;
+ this.fromType = fromType;
+ this.name = name;
+ }
+
+ public RowKeyColumnExpression(PDatum datum, RowKeyValueAccessor accessor) {
+ this(datum, accessor, datum.getDataType(), datum.toString());
+ }
+
+ public RowKeyColumnExpression(PDatum datum, RowKeyValueAccessor accessor, String name) {
+ this(datum, accessor, datum.getDataType(), name);
+ }
+
+ public RowKeyColumnExpression(PDatum datum, RowKeyValueAccessor accessor, PDataType fromType) {
+ this(datum, accessor, fromType, datum.toString());
+ }
+
+ public int getPosition() {
+ return accessor.getIndex();
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = super.hashCode();
+ result = prime * result + ((accessor == null) ? 0 : accessor.hashCode());
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return name == null ? "PK[" + accessor.getIndex() + "]" : name;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (!super.equals(obj)) return false;
+ if (getClass() != obj.getClass()) return false;
+ RowKeyColumnExpression other = (RowKeyColumnExpression)obj;
+ return accessor.equals(other.accessor);
+ }
+
+ @Override
+ public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) {
+ tuple.getKey(ptr);
+ int offset = accessor.getOffset(ptr.get(), ptr.getOffset());
+ // Null is represented in the last expression of a multi-part key
+ // by the bytes not being present.
+ int maxOffset = ptr.getOffset() + ptr.getLength();
+ if (offset < maxOffset) {
+ byte[] buffer = ptr.get();
+ int fixedByteSize = -1;
+ // FIXME: fixedByteSize <= maxByteSize ? fixedByteSize : 0 required because HBase passes bogus keys to filter to position scan (HBASE-6562)
+ if (fromType.isFixedWidth()) {
+ fixedByteSize = getByteSize();
+ fixedByteSize = fixedByteSize <= maxOffset ? fixedByteSize : 0;
+ }
+ int length = fixedByteSize >= 0 ? fixedByteSize : accessor.getLength(buffer, offset, maxOffset);
+ // In the middle of the key, an empty variable length byte array represents null
+ if (length > 0) {
+ /*
+ if (type == fromType) {
+ ptr.set(buffer,offset,length);
+ } else {
+ ptr.set(type.toBytes(type.toObject(buffer, offset, length, fromType)));
+ }
+ */
+ ptr.set(buffer,offset,length);
+ type.coerceBytes(ptr, fromType, getColumnModifier(), getColumnModifier());
+ } else {
+ ptr.set(ByteUtil.EMPTY_BYTE_ARRAY);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void readFields(DataInput input) throws IOException {
+ super.readFields(input);
+ accessor = new RowKeyValueAccessor();
+ accessor.readFields(input);
+ fromType = type; // fromType only needed on client side
+ }
+
+ @Override
+ public void write(DataOutput output) throws IOException {
+ super.write(output);
+ accessor.write(output);
+ }
+
+ @Override
+ public final <T> T accept(ExpressionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/c5b80246/src/main/java/org/apache/phoenix/expression/RowKeyExpression.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/phoenix/expression/RowKeyExpression.java b/src/main/java/org/apache/phoenix/expression/RowKeyExpression.java
new file mode 100644
index 0000000..c311932
--- /dev/null
+++ b/src/main/java/org/apache/phoenix/expression/RowKeyExpression.java
@@ -0,0 +1,25 @@
+package org.apache.phoenix.expression;
+
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+
+import org.apache.phoenix.schema.PDataType;
+import org.apache.phoenix.schema.tuple.Tuple;
+
+public class RowKeyExpression extends BaseTerminalExpression {
+ public static final RowKeyExpression INSTANCE = new RowKeyExpression();
+
+ private RowKeyExpression() {
+ }
+
+ @Override
+ public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) {
+ tuple.getKey(ptr);
+ return true;
+ }
+
+ @Override
+ public PDataType getDataType() {
+ return PDataType.VARBINARY;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/c5b80246/src/main/java/org/apache/phoenix/expression/RowValueConstructorExpression.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/phoenix/expression/RowValueConstructorExpression.java b/src/main/java/org/apache/phoenix/expression/RowValueConstructorExpression.java
new file mode 100644
index 0000000..ba7ec9c
--- /dev/null
+++ b/src/main/java/org/apache/phoenix/expression/RowValueConstructorExpression.java
@@ -0,0 +1,355 @@
+/*
+ * 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.
+ */
+
+/**
+ * Implementation for row value constructor (a,b,c) expression.
+ *
+ * @author samarth.jain
+ * @since 0.1
+ */
+package org.apache.phoenix.expression;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.sql.SQLException;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp;
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+
+import com.google.common.collect.Lists;
+import org.apache.phoenix.expression.visitor.ExpressionVisitor;
+import org.apache.phoenix.query.QueryConstants;
+import org.apache.phoenix.schema.PDataType;
+import org.apache.phoenix.schema.TypeMismatchException;
+import org.apache.phoenix.schema.tuple.Tuple;
+import org.apache.phoenix.util.ByteUtil;
+import org.apache.phoenix.util.TrustedByteArrayOutputStream;
+
+public class RowValueConstructorExpression extends BaseCompoundExpression {
+
+ private ImmutableBytesWritable ptrs[];
+ private ImmutableBytesWritable literalExprPtr;
+ private int counter;
+ private int estimatedByteSize;
+
+ public static interface ExpressionComparabilityWrapper {
+ public Expression wrap(Expression lhs, Expression rhs);
+ }
+ /*
+ * Used to coerce the RHS to the expected type based on the LHS. In some circumstances,
+ * we may need to round the value up or down. For example:
+ * WHERE (a,b) < (2.4, 'foo')
+ * We take the ceiling of 2.4 to make it 3 if a is an INTEGER to prevent needing to coerce
+ * every time during evaluation.
+ */
+ private static ExpressionComparabilityWrapper[] WRAPPERS = new ExpressionComparabilityWrapper[CompareOp.values().length];
+ static {
+ WRAPPERS[CompareOp.LESS.ordinal()] = new ExpressionComparabilityWrapper() {
+
+ @Override
+ public Expression wrap(Expression lhs, Expression rhs) {
+ Expression e = rhs;
+ PDataType rhsType = rhs.getDataType();
+ PDataType lhsType = lhs.getDataType();
+ if (rhsType == PDataType.DECIMAL && lhsType != PDataType.DECIMAL) {
+ e = new FloorDecimalExpression(rhs);
+ } else if (rhsType == PDataType.TIMESTAMP && lhsType != PDataType.TIMESTAMP) {
+ e = new FloorTimestampExpression(rhs);
+ }
+ e = new CoerceExpression(e, lhsType, lhs.getColumnModifier(), lhs.getByteSize());
+ return e;
+ }
+
+ };
+ WRAPPERS[CompareOp.LESS_OR_EQUAL.ordinal()] = WRAPPERS[CompareOp.LESS.ordinal()];
+
+ WRAPPERS[CompareOp.GREATER.ordinal()] = new ExpressionComparabilityWrapper() {
+
+ @Override
+ public Expression wrap(Expression lhs, Expression rhs) {
+ Expression e = rhs;
+ PDataType rhsType = rhs.getDataType();
+ PDataType lhsType = lhs.getDataType();
+ if (rhsType == PDataType.DECIMAL && lhsType != PDataType.DECIMAL) {
+ e = new CeilingDecimalExpression(rhs);
+ } else if (rhsType == PDataType.TIMESTAMP && lhsType != PDataType.TIMESTAMP) {
+ e = new CeilingTimestampExpression(rhs);
+ }
+ e = new CoerceExpression(e, lhsType, lhs.getColumnModifier(), lhs.getByteSize());
+ return e;
+ }
+
+ };
+ WRAPPERS[CompareOp.GREATER_OR_EQUAL.ordinal()] = WRAPPERS[CompareOp.GREATER.ordinal()];
+ }
+
+ private static ExpressionComparabilityWrapper getWrapper(CompareOp op) {
+ ExpressionComparabilityWrapper wrapper = WRAPPERS[op.ordinal()];
+ if (wrapper == null) {
+ throw new IllegalStateException("Unexpected compare op of " + op + " for row value constructor");
+ }
+ return wrapper;
+ }
+
+ /**
+ * Recursively coerce the RHS to match the LHS type, throwing if the types are incompatible. The
+ * recursion occurs when the RHS or LHS is a row value constructor.
+ * TODO: this no longer needs to be recursive, as we flatten out rvc when we normalize the statement.
+ * @param lhs left hand side expression
+ * @param rhs right hand side expression
+ * @param op operator being used to compare the expressions, which can affect rounding we may need to do.
+ * @return new row value constructor expression that has been coerced
+ * @throws SQLException
+ */
+ public static Expression coerce(Expression lhs, Expression rhs, CompareOp op) throws SQLException {
+ return coerce(lhs, rhs, getWrapper(op));
+ }
+
+ public static Expression coerce(Expression lhs, Expression rhs, ExpressionComparabilityWrapper wrapper) throws SQLException {
+
+ if (lhs instanceof RowValueConstructorExpression && rhs instanceof RowValueConstructorExpression) {
+ int i = 0;
+ List<Expression> coercedNodes = Lists.newArrayListWithExpectedSize(Math.max(lhs.getChildren().size(), rhs.getChildren().size()));
+ for (; i < Math.min(lhs.getChildren().size(),rhs.getChildren().size()); i++) {
+ coercedNodes.add(coerce(lhs.getChildren().get(i), rhs.getChildren().get(i), wrapper));
+ }
+ for (; i < lhs.getChildren().size(); i++) {
+ coercedNodes.add(coerce(lhs.getChildren().get(i), null, wrapper));
+ }
+ for (; i < rhs.getChildren().size(); i++) {
+ coercedNodes.add(coerce(null, rhs.getChildren().get(i), wrapper));
+ }
+ trimTrailingNulls(coercedNodes);
+ return coercedNodes.equals(rhs.getChildren()) ? rhs : new RowValueConstructorExpression(coercedNodes, rhs.isConstant());
+ } else if (lhs instanceof RowValueConstructorExpression) {
+ List<Expression> coercedNodes = Lists.newArrayListWithExpectedSize(Math.max(rhs.getChildren().size(), lhs.getChildren().size()));
+ coercedNodes.add(coerce(lhs.getChildren().get(0), rhs, wrapper));
+ for (int i = 1; i < lhs.getChildren().size(); i++) {
+ coercedNodes.add(coerce(lhs.getChildren().get(i), null, wrapper));
+ }
+ trimTrailingNulls(coercedNodes);
+ return coercedNodes.equals(rhs.getChildren()) ? rhs : new RowValueConstructorExpression(coercedNodes, rhs.isConstant());
+ } else if (rhs instanceof RowValueConstructorExpression) {
+ List<Expression> coercedNodes = Lists.newArrayListWithExpectedSize(Math.max(rhs.getChildren().size(), lhs.getChildren().size()));
+ coercedNodes.add(coerce(lhs, rhs.getChildren().get(0), wrapper));
+ for (int i = 1; i < rhs.getChildren().size(); i++) {
+ coercedNodes.add(coerce(null, rhs.getChildren().get(i), wrapper));
+ }
+ trimTrailingNulls(coercedNodes);
+ return coercedNodes.equals(rhs.getChildren()) ? rhs : new RowValueConstructorExpression(coercedNodes, rhs.isConstant());
+ } else if (lhs == null && rhs == null) {
+ return LiteralExpression.newConstant(null);
+ } else if (lhs == null) {
+ return rhs;
+ } else if (rhs == null) {
+ return LiteralExpression.newConstant(null, lhs.getDataType());
+ } else {
+ if (rhs.getDataType() != null && lhs.getDataType() != null && !rhs.getDataType().isCastableTo(lhs.getDataType())) {
+ throw new TypeMismatchException(lhs.getDataType(), rhs.getDataType());
+ }
+ return wrapper.wrap(lhs, rhs);
+ }
+ }
+
+ private static void trimTrailingNulls(List<Expression> expressions) {
+ for (int i = expressions.size() - 1; i >= 0; i--) {
+ Expression e = expressions.get(i);
+ if (e instanceof LiteralExpression && ((LiteralExpression)e).getValue() == null) {
+ expressions.remove(i);
+ } else {
+ break;
+ }
+ }
+ }
+
+
+ public RowValueConstructorExpression() {
+ }
+
+ public RowValueConstructorExpression(List<Expression> children, boolean isConstant) {
+ super(children);
+ counter = 0;
+ estimatedByteSize = 0;
+ init(isConstant);
+ }
+
+ public int getEstimatedSize() {
+ return estimatedByteSize;
+ }
+
+ @Override
+ public boolean isConstant() {
+ return literalExprPtr != null;
+ }
+
+ @Override
+ public final <T> T accept(ExpressionVisitor<T> visitor) {
+ List<T> l = acceptChildren(visitor, visitor.visitEnter(this));
+ T t = visitor.visitLeave(this, l);
+ if (t == null) {
+ t = visitor.defaultReturn(this, l);
+ }
+ return t;
+ }
+
+ @Override
+ public void readFields(DataInput input) throws IOException {
+ super.readFields(input);
+ init(input.readBoolean());
+ }
+
+ @Override
+ public void write(DataOutput output) throws IOException {
+ super.write(output);
+ output.writeBoolean(literalExprPtr != null);
+ }
+
+ private void init(boolean isConstant) {
+ this.ptrs = new ImmutableBytesWritable[children.size()];
+ if(isConstant) {
+ ImmutableBytesWritable ptr = new ImmutableBytesWritable();
+ this.evaluate(null, ptr);
+ literalExprPtr = ptr;
+ }
+ }
+
+ @Override
+ public PDataType getDataType() {
+ return PDataType.VARBINARY;
+ }
+
+ @Override
+ public void reset() {
+ counter = 0;
+ estimatedByteSize = 0;
+ Arrays.fill(ptrs, null);
+ }
+
+ private static int getExpressionByteCount(Expression e) {
+ PDataType childType = e.getDataType();
+ if (childType != null && !childType.isFixedWidth()) {
+ return 1;
+ } else {
+ // Write at least one null byte in the case of the child being null with a childType of null
+ Integer byteSize = e.getByteSize();
+ int bytesToWrite = byteSize == null ? 1 : Math.max(1, byteSize);
+ return bytesToWrite;
+ }
+ }
+
+ @Override
+ public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) {
+ if(literalExprPtr != null) {
+ // if determined during construction that the row value constructor is just comprised of literal expressions,
+ // let's just return the ptr we have already computed and be done with evaluation.
+ ptr.set(literalExprPtr.get(), literalExprPtr.getOffset(), literalExprPtr.getLength());
+ return true;
+ }
+ try {
+ int j;
+ int expressionCount = counter;
+ for(j = counter; j < ptrs.length; j++) {
+ final Expression expression = children.get(j);
+ // TODO: handle overflow and underflow
+ if (expression.evaluate(tuple, ptr)) {
+ if (ptr.getLength() == 0) {
+ estimatedByteSize += getExpressionByteCount(expression);
+ } else {
+ expressionCount = j+1;
+ ptrs[j] = new ImmutableBytesWritable();
+ ptrs[j].set(ptr.get(), ptr.getOffset(), ptr.getLength());
+ estimatedByteSize += ptr.getLength() + (expression.getDataType().isFixedWidth() ? 0 : 1); // 1 extra for the separator byte.
+ }
+ counter++;
+ } else if (tuple == null || tuple.isImmutable()) {
+ estimatedByteSize += getExpressionByteCount(expression);
+ counter++;
+ } else {
+ return false;
+ }
+ }
+
+ if (j == ptrs.length) {
+ if (expressionCount == 0) {
+ ptr.set(ByteUtil.EMPTY_BYTE_ARRAY);
+ return true;
+ }
+ if (expressionCount == 1) {
+ ptr.set(ptrs[0].get(), ptrs[0].getOffset(), ptrs[0].getLength());
+ return true;
+ }
+ TrustedByteArrayOutputStream output = new TrustedByteArrayOutputStream(estimatedByteSize);
+ try {
+ boolean previousCarryOver = false;
+ for (int i = 0; i< expressionCount; i++) {
+ Expression child = getChildren().get(i);
+ PDataType childType = child.getDataType();
+ ImmutableBytesWritable tempPtr = ptrs[i];
+ if (tempPtr == null) {
+ // Since we have a null and have no representation for null,
+ // we must decrement the value of the current. Otherwise,
+ // we'd have an ambiguity if this value happened to be the
+ // min possible value.
+ previousCarryOver = childType == null || childType.isFixedWidth();
+ int bytesToWrite = getExpressionByteCount(child);
+ for (int m = 0; m < bytesToWrite; m++) {
+ output.write(QueryConstants.SEPARATOR_BYTE);
+ }
+ } else {
+ output.write(tempPtr.get(), tempPtr.getOffset(), tempPtr.getLength());
+ if (!childType.isFixedWidth()) {
+ output.write(QueryConstants.SEPARATOR_BYTE);
+ }
+ if (previousCarryOver) {
+ previousCarryOver = !ByteUtil.previousKey(output.getBuffer(), output.size());
+ }
+ }
+ }
+ int outputSize = output.size();
+ byte[] outputBytes = output.getBuffer();
+ for (int k = expressionCount -1 ;
+ k >=0 && getChildren().get(k).getDataType() != null && !getChildren().get(k).getDataType().isFixedWidth() && outputBytes[outputSize-1] == QueryConstants.SEPARATOR_BYTE ; k--) {
+ outputSize--;
+ }
+ ptr.set(outputBytes, 0, outputSize);
+ return true;
+ } finally {
+ output.close();
+ }
+ }
+ return false;
+ } catch (IOException e) {
+ throw new RuntimeException(e); //Impossible.
+ }
+ }
+
+ @Override
+ public final String toString() {
+ StringBuilder buf = new StringBuilder("(");
+ for (int i = 0; i < children.size() - 1; i++) {
+ buf.append(children.get(i) + ", ");
+ }
+ buf.append(children.get(children.size()-1) + ")");
+ return buf.toString();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/c5b80246/src/main/java/org/apache/phoenix/expression/StringConcatExpression.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/phoenix/expression/StringConcatExpression.java b/src/main/java/org/apache/phoenix/expression/StringConcatExpression.java
new file mode 100644
index 0000000..24e96f7
--- /dev/null
+++ b/src/main/java/org/apache/phoenix/expression/StringConcatExpression.java
@@ -0,0 +1,95 @@
+/*
+ * 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;
+
+
+import java.util.List;
+
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+
+import org.apache.phoenix.expression.visitor.ExpressionVisitor;
+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;
+
+
+/**
+ *
+ * Implementation for || string concatenation expression.
+ * @author kmahadik
+ * @since 0.1
+ */
+
+public class StringConcatExpression extends BaseCompoundExpression {
+ public StringConcatExpression() {
+ }
+
+ public StringConcatExpression(List<Expression> children) {
+ super(children);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder buf = new StringBuilder("(");
+ for (int i = 0; i < children.size() - 1; i++) {
+ buf.append(children.get(i) + " || ");
+ }
+ buf.append(children.get(children.size()-1));
+ buf.append(')');
+ return buf.toString();
+ }
+
+ @Override
+ public final <T> T accept(ExpressionVisitor<T> visitor) {
+ List<T> l = acceptChildren(visitor, visitor.visitEnter(this));
+ T t = visitor.visitLeave(this, l);
+ if (t == null) {
+ t = visitor.defaultReturn(this, l);
+ }
+ return t;
+ }
+
+ @Override
+ public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) {
+ byte[] result = ByteUtil.EMPTY_BYTE_ARRAY;
+ for (int i=0; i<children.size(); i++) {
+ if (children.get(i).getDataType() == null || !children.get(i).evaluate(tuple, ptr)) {
+ continue;
+ }
+ PDataType childType = children.get(i).getDataType();
+ ColumnModifier columnModifier = children.get(i).getColumnModifier();
+ // We could potentially not invert the bytes, but we might as well since we're allocating
+ // additional space here anyway.
+ if (childType.isCoercibleTo(PDataType.VARCHAR)) {
+ result = ByteUtil.concat(result, ByteUtil.concat(columnModifier, ptr));
+ } else {
+ result = ByteUtil.concat(result, PDataType.VARCHAR.toBytes(childType.toObject(ptr, columnModifier).toString()));
+ }
+ }
+ ptr.set(result);
+ return true;
+ }
+
+ @Override
+ public PDataType getDataType() {
+ return PDataType.VARCHAR;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/c5b80246/src/main/java/org/apache/phoenix/expression/SubtractExpression.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/phoenix/expression/SubtractExpression.java b/src/main/java/org/apache/phoenix/expression/SubtractExpression.java
new file mode 100644
index 0000000..a9c9c93
--- /dev/null
+++ b/src/main/java/org/apache/phoenix/expression/SubtractExpression.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.expression;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+import org.apache.phoenix.expression.visitor.ExpressionVisitor;
+import org.apache.phoenix.query.QueryConstants;
+
+
+/**
+ *
+ * Subtract expression implementation
+ *
+ * @author kmahadik
+ * @since 0.1
+ */
+public abstract class SubtractExpression extends BaseAddSubtractExpression {
+ protected static final BigDecimal BD_MILLIS_IN_DAY = BigDecimal.valueOf(QueryConstants.MILLIS_IN_DAY);
+
+ public SubtractExpression() {
+ }
+
+ public SubtractExpression(List<Expression> children) {
+ super(children);
+ }
+
+ @Override
+ public final <T> T accept(ExpressionVisitor<T> visitor) {
+ List<T> l = acceptChildren(visitor, visitor.visitEnter(this));
+ T t = visitor.visitLeave(this, l);
+ if (t == null) {
+ t = visitor.defaultReturn(this, l);
+ }
+ return t;
+ }
+
+ @Override
+ public String getOperatorString() {
+ return " - ";
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/c5b80246/src/main/java/org/apache/phoenix/expression/TimestampAddExpression.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/phoenix/expression/TimestampAddExpression.java b/src/main/java/org/apache/phoenix/expression/TimestampAddExpression.java
new file mode 100644
index 0000000..ae33ad4
--- /dev/null
+++ b/src/main/java/org/apache/phoenix/expression/TimestampAddExpression.java
@@ -0,0 +1,89 @@
+/*
+ * 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;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.sql.Timestamp;
+import java.util.List;
+
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+
+import org.apache.phoenix.query.QueryConstants;
+import org.apache.phoenix.schema.ColumnModifier;
+import org.apache.phoenix.schema.PDataType;
+import org.apache.phoenix.schema.tuple.Tuple;
+import org.apache.phoenix.util.DateUtil;
+
+/**
+ *
+ * Class to encapsulate addition arithmetic for {@link PDataType#TIMESTAMP}.
+ *
+ * @author samarth.jain
+ * @since 2.1.3
+ */
+
+public class TimestampAddExpression extends AddExpression {
+
+ public TimestampAddExpression() {
+ }
+
+ public TimestampAddExpression(List<Expression> children) {
+ super(children);
+ }
+
+ @Override
+ public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) {
+ BigDecimal finalResult = BigDecimal.ZERO;
+
+ for(int i=0; i<children.size(); i++) {
+ if (!children.get(i).evaluate(tuple, ptr)) {
+ return false;
+ }
+ if (ptr.getLength() == 0) {
+ return true;
+ }
+ BigDecimal value;
+ PDataType type = children.get(i).getDataType();
+ ColumnModifier columnModifier = children.get(i).getColumnModifier();
+ if(type == PDataType.TIMESTAMP) {
+ value = (BigDecimal)(PDataType.DECIMAL.toObject(ptr, PDataType.TIMESTAMP, columnModifier));
+ } else if (type.isCoercibleTo(PDataType.DECIMAL)) {
+ value = (((BigDecimal)PDataType.DECIMAL.toObject(ptr, columnModifier)).multiply(QueryConstants.BD_MILLIS_IN_DAY)).setScale(6, RoundingMode.HALF_UP);
+ } else if (type.isCoercibleTo(PDataType.DOUBLE)) {
+ value = ((BigDecimal.valueOf(type.getCodec().decodeDouble(ptr, columnModifier))).multiply(QueryConstants.BD_MILLIS_IN_DAY)).setScale(6, RoundingMode.HALF_UP);
+ } else {
+ value = BigDecimal.valueOf(type.getCodec().decodeLong(ptr, columnModifier));
+ }
+ finalResult = finalResult.add(value);
+ }
+ Timestamp ts = DateUtil.getTimestamp(finalResult);
+ byte[] resultPtr = new byte[getDataType().getByteSize()];
+ PDataType.TIMESTAMP.toBytes(ts, resultPtr, 0);
+ ptr.set(resultPtr);
+ return true;
+ }
+
+ @Override
+ public final PDataType getDataType() {
+ return PDataType.TIMESTAMP;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/c5b80246/src/main/java/org/apache/phoenix/expression/TimestampSubtractExpression.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/phoenix/expression/TimestampSubtractExpression.java b/src/main/java/org/apache/phoenix/expression/TimestampSubtractExpression.java
new file mode 100644
index 0000000..2841d25
--- /dev/null
+++ b/src/main/java/org/apache/phoenix/expression/TimestampSubtractExpression.java
@@ -0,0 +1,89 @@
+/*
+ * 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;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.sql.Timestamp;
+import java.util.List;
+
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+
+import org.apache.phoenix.schema.ColumnModifier;
+import org.apache.phoenix.schema.PDataType;
+import org.apache.phoenix.schema.tuple.Tuple;
+import org.apache.phoenix.util.DateUtil;
+/**
+ *
+ * Class to encapsulate subtraction arithmetic for {@link PDataType#TIMESTAMP}.
+ *
+ * @author samarth.jain
+ * @since 2.1.3
+ */
+public class TimestampSubtractExpression extends SubtractExpression {
+
+ public TimestampSubtractExpression() {
+ }
+
+ public TimestampSubtractExpression(List<Expression> children) {
+ super(children);
+ }
+
+ @Override
+ public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) {
+ BigDecimal finalResult = BigDecimal.ZERO;
+
+ for(int i=0; i<children.size(); i++) {
+ if (!children.get(i).evaluate(tuple, ptr)) {
+ return false;
+ }
+ if (ptr.getLength() == 0) {
+ return true;
+ }
+ BigDecimal value;
+ PDataType type = children.get(i).getDataType();
+ ColumnModifier columnModifier = children.get(i).getColumnModifier();
+ if(type == PDataType.TIMESTAMP) {
+ value = (BigDecimal)(PDataType.DECIMAL.toObject(ptr, PDataType.TIMESTAMP, columnModifier));
+ } else if (type.isCoercibleTo(PDataType.DECIMAL)) {
+ value = (((BigDecimal)PDataType.DECIMAL.toObject(ptr, columnModifier)).multiply(BD_MILLIS_IN_DAY)).setScale(6, RoundingMode.HALF_UP);
+ } else if (type.isCoercibleTo(PDataType.DOUBLE)) {
+ value = ((BigDecimal.valueOf(type.getCodec().decodeDouble(ptr, columnModifier))).multiply(BD_MILLIS_IN_DAY)).setScale(6, RoundingMode.HALF_UP);
+ } else {
+ value = BigDecimal.valueOf(type.getCodec().decodeLong(ptr, columnModifier));
+ }
+ if (i == 0) {
+ finalResult = value;
+ } else {
+ finalResult = finalResult.subtract(value);
+ }
+ }
+ Timestamp ts = DateUtil.getTimestamp(finalResult);
+ byte[] resultPtr = new byte[getDataType().getByteSize()];
+ PDataType.TIMESTAMP.toBytes(ts, resultPtr, 0);
+ ptr.set(resultPtr);
+ return true;
+ }
+
+ @Override
+ public final PDataType getDataType() {
+ return PDataType.TIMESTAMP;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/c5b80246/src/main/java/org/apache/phoenix/expression/aggregator/Aggregator.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/phoenix/expression/aggregator/Aggregator.java b/src/main/java/org/apache/phoenix/expression/aggregator/Aggregator.java
new file mode 100644
index 0000000..4067eb4
--- /dev/null
+++ b/src/main/java/org/apache/phoenix/expression/aggregator/Aggregator.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.expression.aggregator;
+
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+
+import org.apache.phoenix.expression.Expression;
+import org.apache.phoenix.schema.tuple.Tuple;
+
+
+/**
+ *
+ * Interface to abstract the incremental calculation of an aggregated value.
+ *
+ * @author jtaylor
+ * @since 0.1
+ */
+public interface Aggregator extends Expression {
+
+ /**
+ * Incrementally aggregate the value with the current row
+ * @param tuple the result containing all the key values of the row
+ * @param ptr the bytes pointer to the underlying result
+ */
+ public void aggregate(Tuple tuple, ImmutableBytesWritable ptr);
+
+ /**
+ * Get the size in bytes
+ */
+ public int getSize();
+}
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/c5b80246/src/main/java/org/apache/phoenix/expression/aggregator/Aggregators.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/phoenix/expression/aggregator/Aggregators.java b/src/main/java/org/apache/phoenix/expression/aggregator/Aggregators.java
new file mode 100644
index 0000000..c25f02b
--- /dev/null
+++ b/src/main/java/org/apache/phoenix/expression/aggregator/Aggregators.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.expression.aggregator;
+
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+
+import org.apache.phoenix.expression.function.SingleAggregateFunction;
+import org.apache.phoenix.schema.KeyValueSchema;
+import org.apache.phoenix.schema.KeyValueSchema.KeyValueSchemaBuilder;
+import org.apache.phoenix.schema.ValueBitSet;
+import org.apache.phoenix.schema.tuple.Tuple;
+import org.apache.phoenix.util.SizedUtil;
+
+
+/**
+ *
+ * Represents an ordered list of Aggregators
+ *
+ * @author jtaylor
+ * @since 0.1
+ */
+abstract public class Aggregators {
+ protected final int size;
+ protected final KeyValueSchema schema;
+ protected final ImmutableBytesWritable ptr = new ImmutableBytesWritable();
+ protected final ValueBitSet valueSet;
+ protected final Aggregator[] aggregators;
+ protected final SingleAggregateFunction[] functions;
+
+ public int getSize() {
+ return size;
+ }
+
+ public Aggregators(SingleAggregateFunction[] functions, Aggregator[] aggregators, int minNullableIndex) {
+ this.functions = functions;
+ this.aggregators = aggregators;
+ this.size = calculateSize(aggregators);
+ this.schema = newValueSchema(aggregators, minNullableIndex);
+ this.valueSet = ValueBitSet.newInstance(schema);
+ }
+
+ public KeyValueSchema getValueSchema() {
+ return schema;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder buf = new StringBuilder(this.getClass().getName() + " [" + functions.length + "]:");
+ for (int i = 0; i < functions.length; i++) {
+ SingleAggregateFunction function = functions[i];
+ buf.append("\t" + i + ") " + function );
+ }
+ return buf.toString();
+ }
+ /**
+ * Aggregate over aggregators
+ * @param result the single row Result from scan iteration
+ */
+ abstract public void aggregate(Aggregator[] aggregators, Tuple result);
+
+ protected static int calculateSize(Aggregator[] aggregators) {
+
+ int size = SizedUtil.ARRAY_SIZE /*aggregators[]*/ + (SizedUtil.POINTER_SIZE * aggregators.length);
+ for (Aggregator aggregator : aggregators) {
+ size += aggregator.getSize();
+ }
+ return size;
+ }
+
+ /**
+ * Get the ValueSchema for the Aggregators
+ */
+ private static KeyValueSchema newValueSchema(Aggregator[] aggregators, int minNullableIndex) {
+ KeyValueSchemaBuilder builder = new KeyValueSchemaBuilder(minNullableIndex);
+ for (int i = 0; i < aggregators.length; i++) {
+ Aggregator aggregator = aggregators[i];
+ builder.addField(aggregator);
+ }
+ return builder.build();
+ }
+
+ /**
+ * @return byte representation of the ValueSchema
+ */
+ public byte[] toBytes(Aggregator[] aggregators) {
+ return schema.toBytes(aggregators, valueSet, ptr);
+ }
+
+ public int getAggregatorCount() {
+ return aggregators.length;
+ }
+
+ public Aggregator[] getAggregators() {
+ return aggregators;
+ }
+
+ abstract public Aggregator[] newAggregators();
+
+ public void reset(Aggregator[] aggregators) {
+ for (int i = 0; i < aggregators.length; i++) {
+ aggregators[i].reset();
+ }
+ }
+
+ protected Aggregator getAggregator(int position) {
+ return aggregators[position];
+ }
+}