content: /*
   * Licensed to the Apache Software Foundation (ASF) under one or more
   * contributor license agreements.  See the NOTICE file distributed with
   * this work for additional information regarding copyright ownership.
   * The ASF licenses this file to you under the Apache License, Version 2.0
   * (the "License"); you may not use this file except in compliance with
   * the License.  You may obtain a copy of the License at
   *
   * http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package org.apache.calcite.sql;
  
  import org.apache.calcite.linq4j.function.Functions;
  import org.apache.calcite.rel.type.RelDataType;
  import org.apache.calcite.rel.type.RelDataTypeFactory;
  import org.apache.calcite.sql.parser.SqlParserPos;
  import org.apache.calcite.sql.type.OperandTypes;
  import org.apache.calcite.sql.type.SqlOperandMetadata;
  import org.apache.calcite.sql.type.SqlOperandTypeChecker;
  import org.apache.calcite.sql.type.SqlOperandTypeInference;
  import org.apache.calcite.sql.type.SqlReturnTypeInference;
  import org.apache.calcite.sql.validate.SqlValidator;
  import org.apache.calcite.sql.validate.SqlValidatorScope;
  import org.apache.calcite.sql.validate.implicit.TypeCoercion;
  import org.apache.calcite.util.Util;
  
  import org.checkerframework.checker.nullness.qual.Nullable;
  import org.checkerframework.dataflow.qual.Pure;
  
  import java.util.List;
  
  import static org.apache.calcite.linq4j.Nullness.castNonNull;
  import static org.apache.calcite.util.Static.RESOURCE;
  
  import static java.util.Objects.requireNonNull;
  
  /**
   * A <code>SqlFunction</code> is a type of operator which has conventional
   * function-call syntax.
   */
  public class SqlFunction extends SqlOperator {
  
    //~ Instance fields --------------------------------------------------------
  
    private final SqlFunctionCategory category;
  
    private final @Nullable SqlIdentifier sqlIdentifier;
  
    //~ Constructors -----------------------------------------------------------
  
    /**
     * Creates a new SqlFunction for a call to a built-in function.
     *
     * @param name                 Name of built-in function
     * @param kind                 kind of operator implemented by function
     * @param returnTypeInference  strategy to use for return type inference
     * @param operandTypeInference strategy to use for parameter type inference
     * @param operandTypeChecker   strategy to use for parameter type checking
     * @param category             categorization for function
     */
    public SqlFunction(
        String name,
        SqlKind kind,
        @Nullable SqlReturnTypeInference returnTypeInference,
        @Nullable SqlOperandTypeInference operandTypeInference,
        @Nullable SqlOperandTypeChecker operandTypeChecker,
        SqlFunctionCategory category) {
      // We leave sqlIdentifier as null to indicate
      // that this is a built-in.
      this(name, null, kind, returnTypeInference, operandTypeInference,
          operandTypeChecker, category);
  
      assert !((category == SqlFunctionCategory.USER_DEFINED_CONSTRUCTOR)
          && (returnTypeInference == null));
    }
  
    /**
     * Creates a placeholder SqlFunction for an invocation of a function with a
     * possibly qualified name. This name must be resolved into either a built-in
     * function or a user-defined function.
     *
     * @param sqlIdentifier        possibly qualified identifier for function
     * @param returnTypeInference  strategy to use for return type inference
     * @param operandTypeInference strategy to use for parameter type inference
     * @param operandTypeChecker   strategy to use for parameter type checking
     * @param paramTypes           array of parameter types
     * @param funcType             function category
     */
    public SqlFunction(
        SqlIdentifier sqlIdentifier,
        @Nullable SqlReturnTypeInference returnTypeInference,
        @Nullable SqlOperandTypeInference operandTypeInference,
        @Nullable SqlOperandTypeChecker operandTypeChecker,
        @Nullable List<RelDataType> paramTypes,
        SqlFunctionCategory funcType) {
      this(Util.last(sqlIdentifier.names), sqlIdentifier, SqlKind.OTHER_FUNCTION,
          returnTypeInference, operandTypeInference, operandTypeChecker,
          paramTypes, funcType);
    }
  
    @Deprecated // to be removed before 2.0
    protected SqlFunction(
        String name,
        @Nullable SqlIdentifier sqlIdentifier,
        SqlKind kind,
        @Nullable SqlReturnTypeInference returnTypeInference,
        @Nullable SqlOperandTypeInference operandTypeInference,
        @Nullable SqlOperandTypeChecker operandTypeChecker,
        @Nullable List<RelDataType> paramTypes,
        SqlFunctionCategory category) {
      this(name, sqlIdentifier, kind, returnTypeInference, operandTypeInference,
          operandTypeChecker, category);
    }
  
    /**
     * Internal constructor.
     */
    protected SqlFunction(
        String name,
        @Nullable SqlIdentifier sqlIdentifier,
        SqlKind kind,
        @Nullable SqlReturnTypeInference returnTypeInference,
        @Nullable SqlOperandTypeInference operandTypeInference,
        @Nullable SqlOperandTypeChecker operandTypeChecker,
        SqlFunctionCategory category) {
      super(name, kind, 100, 100, returnTypeInference, operandTypeInference,
          operandTypeChecker);
  
      this.sqlIdentifier = sqlIdentifier;
      this.category = requireNonNull(category, "category");
    }
  
    //~ Methods ----------------------------------------------------------------
  
    @Override public SqlSyntax getSyntax() {
      return SqlSyntax.FUNCTION;
    }
  
    /**
     * Returns the fully-qualified name of function, or null for a built-in
     * function.
     */
    public @Nullable SqlIdentifier getSqlIdentifier() {
      return sqlIdentifier;
    }
  
    @Override public SqlIdentifier getNameAsId() {
      if (sqlIdentifier != null) {
        return sqlIdentifier;
      }
      return super.getNameAsId();
    }
  
    /** Use {@link SqlOperandMetadata#paramTypes(RelDataTypeFactory)} on the
     * result of {@link #getOperandTypeChecker()}. */
    @Deprecated // to be removed before 2.0
    public @Nullable List<RelDataType> getParamTypes() {
      return null;
    }
  
    /** Use {@link SqlOperandMetadata#paramNames()} on the result of
     * {@link #getOperandTypeChecker()}. */
    @Deprecated // to be removed before 2.0
    public List<String> getParamNames() {
      return Functions.generate(castNonNull(getParamTypes()).size(), i -> "arg" + i);
    }
  
    @Override public void unparse(
        SqlWriter writer,
        SqlCall call,
        int leftPrec,
        int rightPrec) {
      getSyntax().unparse(writer, this, call, leftPrec, rightPrec);
    }
  
    /**
     * Return function category.
     */
    public SqlFunctionCategory getFunctionType() {
      return this.category;
    }
  
    /**
     * Returns whether this function allows a <code>DISTINCT</code> or <code>
     * ALL</code> quantifier. The default is <code>false</code>; some aggregate
     * functions return <code>true</code>.
     */
    @Pure
    public boolean isQuantifierAllowed() {
      return false;
    }
  
    @Override public void validateCall(
        SqlCall call,
        SqlValidator validator,
        SqlValidatorScope scope,
        SqlValidatorScope operandScope) {
      // This implementation looks for the quantifier keywords DISTINCT or
      // ALL as the first operand in the list.  If found then the literal is
      // not called to validate itself.  Further the function is checked to
      // make sure that a quantifier is valid for that particular function.
      //
      // If the first operand does not appear to be a quantifier then the
      // parent ValidateCall is invoked to do normal function validation.
  
      super.validateCall(call, validator, scope, operandScope);
      validateQuantifier(validator, call);
    }
  
    /**
     * Throws a validation error if a DISTINCT or ALL quantifier is present but
     * not allowed.
     */
    protected void validateQuantifier(SqlValidator validator, SqlCall call) {
      SqlLiteral functionQuantifier = call.getFunctionQuantifier();
      if ((null != functionQuantifier) && !isQuantifierAllowed()) {
        throw validator.newValidationError(functionQuantifier,
            RESOURCE.functionQuantifierNotAllowed(call.getOperator().getName()));
      }
    }
  
    @Override public RelDataType deriveType(
        SqlValidator validator,
        SqlValidatorScope scope,
        SqlCall call) {
      return deriveType(validator, scope, call, false);
    }
  
    private RelDataType deriveType(
        SqlValidator validator,
        SqlValidatorScope scope,
        SqlCall call,
        boolean convertRowArgToColumnList) {
      // Scope for operands. Usually the same as 'scope'.
      final SqlValidatorScope operandScope = scope.getOperandScope(call);
  
      // Indicate to the validator that we're validating a new function call
      validator.pushFunctionCall();
  
      final List<String> argNames = constructArgNameList(call);
  
      final List<SqlNode> args = constructOperandList(validator, call, argNames);
  
      final List<RelDataType> argTypes =
          constructArgTypeList(validator, scope,
              call, args, convertRowArgToColumnList);
  
      SqlFunction function =
          (SqlFunction) SqlUtil.lookupRoutine(validator.getOperatorTable(),
              validator.getTypeFactory(), getNameAsId(), argTypes, argNames,
              getFunctionType(), SqlSyntax.FUNCTION, getKind(),
              validator.getCatalogReader().nameMatcher(), false);
  
      // If the call already has an operator and its syntax is SPECIAL, it must
      // have been created intentionally by the parser.
      if (function == null
          && call.getOperator().getSyntax() == SqlSyntax.SPECIAL
          && call.getOperator() instanceof SqlFunction
          && validator.getOperatorTable().getOperatorList().contains(
              call.getOperator())) {
        function = (SqlFunction) call.getOperator();
      }
  
      try {
        // if we have a match on function name and parameter count, but
        // couldn't find a function with  a COLUMN_LIST type, retry, but
        // this time, don't convert the row argument to a COLUMN_LIST type;
        // if we did find a match, go back and re-validate the row operands
        // (corresponding to column references), now that we can set the
        // scope to that of the source cursor referenced by that ColumnList
        // type
        if (convertRowArgToColumnList && containsRowArg(args)) {
          if (function == null
              && SqlUtil.matchRoutinesByParameterCount(
                  validator.getOperatorTable(), getNameAsId(), argTypes,
                  getFunctionType(),
                  validator.getCatalogReader().nameMatcher())) {
            // remove the already validated node types corresponding to
            // row arguments before re-validating
            for (SqlNode operand : args) {
              if (operand.getKind() == SqlKind.ROW) {
                validator.removeValidatedNodeType(operand);
              }
            }
            return deriveType(validator, scope, call, false);
          }
        }
  
        if (getFunctionType() == SqlFunctionCategory.USER_DEFINED_CONSTRUCTOR) {
          return validator.deriveConstructorType(scope, call, this, function,
              argTypes);
        }
  
        validCoercionType:
        if (function == null) {
          if (validator.config().typeCoercionEnabled()) {
            // try again if implicit type coercion is allowed.
            function = (SqlFunction)
                SqlUtil.lookupRoutine(validator.getOperatorTable(),
                    validator.getTypeFactory(),
                    getNameAsId(),
                    argTypes, argNames, getFunctionType(), SqlSyntax.FUNCTION,
                    getKind(), validator.getCatalogReader().nameMatcher(), true);
            // try to coerce the function arguments to the declared sql type name.
            // if we succeed, the arguments would be wrapped with CAST operator.
            if (function != null) {
              TypeCoercion typeCoercion = validator.getTypeCoercion();
              if ((function.category == SqlFunctionCategory.USER_DEFINED_FUNCTION
                  || function.category == SqlFunctionCategory.USER_DEFINED_TABLE_FUNCTION)
                  && typeCoercion.userDefinedFunctionCoercion(scope, call, function)) {
                break validCoercionType;
              }
            }
          }
  
          // check if the identifier represents type
          final SqlFunction x = (SqlFunction) call.getOperator();
          final SqlIdentifier identifier =
              Util.first(x.getSqlIdentifier(),
                  new SqlIdentifier(x.getName(), SqlParserPos.ZERO));
          RelDataType type = validator.getCatalogReader().getNamedType(identifier);
          if (type != null) {
            function = new SqlTypeConstructorFunction(identifier, type);
            break validCoercionType;
          }
  
          // if function doesn't exist within operator table and known function
          // handling is turned off then create a more permissive function
          if (function == null && validator.config().lenientOperatorLookup()) {
            function =
                new SqlUnresolvedFunction(identifier, null,
                    null, OperandTypes.VARIADIC, null, x.getFunctionType());
            break validCoercionType;
          }
          throw validator.handleUnresolvedFunction(call, this, argTypes,
              argNames);
        }
  
        // REVIEW jvs 25-Mar-2005:  This is, in a sense, expanding
        // identifiers, but we ignore shouldExpandIdentifiers()
        // because otherwise later validation code will
        // choke on the unresolved function.
        ((SqlBasicCall) call).setOperator(function);
        return function.validateOperands(
            validator,
            operandScope,
            call);
      } finally {
        validator.popFunctionCall();
      }
    }
  
    private static boolean containsRowArg(List<SqlNode> args) {
      for (SqlNode operand : args) {
        if (operand.getKind() == SqlKind.ROW) {
          return true;
        }
      }
      return false;
    }
  }
  