private static ExprNodeDesc foldExprFull(ExprNodeDesc desc, Map<ColumnInfo, ExprNodeDesc> constants,
      ConstantPropagateProcCtx cppCtx, Operator<? extends Serializable> op, int tag,
      boolean propagate) throws UDFArgumentException {
    // Combine NOT operator with the child operator. Otherwise, the following optimization
    // from bottom up could lead to incorrect result, such as not(x > 3 and x is not null),
    // should not be optimized to not(x > 3), but (x <=3 or x is null).
    desc = foldNegative(desc);

    if (desc instanceof ExprNodeGenericFuncDesc) {
      ExprNodeGenericFuncDesc funcDesc = (ExprNodeGenericFuncDesc) desc;

      GenericUDF udf = funcDesc.getGenericUDF();

      boolean propagateNext = propagate && propagatableUdfs.contains(udf.getClass());
      List<ExprNodeDesc> newExprs = new ArrayList<ExprNodeDesc>();
      for (ExprNodeDesc childExpr : desc.getChildren()) {
        newExprs.add(foldExpr(childExpr, constants, cppCtx, op, tag, propagateNext));
      }

      // Don't evaluate nondeterministic function since the value can only calculate during runtime.
      if (!isConstantFoldableUdf(udf, newExprs)) {
        if (LOG.isDebugEnabled()) {
          LOG.debug("Function " + udf.getClass() + " is not deterministic. Don't evaluate immediately.");
        }
        ((ExprNodeGenericFuncDesc) desc).setChildren(newExprs);
        return desc;
      } else {
        // If all child expressions of deterministic function are constants, evaluate such UDF immediately
        ExprNodeDesc constant = evaluateFunction(udf, newExprs, desc.getChildren());
        if (constant != null) {
          LOG.debug("Folding expression: {} -> {}", desc, constant);
          return constant;
        } else {
          // Check if the function can be short cut.
          ExprNodeDesc shortcut = shortcutFunction(udf, newExprs, op);
          if (shortcut != null) {
            LOG.debug("Folding expression: {} -> {}", desc, shortcut);
            return shortcut;
          }
          ((ExprNodeGenericFuncDesc) desc).setChildren(newExprs);
        }

        // If in some selected binary operators (=, is null, etc), one of the
        // expressions are
        // constant, add them to colToConstants as half-deterministic columns.
        if (propagate) {
          propagate(udf, newExprs, op.getSchema(), constants);
        }
      }

      return desc;
    } else if (desc instanceof ExprNodeColumnDesc) {
      if (op.getParentOperators() == null || op.getParentOperators().isEmpty()) {
        return desc;
      }
      Operator<? extends Serializable> parent = op.getParentOperators().get(tag);
      ExprNodeDesc col = evaluateColumn((ExprNodeColumnDesc) desc, cppCtx, parent);
      if (col != null) {
        LOG.debug("Folding expression: {} -> {}", desc, col);
        return col;
      }
    }
    return desc;
  }
Fold input expression desc.

This function recursively checks if any subexpression of a specified expression
can be evaluated to be constant and replaces such subexpression with the constant.
If the expression is a deterministic UDF and all the subexpressions are constants,
the value will be calculated immediately (during compilation time vs. runtime).
e.g.:
  concat(year, month) => 200112 for year=2001, month=12 since concat is deterministic UDF
  unix_timestamp(time) => unix_timestamp(123) for time=123 since unix_timestamp is nondeterministic UDF
@param desc folding expression
@param constants current propagated constant map
@param cppCtx
@param op processing operator
@param propagate if true, assignment expressions will be added to constants.
@return fold expression
@throws UDFArgumentException
ql/src/java/org/apache/hadoop/hive/ql/optimizer/ConstantPropagateProcFactory.java