@Override
  public SelectResults evaluate(ExecutionContext context) throws FunctionDomainException,
      TypeMismatchException, NameResolutionException, QueryInvocationTargetException {
    context.newScope((Integer) context.cacheGet(scopeID));
    context.pushExecCache((Integer) context.cacheGet(scopeID));
    boolean prevDistinctState = context.isDistinct();
    context.setDistinct(distinct);
    if (hasUnmappedOrderByCols && context.getBucketList() != null) {
      throw new QueryInvalidException(
          "Query contains atleast one order by field which is not present in projected fields.");
    }
    if (hints != null) {
      context.cachePut(QUERY_INDEX_HINTS, hints);
    }

    try {
      // set flag to keep objects serialized for "select *" queries
      if (context.getQuery() != null) {
        ((DefaultQuery) context.getQuery()).keepResultsSerialized(this, context);
      }
      for (final Object iterator : iterators) {
        CompiledIteratorDef iterDef = (CompiledIteratorDef) iterator;
        RuntimeIterator rIter = iterDef.getRuntimeIterator(context);
        context.bindIterator(rIter);
        // Ideally the function below should always be called after binding has occurred
        // So that the internal ID gets set during binding to the scope. If not so then chances
        // are that internal_id is still null causing index_internal_id to be null.
        // Though in our case it may not be an issue as the compute dependency phase must have
        // already set the index id
      }
      Integer limitValue = evaluateLimitValue(context, limit);
      SelectResults result = null;
      boolean evalAsFilters = false;
      if (whereClause == null) {
        result = doIterationEvaluate(context, false);
      } else {
        if (!whereClause.isDependentOnCurrentScope(context)) { // independent
                                                               // where
                                                               // @todo
                                                               // check
                                                               // for
                                                               // dependency
                                                               // on
                                                               // current
                                                               // scope
                                                               // only?
          // clause
          Object b = whereClause.evaluate(context);
          if (b == null || b == QueryService.UNDEFINED) {
            // treat as if all elements are undefined
            result = prepareEmptyResultSet(context, false);
            // ResultsSet.emptyResultsSet(resultSet, 0);
            // return result;
          } else if (!(b instanceof Boolean)) {
            throw new TypeMismatchException(
                String.format("The WHERE clause was type ' %s ' instead of boolean",
                    b.getClass().getName()));
          } else if ((Boolean) b) {
            result = doIterationEvaluate(context, false);
          } else {
            result = prepareEmptyResultSet(context, false);
            // ResultsSet.emptyResultsSet(resultSet, 0);
            // return result;
          }
        } else {
          // Check the numer of independent iterators
          int numInd = context.getAllIndependentIteratorsOfCurrentScope().size();
          // If order by clause is defined, then the first column should be the preferred index
          if (orderByAttrs != null && numInd == 1) {
            CompiledSortCriterion csc = orderByAttrs.get(0);
            StringBuilder preferredIndexCondn = new StringBuilder();
            evalCanonicalizedExpressionForCSC(csc, context, preferredIndexCondn);
            context.cachePut(PREF_INDEX_COND, preferredIndexCondn.toString());
          }
          boolean unlock = true;
          Object obj = context.cacheGet(whereClause);
          if (obj != null && (obj instanceof IndexInfo[] || obj.equals(CLAUSE_EVALUATED))) {
            // if indexinfo is cached means the read lock
            // is not being taken this time, so releasing
            // the lock is not required
            unlock = false;
          }
          // see if we should evaluate as filters,
          // and count how many actual index lookups will be performed
          PlanInfo planInfo = whereClause.getPlanInfo(context);
          if (context.cacheGet(whereClause) == null) {
            context.cachePut(whereClause, CLAUSE_EVALUATED);
          }
          try {
            evalAsFilters = planInfo.evalAsFilter;
            // let context know if there is exactly one index lookup
            context.setOneIndexLookup(planInfo.indexes.size() == 1);
            if (evalAsFilters) {
              ((QueryExecutionContext) context).setIndexUsed(true);
              // Ignore order by attribs for a while

              boolean canApplyOrderByAtIndex = false;
              if (limitValue >= 0 && numInd == 1
                  && ((Filter) whereClause).isLimitApplicableAtIndexLevel(context)) {
                context.cachePut(CAN_APPLY_LIMIT_AT_INDEX, Boolean.TRUE);
              }
              StringBuilder temp = null;
              if (orderByAttrs != null) {
                temp = new StringBuilder();
                CompiledSortCriterion csc = orderByAttrs.get(0);
                evalCanonicalizedExpressionForCSC(csc, context, temp);
              }

              boolean needsTopLevelOrdering = true;
              if (temp != null && numInd == 1 && ((Filter) whereClause)
                  .isOrderByApplicableAtIndexLevel(context, temp.toString())) {
                context.cachePut(CAN_APPLY_ORDER_BY_AT_INDEX, Boolean.TRUE);
                context.cachePut(ORDERBY_ATTRIB, orderByAttrs);
                canApplyOrderByAtIndex = true;
                if (orderByAttrs.size() == 1) {
                  needsTopLevelOrdering = false;
                  // If there is a limit present and we are executing on a partitioned region
                  // we should use a sorted set
                  if (limit != null) {
                    // Currently check bucket list to determine if it's a pr query
                    if (context.getBucketList() != null && context.getBucketList().size() > 0) {
                      needsTopLevelOrdering = true;
                    }
                  }
                }
              } else if (temp != null) {
                // If order by is present but cannot be applied at index level,
                // then limit also cannot be applied
                // at index level
                context.cachePut(CAN_APPLY_LIMIT_AT_INDEX, Boolean.FALSE);
              }

              context.cachePut(RESULT_LIMIT, limitValue);
              if (numInd == 1
                  && ((Filter) whereClause).isProjectionEvaluationAPossibility(context)
                  && (orderByAttrs == null
                      || (canApplyOrderByAtIndex && !needsTopLevelOrdering))
                  && projAttrs != null) {
                // Possibility of evaluating the resultset as filter itself
                ObjectType resultType = cachedElementTypeForOrderBy != null
                    ? cachedElementTypeForOrderBy : prepareResultType(context);
                context.cachePut(RESULT_TYPE, resultType);
                context.cachePut(PROJ_ATTRIB, projAttrs);
              }


              result = ((Filter) whereClause).filterEvaluate(context, null);
              if (!(context.cacheGet(RESULT_TYPE) instanceof Boolean)) {
                QueryObserverHolder.getInstance()
                    .beforeApplyingProjectionOnFilterEvaluatedResults(result);
                result = applyProjectionOnCollection(result, context, !needsTopLevelOrdering);
              }
            } else {
              // otherwise iterate over the single from var to evaluate
              result = doIterationEvaluate(context, true);
            }
          } finally {
            // The Read lock is acquired in {@link
            // IndexManager#getBestMatchIndex()},
            // because we need to select index which can be read-locked.
            if (unlock) {
              releaseReadLockOnUsedIndex(planInfo);
            }
          }
        }
      }
      // TODO: It does not appear that results would be null ever.
      // if (result == null) { return QueryService.UNDEFINED; }
      assert result != null;
      // drop duplicates if this is DISTINCT
      if (result instanceof SelectResults) {
        SelectResults sr = result;
        CollectionType colnType = sr.getCollectionType();
        // if (this.distinct && colnType.allowsDuplicates()) {
        if (distinct) {
          Collection r;
          // Set s = sr.asSet();
          if (colnType.allowsDuplicates()) {
            // don't just convert to a ResultsSet (or StructSet), since
            // the bags can convert themselves to a Set more efficiently
            r = sr.asSet();
          } else {
            r = sr;
          }

          result = new ResultsCollectionWrapper(colnType.getElementType(), r, limitValue);
          if (r instanceof Bag.SetView) {
            ((ResultsCollectionWrapper) result).setModifiable(false);
          }
        } else {
          // SelectResults is of type
          if (limitValue > -1) {
            ((Bag) sr).applyLimit(limitValue);
          }
        }

        /*
         * We still have to get size of SelectResults in some cases like, if index was used OR query
         * is a distinct query.
         *
         * If SelectResult size is zero then we need to put Integer for 0 count.
         */
        if (count) {
          SelectResults res = result;

          if ((distinct || evalAsFilters || countStartQueryResult == 0)) {
            // Retrun results as it is as distinct is applied
            // at coordinator node for PR queries.
            if (context.getBucketList() != null && distinct) {
              return result;
            }
            // Take size and empty the results
            int resultCount = res.size();
            res.clear();

            ResultsBag countResult =
                new ResultsBag(new ObjectTypeImpl(Integer.class), context.getCachePerfStats());
            countResult.addAndGetOccurence(resultCount);
            result = countResult;

          } else {
            ((Bag) res).addAndGetOccurence(countStartQueryResult);
          }
        }
      }
      return result;
    } finally {
      context.setDistinct(prevDistinctState);
      context.popScope();
      context.popExecCache();
    }
  }
Transforms the group by clause into distinct order by clause, if possible
geode-core/src/main/java/org/apache/geode/cache/query/internal/CompiledSelect.java