public void reduce()

in sql/src/main/java/org/apache/druid/sql/calcite/planner/DruidRexExecutor.java [60:222]


  public void reduce(
      final RexBuilder rexBuilder,
      final List<RexNode> constExps,
      final List<RexNode> reducedValues
  )
  {
    for (RexNode constExp : constExps) {
      final DruidExpression druidExpression = Expressions.toDruidExpression(
          plannerContext,
          EMPTY_ROW_SIGNATURE,
          constExp
      );

      if (druidExpression == null) {
        reducedValues.add(constExp);
      } else {
        final SqlTypeName sqlTypeName = constExp.getType().getSqlTypeName();
        final Expr expr = Parser.parse(
            druidExpression.getExpression(),
            plannerContext.getPlannerToolbox().exprMacroTable()
        );

        final ExprEval exprResult = expr.eval(InputBindings.validateConstant(expr));

        final RexNode literal;

        if (sqlTypeName == SqlTypeName.BOOLEAN) {
          if (exprResult.valueOrDefault() == null) {
            literal = rexBuilder.makeNullLiteral(constExp.getType());
          } else {
            literal = rexBuilder.makeLiteral(exprResult.asBoolean(), constExp.getType(), true);
          }
        } else if (sqlTypeName == SqlTypeName.DATE) {
          if (exprResult.isNumericNull()) {
            if (constExp.getType().isNullable()) {
              literal = rexBuilder.makeNullLiteral(constExp.getType());
            } else {
              // There can be implicit casts of VARCHAR to TIMESTAMP where the VARCHAR is an invalid timestamp, but the
              // TIMESTAMP type is not nullable. In this case it's best to throw an error, since it likely means the
              // user's SQL query contains an invalid literal.
              throw InvalidSqlInput.exception("Illegal DATE constant [%s]", constExp);
            }
          } else {
            literal = rexBuilder.makeDateLiteral(
                Calcites.jodaToCalciteDateString(
                    DateTimes.utc(exprResult.asLong()),
                    plannerContext.getTimeZone()
                )
            );
          }
        } else if (sqlTypeName == SqlTypeName.TIMESTAMP) {
          if (exprResult.isNumericNull()) {
            if (constExp.getType().isNullable()) {
              literal = rexBuilder.makeNullLiteral(constExp.getType());
            } else {
              // There can be implicit casts of VARCHAR to TIMESTAMP where the VARCHAR is an invalid timestamp, but the
              // TIMESTAMP type is not nullable. In this case it's best to throw an error, since it likely means the
              // user's SQL query contains an invalid literal.
              throw InvalidSqlInput.exception("Illegal TIMESTAMP constant [%s]", constExp);
            }
          } else {
            literal = Calcites.jodaToCalciteTimestampLiteral(
                rexBuilder,
                DateTimes.utc(exprResult.asLong()),
                plannerContext.getTimeZone(),
                constExp.getType().getPrecision()
            );
          }
        } else if (SqlTypeName.NUMERIC_TYPES.contains(sqlTypeName)) {
          final BigDecimal bigDecimal;

          if (exprResult.isNumericNull()) {
            literal = rexBuilder.makeNullLiteral(constExp.getType());
          } else {
            if (exprResult.type().is(ExprType.LONG)) {
              bigDecimal = BigDecimal.valueOf(exprResult.asLong());

            } else {
              // if exprResult evaluates to Nan or infinity, this will throw a NumberFormatException.
              // If you find yourself in such a position, consider casting the literal to a BIGINT so that
              // the query can execute.
              double exprResultDouble = exprResult.asDouble();
              if (Double.isNaN(exprResultDouble) || Double.isInfinite(exprResultDouble)) {
                throw InvalidSqlInput.exception(
                        "Expression [%s] evaluates to an unsupported value [%s], expected something that"
                        + " can be a Double.  Consider casting with 'CAST(<col> AS BIGINT)'",
                        druidExpression.getExpression(),
                        exprResultDouble
                    );
              }
              bigDecimal = BigDecimal.valueOf(exprResult.asDouble());
            }
            literal = rexBuilder.makeLiteral(bigDecimal, constExp.getType(), true);
          }
        } else if (sqlTypeName == SqlTypeName.ARRAY) {
          assert exprResult.isArray();
          final Object[] array = exprResult.asArray();
          if (array == null) {
            literal = rexBuilder.makeNullLiteral(constExp.getType());
          } else if (SqlTypeName.NUMERIC_TYPES.contains(constExp.getType().getComponentType().getSqlTypeName())) {
            if (exprResult.type().getElementType().is(ExprType.LONG)) {
              List<BigDecimal> resultAsBigDecimalList = new ArrayList<>(array.length);
              for (Object val : exprResult.castTo(ExpressionType.LONG_ARRAY).asArray()) {
                final Number longVal = (Number) val;
                if (longVal == null) {
                  resultAsBigDecimalList.add(null);
                } else {
                  resultAsBigDecimalList.add(BigDecimal.valueOf(longVal.longValue()));
                }
              }
              literal = rexBuilder.makeLiteral(resultAsBigDecimalList, constExp.getType(), true);
            } else {
              List<BigDecimal> resultAsBigDecimalList = new ArrayList<>(array.length);
              for (Object val : exprResult.castTo(ExpressionType.fromColumnType(druidExpression.getDruidType())).asArray()) {
                final Number doubleVal = (Number) val;
                if (doubleVal == null) {
                  resultAsBigDecimalList.add(null);
                } else if (Double.isNaN(doubleVal.doubleValue()) || Double.isInfinite(doubleVal.doubleValue())) {
                  throw InvalidSqlInput.exception(
                          "Expression [%s] was expected to generate values that are all Doubles,"
                          + " but entry at index[%d] was not: [%s]."
                          + "  Consider Casting values to ensure a consistent type.",
                          druidExpression.getExpression(),
                          resultAsBigDecimalList.size(),
                          doubleVal
                      );
                } else {
                  resultAsBigDecimalList.add(BigDecimal.valueOf(doubleVal.doubleValue()));
                }
              }
              literal = rexBuilder.makeLiteral(resultAsBigDecimalList, constExp.getType(), true);
            }
          } else if (constExp.getType().getComponentType().getSqlTypeName() == SqlTypeName.BOOLEAN) {
            List<Boolean> resultAsBooleanList = new ArrayList<>(array.length);
            for (Object val : exprResult.castTo(ExpressionType.LONG_ARRAY).asArray()) {
              final Number longVal = (Number) val;
              if (longVal == null) {
                resultAsBooleanList.add(null);
              } else {
                resultAsBooleanList.add(Evals.asBoolean(longVal.longValue()));
              }
            }
            literal = rexBuilder.makeLiteral(resultAsBooleanList, constExp.getType(), true);
          } else {
            literal = rexBuilder.makeLiteral(Arrays.asList(array), constExp.getType(), true);
          }
        } else if (sqlTypeName == SqlTypeName.OTHER) {
          // complex constant is not reducible, so just leave it as an expression
          literal = constExp;
        } else {
          if (exprResult.isArray()) {
            // just leave array expressions on multi-value strings alone, we're going to push them down into a virtual
            // column selector anyway
            literal = constExp;
          } else {
            literal = rexBuilder.makeLiteral(exprResult.valueOrDefault(), constExp.getType(), true);
          }
        }

        reducedValues.add(literal);
      }
    }
  }