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/10/04 07:28:35 UTC

git commit: PHOENIX-1030 Change Expression.isDeterministic() to return a enum of values ALWAYS, PER_STATEMENT, PER_ROW (Thomas D'Silva)

Repository: phoenix
Updated Branches:
  refs/heads/master 340280b2e -> b1be0f8b8


PHOENIX-1030 Change Expression.isDeterministic() to return a enum of values ALWAYS, PER_STATEMENT, PER_ROW (Thomas D'Silva)


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/b1be0f8b
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/b1be0f8b
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/b1be0f8b

Branch: refs/heads/master
Commit: b1be0f8b8b5921bd6d0a91f549294eea7f27da95
Parents: 340280b
Author: James Taylor <jt...@salesforce.com>
Authored: Fri Oct 3 22:30:45 2014 -0700
Committer: James Taylor <jt...@salesforce.com>
Committed: Fri Oct 3 22:33:34 2014 -0700

----------------------------------------------------------------------
 .../phoenix/compile/CreateTableCompiler.java    |  4 +-
 .../phoenix/compile/ExpressionCompiler.java     | 64 ++++++-------
 .../apache/phoenix/compile/SequenceManager.java |  5 +-
 .../apache/phoenix/compile/UpsertCompiler.java  |  7 +-
 .../apache/phoenix/compile/WhereCompiler.java   |  3 +-
 .../apache/phoenix/compile/WhereOptimizer.java  |  3 +-
 .../apache/phoenix/execute/HashJoinPlan.java    |  3 +-
 .../phoenix/expression/AndExpression.java       |  6 +-
 .../expression/BaseCompoundExpression.java      | 11 +--
 .../phoenix/expression/BaseExpression.java      |  6 +-
 .../expression/ComparisonExpression.java        | 34 +++----
 .../expression/CurrentDateTimeFunction.java     |  4 +-
 .../apache/phoenix/expression/Determinism.java  | 19 ++++
 .../apache/phoenix/expression/Expression.java   |  6 +-
 .../phoenix/expression/InListExpression.java    |  4 +-
 .../phoenix/expression/IsNullExpression.java    |  4 +-
 .../phoenix/expression/LiteralExpression.java   | 99 +++++++++++---------
 .../phoenix/expression/NotExpression.java       |  4 +-
 .../expression/function/AggregateFunction.java  |  5 +-
 .../function/CeilDecimalExpression.java         |  8 +-
 .../expression/function/CoalesceFunction.java   |  3 +-
 .../function/CountAggregateFunction.java        |  4 +-
 .../function/FloorDecimalExpression.java        |  8 +-
 .../function/RoundDateExpression.java           |  5 +-
 .../function/RoundDecimalExpression.java        |  6 +-
 .../function/SingleAggregateFunction.java       |  4 +-
 .../apache/phoenix/parse/FunctionParseNode.java | 10 +-
 .../org/apache/phoenix/util/ExpressionUtil.java | 28 +++---
 .../phoenix/expression/DeterminismTest.java     | 37 ++++++++
 29 files changed, 248 insertions(+), 156 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/b1be0f8b/phoenix-core/src/main/java/org/apache/phoenix/compile/CreateTableCompiler.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/CreateTableCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/CreateTableCompiler.java
index 7794416..7a8ebf4 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/CreateTableCompiler.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/CreateTableCompiler.java
@@ -32,6 +32,7 @@ import org.apache.phoenix.exception.SQLExceptionInfo;
 import org.apache.phoenix.execute.MutationState;
 import org.apache.phoenix.expression.AndExpression;
 import org.apache.phoenix.expression.ComparisonExpression;
+import org.apache.phoenix.expression.Determinism;
 import org.apache.phoenix.expression.Expression;
 import org.apache.phoenix.expression.IsNullExpression;
 import org.apache.phoenix.expression.KeyValueColumnExpression;
@@ -257,7 +258,8 @@ public class CreateTableCompiler {
 
         @Override
         public Iterator<Expression> visitEnter(ComparisonExpression node) {
-            if (node.getFilterOp() == CompareOp.EQUAL && node.getChildren().get(1).isStateless() && node.getChildren().get(1).isDeterministic()) {
+            if (node.getFilterOp() == CompareOp.EQUAL && node.getChildren().get(1).isStateless() 
+            		&& node.getChildren().get(1).getDeterminism() == Determinism.ALWAYS ) {
                 return Iterators.singletonIterator(node.getChildren().get(0));
             }
             return super.visitEnter(node);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/b1be0f8b/phoenix-core/src/main/java/org/apache/phoenix/compile/ExpressionCompiler.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/ExpressionCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/ExpressionCompiler.java
index 1511539..573cb55 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/ExpressionCompiler.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/ExpressionCompiler.java
@@ -41,6 +41,7 @@ import org.apache.phoenix.expression.DecimalAddExpression;
 import org.apache.phoenix.expression.DecimalDivideExpression;
 import org.apache.phoenix.expression.DecimalMultiplyExpression;
 import org.apache.phoenix.expression.DecimalSubtractExpression;
+import org.apache.phoenix.expression.Determinism;
 import org.apache.phoenix.expression.DoubleAddExpression;
 import org.apache.phoenix.expression.DoubleDivideExpression;
 import org.apache.phoenix.expression.DoubleMultiplyExpression;
@@ -226,7 +227,7 @@ public class ExpressionCompiler extends UnsupportedAllParseNodeVisitor<Expressio
 
     private Expression orExpression(List<Expression> children) throws SQLException {
         Iterator<Expression> iterator = children.iterator();
-        boolean isDeterministic = true;
+        Determinism determinism = Determinism.ALWAYS;
         while (iterator.hasNext()) {
             Expression child = iterator.next();
             if (child.getDataType() != PDataType.BOOLEAN) {
@@ -238,10 +239,10 @@ public class ExpressionCompiler extends UnsupportedAllParseNodeVisitor<Expressio
             if (LiteralExpression.isTrue(child)) {
                 return child;
             }
-            isDeterministic &= child.isDeterministic();
+            determinism = determinism.combine(child.getDeterminism());
         }
         if (children.size() == 0) {
-            return LiteralExpression.newConstant(true, isDeterministic);
+            return LiteralExpression.newConstant(true, determinism);
         }
         if (children.size() == 1) {
             return children.get(0);
@@ -395,12 +396,12 @@ public class ExpressionCompiler extends UnsupportedAllParseNodeVisitor<Expressio
     @Override
     public Expression visit(BindParseNode node) throws SQLException {
         Object value = context.getBindManager().getBindValue(node);
-        return LiteralExpression.newConstant(value, true);
+        return LiteralExpression.newConstant(value, Determinism.ALWAYS);
     }
 
     @Override
     public Expression visit(LiteralParseNode node) throws SQLException {
-        return LiteralExpression.newConstant(node.getValue(), node.getType(), true);
+        return LiteralExpression.newConstant(node.getValue(), node.getType(), Determinism.ALWAYS);
     }
 
     @Override
@@ -420,13 +421,12 @@ public class ExpressionCompiler extends UnsupportedAllParseNodeVisitor<Expressio
         return true;
     }
 
-    private static boolean isDeterministic(List<Expression> l) {
+    private static Determinism getDeterminism(List<Expression> l) {
+    	Determinism determinism = Determinism.ALWAYS;
         for (Expression e : l) {
-            if (!e.isDeterministic()) {
-                return false;
-            }
+        	determinism.combine(e.getDeterminism());
         }
-        return true;
+        return determinism;
     }
 
     @Override
@@ -442,7 +442,7 @@ public class ExpressionCompiler extends UnsupportedAllParseNodeVisitor<Expressio
             ImmutableBytesWritable ptr = context.getTempPtr();
             int index = caseExpression.evaluateIndexOf(null, ptr);
             if (index < 0) {
-                return LiteralExpression.newConstant(null, isDeterministic(l));
+                return LiteralExpression.newConstant(null, getDeterminism(l));
             }
             return caseExpression.getChildren().get(index);
         }
@@ -474,7 +474,7 @@ public class ExpressionCompiler extends UnsupportedAllParseNodeVisitor<Expressio
         if (rhs instanceof LiteralExpression) {
             String pattern = (String)((LiteralExpression)rhs).getValue();
             if (pattern == null || pattern.length() == 0) {
-                return LiteralExpression.newConstant(null, rhs.isDeterministic());
+                return LiteralExpression.newConstant(null, rhs.getDeterminism());
             }
             // TODO: for pattern of '%' optimize to strlength(lhs) > 0
             // We can't use lhs IS NOT NULL b/c if lhs is NULL we need
@@ -483,19 +483,19 @@ public class ExpressionCompiler extends UnsupportedAllParseNodeVisitor<Expressio
             // Can't possibly be as long as the constant, then FALSE
             Integer lhsMaxLength = lhs.getMaxLength();
             if (lhsMaxLength != null && lhsMaxLength < index) {
-                return LiteralExpression.newConstant(false, rhs.isDeterministic());
+                return LiteralExpression.newConstant(false, rhs.getDeterminism());
             }
             if (index == -1) {
                 String rhsLiteral = LikeExpression.unescapeLike(pattern);
                 if (lhsMaxLength != null && lhsMaxLength != rhsLiteral.length()) {
-                    return LiteralExpression.newConstant(false, rhs.isDeterministic());
+                    return LiteralExpression.newConstant(false, rhs.getDeterminism());
                 }
                 if (node.getLikeType() == LikeType.CASE_SENSITIVE) {
                   CompareOp op = node.isNegate() ? CompareOp.NOT_EQUAL : CompareOp.EQUAL;
                   if (pattern.equals(rhsLiteral)) {
                       return new ComparisonExpression(op, children);
                   } else {
-                      rhs = LiteralExpression.newConstant(rhsLiteral, PDataType.CHAR, rhs.isDeterministic());
+                      rhs = LiteralExpression.newConstant(rhsLiteral, PDataType.CHAR, rhs.getDeterminism());
                       return new ComparisonExpression(op, Arrays.asList(lhs,rhs));
                   }
                 }
@@ -505,9 +505,9 @@ public class ExpressionCompiler extends UnsupportedAllParseNodeVisitor<Expressio
         if (ExpressionUtil.isConstant(expression)) {
             ImmutableBytesWritable ptr = context.getTempPtr();
             if (!expression.evaluate(null, ptr)) {
-                return LiteralExpression.newConstant(null, expression.isDeterministic());
+                return LiteralExpression.newConstant(null, expression.getDeterminism());
             } else {
-                return LiteralExpression.newConstant(Boolean.TRUE.equals(PDataType.BOOLEAN.toObject(ptr)) ^ node.isNegate(), expression.isDeterministic());
+                return LiteralExpression.newConstant(Boolean.TRUE.equals(PDataType.BOOLEAN.toObject(ptr)) ^ node.isNegate(), expression.getDeterminism());
             }
         }
         if (node.isNegate()) {
@@ -691,7 +691,7 @@ public class ExpressionCompiler extends UnsupportedAllParseNodeVisitor<Expressio
             return ExpressionUtil.getConstantExpression(expression, ptr); 
         } 
         else if (isNull) {
-            return LiteralExpression.newConstant(null, expression.getDataType(), expression.isDeterministic());
+            return LiteralExpression.newConstant(null, expression.getDataType(), expression.getDeterminism());
         }
         // Otherwise create and return the expression
         return wrapGroupByExpression(expression);
@@ -740,11 +740,11 @@ public class ExpressionCompiler extends UnsupportedAllParseNodeVisitor<Expressio
             @Override
             public Expression create(ArithmeticParseNode node, List<Expression> children) throws SQLException {
                 boolean foundDate = false;
-                boolean isDeterministic = true;
+                Determinism determinism = Determinism.ALWAYS;
                 PDataType theType = null;
                 for(int i = 0; i < children.size(); i++) {
                     Expression e = children.get(i);
-                    isDeterministic &= e.isDeterministic();
+                    determinism = determinism.combine(e.getDeterminism());
                     PDataType type = e.getDataType();
                     if (type == null) {
                         continue; 
@@ -779,7 +779,7 @@ public class ExpressionCompiler extends UnsupportedAllParseNodeVisitor<Expressio
                 } else if (theType == PDataType.DOUBLE) {
                     return new DoubleAddExpression(children);
                 } else if (theType == null) {
-                    return LiteralExpression.newConstant(null, theType, isDeterministic);
+                	return LiteralExpression.newConstant(null, theType, determinism);
                 } else if (theType == PDataType.TIMESTAMP || theType == PDataType.UNSIGNED_TIMESTAMP) {
                     return new TimestampAddExpression(children);
                 } else if (theType.isCoercibleTo(PDataType.DATE)) {
@@ -867,7 +867,7 @@ public class ExpressionCompiler extends UnsupportedAllParseNodeVisitor<Expressio
                 PDataType theType = null;
                 Expression e1 = children.get(0);
                 Expression e2 = children.get(1);
-                boolean isDeterministic = e1.isDeterministic() && e2.isDeterministic();
+                Determinism determinism = e1.getDeterminism().combine(e2.getDeterminism());
                 PDataType type1 = e1.getDataType();
                 PDataType type2 = e2.getDataType();
                 // TODO: simplify this special case for DATE conversion
@@ -919,7 +919,7 @@ public class ExpressionCompiler extends UnsupportedAllParseNodeVisitor<Expressio
                     // This logic finds the common type to which all child types are coercible
                     // without losing precision.
                     Expression e = children.get(i);
-                    isDeterministic &= e.isDeterministic();
+                    determinism = determinism.combine(e.getDeterminism());
                     PDataType type = e.getDataType();
                     if (type == null) {
                         continue;
@@ -952,7 +952,7 @@ public class ExpressionCompiler extends UnsupportedAllParseNodeVisitor<Expressio
                 } else if (theType == PDataType.DOUBLE) {
                     return new DoubleSubtractExpression(children);
                 } else if (theType == null) {
-                    return LiteralExpression.newConstant(null, theType, isDeterministic);
+                	return LiteralExpression.newConstant(null, theType, determinism);
                 } else if (theType == PDataType.TIMESTAMP || theType == PDataType.UNSIGNED_TIMESTAMP) {
                     return new TimestampSubtractExpression(children);
                 } else if (theType.isCoercibleTo(PDataType.DATE)) {
@@ -975,10 +975,10 @@ public class ExpressionCompiler extends UnsupportedAllParseNodeVisitor<Expressio
             @Override
             public Expression create(ArithmeticParseNode node, List<Expression> children) throws SQLException {
                 PDataType theType = null;
-                boolean isDeterministic = true;
+                Determinism determinism = Determinism.ALWAYS;
                 for(int i = 0; i < children.size(); i++) {
                     Expression e = children.get(i);
-                    isDeterministic &= e.isDeterministic();
+                    determinism = determinism.combine(e.getDeterminism());
                     PDataType type = e.getDataType();
                     if (type == null) {
                         continue;
@@ -1004,7 +1004,7 @@ public class ExpressionCompiler extends UnsupportedAllParseNodeVisitor<Expressio
                 case DOUBLE:
                     return new DoubleMultiplyExpression( children);
                 default:
-                    return LiteralExpression.newConstant(null, theType, isDeterministic);
+                    return LiteralExpression.newConstant(null, theType, determinism);
                 }
             }
         });
@@ -1036,10 +1036,10 @@ public class ExpressionCompiler extends UnsupportedAllParseNodeVisitor<Expressio
             @Override
             public Expression create(ArithmeticParseNode node, List<Expression> children) throws SQLException {
                 PDataType theType = null;
-                boolean isDeterministic = true;
+                Determinism determinism = Determinism.ALWAYS;
                 for(int i = 0; i < children.size(); i++) {
                     Expression e = children.get(i);
-                    isDeterministic &= e.isDeterministic();
+                    determinism = determinism.combine(e.getDeterminism());
                     PDataType type = e.getDataType();
                     if (type == null) {
                         continue;
@@ -1065,7 +1065,7 @@ public class ExpressionCompiler extends UnsupportedAllParseNodeVisitor<Expressio
                 case DOUBLE:
                     return new DoubleDivideExpression(children);
                 default:
-                    return LiteralExpression.newConstant(null, theType, isDeterministic);
+                    return LiteralExpression.newConstant(null, theType, determinism);
                 }
             }
         });
@@ -1230,11 +1230,11 @@ public class ExpressionCompiler extends UnsupportedAllParseNodeVisitor<Expressio
                 Expression child = children.get(i);
                 child.evaluate(null, ptr);
                 Object value = arrayElemDataType.toObject(ptr, child.getDataType(), child.getSortOrder());
-                elements[i] = LiteralExpression.newConstant(value, child.getDataType(), child.isDeterministic()).getValue();
+                elements[i] = LiteralExpression.newConstant(value, child.getDataType(), child.getDeterminism()).getValue();
             }
             Object value = PArrayDataType.instantiatePhoenixArray(arrayElemDataType, elements);
             return LiteralExpression.newConstant(value,
-                    PDataType.fromTypeId(arrayElemDataType.getSqlType() + PDataType.ARRAY_TYPE_BASE), true);
+                    PDataType.fromTypeId(arrayElemDataType.getSqlType() + PDataType.ARRAY_TYPE_BASE), Determinism.ALWAYS);
         }
         
         return wrapGroupByExpression(arrayExpression);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/b1be0f8b/phoenix-core/src/main/java/org/apache/phoenix/compile/SequenceManager.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/SequenceManager.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/SequenceManager.java
index de352b5..9be45a4 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/SequenceManager.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/SequenceManager.java
@@ -26,6 +26,7 @@ import java.util.Map;
 import org.apache.hadoop.hbase.HConstants;
 import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
 import org.apache.phoenix.expression.BaseTerminalExpression;
+import org.apache.phoenix.expression.Determinism;
 import org.apache.phoenix.jdbc.PhoenixStatement;
 import org.apache.phoenix.parse.SequenceValueParseNode;
 import org.apache.phoenix.parse.TableName;
@@ -202,8 +203,8 @@ public class SequenceManager {
         }
         
         @Override
-        public boolean isDeterministic() {
-            return false;
+        public Determinism getDeterminism() {
+            return Determinism.PER_ROW;
         }
         
         @Override

http://git-wip-us.apache.org/repos/asf/phoenix/blob/b1be0f8b/phoenix-core/src/main/java/org/apache/phoenix/compile/UpsertCompiler.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/UpsertCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/UpsertCompiler.java
index 612f322..3a48a93 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/UpsertCompiler.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/UpsertCompiler.java
@@ -42,6 +42,7 @@ import org.apache.phoenix.exception.SQLExceptionCode;
 import org.apache.phoenix.exception.SQLExceptionInfo;
 import org.apache.phoenix.execute.AggregatePlan;
 import org.apache.phoenix.execute.MutationState;
+import org.apache.phoenix.expression.Determinism;
 import org.apache.phoenix.expression.Expression;
 import org.apache.phoenix.expression.LiteralExpression;
 import org.apache.phoenix.hbase.index.util.ImmutableBytesPtr;
@@ -511,7 +512,7 @@ public class UpsertCompiler {
                         }
                         // Add literal null for missing PK columns
                         pos = projectedExpressions.size();
-                        Expression literalNull = LiteralExpression.newConstant(null, column.getDataType(), true);
+                        Expression literalNull = LiteralExpression.newConstant(null, column.getDataType(), Determinism.ALWAYS);
                         projectedExpressions.add(literalNull);
                         allColumnsIndexes[pos] = column.getPosition();
                     } 
@@ -812,7 +813,7 @@ public class UpsertCompiler {
             if (isTopLevel()) {
                 context.getBindManager().addParamMetaData(node, column);
                 Object value = context.getBindManager().getBindValue(node);
-                return LiteralExpression.newConstant(value, column.getDataType(), column.getSortOrder(), true);
+                return LiteralExpression.newConstant(value, column.getDataType(), column.getSortOrder(), Determinism.ALWAYS);
             }
             return super.visit(node);
         }    
@@ -820,7 +821,7 @@ public class UpsertCompiler {
         @Override
         public Expression visit(LiteralParseNode node) throws SQLException {
             if (isTopLevel()) {
-                return LiteralExpression.newConstant(node.getValue(), column.getDataType(), column.getSortOrder(), true);
+                return LiteralExpression.newConstant(node.getValue(), column.getDataType(), column.getSortOrder(), Determinism.ALWAYS);
             }
             return super.visit(node);
         }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/b1be0f8b/phoenix-core/src/main/java/org/apache/phoenix/compile/WhereCompiler.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/WhereCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/WhereCompiler.java
index 27a4490..51d0ffc 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/WhereCompiler.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/WhereCompiler.java
@@ -29,6 +29,7 @@ import org.apache.hadoop.hbase.filter.Filter;
 import org.apache.phoenix.exception.SQLExceptionCode;
 import org.apache.phoenix.exception.SQLExceptionInfo;
 import org.apache.phoenix.expression.AndExpression;
+import org.apache.phoenix.expression.Determinism;
 import org.apache.phoenix.expression.Expression;
 import org.apache.phoenix.expression.KeyValueColumnExpression;
 import org.apache.phoenix.expression.LiteralExpression;
@@ -120,7 +121,7 @@ public class WhereCompiler {
         
         Set<Expression> extractedNodes = Sets.<Expression>newHashSet();
         WhereExpressionCompiler whereCompiler = new WhereExpressionCompiler(context);
-        Expression expression = where == null ? LiteralExpression.newConstant(true,PDataType.BOOLEAN,true) : where.accept(whereCompiler);
+        Expression expression = where == null ? LiteralExpression.newConstant(true,PDataType.BOOLEAN,Determinism.ALWAYS) : where.accept(whereCompiler);
         if (whereCompiler.isAggregate()) {
             throw new SQLExceptionInfo.Builder(SQLExceptionCode.AGGREGATE_IN_WHERE).build().buildException();
         }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/b1be0f8b/phoenix-core/src/main/java/org/apache/phoenix/compile/WhereOptimizer.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/WhereOptimizer.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/WhereOptimizer.java
index a9908b9..5f652f1 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/WhereOptimizer.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/WhereOptimizer.java
@@ -37,6 +37,7 @@ import org.apache.phoenix.expression.BaseExpression.ExpressionComparabilityWrapp
 import org.apache.phoenix.expression.BaseTerminalExpression;
 import org.apache.phoenix.expression.CoerceExpression;
 import org.apache.phoenix.expression.ComparisonExpression;
+import org.apache.phoenix.expression.Determinism;
 import org.apache.phoenix.expression.Expression;
 import org.apache.phoenix.expression.InListExpression;
 import org.apache.phoenix.expression.IsNullExpression;
@@ -407,7 +408,7 @@ public class WhereOptimizer {
             if (l.size() != node.getChildren().size()) {
                 if (l.isEmpty()) {
                     // Don't return null here, because then our defaultReturn will kick in
-                    return LiteralExpression.newConstant(true, true);
+                    return LiteralExpression.newConstant(true, Determinism.ALWAYS);
                 }
                 if (l.size() == 1) {
                     return l.get(0);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/b1be0f8b/phoenix-core/src/main/java/org/apache/phoenix/execute/HashJoinPlan.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/execute/HashJoinPlan.java b/phoenix-core/src/main/java/org/apache/phoenix/execute/HashJoinPlan.java
index 7ee242e..6e552d8 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/execute/HashJoinPlan.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/execute/HashJoinPlan.java
@@ -49,6 +49,7 @@ import org.apache.phoenix.exception.SQLExceptionCode;
 import org.apache.phoenix.exception.SQLExceptionInfo;
 import org.apache.phoenix.expression.AndExpression;
 import org.apache.phoenix.expression.ComparisonExpression;
+import org.apache.phoenix.expression.Determinism;
 import org.apache.phoenix.expression.Expression;
 import org.apache.phoenix.expression.InListExpression;
 import org.apache.phoenix.expression.LiteralExpression;
@@ -217,7 +218,7 @@ public class HashJoinPlan implements QueryPlan {
             Expression rhsExpression, List<ImmutableBytesWritable> rhsValues, 
             ImmutableBytesWritable ptr, boolean hasFilters) throws SQLException {
         if (rhsValues.isEmpty())
-            return LiteralExpression.newConstant(null, PDataType.BOOLEAN, true);
+            return LiteralExpression.newConstant(null, PDataType.BOOLEAN, Determinism.ALWAYS);
         
         PDataType type = rhsExpression.getDataType();
         if (!useInClause(hasFilters)) {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/b1be0f8b/phoenix-core/src/main/java/org/apache/phoenix/expression/AndExpression.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/AndExpression.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/AndExpression.java
index 2806394..e9c2740 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/expression/AndExpression.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/AndExpression.java
@@ -37,7 +37,7 @@ public class AndExpression extends AndOrExpression {
     private static final String AND = "AND";
     
     public static Expression create(List<Expression> children) throws SQLException {
-        boolean isDeterministic = true;
+    	Determinism determinism = Determinism.ALWAYS;
         Iterator<Expression> iterator = children.iterator();
         while (iterator.hasNext()) {
             Expression child = iterator.next();
@@ -50,10 +50,10 @@ public class AndExpression extends AndOrExpression {
             if (LiteralExpression.isTrue(child)) {
                 iterator.remove();
             }
-            isDeterministic &= child.isDeterministic();
+			determinism.combine(child.getDeterminism());
         }
         if (children.size() == 0) {
-            return LiteralExpression.newConstant(true, isDeterministic);
+            return LiteralExpression.newConstant(true, determinism);
         }
         if (children.size() == 1) {
             return children.get(0);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/b1be0f8b/phoenix-core/src/main/java/org/apache/phoenix/expression/BaseCompoundExpression.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/BaseCompoundExpression.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/BaseCompoundExpression.java
index 03df653..bd6161c 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/expression/BaseCompoundExpression.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/BaseCompoundExpression.java
@@ -34,7 +34,7 @@ public abstract class BaseCompoundExpression extends BaseExpression {
     protected List<Expression> children;
     private boolean isNullable;
     private boolean isStateless;
-    private boolean isDeterministic;
+    private Determinism determinism;
     private boolean requiresFinalEvaluation;
    
     public BaseCompoundExpression() {
@@ -48,18 +48,17 @@ public abstract class BaseCompoundExpression extends BaseExpression {
     private void init(List<Expression> children) {
         this.children = ImmutableList.copyOf(children);
         boolean isStateless = true;
-        boolean isDeterministic = true;
         boolean isNullable = false;
         boolean requiresFinalEvaluation = false;
+        this.determinism = Determinism.ALWAYS;
         for (int i = 0; i < children.size(); i++) {
             Expression child = children.get(i);
             isNullable |= child.isNullable();
             isStateless &= child.isStateless();
-            isDeterministic &= child.isDeterministic();
+            this.determinism = this.determinism.combine(child.getDeterminism());
             requiresFinalEvaluation |= child.requiresFinalEvaluation();
         }
         this.isStateless = isStateless;
-        this.isDeterministic = isDeterministic;
         this.isNullable = isNullable;
         this.requiresFinalEvaluation = requiresFinalEvaluation;
     }
@@ -71,8 +70,8 @@ public abstract class BaseCompoundExpression extends BaseExpression {
     
     
     @Override
-    public boolean isDeterministic() {
-        return isDeterministic;
+    public Determinism getDeterminism() {
+        return determinism;
     }
     
     @Override

http://git-wip-us.apache.org/repos/asf/phoenix/blob/b1be0f8b/phoenix-core/src/main/java/org/apache/phoenix/expression/BaseExpression.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/BaseExpression.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/BaseExpression.java
index fac82a8..8993e37 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/expression/BaseExpression.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/BaseExpression.java
@@ -168,7 +168,7 @@ public abstract class BaseExpression implements Expression {
         } else if (lhs == null) { 
             return rhs;
         } else if (rhs == null) {
-            return LiteralExpression.newConstant(null, lhs.getDataType(), lhs.isDeterministic());
+            return LiteralExpression.newConstant(null, lhs.getDataType(), lhs.getDeterminism());
         } else {
             if (rhs.getDataType() != null && lhs.getDataType() != null && !rhs.getDataType().isCastableTo(lhs.getDataType())) {
                 throw TypeMismatchException.newException(lhs.getDataType(), rhs.getDataType());
@@ -239,8 +239,8 @@ public abstract class BaseExpression implements Expression {
     }
     
     @Override
-    public boolean isDeterministic() {
-        return true;
+    public Determinism getDeterminism() {
+        return Determinism.ALWAYS;
     }
     
     @Override

http://git-wip-us.apache.org/repos/asf/phoenix/blob/b1be0f8b/phoenix-core/src/main/java/org/apache/phoenix/expression/ComparisonExpression.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/ComparisonExpression.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/ComparisonExpression.java
index 8e352e6..a1ed221 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/expression/ComparisonExpression.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/ComparisonExpression.java
@@ -133,7 +133,7 @@ public class ComparisonExpression extends BaseCompoundExpression {
         } else if(lhsExprDataType != null && rhsExprDataType != null && !lhsExprDataType.isComparableTo(rhsExprDataType)) {
             throw TypeMismatchException.newException(lhsExprDataType, rhsExprDataType, toString(op, children));
         }
-        boolean isDeterministic = lhsExpr.isDeterministic() || rhsExpr.isDeterministic();
+        Determinism determinism =  lhsExpr.getDeterminism().combine(rhsExpr.getDeterminism());
         
         Object lhsValue = null;
         // Can't use lhsNode.isConstant(), because we have cases in which we don't know
@@ -142,7 +142,7 @@ public class ComparisonExpression extends BaseCompoundExpression {
         if (lhsExpr instanceof LiteralExpression) {
             lhsValue = ((LiteralExpression)lhsExpr).getValue();
             if (lhsValue == null) {
-                return LiteralExpression.newConstant(false, PDataType.BOOLEAN, lhsExpr.isDeterministic());
+                return LiteralExpression.newConstant(false, PDataType.BOOLEAN, lhsExpr.getDeterminism());
             }
         }
         Object rhsValue = null;
@@ -150,11 +150,11 @@ public class ComparisonExpression extends BaseCompoundExpression {
         if (rhsExpr instanceof LiteralExpression) {
             rhsValue = ((LiteralExpression)rhsExpr).getValue();
             if (rhsValue == null) {
-                return LiteralExpression.newConstant(false, PDataType.BOOLEAN, rhsExpr.isDeterministic());
+                return LiteralExpression.newConstant(false, PDataType.BOOLEAN, rhsExpr.getDeterminism());
             }
         }
         if (lhsValue != null && rhsValue != null) {
-            return LiteralExpression.newConstant(ByteUtil.compare(op,lhsExprDataType.compareTo(lhsValue, rhsValue, rhsExprDataType)), isDeterministic);
+            return LiteralExpression.newConstant(ByteUtil.compare(op,lhsExprDataType.compareTo(lhsValue, rhsValue, rhsExprDataType)), determinism);
         }
         // Coerce constant to match type of lhs so that we don't need to
         // convert at filter time. Since we normalize the select statement
@@ -168,11 +168,11 @@ public class ComparisonExpression extends BaseCompoundExpression {
                 // TODO: if lengths are unequal and fixed width?
                 if (rhsExprDataType.isCoercibleTo(lhsExprDataType, rhsValue)) { // will convert 2.0 -> 2
                     children = Arrays.asList(children.get(0), LiteralExpression.newConstant(rhsValue, lhsExprDataType, 
-                            lhsExpr.getMaxLength(), null, lhsExpr.getSortOrder(), isDeterministic));
+                            lhsExpr.getMaxLength(), null, lhsExpr.getSortOrder(), determinism));
                 } else if (op == CompareOp.EQUAL) {
-                    return LiteralExpression.newConstant(false, PDataType.BOOLEAN, true);
+                    return LiteralExpression.newConstant(false, PDataType.BOOLEAN, Determinism.ALWAYS);
                 } else if (op == CompareOp.NOT_EQUAL) {
-                    return LiteralExpression.newConstant(true, PDataType.BOOLEAN, true);
+                    return LiteralExpression.newConstant(true, PDataType.BOOLEAN, Determinism.ALWAYS);
                 } else { // TODO: generalize this with PDataType.getMinValue(), PDataTypeType.getMaxValue() methods
                     switch(rhsExprDataType) {
                     case DECIMAL:
@@ -190,7 +190,7 @@ public class ComparisonExpression extends BaseCompoundExpression {
                         default: // Else, we truncate the value
                             BigDecimal bd = (BigDecimal)rhsValue;
                             rhsValue = bd.longValue() + increment;
-                            children = Arrays.asList(lhsExpr, LiteralExpression.newConstant(rhsValue, lhsExprDataType, lhsExpr.getSortOrder(), rhsExpr.isDeterministic()));
+                            children = Arrays.asList(lhsExpr, LiteralExpression.newConstant(rhsValue, lhsExprDataType, lhsExpr.getSortOrder(), rhsExpr.getDeterminism()));
                             break;
                         }
                         break;
@@ -212,16 +212,16 @@ public class ComparisonExpression extends BaseCompoundExpression {
                             case LESS:
                             case LESS_OR_EQUAL:
                                 if ((Long)rhsValue > 0) {
-                                    return LiteralExpression.newConstant(true, PDataType.BOOLEAN, isDeterministic);
+                                    return LiteralExpression.newConstant(true, PDataType.BOOLEAN, determinism);
                                 } else {
-                                    return LiteralExpression.newConstant(false, PDataType.BOOLEAN, isDeterministic);
+                                    return LiteralExpression.newConstant(false, PDataType.BOOLEAN, determinism);
                                 }
                             case GREATER:
                             case GREATER_OR_EQUAL:
                                 if ((Long)rhsValue > 0) {
-                                    return LiteralExpression.newConstant(false, PDataType.BOOLEAN, isDeterministic);
+                                    return LiteralExpression.newConstant(false, PDataType.BOOLEAN, determinism);
                                 } else {
-                                    return LiteralExpression.newConstant(true, PDataType.BOOLEAN, isDeterministic);
+                                    return LiteralExpression.newConstant(true, PDataType.BOOLEAN, determinism);
                                 }
                             default:
                                 break;
@@ -230,15 +230,15 @@ public class ComparisonExpression extends BaseCompoundExpression {
                             switch (op) {
                             case LESS:
                             case LESS_OR_EQUAL:
-                                return LiteralExpression.newConstant(false, PDataType.BOOLEAN, isDeterministic);
+                                return LiteralExpression.newConstant(false, PDataType.BOOLEAN, determinism);
                             case GREATER:
                             case GREATER_OR_EQUAL:
-                                return LiteralExpression.newConstant(true, PDataType.BOOLEAN, isDeterministic);
+                                return LiteralExpression.newConstant(true, PDataType.BOOLEAN, determinism);
                             default:
                                 break;
                             }
                         }
-                        children = Arrays.asList(lhsExpr, LiteralExpression.newConstant(rhsValue, rhsExprDataType, lhsExpr.getSortOrder(), isDeterministic));
+                        children = Arrays.asList(lhsExpr, LiteralExpression.newConstant(rhsValue, rhsExprDataType, lhsExpr.getSortOrder(), determinism));
                         break;
                     }
                 }
@@ -249,9 +249,9 @@ public class ComparisonExpression extends BaseCompoundExpression {
             if (children.get(1).getMaxLength() != null && lhsExpr.getMaxLength() != null && lhsExpr.getMaxLength() < children.get(1).getMaxLength()) {
                 switch (op) {
                 case EQUAL:
-                    return LiteralExpression.newConstant(false, PDataType.BOOLEAN, isDeterministic);
+                    return LiteralExpression.newConstant(false, PDataType.BOOLEAN, determinism);
                 case NOT_EQUAL:
-                    return LiteralExpression.newConstant(true, PDataType.BOOLEAN, isDeterministic);
+                    return LiteralExpression.newConstant(true, PDataType.BOOLEAN, determinism);
                 default:
                     break;
                 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/b1be0f8b/phoenix-core/src/main/java/org/apache/phoenix/expression/CurrentDateTimeFunction.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/CurrentDateTimeFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/CurrentDateTimeFunction.java
index 63e6d23..0b5faa2 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/expression/CurrentDateTimeFunction.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/CurrentDateTimeFunction.java
@@ -36,7 +36,7 @@ public abstract class CurrentDateTimeFunction extends ScalarFunction {
     }
 
     @Override
-    public boolean isDeterministic() {
-        return false;
+    public Determinism getDeterminism() {
+        return Determinism.PER_STATEMENT;
     }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/b1be0f8b/phoenix-core/src/main/java/org/apache/phoenix/expression/Determinism.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/Determinism.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/Determinism.java
new file mode 100644
index 0000000..b2f3524
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/Determinism.java
@@ -0,0 +1,19 @@
+/*
+ * 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;
+
+public enum Determinism {
+	
+	ALWAYS, PER_STATEMENT, PER_ROW;
+	
+	public Determinism combine (Determinism that) {
+		return Determinism.values()[Math.max(this.ordinal(), that.ordinal())];
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/phoenix/blob/b1be0f8b/phoenix-core/src/main/java/org/apache/phoenix/expression/Expression.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/Expression.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/Expression.java
index ccd4d7b..aeea0c8 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/expression/Expression.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/Expression.java
@@ -34,6 +34,7 @@ import org.apache.phoenix.schema.tuple.Tuple;
  * @since 0.1
  */
 public interface Expression extends PDatum, Writable {
+	
     /**
      * Access the value by setting a pointer to it (as opposed to making
      * a copy of it which can be expensive)
@@ -75,10 +76,9 @@ public interface Expression extends PDatum, Writable {
     boolean isStateless();
     
     /**
-     * @return true if the expression returns the same output every
-     * time given the same input.
+     * @return Determinism enum 
      */
-    boolean isDeterministic();
+    Determinism getDeterminism();
     
     /**
      * Determines if an evaluate is required after partial evaluation

http://git-wip-us.apache.org/repos/asf/phoenix/blob/b1be0f8b/phoenix-core/src/main/java/org/apache/phoenix/expression/InListExpression.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/InListExpression.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/InListExpression.java
index c171153..fdb20ff 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/expression/InListExpression.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/InListExpression.java
@@ -60,7 +60,7 @@ public class InListExpression extends BaseSingleExpression {
         Expression firstChild = children.get(0);
         
         if (firstChild.isStateless() && (!firstChild.evaluate(null, ptr) || ptr.getLength() == 0)) {
-            return LiteralExpression.newConstant(null, PDataType.BOOLEAN, firstChild.isDeterministic());
+            return LiteralExpression.newConstant(null, PDataType.BOOLEAN, firstChild.getDeterminism());
         }
         if (children.size() == 2) {
             return ComparisonExpression.create(isNegate ? CompareOp.NOT_EQUAL : CompareOp.EQUAL, children, ptr);
@@ -86,7 +86,7 @@ public class InListExpression extends BaseSingleExpression {
             throw sqlE;
         }
         if (coercedKeyExpressions.size() == 2 && addedNull) {
-            return LiteralExpression.newConstant(null, PDataType.BOOLEAN, true);
+            return LiteralExpression.newConstant(null, PDataType.BOOLEAN, Determinism.ALWAYS);
         }
         Expression expression = new InListExpression(coercedKeyExpressions);
         if (isNegate) { 

http://git-wip-us.apache.org/repos/asf/phoenix/blob/b1be0f8b/phoenix-core/src/main/java/org/apache/phoenix/expression/IsNullExpression.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/IsNullExpression.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/IsNullExpression.java
index 4162dfc..3fb7a62 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/expression/IsNullExpression.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/IsNullExpression.java
@@ -42,11 +42,11 @@ public class IsNullExpression extends BaseSingleExpression {
 
     public static Expression create(Expression child, boolean negate, ImmutableBytesWritable ptr) throws SQLException {
         if (!child.isNullable()) {
-            return LiteralExpression.newConstant(negate, PDataType.BOOLEAN, child.isDeterministic());
+            return LiteralExpression.newConstant(negate, PDataType.BOOLEAN, child.getDeterminism());
         }
         if (ExpressionUtil.isConstant(child)) {
             boolean evaluated = child.evaluate(null, ptr);
-            return LiteralExpression.newConstant(negate ^ (!evaluated || ptr.getLength() == 0), PDataType.BOOLEAN, child.isDeterministic());
+            return LiteralExpression.newConstant(negate ^ (!evaluated || ptr.getLength() == 0), PDataType.BOOLEAN, child.getDeterminism());
         }
         return new IsNullExpression(child, negate);
     }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/b1be0f8b/phoenix-core/src/main/java/org/apache/phoenix/expression/LiteralExpression.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/LiteralExpression.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/LiteralExpression.java
index 91a3125..a84135c 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/expression/LiteralExpression.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/LiteralExpression.java
@@ -48,25 +48,25 @@ import com.google.common.base.Preconditions;
  * @since 0.1
  */
 public class LiteralExpression extends BaseTerminalExpression {
-    public static final LiteralExpression NULL_EXPRESSION = new LiteralExpression(null, true);
-    private static final LiteralExpression ND_NULL_EXPRESSION = new LiteralExpression(null, false);
+    public static final LiteralExpression NULL_EXPRESSION = new LiteralExpression(null, Determinism.ALWAYS);
+    private static final LiteralExpression ND_NULL_EXPRESSION = new LiteralExpression(null, Determinism.PER_ROW);
     private static final LiteralExpression[] TYPED_NULL_EXPRESSIONS = new LiteralExpression[PDataType.values().length * 2];
     static {
         for (int i = 0; i < PDataType.values().length; i++) {
-            TYPED_NULL_EXPRESSIONS[i] = new LiteralExpression(PDataType.values()[i], true);
+            TYPED_NULL_EXPRESSIONS[i] = new LiteralExpression(PDataType.values()[i], Determinism.ALWAYS);
         }
         for (int i = 0; i < PDataType.values().length; i++) {
-            TYPED_NULL_EXPRESSIONS[i+PDataType.values().length] = new LiteralExpression(PDataType.values()[i], false);
+            TYPED_NULL_EXPRESSIONS[i+PDataType.values().length] = new LiteralExpression(PDataType.values()[i], Determinism.PER_ROW);
         }
     }
-    private static final LiteralExpression FALSE_EXPRESSION = new LiteralExpression(Boolean.FALSE, PDataType.BOOLEAN, PDataType.BOOLEAN.toBytes(Boolean.FALSE), true);
-    private static final LiteralExpression TRUE_EXPRESSION = new LiteralExpression(Boolean.TRUE, PDataType.BOOLEAN, PDataType.BOOLEAN.toBytes(Boolean.TRUE), true);
-    private static final LiteralExpression ND_FALSE_EXPRESSION = new LiteralExpression(Boolean.FALSE, PDataType.BOOLEAN, PDataType.BOOLEAN.toBytes(Boolean.FALSE), false);
-    private static final LiteralExpression ND_TRUE_EXPRESSION = new LiteralExpression(Boolean.TRUE, PDataType.BOOLEAN, PDataType.BOOLEAN.toBytes(Boolean.TRUE), false);
+    private static final LiteralExpression FALSE_EXPRESSION = new LiteralExpression(Boolean.FALSE, PDataType.BOOLEAN, PDataType.BOOLEAN.toBytes(Boolean.FALSE), Determinism.ALWAYS);
+    private static final LiteralExpression TRUE_EXPRESSION = new LiteralExpression(Boolean.TRUE, PDataType.BOOLEAN, PDataType.BOOLEAN.toBytes(Boolean.TRUE), Determinism.ALWAYS);
+    private static final LiteralExpression ND_FALSE_EXPRESSION = new LiteralExpression(Boolean.FALSE, PDataType.BOOLEAN, PDataType.BOOLEAN.toBytes(Boolean.FALSE), Determinism.PER_ROW);
+    private static final LiteralExpression ND_TRUE_EXPRESSION = new LiteralExpression(Boolean.TRUE, PDataType.BOOLEAN, PDataType.BOOLEAN.toBytes(Boolean.TRUE), Determinism.PER_ROW);
 
     private Object value;
     private PDataType type;
-    private boolean isDeterministic;
+    private Determinism determinism;
     private byte[] byteValue;
     private Integer maxLength;
     private Integer scale;
@@ -82,24 +82,24 @@ public class LiteralExpression extends BaseTerminalExpression {
     }
     
     public static LiteralExpression newConstant(Object value) {
-        return newConstant(value, true);
+        return newConstant(value, Determinism.ALWAYS);
     }
     
     // TODO: cache?
-    public static LiteralExpression newConstant(Object value, boolean isDeterministic) {
+    public static LiteralExpression newConstant(Object value, Determinism determinism) {
         if (Boolean.FALSE.equals(value)) {
-            return isDeterministic ? FALSE_EXPRESSION : ND_FALSE_EXPRESSION;
+            return determinism == Determinism.ALWAYS ? FALSE_EXPRESSION : ND_FALSE_EXPRESSION;
         }
         if (Boolean.TRUE.equals(value)) {
-            return isDeterministic ? TRUE_EXPRESSION : ND_TRUE_EXPRESSION;
+            return determinism == Determinism.ALWAYS ? TRUE_EXPRESSION : ND_TRUE_EXPRESSION;
         }
         if (value == null) {
-            return isDeterministic ? NULL_EXPRESSION : ND_NULL_EXPRESSION;
+            return determinism == Determinism.ALWAYS ? NULL_EXPRESSION : ND_NULL_EXPRESSION;
         }
         PDataType type = PDataType.fromLiteral(value);
         byte[] b = type.toBytes(value);
         if (type.isNull(b)) {
-            return TYPED_NULL_EXPRESSIONS[type.ordinal() + ( isDeterministic ? 0 : TYPED_NULL_EXPRESSIONS.length/2)];
+            return TYPED_NULL_EXPRESSIONS[type.ordinal() + ( determinism == Determinism.ALWAYS ? 0 : TYPED_NULL_EXPRESSIONS.length/2)];
         }
         if (type == PDataType.VARCHAR) {
             String s = (String) value;
@@ -107,35 +107,35 @@ public class LiteralExpression extends BaseTerminalExpression {
                 type = PDataType.CHAR;
             }
         }
-        return new LiteralExpression(value, type, b, isDeterministic);
+        return new LiteralExpression(value, type, b, determinism);
     }
 
     public static LiteralExpression newConstant(Object value, PDataType type) throws SQLException {
-        return newConstant(value, type, true);
+        return newConstant(value, type, Determinism.ALWAYS);
     }
     
-    public static LiteralExpression newConstant(Object value, PDataType type, boolean isDeterministic) throws SQLException {
-        return newConstant(value, type, SortOrder.getDefault(), isDeterministic);
+    public static LiteralExpression newConstant(Object value, PDataType type, Determinism determinism) throws SQLException {
+        return newConstant(value, type, SortOrder.getDefault(), determinism);
     }
     
     public static LiteralExpression newConstant(Object value, PDataType type, SortOrder sortOrder) throws SQLException {
-        return newConstant(value, type, null, null, sortOrder, true);
+        return newConstant(value, type, null, null, sortOrder, Determinism.ALWAYS);
     }
     
-    public static LiteralExpression newConstant(Object value, PDataType type, SortOrder sortOrder, boolean isDeterministic) throws SQLException {
-        return newConstant(value, type, null, null, sortOrder, isDeterministic);
+    public static LiteralExpression newConstant(Object value, PDataType type, SortOrder sortOrder, Determinism determinism) throws SQLException {
+        return newConstant(value, type, null, null, sortOrder, determinism);
     }
     
     public static LiteralExpression newConstant(Object value, PDataType type, Integer maxLength, Integer scale) throws SQLException {
-        return newConstant(value, type, maxLength, scale, SortOrder.getDefault(), true);
+        return newConstant(value, type, maxLength, scale, SortOrder.getDefault(), Determinism.ALWAYS);
     }
     
-    public static LiteralExpression newConstant(Object value, PDataType type, Integer maxLength, Integer scale, boolean isDeterministic) throws SQLException { // remove?
-        return newConstant(value, type, maxLength, scale, SortOrder.getDefault(), isDeterministic);
+    public static LiteralExpression newConstant(Object value, PDataType type, Integer maxLength, Integer scale, Determinism determinism) throws SQLException { // remove?
+        return newConstant(value, type, maxLength, scale, SortOrder.getDefault(), determinism);
     }
 
     // TODO: cache?
-    public static LiteralExpression newConstant(Object value, PDataType type, Integer maxLength, Integer scale, SortOrder sortOrder, boolean isDeterministic)
+    public static LiteralExpression newConstant(Object value, PDataType type, Integer maxLength, Integer scale, SortOrder sortOrder, Determinism determinism)
             throws SQLException {
         if (value == null) {
             if (type == null) {
@@ -144,10 +144,10 @@ public class LiteralExpression extends BaseTerminalExpression {
             return TYPED_NULL_EXPRESSIONS[type.ordinal()];
         }
         if (Boolean.FALSE.equals(value)) {
-            return isDeterministic ? FALSE_EXPRESSION : ND_FALSE_EXPRESSION;
+            return determinism == Determinism.ALWAYS ? FALSE_EXPRESSION : ND_FALSE_EXPRESSION;
         }
         if (Boolean.TRUE.equals(value)) {
-            return isDeterministic ? TRUE_EXPRESSION : ND_TRUE_EXPRESSION;
+            return determinism == Determinism.ALWAYS ? TRUE_EXPRESSION : ND_TRUE_EXPRESSION;
         }
         PDataType actualType = PDataType.fromLiteral(value);
         // For array we should check individual element in it?
@@ -173,7 +173,7 @@ public class LiteralExpression extends BaseTerminalExpression {
             if (maxLength == null) {
                 maxLength = type == null || !type.isFixedWidth() ? null : type.getMaxLength(value);
             }
-            return new LiteralExpression(value, type, b, maxLength, scale, sortOrder, isDeterministic);
+            return new LiteralExpression(value, type, b, maxLength, scale, sortOrder, determinism);
         } catch (IllegalDataException e) {
             throw new SQLExceptionInfo.Builder(SQLExceptionCode.ILLEGAL_DATA).setRootCause(e).build().buildException();
         }
@@ -182,16 +182,16 @@ public class LiteralExpression extends BaseTerminalExpression {
     public LiteralExpression() {
     }
 
-    private LiteralExpression(PDataType type, boolean isDeterministic) {
-        this(null, type, ByteUtil.EMPTY_BYTE_ARRAY, isDeterministic);
+    private LiteralExpression(PDataType type, Determinism determinism) {
+        this(null, type, ByteUtil.EMPTY_BYTE_ARRAY, determinism);
     }
 
-    private LiteralExpression(Object value, PDataType type, byte[] byteValue, boolean isDeterministic) {
-        this(value, type, byteValue, type == null || !type.isFixedWidth() ? null : type.getMaxLength(value), null, SortOrder.getDefault(), isDeterministic);
+    private LiteralExpression(Object value, PDataType type, byte[] byteValue, Determinism determinism) {
+        this(value, type, byteValue, type == null || !type.isFixedWidth() ? null : type.getMaxLength(value), null, SortOrder.getDefault(), determinism);
     }
 
     private LiteralExpression(Object value, PDataType type, byte[] byteValue,
-            Integer maxLength, Integer scale, SortOrder sortOrder, boolean isDeterministic) {
+            Integer maxLength, Integer scale, SortOrder sortOrder, Determinism deterministic) {
     	Preconditions.checkNotNull(sortOrder);
         this.value = value;
         this.type = type;
@@ -199,12 +199,12 @@ public class LiteralExpression extends BaseTerminalExpression {
         this.maxLength = maxLength;
         this.scale = scale != null ? scale : type == null ? null : type.getScale(value);
         this.sortOrder = sortOrder;
-        this.isDeterministic = isDeterministic;
+        this.determinism = deterministic;
     }
 
     @Override
-    public boolean isDeterministic() {
-        return isDeterministic;
+    public Determinism getDeterminism() {
+        return determinism;
     }
     
     @Override
@@ -234,12 +234,22 @@ public class LiteralExpression extends BaseTerminalExpression {
 
     @Override
     public void readFields(DataInput input) throws IOException {
-        int encodedByteLengthAndBool = WritableUtils.readVInt(input);
-        this.isDeterministic = encodedByteLengthAndBool > 0;
+    	int encodedByteLengthAndBool = WritableUtils.readVInt(input);
         int byteLength = Math.abs(encodedByteLengthAndBool)-1;
         this.byteValue = new byte[byteLength];
         input.readFully(byteValue, 0, byteLength);
-        sortOrder = SortOrder.fromSystemValue(WritableUtils.readVInt(input));
+        int sortOrderAndDeterminism = WritableUtils.readVInt(input);
+        if (sortOrderAndDeterminism<=2) {
+        	//client is on an older version
+        	this.determinism = encodedByteLengthAndBool > 0 ? Determinism.ALWAYS : Determinism.PER_ROW;  	
+        	this.sortOrder = SortOrder.fromSystemValue(sortOrderAndDeterminism);;
+        }
+        else {
+        	int determinismOrdinal = (sortOrderAndDeterminism>>2)-1;
+        	this.determinism = Determinism.values()[determinismOrdinal];
+        	int sortOrderValue = sortOrderAndDeterminism & ((1 << 2) - 1); //get the least 2 significant bits
+        	this.sortOrder = SortOrder.fromSystemValue(sortOrderValue);
+        } 
         int typeOrdinal = WritableUtils.readVInt(input);
         if (typeOrdinal < 0) {
             this.type = null;
@@ -255,10 +265,13 @@ public class LiteralExpression extends BaseTerminalExpression {
 
     @Override
     public void write(DataOutput output) throws IOException {
-        WritableUtils.writeVInt(output, (byteValue.length + 1) * (this.isDeterministic ? 1 : -1));
+    	WritableUtils.writeVInt(output, (byteValue.length + 1) * (this.determinism==Determinism.ALWAYS ? 1 : -1));
         output.write(byteValue);
-        WritableUtils.writeVInt(output, sortOrder.getSystemValue());
-        WritableUtils.writeVInt(output, type == null ? -1 : this.type.ordinal());
+        // since we need to support clients of a lower version, serialize the determinism enum ordinal in the int used to 
+        // serialize sort order system value (which is either 1 or 2)
+        int sortOrderAndDeterminism = ((this.determinism.ordinal()+1)<<2) + sortOrder.getSystemValue();
+        WritableUtils.writeVInt(output, sortOrderAndDeterminism);
+        WritableUtils.writeVInt(output, this.type == null ? -1 : this.type.ordinal());
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/phoenix/blob/b1be0f8b/phoenix-core/src/main/java/org/apache/phoenix/expression/NotExpression.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/NotExpression.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/NotExpression.java
index e25f1e2..0c17ab8 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/expression/NotExpression.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/NotExpression.java
@@ -43,9 +43,9 @@ public class NotExpression extends BaseSingleExpression {
         }
         if (child.isStateless()) {
             if (!child.evaluate(null, ptr) || ptr.getLength() == 0) {
-                return LiteralExpression.newConstant(null, PDataType.BOOLEAN, child.isDeterministic());
+                return LiteralExpression.newConstant(null, PDataType.BOOLEAN, child.getDeterminism());
             }
-            return LiteralExpression.newConstant(!(Boolean)PDataType.BOOLEAN.toObject(ptr), PDataType.BOOLEAN, child.isDeterministic());
+            return LiteralExpression.newConstant(!(Boolean)PDataType.BOOLEAN.toObject(ptr), PDataType.BOOLEAN, child.getDeterminism());
         }
         return new NotExpression(child);
     }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/b1be0f8b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/AggregateFunction.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/AggregateFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/AggregateFunction.java
index 257890c..32cae19 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/AggregateFunction.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/AggregateFunction.java
@@ -19,6 +19,7 @@ package org.apache.phoenix.expression.function;
 
 import java.util.List;
 
+import org.apache.phoenix.expression.Determinism;
 import org.apache.phoenix.expression.Expression;
 
 
@@ -46,7 +47,7 @@ abstract public class AggregateFunction extends FunctionExpression {
     }
 
     @Override
-    public boolean isDeterministic() {
-        return false;
+    public Determinism getDeterminism() {
+        return Determinism.PER_ROW;
     }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/b1be0f8b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/CeilDecimalExpression.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/CeilDecimalExpression.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/CeilDecimalExpression.java
index d6b4e45..bd36d0f 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/CeilDecimalExpression.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/CeilDecimalExpression.java
@@ -21,13 +21,17 @@ import java.math.RoundingMode;
 import java.sql.SQLException;
 import java.util.List;
 
+import org.apache.phoenix.expression.Determinism;
 import org.apache.phoenix.expression.Expression;
 import org.apache.phoenix.expression.LiteralExpression;
 import org.apache.phoenix.schema.PDataType;
 
 import com.google.common.collect.Lists;
+
 import java.math.BigDecimal;
+
 import org.apache.phoenix.query.KeyRange;
+
 import static org.apache.phoenix.schema.PDataType.DECIMAL;
 
 /**
@@ -53,7 +57,7 @@ public class CeilDecimalExpression extends RoundDecimalExpression {
        if (expr.getDataType().isCoercibleTo(PDataType.LONG)) {
             return expr;
         }
-        Expression scaleExpr = LiteralExpression.newConstant(scale, PDataType.INTEGER, true);
+        Expression scaleExpr = LiteralExpression.newConstant(scale, PDataType.INTEGER, Determinism.ALWAYS);
         List<Expression> expressions = Lists.newArrayList(expr, scaleExpr);
         return new CeilDecimalExpression(expressions);
     }
@@ -64,7 +68,7 @@ public class CeilDecimalExpression extends RoundDecimalExpression {
             return expr;
         }
        if (exprs.size() == 1) {
-            Expression scaleExpr = LiteralExpression.newConstant(0, PDataType.INTEGER, true);
+            Expression scaleExpr = LiteralExpression.newConstant(0, PDataType.INTEGER, Determinism.ALWAYS);
             exprs = Lists.newArrayList(expr, scaleExpr);
         }
         return new CeilDecimalExpression(exprs);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/b1be0f8b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/CoalesceFunction.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/CoalesceFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/CoalesceFunction.java
index 87fc908..2eef8b3 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/CoalesceFunction.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/CoalesceFunction.java
@@ -30,6 +30,7 @@ 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.ExpressionUtil;
 
 
 /**
@@ -59,7 +60,7 @@ public class CoalesceFunction extends ScalarFunction {
         Expression firstChild = children.get(0);
         Expression secondChild = children.get(1);
 
-        if (secondChild.isStateless() && secondChild.isDeterministic()) { // is literal
+        if (ExpressionUtil.isConstant(secondChild)) { // is literal
 
             ImmutableBytesWritable ptr = new ImmutableBytesPtr();
             secondChild.evaluate(null, ptr);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/b1be0f8b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/CountAggregateFunction.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/CountAggregateFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/CountAggregateFunction.java
index b7a5861..f1e8ab5 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/CountAggregateFunction.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/CountAggregateFunction.java
@@ -22,7 +22,7 @@ import java.util.List;
 
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
-
+import org.apache.phoenix.expression.Determinism;
 import org.apache.phoenix.expression.Expression;
 import org.apache.phoenix.expression.LiteralExpression;
 import org.apache.phoenix.expression.aggregator.Aggregator;
@@ -45,7 +45,7 @@ import org.apache.phoenix.util.SchemaUtil;
 @BuiltInFunction(name=CountAggregateFunction.NAME, args= {@Argument()} )
 public class CountAggregateFunction extends SingleAggregateFunction {
     public static final String NAME = "COUNT";
-    public static final List<Expression> STAR = Arrays.<Expression>asList(LiteralExpression.newConstant(1, true));
+    public static final List<Expression> STAR = Arrays.<Expression>asList(LiteralExpression.newConstant(1, Determinism.ALWAYS));
     public static final String NORMALIZED_NAME = SchemaUtil.normalizeIdentifier(NAME);
     
     public CountAggregateFunction() {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/b1be0f8b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/FloorDecimalExpression.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/FloorDecimalExpression.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/FloorDecimalExpression.java
index b5566ca..037fcc5 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/FloorDecimalExpression.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/FloorDecimalExpression.java
@@ -21,13 +21,17 @@ import java.math.RoundingMode;
 import java.sql.SQLException;
 import java.util.List;
 
+import org.apache.phoenix.expression.Determinism;
 import org.apache.phoenix.expression.Expression;
 import org.apache.phoenix.expression.LiteralExpression;
 import org.apache.phoenix.schema.PDataType;
 
 import com.google.common.collect.Lists;
+
 import java.math.BigDecimal;
+
 import org.apache.phoenix.query.KeyRange;
+
 import static org.apache.phoenix.schema.PDataType.DECIMAL;
 
 /**
@@ -54,7 +58,7 @@ public class FloorDecimalExpression extends RoundDecimalExpression {
         if (expr.getDataType().isCoercibleTo(PDataType.LONG)) {
             return expr;
         }
-        Expression scaleExpr = LiteralExpression.newConstant(scale, PDataType.INTEGER, true);
+        Expression scaleExpr = LiteralExpression.newConstant(scale, PDataType.INTEGER, Determinism.ALWAYS);
         List<Expression> expressions = Lists.newArrayList(expr, scaleExpr);
         return new FloorDecimalExpression(expressions);
     }
@@ -65,7 +69,7 @@ public class FloorDecimalExpression extends RoundDecimalExpression {
             return expr;
         }
         if (exprs.size() == 1) {
-            Expression scaleExpr = LiteralExpression.newConstant(0, PDataType.INTEGER, true);
+            Expression scaleExpr = LiteralExpression.newConstant(0, PDataType.INTEGER, Determinism.ALWAYS);
             exprs = Lists.newArrayList(expr, scaleExpr);
         }
         return new FloorDecimalExpression(exprs);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/b1be0f8b/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
index f928218..58ff652 100644
--- 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
@@ -29,6 +29,7 @@ import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp;
 import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
 import org.apache.hadoop.io.WritableUtils;
 import org.apache.phoenix.compile.KeyPart;
+import org.apache.phoenix.expression.Determinism;
 import org.apache.phoenix.expression.Expression;
 import org.apache.phoenix.expression.LiteralExpression;
 import org.apache.phoenix.query.KeyRange;
@@ -92,11 +93,11 @@ public class RoundDateExpression extends ScalarFunction {
     }
     
     static Expression getTimeUnitExpr(TimeUnit timeUnit) throws SQLException {
-        return LiteralExpression.newConstant(timeUnit.name(), PDataType.VARCHAR, true);
+        return LiteralExpression.newConstant(timeUnit.name(), PDataType.VARCHAR, Determinism.ALWAYS);
     }
     
     static Expression getMultiplierExpr(int multiplier) throws SQLException {
-        return LiteralExpression.newConstant(multiplier, PDataType.INTEGER, true);
+        return LiteralExpression.newConstant(multiplier, PDataType.INTEGER, Determinism.ALWAYS);
     }
     
     RoundDateExpression(List<Expression> children) {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/b1be0f8b/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
index 92756a5..e8d96a8 100644
--- 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
@@ -27,6 +27,7 @@ import java.util.List;
 
 import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
 import org.apache.hadoop.io.WritableUtils;
+import org.apache.phoenix.expression.Determinism;
 import org.apache.phoenix.expression.Expression;
 import org.apache.phoenix.expression.LiteralExpression;
 import org.apache.phoenix.schema.IllegalDataException;
@@ -37,7 +38,6 @@ import com.google.common.collect.Lists;
 import java.util.Collections;
 import org.apache.hadoop.hbase.filter.CompareFilter;
 import org.apache.phoenix.compile.KeyPart;
-import static org.apache.phoenix.expression.function.ScalarFunction.evaluateExpression;
 import org.apache.phoenix.query.KeyRange;
 import org.apache.phoenix.schema.PColumn;
 import static org.apache.phoenix.schema.PDataType.DECIMAL;
@@ -62,7 +62,7 @@ public class RoundDecimalExpression extends ScalarFunction {
         if (expr.getDataType().isCoercibleTo(PDataType.LONG)) {
             return expr;
         }
-        Expression scaleExpr = LiteralExpression.newConstant(scale, PDataType.INTEGER, true);
+        Expression scaleExpr = LiteralExpression.newConstant(scale, PDataType.INTEGER, Determinism.ALWAYS);
         List<Expression> expressions = Lists.newArrayList(expr, scaleExpr);
         return new RoundDecimalExpression(expressions);
     }
@@ -81,7 +81,7 @@ public class RoundDecimalExpression extends ScalarFunction {
             return expr;
         }
         if (exprs.size() == 1) {
-            Expression scaleExpr = LiteralExpression.newConstant(0, PDataType.INTEGER, true);
+            Expression scaleExpr = LiteralExpression.newConstant(0, PDataType.INTEGER, Determinism.ALWAYS);
             exprs = Lists.newArrayList(expr, scaleExpr);
         }
         return new RoundDecimalExpression(exprs);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/b1be0f8b/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
index 154bdf8..d33d555 100644
--- 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
@@ -25,7 +25,7 @@ import java.util.List;
 
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
-
+import org.apache.phoenix.expression.Determinism;
 import org.apache.phoenix.expression.Expression;
 import org.apache.phoenix.expression.LiteralExpression;
 import org.apache.phoenix.expression.aggregator.Aggregator;
@@ -43,7 +43,7 @@ import org.apache.phoenix.schema.tuple.Tuple;
  * @since 0.1
  */
 abstract public class SingleAggregateFunction extends AggregateFunction {
-    private static final List<Expression> DEFAULT_EXPRESSION_LIST = Arrays.<Expression>asList(LiteralExpression.newConstant(1, true));
+    private static final List<Expression> DEFAULT_EXPRESSION_LIST = Arrays.<Expression>asList(LiteralExpression.newConstant(1, Determinism.ALWAYS));
     protected boolean isConstant;
     private Aggregator aggregator;
     

http://git-wip-us.apache.org/repos/asf/phoenix/blob/b1be0f8b/phoenix-core/src/main/java/org/apache/phoenix/parse/FunctionParseNode.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/FunctionParseNode.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/FunctionParseNode.java
index 10c7208..ea8c1fb 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/parse/FunctionParseNode.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/FunctionParseNode.java
@@ -33,7 +33,9 @@ import java.util.Set;
 import org.apache.http.annotation.Immutable;
 
 import com.google.common.collect.ImmutableSet;
+
 import org.apache.phoenix.compile.StatementContext;
+import org.apache.phoenix.expression.Determinism;
 import org.apache.phoenix.expression.Expression;
 import org.apache.phoenix.expression.LiteralExpression;
 import org.apache.phoenix.expression.function.AggregateFunction;
@@ -137,7 +139,7 @@ public class FunctionParseNode extends CompoundParseNode {
         if (args.length > children.size()) {
             List<Expression> moreChildren = new ArrayList<Expression>(children);
             for (int i = children.size(); i < info.getArgs().length; i++) {
-                moreChildren.add(LiteralExpression.newConstant(null, args[i].allowedTypes.length == 0 ? null :  args[i].allowedTypes[0], true));
+                moreChildren.add(LiteralExpression.newConstant(null, args[i].allowedTypes.length == 0 ? null :  args[i].allowedTypes[0], Determinism.ALWAYS));
             }
             children = moreChildren;
         }
@@ -174,7 +176,7 @@ public class FunctionParseNode extends CompoundParseNode {
                     // based on the function argument annonation set the parameter meta data.
                     if (child.getDataType() == null) {
                         if (allowedTypes.length > 0) {
-                            context.getBindManager().addParamMetaData(bindNode, LiteralExpression.newConstant(null, allowedTypes[0], true));
+                            context.getBindManager().addParamMetaData(bindNode, LiteralExpression.newConstant(null, allowedTypes[0], Determinism.ALWAYS));
                         }
                     } else { // Use expression as is, since we already have the data type set
                         context.getBindManager().addParamMetaData(bindNode, child);
@@ -376,11 +378,11 @@ public class FunctionParseNode extends CompoundParseNode {
                 SQLParser parser = new SQLParser(strValue);
                 try {
                     LiteralParseNode node = parser.parseLiteral();
-                    LiteralExpression defaultValue = LiteralExpression.newConstant(node.getValue(), this.allowedTypes[0], true);
+                    LiteralExpression defaultValue = LiteralExpression.newConstant(node.getValue(), this.allowedTypes[0], Determinism.ALWAYS);
                     if (this.getAllowedTypes().length > 0) {
                         for (PDataType type : this.getAllowedTypes()) {
                             if (defaultValue.getDataType() == null || defaultValue.getDataType().isCoercibleTo(type, node.getValue())) {
-                                return LiteralExpression.newConstant(node.getValue(), type, true);
+                                return LiteralExpression.newConstant(node.getValue(), type, Determinism.ALWAYS);
                             }
                         }
                         throw new IllegalStateException("Unable to coerce default value " + strValue + " to any of the allowed types of " + Arrays.toString(this.getAllowedTypes()));

http://git-wip-us.apache.org/repos/asf/phoenix/blob/b1be0f8b/phoenix-core/src/main/java/org/apache/phoenix/util/ExpressionUtil.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/util/ExpressionUtil.java b/phoenix-core/src/main/java/org/apache/phoenix/util/ExpressionUtil.java
index 227a385..eac396b 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/util/ExpressionUtil.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/util/ExpressionUtil.java
@@ -13,6 +13,7 @@ import java.sql.SQLException;
 import java.util.List;
 
 import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+import org.apache.phoenix.expression.Determinism;
 import org.apache.phoenix.expression.Expression;
 import org.apache.phoenix.expression.LiteralExpression;
 import org.apache.phoenix.expression.function.CurrentDateFunction;
@@ -24,16 +25,20 @@ import com.google.common.collect.Lists;
 
 public class ExpressionUtil {
 
-    @SuppressWarnings("unchecked")
-    private static final List<Class<? extends FunctionExpression>> OVERRIDE_LITERAL_FUNCTIONS = Lists
-            .<Class<? extends FunctionExpression>> newArrayList(CurrentDateFunction.class, CurrentTimeFunction.class);
+	@SuppressWarnings("unchecked")
+	private static final List<Class<? extends FunctionExpression>> OVERRIDE_LITERAL_FUNCTIONS = Lists
+			.<Class<? extends FunctionExpression>> newArrayList(
+					CurrentDateFunction.class, CurrentTimeFunction.class);
 
-    private ExpressionUtil() {}
+	private ExpressionUtil() {
+	}
 
-    public static boolean isConstant(Expression expression) {
-        return (expression.isStateless() && expression.isDeterministic() || OVERRIDE_LITERAL_FUNCTIONS
-                .contains(expression.getClass()));
-    }
+	public static boolean isConstant(Expression expression) {
+		return (expression.isStateless() && (expression.getDeterminism() == Determinism.ALWAYS
+				|| expression.getDeterminism() == Determinism.PER_STATEMENT 
+				// TODO remove this in 3.4/4.4 (need to support clients on 3.1/4.1)
+				|| OVERRIDE_LITERAL_FUNCTIONS.contains(expression.getClass())));
+	}
 
     public static LiteralExpression getConstantExpression(Expression expression, ImmutableBytesWritable ptr)
             throws SQLException {
@@ -42,16 +47,15 @@ public class ExpressionUtil {
         if (expression.evaluate(null, ptr) && ptr.getLength() != 0) {
             value = type.toObject(ptr);
         }
-        return LiteralExpression.newConstant(value, type, expression.isDeterministic());
+        return LiteralExpression.newConstant(value, type, expression.getDeterminism());
     }
 
     public static boolean isNull(Expression expression, ImmutableBytesWritable ptr) {
-        return expression.isStateless() && expression.isDeterministic()
-                && (!expression.evaluate(null, ptr) || ptr.getLength() == 0);
+        return isConstant(expression) && (!expression.evaluate(null, ptr) || ptr.getLength() == 0);
     }
 
     public static LiteralExpression getNullExpression(Expression expression) throws SQLException {
-        return LiteralExpression.newConstant(null, expression.getDataType(), expression.isDeterministic());
+        return LiteralExpression.newConstant(null, expression.getDataType(), expression.getDeterminism());
     }
 
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/b1be0f8b/phoenix-core/src/test/java/org/apache/phoenix/expression/DeterminismTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/expression/DeterminismTest.java b/phoenix-core/src/test/java/org/apache/phoenix/expression/DeterminismTest.java
new file mode 100644
index 0000000..4e4a648
--- /dev/null
+++ b/phoenix-core/src/test/java/org/apache/phoenix/expression/DeterminismTest.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE
+ * file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
+ * applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+package org.apache.phoenix.expression;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+public class DeterminismTest {
+	@Test
+    public void testCombine() {
+		// combining a determinism enum with ALWAYS should always return the
+		// other determinism
+		assertEquals("Unexpected result ", Determinism.PER_ROW,
+				Determinism.ALWAYS.combine(Determinism.PER_ROW));
+		assertEquals("Unexpected result ", Determinism.PER_STATEMENT,
+				Determinism.ALWAYS.combine(Determinism.PER_STATEMENT));
+		assertEquals("Unexpected result ", Determinism.PER_STATEMENT,
+				Determinism.PER_STATEMENT.combine(Determinism.ALWAYS));
+		assertEquals("Unexpected result ", Determinism.PER_ROW,
+				Determinism.PER_ROW.combine(Determinism.ALWAYS));
+		
+		// combining PER_STATEMENT and PER_ROW should return PER_ROW
+		assertEquals("Unexpected result ", Determinism.PER_ROW,
+				Determinism.PER_STATEMENT.combine(Determinism.PER_ROW));
+		assertEquals("Unexpected result ", Determinism.PER_ROW,
+				Determinism.PER_ROW.combine(Determinism.PER_STATEMENT));
+
+	}
+}