private boolean hasEqualityConstraints(int startPos, int endPos) {
   ScanRanges ranges = context.getScanRanges();
   // If a GROUP BY is being done, then the rows are ordered according to the GROUP BY key,
   // not by the original row key order of the table (see PHOENIX-3451).
   // We check each GROUP BY expression to see if it only references columns that are
   // matched by equality constraints, in which case the expression itself would be constant.
   // FIXME: this only recognizes row key columns that are held constant, not all columns.
   // FIXME: we should optimize out any GROUP BY or ORDER BY expression which is deemed to
   // be held constant based on the WHERE clause.
   if (!groupBy.isEmpty()) {
     for (int pos = startPos; pos < endPos; pos++) {
       IsConstantVisitor visitor = new IsConstantVisitor(this.projector, ranges);
       List<Expression> groupByExpressions = groupBy.getExpressions();
       if (pos >= groupByExpressions.size()) { // sanity check - shouldn't be necessary
         return false;
       }
       Expression groupByExpression = groupByExpressions.get(pos);
       if (groupByExpression.getDeterminism().ordinal() > Determinism.PER_STATEMENT.ordinal()) {
         return false;
       }
       Boolean isConstant = groupByExpression.accept(visitor);
       if (!Boolean.TRUE.equals(isConstant)) {
         return false;
       }
     }
     return true;
   }
   for (int pos = startPos; pos < endPos; pos++) {
     if (!ranges.hasEqualityConstraint(pos)) {
       return false;
     }
   }
   return true;
 }
 @Override
 public Boolean defaultReturn(Expression node, List<Boolean> returnValues) {
   if (node.getDeterminism().ordinal() > Determinism.PER_STATEMENT.ordinal()
       || returnValues.size() < node.getChildren().size()) {
     return Boolean.FALSE;
   }
   for (Boolean returnValue : returnValues) {
     if (!returnValue) {
       return Boolean.FALSE;
     }
   }
   return Boolean.TRUE;
 }