phoenix-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jamestay...@apache.org
Subject [28/51] [partial] Initial commit of master branch from github
Date Mon, 27 Jan 2014 22:15:44 GMT
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(&lt;date&gt;/&lt;number&gt;,[&lt;format-string&gt;] 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;
+    }
+}


Mime
View raw message