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:44 UTC
[28/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/RegexpSubstrFunction.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/RegexpSubstrFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/RegexpSubstrFunction.java
new file mode 100644
index 0000000..2308581
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/RegexpSubstrFunction.java
@@ -0,0 +1,175 @@
+/*
+ * 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.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+
+import org.apache.phoenix.expression.Expression;
+import org.apache.phoenix.expression.LiteralExpression;
+import org.apache.phoenix.parse.FunctionParseNode.Argument;
+import org.apache.phoenix.parse.FunctionParseNode.BuiltInFunction;
+import org.apache.phoenix.schema.PDataType;
+import org.apache.phoenix.schema.tuple.Tuple;
+import org.apache.phoenix.util.ByteUtil;
+
+
+/**
+ *
+ * Implementation of REGEXP_SUBSTR(<source>, <pattern>, <offset>) built-in function,
+ * where <offset> is the offset from the start of <string>. Positive offset is treated as 1-based,
+ * a zero offset is treated as 0-based, and a negative offset starts from the end of the string
+ * working backwards. The <pattern> is the pattern we would like to search for in the <source> string.
+ * The function returns the first occurrence of any substring in the <source> string that matches
+ * the <pattern> input as a VARCHAR.
+ *
+ * @author zhuang
+ * @since 0.1
+ */
+@BuiltInFunction(name=RegexpSubstrFunction.NAME, args={
+ @Argument(allowedTypes={PDataType.VARCHAR}),
+ @Argument(allowedTypes={PDataType.VARCHAR}),
+ @Argument(allowedTypes={PDataType.LONG}, defaultValue="1")} )
+public class RegexpSubstrFunction extends PrefixFunction {
+ public static final String NAME = "REGEXP_SUBSTR";
+
+ private Pattern pattern;
+ private boolean isOffsetConstant;
+ private Integer byteSize;
+
+ public RegexpSubstrFunction() { }
+
+ public RegexpSubstrFunction(List<Expression> children) {
+ super(children);
+ init();
+ }
+
+ private void init() {
+ Object patternString = ((LiteralExpression)children.get(1)).getValue();
+ if (patternString != null) {
+ pattern = Pattern.compile((String)patternString);
+ }
+ // If the source string has a fixed width, then the max length would be the length
+ // of the source string minus the offset, or the absolute value of the offset if
+ // it's negative. Offset number is a required argument. However, if the source string
+ // is not fixed width, the maxLength would be null.
+ isOffsetConstant = getOffsetExpression() instanceof LiteralExpression;
+ Number offsetNumber = (Number)((LiteralExpression)getOffsetExpression()).getValue();
+ if (offsetNumber != null) {
+ int offset = offsetNumber.intValue();
+ if (getSourceStrExpression().getDataType().isFixedWidth()) {
+ if (offset >= 0) {
+ byteSize = getSourceStrExpression().getByteSize() - offset - (offset == 0 ? 0 : 1);
+ } else {
+ byteSize = -offset;
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) {
+ if (pattern == null) {
+ return false;
+ }
+ if (!getSourceStrExpression().evaluate(tuple, ptr)) {
+ return false;
+ }
+ String sourceStr = (String)PDataType.VARCHAR.toObject(ptr, getSourceStrExpression().getColumnModifier());
+ if (sourceStr == null) {
+ return false;
+ }
+
+ Expression offsetExpression = getOffsetExpression();
+ if (!offsetExpression.evaluate(tuple, ptr)) {
+ return false;
+ }
+ int offset = offsetExpression.getDataType().getCodec().decodeInt(ptr, offsetExpression.getColumnModifier());
+
+ int strlen = sourceStr.length();
+ // Account for 1 versus 0-based offset
+ offset = offset - (offset <= 0 ? 0 : 1);
+ if (offset < 0) { // Offset < 0 means get from end
+ offset = strlen + offset;
+ }
+ if (offset < 0 || offset >= strlen) {
+ return false;
+ }
+
+ Matcher matcher = pattern.matcher(sourceStr);
+ boolean hasSubString = matcher.find(offset);
+ if (!hasSubString) {
+ ptr.set(ByteUtil.EMPTY_BYTE_ARRAY);
+ return true;
+ }
+ String subString = matcher.group();
+ ptr.set(PDataType.VARCHAR.toBytes(subString));
+ return true;
+ }
+
+ @Override
+ public Integer getByteSize() {
+ return byteSize;
+ }
+
+ @Override
+ public OrderPreserving preservesOrder() {
+ if (isOffsetConstant) {
+ LiteralExpression literal = (LiteralExpression) getOffsetExpression();
+ Number offsetNumber = (Number) literal.getValue();
+ if (offsetNumber != null) {
+ int offset = offsetNumber.intValue();
+ if (offset == 0 || offset == 1) {
+ return OrderPreserving.YES_IF_LAST;
+ }
+ }
+ }
+ return OrderPreserving.NO;
+ }
+
+ @Override
+ public int getKeyFormationTraversalIndex() {
+ return preservesOrder() == OrderPreserving.NO ? NO_TRAVERSAL : 0;
+ }
+
+ private Expression getOffsetExpression() {
+ return children.get(2);
+ }
+
+ private Expression getSourceStrExpression() {
+ return children.get(0);
+ }
+
+ @Override
+ public PDataType getDataType() {
+ // ALways VARCHAR since we do not know in advanced how long the
+ // matched string will be.
+ 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/ReverseFunction.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ReverseFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ReverseFunction.java
new file mode 100644
index 0000000..b90fd7c
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ReverseFunction.java
@@ -0,0 +1,70 @@
+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.StringUtil;
+
+@BuiltInFunction(name=ReverseFunction.NAME, args={
+ @Argument(allowedTypes={PDataType.VARCHAR})} )
+public class ReverseFunction extends ScalarFunction {
+ public static final String NAME = "REVERSE";
+
+ public ReverseFunction() {
+ }
+
+ public ReverseFunction(List<Expression> children) throws SQLException {
+ super(children);
+ }
+
+ @Override
+ public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) {
+ Expression arg = getChildren().get(0);
+ if (!arg.evaluate(tuple, ptr)) {
+ return false;
+ }
+
+ int targetOffset = ptr.getLength();
+ if (targetOffset == 0) {
+ return true;
+ }
+
+ byte[] source = ptr.get();
+ byte[] target = new byte[targetOffset];
+ int sourceOffset = ptr.getOffset();
+ int endOffset = sourceOffset + ptr.getLength();
+ ColumnModifier modifier = arg.getColumnModifier();
+ while (sourceOffset < endOffset) {
+ int nBytes = StringUtil.getBytesInChar(source[sourceOffset], modifier);
+ targetOffset -= nBytes;
+ System.arraycopy(source, sourceOffset, target, targetOffset, nBytes);
+ sourceOffset += nBytes;
+ }
+ ptr.set(target);
+ return true;
+ }
+
+ @Override
+ public ColumnModifier getColumnModifier() {
+ return getChildren().get(0).getColumnModifier();
+ }
+
+ @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/RoundDateExpression.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/RoundDateExpression.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/RoundDateExpression.java
new file mode 100644
index 0000000..eab4b28
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/RoundDateExpression.java
@@ -0,0 +1,274 @@
+/*
+ * 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.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.sql.Date;
+import java.sql.SQLException;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp;
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+import org.apache.hadoop.io.WritableUtils;
+
+import com.google.common.collect.Lists;
+import org.apache.phoenix.compile.KeyPart;
+import org.apache.phoenix.expression.Expression;
+import org.apache.phoenix.expression.LiteralExpression;
+import org.apache.phoenix.query.KeyRange;
+import org.apache.phoenix.schema.PColumn;
+import org.apache.phoenix.schema.PDataType;
+import org.apache.phoenix.schema.PDataType.PDataCodec;
+import org.apache.phoenix.schema.tuple.Tuple;
+import org.apache.phoenix.util.ByteUtil;
+
+/**
+ * Function used to bucketize date/time values by rounding them to
+ * an even increment. Usage:
+ * ROUND(<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, samarth.jain
+ * @since 0.1
+ */
+public class RoundDateExpression extends ScalarFunction {
+
+ long divBy;
+
+ public static final String NAME = "ROUND";
+
+ private static final long[] TIME_UNIT_MS = new long[] {
+ 24 * 60 * 60 * 1000,
+ 60 * 60 * 1000,
+ 60 * 1000,
+ 1000,
+ 1
+ };
+
+ public RoundDateExpression() {}
+
+ /**
+ * @param timeUnit - unit of time to round up to.
+ * Creates a {@link RoundDateExpression} with default multiplier of 1.
+ */
+ public static Expression create(Expression expr, TimeUnit timeUnit) throws SQLException {
+ return create(expr, timeUnit, 1);
+ }
+
+ /**
+ * @param timeUnit - unit of time to round up to
+ * @param multiplier - determines the roll up window size.
+ * Create a {@link RoundDateExpression}.
+ */
+ public static Expression create(Expression expr, TimeUnit timeUnit, int multiplier) throws SQLException {
+ Expression timeUnitExpr = getTimeUnitExpr(timeUnit);
+ Expression defaultMultiplierExpr = getMultiplierExpr(multiplier);
+ List<Expression> expressions = Lists.newArrayList(expr, timeUnitExpr, defaultMultiplierExpr);
+ return create(expressions);
+ }
+
+ public static Expression create(List<Expression> children) throws SQLException {
+ return new RoundDateExpression(children);
+ }
+
+ static Expression getTimeUnitExpr(TimeUnit timeUnit) throws SQLException {
+ return LiteralExpression.newConstant(timeUnit.name(), PDataType.VARCHAR, true);
+ }
+
+ static Expression getMultiplierExpr(int multiplier) throws SQLException {
+ return LiteralExpression.newConstant(multiplier, PDataType.INTEGER, true);
+ }
+
+ RoundDateExpression(List<Expression> children) {
+ super(children.subList(0, 1));
+ int numChildren = children.size();
+ if(numChildren < 2 || numChildren > 3) {
+ throw new IllegalArgumentException("Wrong number of arguments : " + numChildren);
+ }
+ Object timeUnitValue = ((LiteralExpression)children.get(1)).getValue();
+ Object multiplierValue = numChildren > 2 ? ((LiteralExpression)children.get(2)).getValue() : null;
+ int multiplier = multiplierValue == null ? 1 :((Number)multiplierValue).intValue();
+ TimeUnit timeUnit = TimeUnit.getTimeUnit(timeUnitValue != null ? timeUnitValue.toString() : null);
+ divBy = multiplier * TIME_UNIT_MS[timeUnit.ordinal()];
+ }
+
+
+ protected long getRoundUpAmount() {
+ return divBy/2;
+ }
+
+
+ protected long roundTime(long time) {
+ long value;
+ long roundUpAmount = getRoundUpAmount();
+ if (time <= Long.MAX_VALUE - roundUpAmount) { // If no overflow, add
+ value = (time + roundUpAmount) / divBy;
+ } else { // Else subtract and add one
+ value = (time - roundUpAmount) / divBy + 1;
+ }
+ return value * divBy;
+ }
+
+ @Override
+ public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) {
+ if (children.get(0).evaluate(tuple, ptr)) {
+ PDataType dataType = getDataType();
+ long time = dataType.getCodec().decodeLong(ptr, children.get(0).getColumnModifier());
+ long value = roundTime(time);
+
+ Date d = new Date(value);
+ byte[] byteValue = dataType.toBytes(d);
+ ptr.set(byteValue);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ long roundUpAmount = this.getRoundUpAmount();
+ result = prime * result + (int)(divBy ^ (divBy >>> 32));
+ result = prime * result + (int)(roundUpAmount ^ (roundUpAmount >>> 32));
+ result = prime * result + children.get(0).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;
+ RoundDateExpression other = (RoundDateExpression)obj;
+ if (divBy != other.divBy) return false;
+ if (getRoundUpAmount() != other.getRoundUpAmount()) return false;
+ return children.get(0).equals(other.children.get(0));
+ }
+
+ @Override
+ public void readFields(DataInput input) throws IOException {
+ super.readFields(input);
+ divBy = WritableUtils.readVLong(input);
+ }
+
+ @Override
+ public void write(DataOutput output) throws IOException {
+ super.write(output);
+ WritableUtils.writeVLong(output, divBy);
+ }
+
+ @Override
+ public PDataType getDataType() {
+ return children.get(0).getDataType();
+ }
+
+ @Override
+ public Integer getByteSize() {
+ return children.get(0).getByteSize();
+ }
+
+ @Override
+ public boolean isNullable() {
+ return children.get(0).isNullable() || divBy == 0;
+ }
+
+ protected PDataCodec getKeyRangeCodec(PDataType columnDataType) {
+ return columnDataType.getCodec();
+ }
+
+ /**
+ * Form the key range from the key to the key right before or at the
+ * next rounded value.
+ */
+ @Override
+ public KeyPart newKeyPart(final KeyPart childPart) {
+ return new KeyPart() {
+ private final List<Expression> extractNodes = Collections.<Expression>singletonList(RoundDateExpression.this);
+
+ @Override
+ public PColumn getColumn() {
+ return childPart.getColumn();
+ }
+
+ @Override
+ public List<Expression> getExtractNodes() {
+ return extractNodes;
+ }
+
+ @Override
+ public KeyRange getKeyRange(CompareOp op, Expression rhs) {
+ PDataType type = getColumn().getDataType();
+ ImmutableBytesWritable ptr = new ImmutableBytesWritable();
+ rhs.evaluate(null, ptr);
+ byte[] key = ByteUtil.copyKeyBytesIfNecessary(ptr);
+ // No need to take into account column modifier, because ROUND
+ // always forces the value to be in ascending order
+ PDataCodec codec = getKeyRangeCodec(type);
+ int offset = ByteUtil.isInclusive(op) ? 1 : 0;
+ long value = codec.decodeLong(key, 0, null);
+ byte[] nextKey = new byte[type.getByteSize()];
+ switch (op) {
+ case EQUAL:
+ // If the value isn't evenly divisible by the div amount, then it
+ // can't possibly be equal to any rounded value. For example, if you
+ // had ROUND(dateCol,'DAY') = TO_DATE('2013-01-01 23:00:00')
+ // it could never be equal, since date constant isn't at a day
+ // boundary.
+ if (value % divBy != 0) {
+ return KeyRange.EMPTY_RANGE;
+ }
+ codec.encodeLong(value + divBy, nextKey, 0);
+ return type.getKeyRange(key, true, nextKey, false);
+ case GREATER:
+ case GREATER_OR_EQUAL:
+ codec.encodeLong((value + divBy - offset)/divBy*divBy, nextKey, 0);
+ return type.getKeyRange(nextKey, true, KeyRange.UNBOUND, false);
+ case LESS:
+ case LESS_OR_EQUAL:
+ codec.encodeLong((value + divBy - (1 -offset))/divBy*divBy, nextKey, 0);
+ return type.getKeyRange(KeyRange.UNBOUND, false, nextKey, false);
+ default:
+ return childPart.getKeyRange(op, rhs);
+ }
+ }
+ };
+ }
+
+
+ @Override
+ public String getName() {
+ return NAME;
+ }
+
+ @Override
+ public OrderPreserving preservesOrder() {
+ return OrderPreserving.YES;
+ }
+
+ @Override
+ public int getKeyFormationTraversalIndex() {
+ return 0;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/50d523f6/phoenix-core/src/main/java/org/apache/phoenix/expression/function/RoundDecimalExpression.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/RoundDecimalExpression.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/RoundDecimalExpression.java
new file mode 100644
index 0000000..85e7b75
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/RoundDecimalExpression.java
@@ -0,0 +1,136 @@
+/*
+ * 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.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.sql.SQLException;
+import java.util.List;
+
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+import org.apache.hadoop.io.WritableUtils;
+
+import com.google.common.collect.Lists;
+import org.apache.phoenix.expression.Expression;
+import org.apache.phoenix.expression.LiteralExpression;
+import org.apache.phoenix.schema.IllegalDataException;
+import org.apache.phoenix.schema.PDataType;
+import org.apache.phoenix.schema.tuple.Tuple;
+
+/**
+ *
+ * Class encapsulating the process for rounding off a column/literal of
+ * type {@link org.apache.phoenix.schema.PDataType#DECIMAL}
+ *
+ * @author samarth.jain
+ * @since 3.0.0
+ */
+
+public class RoundDecimalExpression extends ScalarFunction {
+
+ private int scale;
+
+ /**
+ * Creates a {@link RoundDecimalExpression} with rounding scale given by @param scale.
+ *
+ */
+ public static Expression create(Expression expr, int scale) throws SQLException {
+ if (expr.getDataType().isCoercibleTo(PDataType.LONG)) {
+ return expr;
+ }
+ Expression scaleExpr = LiteralExpression.newConstant(scale, PDataType.INTEGER, true);
+ List<Expression> expressions = Lists.newArrayList(expr, scaleExpr);
+ return new RoundDecimalExpression(expressions);
+ }
+
+ /**
+ * Creates a {@link RoundDecimalExpression} with a default scale of 0 used for rounding.
+ *
+ */
+ public static Expression create(Expression expr) throws SQLException {
+ return create(expr, 0);
+ }
+
+ public RoundDecimalExpression() {}
+
+ public RoundDecimalExpression(List<Expression> children) {
+ super(children);
+ LiteralExpression scaleChild = (LiteralExpression)children.get(1);
+ PDataType scaleType = scaleChild.getDataType();
+ Object scaleValue = scaleChild.getValue();
+ if(scaleValue != null) {
+ if (scaleType.isCoercibleTo(PDataType.INTEGER, scaleValue)) {
+ int scale = (Integer)PDataType.INTEGER.toObject(scaleValue, scaleType);
+ if (scale >=0 && scale <= PDataType.MAX_PRECISION) {
+ this.scale = scale;
+ return;
+ }
+ }
+ throw new IllegalDataException("Invalid second argument for scale: " + scaleValue + ". The scale must be between 0 and " + PDataType.MAX_PRECISION + " inclusive.");
+ }
+ }
+
+ @Override
+ public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) {
+ Expression childExpr = children.get(0);
+ if(childExpr.evaluate(tuple, ptr)) {
+ BigDecimal value = (BigDecimal)PDataType.DECIMAL.toObject(ptr, childExpr.getColumnModifier());
+ BigDecimal scaledValue = value.setScale(scale, getRoundingMode());
+ ptr.set(getDataType().toBytes(scaledValue));
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public PDataType getDataType() {
+ return children.get(0).getDataType();
+ }
+
+ protected RoundingMode getRoundingMode() {
+ return RoundingMode.HALF_UP;
+ }
+
+ @Override
+ public void readFields(DataInput input) throws IOException {
+ super.readFields(input);
+ scale = WritableUtils.readVInt(input);
+ }
+
+ @Override
+ public void write(DataOutput output) throws IOException {
+ super.write(output);
+ WritableUtils.writeVInt(output, scale);
+ }
+
+ @Override
+ public String getName() {
+ return RoundFunction.NAME;
+ }
+
+ @Override
+ public OrderPreserving preservesOrder() {
+ return OrderPreserving.YES;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/50d523f6/phoenix-core/src/main/java/org/apache/phoenix/expression/function/RoundFunction.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/RoundFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/RoundFunction.java
new file mode 100644
index 0000000..1b0782b
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/RoundFunction.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.function;
+
+import java.util.List;
+
+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.RoundParseNode;
+import org.apache.phoenix.schema.PDataType;
+
+
+/**
+ * Base class for RoundFunction.
+ *
+ * @author jtaylor, samarth.jain
+ * @since 0.1
+ */
+@BuiltInFunction(name = RoundFunction.NAME,
+ nodeClass = RoundParseNode.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 RoundFunction extends ScalarFunction {
+
+ public static final String NAME = "ROUND";
+
+ public RoundFunction(List<Expression> children) {
+ 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/function/RoundTimestampExpression.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/RoundTimestampExpression.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/RoundTimestampExpression.java
new file mode 100644
index 0000000..0146b19
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/RoundTimestampExpression.java
@@ -0,0 +1,106 @@
+/*
+ * 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.sql.Timestamp;
+import java.util.List;
+
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+
+import com.google.common.collect.Lists;
+import org.apache.phoenix.expression.CoerceExpression;
+import org.apache.phoenix.expression.Expression;
+import org.apache.phoenix.expression.LiteralExpression;
+import org.apache.phoenix.schema.ColumnModifier;
+import org.apache.phoenix.schema.PDataType;
+import org.apache.phoenix.schema.PDataType.PDataCodec;
+import org.apache.phoenix.schema.tuple.Tuple;
+
+/**
+ *
+ * Class encapsulating the process for rounding off a column/literal of
+ * type {@link org.apache.phoenix.schema.PDataType#TIMESTAMP}
+ * This class only supports rounding off the milliseconds that is for
+ * {@link TimeUnit#MILLISECOND}. If you want more options of rounding like
+ * using {@link TimeUnit#HOUR} use {@link RoundDateExpression}
+ *
+ * @author samarth.jain
+ * @since 3.0.0
+ */
+
+public class RoundTimestampExpression extends RoundDateExpression {
+
+ private static final long HALF_OF_NANOS_IN_MILLI = java.util.concurrent.TimeUnit.MILLISECONDS.toNanos(1)/2;
+
+ public RoundTimestampExpression() {}
+
+ private RoundTimestampExpression(List<Expression> children) {
+ super(children);
+ }
+
+ public static Expression create (List<Expression> children) throws SQLException {
+ Expression firstChild = children.get(0);
+ PDataType firstChildDataType = firstChild.getDataType();
+ String timeUnit = (String)((LiteralExpression)children.get(1)).getValue();
+ LiteralExpression multiplierExpr = (LiteralExpression)children.get(2);
+
+ /*
+ * When rounding off timestamp to milliseconds, nanos play a part only when the multiplier value
+ * is equal to 1. This is because for cases when multiplier value is greater than 1, number of nanos/multiplier
+ * will always be less than half the nanos in a millisecond.
+ */
+ if((timeUnit == null || TimeUnit.MILLISECOND.toString().equalsIgnoreCase(timeUnit)) && ((Number)multiplierExpr.getValue()).intValue() == 1) {
+ return new RoundTimestampExpression(children);
+ }
+ // Coerce TIMESTAMP to DATE, as the nanos has no affect
+ List<Expression> newChildren = Lists.newArrayListWithExpectedSize(children.size());
+ newChildren.add(CoerceExpression.create(firstChild, firstChildDataType == PDataType.TIMESTAMP ? PDataType.DATE : PDataType.UNSIGNED_DATE));
+ newChildren.addAll(children.subList(1, children.size()));
+ return RoundDateExpression.create(newChildren);
+ }
+
+ @Override
+ protected PDataCodec getKeyRangeCodec(PDataType columnDataType) {
+ return columnDataType == PDataType.TIMESTAMP
+ ? PDataType.DATE.getCodec()
+ : columnDataType == PDataType.UNSIGNED_TIMESTAMP
+ ? PDataType.UNSIGNED_DATE.getCodec()
+ : super.getKeyRangeCodec(columnDataType);
+ }
+
+ @Override
+ public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) {
+ if (children.get(0).evaluate(tuple, ptr)) {
+ ColumnModifier columnModifier = children.get(0).getColumnModifier();
+ PDataType dataType = getDataType();
+ int nanos = dataType.getNanos(ptr, columnModifier);
+ if(nanos >= HALF_OF_NANOS_IN_MILLI) {
+ long timeMillis = dataType.getMillis(ptr, columnModifier);
+ Timestamp roundedTs = new Timestamp(timeMillis + 1);
+ byte[] byteValue = dataType.toBytes(roundedTs);
+ ptr.set(byteValue);
+ }
+ return true; // for timestamp we only support rounding up the milliseconds.
+ }
+ return false;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/50d523f6/phoenix-core/src/main/java/org/apache/phoenix/expression/function/SQLTableTypeFunction.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/SQLTableTypeFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/SQLTableTypeFunction.java
new file mode 100644
index 0000000..04465ea
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/SQLTableTypeFunction.java
@@ -0,0 +1,80 @@
+/*
+ * 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.PDataType;
+import org.apache.phoenix.schema.PTableType;
+import org.apache.phoenix.schema.tuple.Tuple;
+
+
+/**
+ *
+ * Function used to get the SQL table type name from the serialized table type.
+ * Usage:
+ * SqlTableType('v') will return 'VIEW' based on
+ * {@link java.sql.DatabaseMetaData#getTableTypes()}
+ *
+ * @author jtaylor
+ * @since 2.2
+ */
+@BuiltInFunction(name=SQLTableTypeFunction.NAME, args= {
+ @Argument(allowedTypes=PDataType.CHAR)} )
+public class SQLTableTypeFunction extends ScalarFunction {
+ public static final String NAME = "SQLTableType";
+
+ public SQLTableTypeFunction() {
+ }
+
+ public SQLTableTypeFunction(List<Expression> children) throws SQLException {
+ super(children);
+ }
+
+ @Override
+ public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) {
+ Expression child = children.get(0);
+ if (!child.evaluate(tuple, ptr)) {
+ return false;
+ }
+ if (ptr.getLength() == 0) {
+ return true;
+ }
+ PTableType tableType = PTableType.fromSerializedValue(ptr.get()[ptr.getOffset()]);
+ ptr.set(tableType.getValue().getBytes());
+ return true;
+ }
+
+ @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/SQLViewTypeFunction.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/SQLViewTypeFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/SQLViewTypeFunction.java
new file mode 100644
index 0000000..e70ec8e
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/SQLViewTypeFunction.java
@@ -0,0 +1,80 @@
+/*
+ * 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.PDataType;
+import org.apache.phoenix.schema.PTable.ViewType;
+import org.apache.phoenix.schema.tuple.Tuple;
+
+
+/**
+ *
+ * Function used to get the SQL view type name from the serialized view type.
+ * Usage:
+ * SQLViewType('v') will return 'VIEW' based on
+ * {@link java.sql.DatabaseMetaData#getTableTypes()}
+ *
+ * @author jtaylor
+ * @since 2.2
+ */
+@BuiltInFunction(name=SQLViewTypeFunction.NAME, args= {
+ @Argument(allowedTypes=PDataType.UNSIGNED_TINYINT)} )
+public class SQLViewTypeFunction extends ScalarFunction {
+ public static final String NAME = "SQLViewType";
+
+ public SQLViewTypeFunction() {
+ }
+
+ public SQLViewTypeFunction(List<Expression> children) throws SQLException {
+ super(children);
+ }
+
+ @Override
+ public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) {
+ Expression child = children.get(0);
+ if (!child.evaluate(tuple, ptr)) {
+ return false;
+ }
+ if (ptr.getLength() == 0) {
+ return true;
+ }
+ ViewType viewType = ViewType.fromSerializedValue(ptr.get()[ptr.getOffset()]);
+ ptr.set(viewType.getBytes());
+ return true;
+ }
+
+ @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/ScalarFunction.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ScalarFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ScalarFunction.java
new file mode 100644
index 0000000..1d1059b
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ScalarFunction.java
@@ -0,0 +1,72 @@
+/*
+ * 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.util.List;
+
+import org.apache.phoenix.compile.KeyPart;
+import org.apache.phoenix.expression.Expression;
+import org.apache.phoenix.expression.visitor.ExpressionVisitor;
+
+
+public abstract class ScalarFunction extends FunctionExpression {
+ public static final int NO_TRAVERSAL = -1;
+
+ public ScalarFunction() {
+ }
+
+ public ScalarFunction(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;
+ }
+
+ /**
+ * Determines whether or not a function may be used to form
+ * the start/stop key of a scan
+ * @return the zero-based position of the argument to traverse
+ * into to look for a primary key column reference, or
+ * {@value #NO_TRAVERSAL} if the function cannot be used to
+ * form the scan key.
+ */
+ public int getKeyFormationTraversalIndex() {
+ return NO_TRAVERSAL;
+ }
+
+ /**
+ * Manufactures a KeyPart used to construct the KeyRange given
+ * a constant and a comparison operator.
+ * @param childPart the KeyPart formulated for the child expression
+ * at the {@link #getKeyFormationTraversalIndex()} position.
+ * @return the KeyPart for constructing the KeyRange for this
+ * function.
+ */
+ public KeyPart newKeyPart(KeyPart childPart) {
+ return null;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/50d523f6/phoenix-core/src/main/java/org/apache/phoenix/expression/function/SingleAggregateFunction.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/SingleAggregateFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/SingleAggregateFunction.java
new file mode 100644
index 0000000..f02c4e7
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/SingleAggregateFunction.java
@@ -0,0 +1,173 @@
+/*
+ * 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.io.DataInput;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+
+import org.apache.phoenix.expression.Expression;
+import org.apache.phoenix.expression.LiteralExpression;
+import org.apache.phoenix.expression.aggregator.Aggregator;
+import org.apache.phoenix.expression.visitor.ExpressionVisitor;
+import org.apache.phoenix.schema.PDataType;
+import org.apache.phoenix.schema.tuple.Tuple;
+
+
+/**
+ *
+ * Base class for aggregate functions that calculate an aggregation
+ * using a single {{@link Aggregator}
+ *
+ * @author jtaylor
+ * @since 0.1
+ */
+abstract public class SingleAggregateFunction extends AggregateFunction {
+ private static final List<Expression> DEFAULT_EXPRESSION_LIST = Arrays.<Expression>asList(LiteralExpression.newConstant(1, true));
+ protected boolean isConstant;
+ private Aggregator aggregator;
+
+ /**
+ * Sort aggregate functions with nullable fields last. This allows us not to have to store trailing null values.
+ * Within non-nullable/nullable groups, put fixed width values first since we can access those more efficiently
+ * (i.e. we can skip over groups of them in-mass instead of reading the length of each one to skip over as
+ * required by a variable length value).
+ */
+ public static final Comparator<SingleAggregateFunction> SCHEMA_COMPARATOR = new Comparator<SingleAggregateFunction>() {
+
+ @Override
+ public int compare(SingleAggregateFunction o1, SingleAggregateFunction o2) {
+ boolean isNullable1 = o1.isNullable();
+ boolean isNullable2 = o2.isNullable();
+ if (isNullable1 != isNullable2) {
+ return isNullable1 ? 1 : -1;
+ }
+ isNullable1 = o1.getAggregatorExpression().isNullable();
+ isNullable2 = o2.getAggregatorExpression().isNullable();
+ if (isNullable1 != isNullable2) {
+ return isNullable1 ? 1 : -1;
+ }
+ // Ensures COUNT(1) sorts first TODO: unit test for this
+ boolean isConstant1 = o1.isConstantExpression();
+ boolean isConstant2 = o2.isConstantExpression();
+ if (isConstant1 != isConstant2) {
+ return isConstant1 ? 1 : -1;
+ }
+ PDataType r1 = o1.getAggregator().getDataType();
+ PDataType r2 = o2.getAggregator().getDataType();
+ if (r1.isFixedWidth() != r2.isFixedWidth()) {
+ return r1.isFixedWidth() ? -1 : 1;
+ }
+ return r1.compareTo(r2);
+ }
+ };
+
+ protected SingleAggregateFunction() {
+ this(DEFAULT_EXPRESSION_LIST, true);
+ }
+
+ public SingleAggregateFunction(List<Expression> children) {
+ this(children, children.get(0) instanceof LiteralExpression);
+ }
+
+ private SingleAggregateFunction(List<Expression> children, boolean isConstant) {
+ super(children);
+ this.isConstant = children.get(0) instanceof LiteralExpression;
+ this.aggregator = newClientAggregator();
+ }
+
+ public boolean isConstantExpression() {
+ return isConstant;
+ }
+
+ @Override
+ public PDataType getDataType() {
+ return children.get(0).getDataType();
+ }
+
+ public Expression getAggregatorExpression() {
+ return children.get(0);
+ }
+
+ public Aggregator getAggregator() {
+ return aggregator;
+ }
+
+ @Override
+ public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) {
+ return getAggregator().evaluate(tuple, ptr);
+ }
+
+ /**
+ * Create the aggregator to do server-side aggregation.
+ * The data type of the returned Aggregator must match
+ * the data type returned by {@link #newClientAggregator()}
+ * @param conf HBase configuration.
+ * @return the aggregator to use on the server-side
+ */
+ abstract public Aggregator newServerAggregator(Configuration conf);
+ /**
+ * Create the aggregator to do client-side aggregation
+ * based on the results returned from the aggregating
+ * coprocessor. The data type of the returned Aggregator
+ * must match the data type returned by {@link #newServerAggregator(Configuration)}
+ * @return the aggregator to use on the client-side
+ */
+ public Aggregator newClientAggregator() {
+ return newServerAggregator(null);
+ }
+
+ public Aggregator newServerAggregator(Configuration config, ImmutableBytesWritable ptr) {
+ Aggregator agg = newServerAggregator(config);
+ agg.aggregate(null, ptr);
+ return agg;
+ }
+
+ public void readFields(DataInput input, Configuration conf) throws IOException {
+ super.readFields(input);
+ aggregator = newServerAggregator(conf);
+ }
+
+ @Override
+ public boolean isNullable() {
+ return true;
+ }
+
+ protected SingleAggregateFunction getDelegate() {
+ return this;
+ }
+
+ @Override
+ public final <T> T accept(ExpressionVisitor<T> visitor) {
+ SingleAggregateFunction function = getDelegate();
+ List<T> l = acceptChildren(visitor, visitor.visitEnter(function));
+ T t = visitor.visitLeave(function, l);
+ if (t == null) {
+ t = visitor.defaultReturn(function, l);
+ }
+ return t;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/50d523f6/phoenix-core/src/main/java/org/apache/phoenix/expression/function/SqlTypeNameFunction.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/SqlTypeNameFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/SqlTypeNameFunction.java
new file mode 100644
index 0000000..5d39123
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/SqlTypeNameFunction.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.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.IllegalDataException;
+import org.apache.phoenix.schema.PDataType;
+import org.apache.phoenix.schema.tuple.Tuple;
+import org.apache.phoenix.util.ByteUtil;
+
+
+/**
+ *
+ * Function used to get the SQL type name from the SQL type integer.
+ * Usage:
+ * SqlTypeName(12)
+ * will return 'VARCHAR' based on {@link java.sql.Types#VARCHAR} being 12
+ *
+ * @author jtaylor
+ * @since 0.1
+ */
+@BuiltInFunction(name=SqlTypeNameFunction.NAME, args= {
+ @Argument(allowedTypes=PDataType.INTEGER)} )
+public class SqlTypeNameFunction extends ScalarFunction {
+ public static final String NAME = "SqlTypeName";
+
+ public SqlTypeNameFunction() {
+ }
+
+ public SqlTypeNameFunction(List<Expression> children) throws SQLException {
+ super(children);
+ }
+
+ @Override
+ public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) {
+ Expression child = children.get(0);
+ if (!child.evaluate(tuple, ptr)) {
+ return false;
+ }
+ if (ptr.getLength() == 0) {
+ return true;
+ }
+ int sqlType = child.getDataType().getCodec().decodeInt(ptr, child.getColumnModifier());
+ try {
+ byte[] sqlTypeNameBytes = PDataType.fromTypeId(sqlType).getSqlTypeNameBytes();
+ ptr.set(sqlTypeNameBytes);
+ } catch (IllegalDataException e) {
+ ptr.set(ByteUtil.EMPTY_BYTE_ARRAY);
+ }
+ return true;
+ }
+
+ @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/StddevPopFunction.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/StddevPopFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/StddevPopFunction.java
new file mode 100644
index 0000000..4a4e7b8
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/StddevPopFunction.java
@@ -0,0 +1,78 @@
+/*
+ * 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.util.List;
+
+import org.apache.hadoop.conf.Configuration;
+
+import org.apache.phoenix.expression.Expression;
+import org.apache.phoenix.expression.aggregator.Aggregator;
+import org.apache.phoenix.expression.aggregator.DecimalStddevPopAggregator;
+import org.apache.phoenix.expression.aggregator.DistinctValueWithCountClientAggregator;
+import org.apache.phoenix.expression.aggregator.DistinctValueWithCountServerAggregator;
+import org.apache.phoenix.expression.aggregator.StddevPopAggregator;
+import org.apache.phoenix.parse.FunctionParseNode.Argument;
+import org.apache.phoenix.parse.FunctionParseNode.BuiltInFunction;
+import org.apache.phoenix.schema.PDataType;
+
+/**
+ *
+ * Built-in function for STDDEV_POP(<expression>) aggregate function
+ *
+ * @author anoopsjohn
+ * @since 1.2.1
+ */
+@BuiltInFunction(name = StddevPopFunction.NAME, args = { @Argument(allowedTypes={PDataType.DECIMAL})})
+public class StddevPopFunction extends DistinctValueWithCountAggregateFunction {
+ public static final String NAME = "STDDEV_POP";
+
+ public StddevPopFunction() {
+
+ }
+
+ public StddevPopFunction(List<Expression> childern) {
+ super(childern);
+ }
+
+ @Override
+ public Aggregator newServerAggregator(Configuration conf) {
+ return new DistinctValueWithCountServerAggregator(conf);
+ }
+
+ @Override
+ public DistinctValueWithCountClientAggregator newClientAggregator() {
+ if (children.get(0).getDataType() == PDataType.DECIMAL) {
+ // Special Aggregators for DECIMAL datatype for more precision than double
+ return new DecimalStddevPopAggregator(children, getAggregatorExpression().getColumnModifier());
+ }
+ return new StddevPopAggregator(children, getAggregatorExpression().getColumnModifier());
+ }
+
+ @Override
+ public String getName() {
+ return NAME;
+ }
+
+ @Override
+ public PDataType getDataType() {
+ return PDataType.DECIMAL;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/50d523f6/phoenix-core/src/main/java/org/apache/phoenix/expression/function/StddevSampFunction.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/StddevSampFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/StddevSampFunction.java
new file mode 100644
index 0000000..0e931c6
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/StddevSampFunction.java
@@ -0,0 +1,78 @@
+/*
+ * 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.util.List;
+
+import org.apache.hadoop.conf.Configuration;
+
+import org.apache.phoenix.expression.Expression;
+import org.apache.phoenix.expression.aggregator.Aggregator;
+import org.apache.phoenix.expression.aggregator.DecimalStddevSampAggregator;
+import org.apache.phoenix.expression.aggregator.DistinctValueWithCountClientAggregator;
+import org.apache.phoenix.expression.aggregator.DistinctValueWithCountServerAggregator;
+import org.apache.phoenix.expression.aggregator.StddevSampAggregator;
+import org.apache.phoenix.parse.FunctionParseNode.Argument;
+import org.apache.phoenix.parse.FunctionParseNode.BuiltInFunction;
+import org.apache.phoenix.schema.PDataType;
+
+/**
+ *
+ * Built-in function for STDDEV_SAMP(<expression>) aggregate function
+ *
+ * @author anoopsjohn
+ * @since 1.2.1
+ */
+@BuiltInFunction(name = StddevSampFunction.NAME, args = { @Argument(allowedTypes={PDataType.DECIMAL})})
+public class StddevSampFunction extends DistinctValueWithCountAggregateFunction {
+ public static final String NAME = "STDDEV_SAMP";
+
+ public StddevSampFunction() {
+
+ }
+
+ public StddevSampFunction(List<Expression> childern) {
+ super(childern);
+ }
+
+ @Override
+ public Aggregator newServerAggregator(Configuration conf) {
+ return new DistinctValueWithCountServerAggregator(conf);
+ }
+
+ @Override
+ public DistinctValueWithCountClientAggregator newClientAggregator() {
+ if (children.get(0).getDataType() == PDataType.DECIMAL) {
+ // Special Aggregators for DECIMAL datatype for more precision than double
+ return new DecimalStddevSampAggregator(children, getAggregatorExpression().getColumnModifier());
+ }
+ return new StddevSampAggregator(children, getAggregatorExpression().getColumnModifier());
+ }
+
+ @Override
+ public String getName() {
+ return NAME;
+ }
+
+ @Override
+ public PDataType getDataType() {
+ return PDataType.DECIMAL;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/50d523f6/phoenix-core/src/main/java/org/apache/phoenix/expression/function/SubstrFunction.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/SubstrFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/SubstrFunction.java
new file mode 100644
index 0000000..90aa3c8
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/SubstrFunction.java
@@ -0,0 +1,217 @@
+/*
+ * 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.io.DataInput;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.List;
+
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+
+import org.apache.phoenix.expression.Expression;
+import org.apache.phoenix.expression.LiteralExpression;
+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.StringUtil;
+
+
+/**
+ *
+ * Implementation of the SUBSTR(<string>,<offset>[,<length>]) built-in function
+ * where <offset> is the offset from the start of <string>. A positive offset
+ * is treated as 1-based, a zero offset is treated as 0-based, and a negative
+ * offset starts from the end of the string working backwards. The optional
+ * <length> argument is the number of characters to return. In the absence of the
+ * <length> argument, the rest of the string starting from <offset> is returned.
+ * If <length> is less than 1, null is returned.
+ *
+ * @author jtaylor
+ * @since 0.1
+ */
+@BuiltInFunction(name=SubstrFunction.NAME, args={
+ @Argument(allowedTypes={PDataType.VARCHAR}),
+ @Argument(allowedTypes={PDataType.LONG}), // These are LONG because negative numbers end up as longs
+ @Argument(allowedTypes={PDataType.LONG},defaultValue="null")} )
+public class SubstrFunction extends PrefixFunction {
+ public static final String NAME = "SUBSTR";
+ private boolean hasLengthExpression;
+ private boolean isOffsetConstant;
+ private boolean isLengthConstant;
+ private boolean isFixedWidth;
+ private Integer byteSize;
+
+ public SubstrFunction() {
+ }
+
+ public SubstrFunction(List<Expression> children) {
+ super(children);
+ init();
+ }
+
+ private void init() {
+ // TODO: when we have ColumnModifier.REVERSE, we'll need to negate offset,
+ // since the bytes are reverse and we'll want to work from the end.
+ isOffsetConstant = getOffsetExpression() instanceof LiteralExpression;
+ isLengthConstant = getLengthExpression() instanceof LiteralExpression;
+ hasLengthExpression = !isLengthConstant || ((LiteralExpression)getLengthExpression()).getValue() != null;
+ isFixedWidth = getStrExpression().getDataType().isFixedWidth() && ((hasLengthExpression && isLengthConstant) || (!hasLengthExpression && isOffsetConstant));
+ if (hasLengthExpression && isLengthConstant) {
+ Integer maxLength = ((Number)((LiteralExpression)getLengthExpression()).getValue()).intValue();
+ this.byteSize = maxLength >= 0 ? maxLength : 0;
+ } else if (isOffsetConstant) {
+ Number offsetNumber = (Number)((LiteralExpression)getOffsetExpression()).getValue();
+ if (offsetNumber != null) {
+ int offset = offsetNumber.intValue();
+ if (getStrExpression().getDataType().isFixedWidth()) {
+ if (offset >= 0) {
+ byteSize = getStrExpression().getByteSize() - offset + (offset == 0 ? 0 : 1);
+ } else {
+ byteSize = -offset;
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) {
+ Expression offsetExpression = getOffsetExpression();
+ if (!offsetExpression.evaluate(tuple, ptr)) {
+ return false;
+ }
+ int offset = offsetExpression.getDataType().getCodec().decodeInt(ptr, offsetExpression.getColumnModifier());
+
+ int length = -1;
+ if (hasLengthExpression) {
+ Expression lengthExpression = getLengthExpression();
+ if (!lengthExpression.evaluate(tuple, ptr)) {
+ return false;
+ }
+ length = lengthExpression.getDataType().getCodec().decodeInt(ptr, lengthExpression.getColumnModifier());
+ if (length <= 0) {
+ return false;
+ }
+ }
+
+ if (!getStrExpression().evaluate(tuple, ptr)) {
+ return false;
+ }
+
+ try {
+ boolean isCharType = getStrExpression().getDataType() == PDataType.CHAR;
+ ColumnModifier columnModifier = getStrExpression().getColumnModifier();
+ int strlen = isCharType ? ptr.getLength() : StringUtil.calculateUTF8Length(ptr.get(), ptr.getOffset(), ptr.getLength(), columnModifier);
+
+ // Account for 1 versus 0-based offset
+ offset = offset - (offset <= 0 ? 0 : 1);
+ if (offset < 0) { // Offset < 0 means get from end
+ offset = strlen + offset;
+ }
+ if (offset < 0 || offset >= strlen) {
+ return false;
+ }
+ int maxLength = strlen - offset;
+ length = length == -1 ? maxLength : Math.min(length,maxLength);
+
+ int byteOffset = isCharType ? offset : StringUtil.getByteLengthForUtf8SubStr(ptr.get(), ptr.getOffset(), offset, columnModifier);
+ int byteLength = isCharType ? length : StringUtil.getByteLengthForUtf8SubStr(ptr.get(), ptr.getOffset() + byteOffset, length, columnModifier);
+ ptr.set(ptr.get(), ptr.getOffset() + byteOffset, byteLength);
+ return true;
+ } catch (UnsupportedEncodingException e) {
+ return false;
+ }
+ }
+
+ @Override
+ public PDataType getDataType() {
+ // If fixed width, then return child expression type.
+ // If not fixed width, then we don't know how big this will be across the board
+ return isFixedWidth ? getStrExpression().getDataType() : PDataType.VARCHAR;
+ }
+
+ @Override
+ public boolean isNullable() {
+ return getStrExpression().isNullable() || !isFixedWidth || getOffsetExpression().isNullable();
+ }
+
+ @Override
+ public Integer getByteSize() {
+ return byteSize;
+ }
+
+ // TODO: we shouldn't need both getByteSize() and getMaxLength()
+ @Override
+ public Integer getMaxLength() {
+ return byteSize;
+ }
+
+ @Override
+ public ColumnModifier getColumnModifier() {
+ return getStrExpression().getColumnModifier();
+ }
+
+ @Override
+ public void readFields(DataInput input) throws IOException {
+ super.readFields(input);
+ init();
+ }
+
+ private Expression getStrExpression() {
+ return children.get(0);
+ }
+
+ private Expression getOffsetExpression() {
+ return children.get(1);
+ }
+
+ private Expression getLengthExpression() {
+ return children.get(2);
+ }
+
+ @Override
+ public OrderPreserving preservesOrder() {
+ if (isOffsetConstant) {
+ LiteralExpression literal = (LiteralExpression) getOffsetExpression();
+ Number offsetNumber = (Number) literal.getValue();
+ if (offsetNumber != null) {
+ int offset = offsetNumber.intValue();
+ if ((offset == 0 || offset == 1) && (!hasLengthExpression || isLengthConstant)) {
+ return OrderPreserving.YES_IF_LAST;
+ }
+ }
+ }
+ return OrderPreserving.NO;
+ }
+
+ @Override
+ protected boolean extractNode() {
+ return true;
+ }
+
+ @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/SumAggregateFunction.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/SumAggregateFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/SumAggregateFunction.java
new file mode 100644
index 0000000..ab64ce8
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/SumAggregateFunction.java
@@ -0,0 +1,146 @@
+/*
+ * 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.math.BigDecimal;
+import java.util.List;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+
+import org.apache.phoenix.expression.Expression;
+import org.apache.phoenix.expression.LiteralExpression;
+import org.apache.phoenix.expression.aggregator.Aggregator;
+import org.apache.phoenix.expression.aggregator.DecimalSumAggregator;
+import org.apache.phoenix.expression.aggregator.DoubleSumAggregator;
+import org.apache.phoenix.expression.aggregator.NumberSumAggregator;
+import org.apache.phoenix.parse.FunctionParseNode.Argument;
+import org.apache.phoenix.parse.FunctionParseNode.BuiltInFunction;
+import org.apache.phoenix.parse.SumAggregateParseNode;
+import org.apache.phoenix.schema.ColumnModifier;
+import org.apache.phoenix.schema.PDataType;
+import org.apache.phoenix.schema.tuple.Tuple;
+
+
+/**
+ *
+ * Built-in function for SUM aggregation function.
+ *
+ * @author jtaylor
+ * @since 0.1
+ */
+@BuiltInFunction(name=SumAggregateFunction.NAME, nodeClass=SumAggregateParseNode.class, args= {@Argument(allowedTypes={PDataType.DECIMAL})} )
+public class SumAggregateFunction extends DelegateConstantToCountAggregateFunction {
+ public static final String NAME = "SUM";
+
+ public SumAggregateFunction() {
+ }
+
+ // TODO: remove when not required at built-in func register time
+ public SumAggregateFunction(List<Expression> childExpressions){
+ super(childExpressions, null);
+ }
+
+ public SumAggregateFunction(List<Expression> childExpressions, CountAggregateFunction delegate){
+ super(childExpressions, delegate);
+ }
+
+ private Aggregator newAggregator(final PDataType type, ColumnModifier columnModifier, ImmutableBytesWritable ptr) {
+ switch( type ) {
+ case DECIMAL:
+ return new DecimalSumAggregator(columnModifier, ptr);
+ case UNSIGNED_DOUBLE:
+ case UNSIGNED_FLOAT:
+ case DOUBLE:
+ case FLOAT:
+ return new DoubleSumAggregator(columnModifier, ptr) {
+ @Override
+ protected PDataType getInputDataType() {
+ return type;
+ }
+ };
+ default:
+ return new NumberSumAggregator(columnModifier, ptr) {
+ @Override
+ protected PDataType getInputDataType() {
+ return type;
+ }
+ };
+ }
+ }
+
+ @Override
+ public Aggregator newClientAggregator() {
+ return newAggregator(getDataType(), null, null);
+ }
+
+ @Override
+ public Aggregator newServerAggregator(Configuration conf) {
+ Expression child = getAggregatorExpression();
+ return newAggregator(child.getDataType(), child.getColumnModifier(), null);
+ }
+
+ @Override
+ public Aggregator newServerAggregator(Configuration conf, ImmutableBytesWritable ptr) {
+ Expression child = getAggregatorExpression();
+ return newAggregator(child.getDataType(), child.getColumnModifier(), ptr);
+ }
+
+ @Override
+ public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) {
+ if (!super.evaluate(tuple, ptr)) {
+ return false;
+ }
+ if (isConstantExpression()) {
+ PDataType type = getDataType();
+ Object constantValue = ((LiteralExpression)children.get(0)).getValue();
+ if (type == PDataType.DECIMAL) {
+ BigDecimal value = ((BigDecimal)constantValue).multiply((BigDecimal)PDataType.DECIMAL.toObject(ptr, PDataType.LONG));
+ ptr.set(PDataType.DECIMAL.toBytes(value));
+ } else {
+ long constantLongValue = ((Number)constantValue).longValue();
+ long value = constantLongValue * type.getCodec().decodeLong(ptr, null);
+ ptr.set(new byte[type.getByteSize()]);
+ type.getCodec().encodeLong(value, ptr);
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public PDataType getDataType() {
+ switch(super.getDataType()) {
+ case DECIMAL:
+ return PDataType.DECIMAL;
+ case UNSIGNED_FLOAT:
+ case UNSIGNED_DOUBLE:
+ case FLOAT:
+ case DOUBLE:
+ return PDataType.DOUBLE;
+ default:
+ return PDataType.LONG;
+ }
+ }
+
+ @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/TimeUnit.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/TimeUnit.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/TimeUnit.java
new file mode 100644
index 0000000..7ea5161
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/TimeUnit.java
@@ -0,0 +1,50 @@
+/*
+ * 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 com.google.common.base.Joiner;
+
+public enum TimeUnit {
+ DAY("day"),
+ HOUR("hour"),
+ MINUTE("minute"),
+ SECOND("second"),
+ MILLISECOND("millisecond");
+
+ private String value;
+
+ private TimeUnit(String value) {
+ this.value = value;
+ }
+
+ public static final String VALID_VALUES = Joiner.on(", ").join(TimeUnit.values());
+
+ public static TimeUnit getTimeUnit(String timeUnit) {
+ if(timeUnit == null) {
+ throw new IllegalArgumentException("No time unit value specified. Only a time unit value that belongs to one of these : " + VALID_VALUES + " is allowed.");
+ }
+ for(TimeUnit tu : values()) {
+ if(timeUnit.equalsIgnoreCase(tu.value)) {
+ return tu;
+ }
+ }
+ throw new IllegalArgumentException("Invalid value of time unit " + timeUnit + ". Only a time unit value that belongs to one of these : " + VALID_VALUES + " is allowed.");
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/50d523f6/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ToCharFunction.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ToCharFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ToCharFunction.java
new file mode 100644
index 0000000..d6cdc99
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ToCharFunction.java
@@ -0,0 +1,135 @@
+/*
+ * 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.io.*;
+import java.sql.SQLException;
+import java.text.Format;
+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 the TO_CHAR(<date>/<number>,[<format-string>] built-in function.
+ * The first argument must be of type DATE or TIME or TIMESTAMP or DECIMAL or INTEGER, and the second argument must be a constant string.
+ *
+ * @author jtaylor
+ * @since 0.1
+ */
+@BuiltInFunction(name=ToCharFunction.NAME, nodeClass=ToCharParseNode.class, args={
+ @Argument(allowedTypes={PDataType.TIMESTAMP, PDataType.DECIMAL}),
+ @Argument(allowedTypes={PDataType.VARCHAR},isConstant=true,defaultValue="null") } )
+public class ToCharFunction extends ScalarFunction {
+ public static final String NAME = "TO_CHAR";
+ private String formatString;
+ private Format formatter;
+ private FunctionArgumentType type;
+
+ public ToCharFunction() {
+ }
+
+ public ToCharFunction(List<Expression> children, FunctionArgumentType type, String formatString, Format formatter) throws SQLException {
+ super(children.subList(0, 1));
+ Preconditions.checkNotNull(formatString);
+ Preconditions.checkNotNull(formatter);
+ Preconditions.checkNotNull(type);
+ this.type = type;
+ this.formatString = formatString;
+ this.formatter = formatter;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + formatString.hashCode();
+ result = prime * result + getExpression().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;
+ ToCharFunction other = (ToCharFunction)obj;
+ if (!getExpression().equals(other.getExpression())) return false;
+ if (!formatString.equals(other.formatString)) return false;
+ return true;
+ }
+
+ @Override
+ public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) {
+ Expression expression = getExpression();
+ if (!expression.evaluate(tuple, ptr)) {
+ return false;
+ }
+ PDataType type = expression.getDataType();
+ Object value = formatter.format(type.toObject(ptr, expression.getColumnModifier()));
+ byte[] b = getDataType().toBytes(value);
+ ptr.set(b);
+ return true;
+ }
+
+ @Override
+ public PDataType getDataType() {
+ return PDataType.VARCHAR;
+ }
+
+ @Override
+ public boolean isNullable() {
+ return getExpression().isNullable();
+ }
+
+ @Override
+ public void readFields(DataInput input) throws IOException {
+ super.readFields(input);
+ formatString = WritableUtils.readString(input);
+ type = WritableUtils.readEnum(input, FunctionArgumentType.class);
+ formatter = type.getFormatter(formatString);
+ }
+
+ @Override
+ public void write(DataOutput output) throws IOException {
+ super.write(output);
+ WritableUtils.writeString(output, formatString);
+ WritableUtils.writeEnum(output, type);
+ }
+
+ private Expression getExpression() {
+ return children.get(0);
+ }
+
+ @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/ToDateFunction.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ToDateFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ToDateFunction.java
new file mode 100644
index 0000000..5ef0034
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ToDateFunction.java
@@ -0,0 +1,133 @@
+/*
+ * 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.io.*;
+import java.sql.SQLException;
+import java.text.Format;
+import java.text.ParseException;
+import java.util.List;
+
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+import org.apache.hadoop.io.WritableUtils;
+
+import org.apache.phoenix.expression.Expression;
+import org.apache.phoenix.parse.*;
+import org.apache.phoenix.parse.FunctionParseNode.Argument;
+import org.apache.phoenix.parse.FunctionParseNode.BuiltInFunction;
+import org.apache.phoenix.schema.PDataType;
+import org.apache.phoenix.schema.tuple.Tuple;
+import org.apache.phoenix.util.DateUtil;
+
+
+/**
+ *
+ * Implementation of the TO_DATE(<string>,[<format-string>]) built-in function.
+ * The second argument is optional and defaults to the phoenix.query.dateFormat value
+ * from the HBase config. If present it must be a constant string.
+ *
+ * @author jtaylor
+ * @since 0.1
+ */
+@BuiltInFunction(name=ToDateFunction.NAME, nodeClass=ToDateParseNode.class, args= {@Argument(allowedTypes={PDataType.VARCHAR}),@Argument(allowedTypes={PDataType.VARCHAR},isConstant=true,defaultValue="null")} )
+public class ToDateFunction extends ScalarFunction {
+ public static final String NAME = "TO_DATE";
+ private Format dateParser;
+ private String dateFormat;
+
+ public ToDateFunction() {
+ }
+
+ public ToDateFunction(List<Expression> children, String dateFormat, Format dateParser) throws SQLException {
+ super(children.subList(0, 1));
+ this.dateFormat = dateFormat;
+ this.dateParser = dateParser;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + dateFormat.hashCode();
+ result = prime * result + getExpression().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;
+ ToDateFunction other = (ToDateFunction)obj;
+ if (!getExpression().equals(other.getExpression())) return false;
+ if (!dateFormat.equals(other.dateFormat)) return false;
+ return true;
+ }
+
+ @Override
+ public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) {
+ Expression expression = getExpression();
+ if (!expression.evaluate(tuple, ptr) || ptr.getLength() == 0) {
+ return false;
+ }
+ PDataType type = expression.getDataType();
+ String dateStr = (String)type.toObject(ptr, expression.getColumnModifier());
+ try {
+ Object value = dateParser.parseObject(dateStr);
+ byte[] byteValue = getDataType().toBytes(value);
+ ptr.set(byteValue);
+ return true;
+ } catch (ParseException e) {
+ throw new IllegalStateException("to_date('" + dateStr + ")' did not match expected date format of '" + dateFormat + "'.");
+ }
+ }
+
+ @Override
+ public PDataType getDataType() {
+ return PDataType.DATE;
+ }
+
+ @Override
+ public boolean isNullable() {
+ return getExpression().isNullable();
+ }
+
+ @Override
+ public void readFields(DataInput input) throws IOException {
+ super.readFields(input);
+ dateFormat = WritableUtils.readString(input);
+ dateParser = DateUtil.getDateParser(dateFormat);
+ }
+
+ @Override
+ public void write(DataOutput output) throws IOException {
+ super.write(output);
+ WritableUtils.writeString(output, dateFormat);
+ }
+
+ private Expression getExpression() {
+ return children.get(0);
+ }
+
+ @Override
+ public String getName() {
+ return NAME;
+ }
+}