private RelDataType deriveType(
      SqlValidator validator,
      SqlValidatorScope scope,
      SqlCall call,
      boolean convertRowArgToColumnList) {
    final SqlNode[] operands = call.operands;
    RelDataType[] argTypes = new RelDataType[operands.length];

    // 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();

    try {
      boolean containsRowArg = false;
      for (int i = 0; i < operands.length; ++i) {
        RelDataType nodeType;

        // for row arguments that should be converted to ColumnList
        // types, set the nodeType to a ColumnList type but defer
        // validating the arguments of the row constructor until we know
        // for sure that the row argument maps to a ColumnList type
        if (operands[i].getKind() == SqlKind.ROW && convertRowArgToColumnList) {
          containsRowArg = true;
          RelDataTypeFactory typeFactory = validator.getTypeFactory();
          nodeType = typeFactory.createSqlType(SqlTypeName.COLUMN_LIST);
        } else {
          nodeType = validator.deriveType(operandScope, operands[i]);
        }
        validator.setValidatedNodeType(operands[i], nodeType);
        argTypes[i] = nodeType;
      }

      SqlFunction function =
          SqlUtil.lookupRoutine(
              validator.getOperatorTable(), getNameAsId(), argTypes, getFunctionType());

      // 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 revalidate 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 (containsRowArg) {
        if ((function == null)
            && SqlUtil.matchRoutinesByParameterCount(
                validator.getOperatorTable(), getNameAsId(), argTypes, getFunctionType())) {
          // remove the already validated node types corresponding to
          // row arguments before revalidating
          for (SqlNode operand : operands) {
            if (operand.getKind() == SqlKind.ROW) {
              validator.removeValidatedNodeType(operand);
            }
          }
          return deriveType(validator, scope, call, false);
        } else if (function != null) {
          validator.validateColumnListParams(function, argTypes, operands);
        }
      }

      if (getFunctionType() == SqlFunctionCategory.UserDefinedConstructor) {
        return validator.deriveConstructorType(scope, call, this, function, argTypes);
      }
      if (function == null) {
        validator.handleUnresolvedFunction(call, this, argTypes);
      }

      // 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.
      call.setOperator(function);
      return function.validateOperands(validator, operandScope, call);
    } finally {
      validator.popFunctionCall();
    }
  }
 public RelDataType deriveType(SqlValidator validator, SqlValidatorScope scope, SqlCall call) {
   // special case for AS:  never try to derive type for alias
   RelDataType nodeType = validator.deriveType(scope, call.operands[0]);
   assert nodeType != null;
   return validateOperands(validator, scope, call);
 }