private LogicalExpression getDrillFunctionFromOptiqCall()

in exec/java-exec/src/main/java/org/apache/drill/exec/planner/logical/DrillOptiq.java [551:783]


    private LogicalExpression getDrillFunctionFromOptiqCall(RexCall call) {
      List<LogicalExpression> args = new ArrayList<>();

      for(RexNode n : call.getOperands()){
        args.add(n.accept(this));
      }

      int argsSize = args.size();
      String functionName = call.getOperator().getName().toLowerCase();

      // TODO: once we have more function rewrites and a patter emerges from different rewrites, factor this out in a better fashion
      /* Rewrite extract functions in the following manner
       * extract(year, date '2008-2-23') ---> extractYear(date '2008-2-23')
       */
      switch (functionName) {
        case "extract": {

          // Assert that the first argument to extract is a QuotedString
          assert args.get(0) instanceof ValueExpressions.QuotedString;

          // Get the unit of time to be extracted
          String timeUnitStr = ((ValueExpressions.QuotedString) args.get(0)).value;
          TimeUnit timeUnit;
          // Clean up day of XXX
          if (timeUnitStr.contentEquals("DAYOFWEEK")) {
            timeUnit = TimeUnit.DOW;
          } else if (timeUnitStr.contentEquals("DAYOFYEAR")) {
            timeUnit = TimeUnit.DOY;
          } else {
            timeUnit = TimeUnit.valueOf(timeUnitStr);
          }

          switch (timeUnit) {
            case YEAR:
            case QUARTER:
            case MONTH:
            case WEEK:
            case DAY:
            case DOW:
            case DOY:
            case EPOCH:
            case HOUR:
            case MINUTE:
            case SECOND:
              String functionPostfix = StringUtils.capitalize(timeUnitStr.toLowerCase());
              functionName += functionPostfix;
              return FunctionCallFactory.createExpression(functionName, args.subList(1, 2));
            default:
              throw new UnsupportedOperationException("extract function supports the following " +
                "time units: YEAR, QUARTER, MONTH, WEEK, DAY, DAYOFWEEK, DAYOFYEAR, EPOCH, HOUR, " +
                "MINUTE, SECOND");
          }
        }
        case "timestampdiff": {

          // Assert that the first argument to extract is a QuotedString
          Preconditions.checkArgument(args.get(0) instanceof ValueExpressions.QuotedString,
            "The first argument of TIMESTAMPDIFF function should be QuotedString");

          String timeUnitStr = ((ValueExpressions.QuotedString) args.get(0)).value;

          TimeUnit timeUnit = TimeUnit.valueOf(timeUnitStr);

          switch (timeUnit) {
            case YEAR:
            case MONTH:
            case DAY:
            case HOUR:
            case MINUTE:
            case SECOND:
            case MILLISECOND:
            case QUARTER:
            case WEEK:
            case MICROSECOND:
            case NANOSECOND:
              String functionPostfix = StringUtils.capitalize(timeUnitStr.toLowerCase());
              functionName += functionPostfix;
              return FunctionCallFactory.createExpression(functionName, args.subList(1, 3));
            default:
              throw new UnsupportedOperationException("TIMESTAMPDIFF function supports the following time units: " +
                  "YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, QUARTER, WEEK, MICROSECOND, NANOSECOND");
          }
        }
        case "trim": {
          String trimFunc;
          List<LogicalExpression> trimArgs = new ArrayList<>();

          assert args.get(0) instanceof ValueExpressions.QuotedString;
          switch (((ValueExpressions.QuotedString) args.get(0)).value.toUpperCase()) {
            case "LEADING":
              trimFunc = "ltrim";
              break;
            case "TRAILING":
              trimFunc = "rtrim";
              break;
            case "BOTH":
              trimFunc = "btrim";
              break;
            default:
              throw new UnsupportedOperationException("Invalid argument for TRIM function. " +
                  "Expected one of the following: LEADING, TRAILING, BOTH");
          }

          trimArgs.add(args.get(2));
          trimArgs.add(args.get(1));

          return FunctionCallFactory.createExpression(trimFunc, trimArgs);
        }
        case "date_part": {
          // Rewrite DATE_PART functions as extract functions
          // assert that the function has exactly two arguments
          assert argsSize == 2;

          /* Based on the first input to the date_part function we rewrite the function as the
           * appropriate extract function. For example
           * date_part('year', date '2008-2-23') ------> extractYear(date '2008-2-23')
           */
          assert args.get(0) instanceof QuotedString;

          QuotedString extractString = (QuotedString) args.get(0);
          String functionPostfix = StringUtils.capitalize(extractString.value.toLowerCase());
          return FunctionCallFactory.createExpression("extract" + functionPostfix, args.subList(1, 2));
        }
        case "concat": {

          if (argsSize == 1) {
            /*
             * We treat concat with one argument as a special case. Since we don't have a function
             * implementation of concat that accepts one argument. We simply add another dummy argument
             * (empty string literal) to the list of arguments.
             */
            List<LogicalExpression> concatArgs = new LinkedList<>(args);
            concatArgs.add(QuotedString.EMPTY_STRING);

            return FunctionCallFactory.createExpression(functionName, concatArgs);

          } else if (argsSize > 2) {
            List<LogicalExpression> concatArgs = new ArrayList<>();

            /* stack concat functions on top of each other if we have more than two arguments
             * Eg: concat(col1, col2, col3) => concat(concat(col1, col2), col3)
             */
            concatArgs.add(args.get(0));
            concatArgs.add(args.get(1));

            LogicalExpression first = FunctionCallFactory.createExpression(functionName, concatArgs);

            for (int i = 2; i < argsSize; i++) {
              concatArgs = new ArrayList<>();
              concatArgs.add(first);
              concatArgs.add(args.get(i));
              first = FunctionCallFactory.createExpression(functionName, concatArgs);
            }

            return first;
          }
          break;
        }
        case "length": {
          if (argsSize == 2) {

            // Second argument should always be a literal specifying the encoding format
            assert args.get(1) instanceof ValueExpressions.QuotedString;

            String encodingType = ((ValueExpressions.QuotedString) args.get(1)).value;
            functionName += StringUtils.capitalize(encodingType.toLowerCase());

            return FunctionCallFactory.createExpression(functionName, args.subList(0, 1));
          }
          break;
        }
        case "convert_from":
        case "convert_to": {
          if (args.get(1) instanceof QuotedString) {
            return FunctionCallFactory.createConvert(functionName, ((QuotedString) args.get(1)).value, args.get(0), ExpressionPosition.UNKNOWN);
          }
          break;
        }
        case "date_trunc": {
          return handleDateTruncFunction(args);
        }
        case "httprequest":
        case "http_request": {
          // This code resolves aliases in the http_request function.
          String completeRawPluginName = ((QuotedString) args.get(0)).value;
          String username = context.getPlannerSettings().getQueryUser();

          AliasRegistryProvider aliasRegistryProvider = context.getPlannerSettings().getAliasRegistryProvider();
          AliasRegistry storageAliasRegistry = aliasRegistryProvider.getStorageAliasesRegistry();
          AliasRegistry tableAliasRegistry = aliasRegistryProvider.getTableAliasesRegistry();

          // Split into plugin and endpoint
          SchemaPath schemaPath = SchemaPath.parseFromString(completeRawPluginName);
          String rawPluginName = SchemaPath.getSimplePath(schemaPath.rootName()).toExpr();
          String rawEndpoint = SchemaPath.getSimplePath(schemaPath.getLastSegment().getNameSegment().getPath()).toExpr();

          // Now resolve plugin name
          String actualPluginName = storageAliasRegistry.getUserAliases(username).get(rawPluginName);
          if (StringUtils.isEmpty(actualPluginName)) {
            // If it is empty, assign it the original name,
            actualPluginName = rawPluginName;
          }

          // Finally remove backticks
          actualPluginName = SchemaPath.parseFromString(actualPluginName).getRootSegmentPath();

          // Now do the same for the endpoint name
          String actualEndpointName = tableAliasRegistry.getUserAliases(username).get(rawEndpoint);
          if (StringUtils.isEmpty(actualEndpointName)) {
            // If it is empty, assign it the original name,
            actualEndpointName = rawEndpoint;
          }

          // Now remove backticks
          actualEndpointName = SchemaPath.parseFromString(actualEndpointName).getRootSegmentPath();

          String finalPluginName = SchemaPath
            .getCompoundPath(actualPluginName, actualEndpointName)
            .getAsUnescapedPath();

          QuotedString q = new QuotedString(finalPluginName, finalPluginName.length(), ExpressionPosition.UNKNOWN);

          // Add args to new arg lists
          List<LogicalExpression> requestArgs = new ArrayList<>();
          requestArgs.add(q);
          requestArgs.addAll(args.subList(1, args.size()));

          return FunctionCallFactory.createExpression(functionName, requestArgs);
        }
      }

      return FunctionCallFactory.createExpression(functionName, args);
    }