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.rel.type.RelDataType;
  import org.apache.calcite.sql.parser.SqlParserPos;
  import org.apache.calcite.sql.util.SqlVisitor;
  import org.apache.calcite.sql.validate.SqlMoniker;
  import org.apache.calcite.sql.validate.SqlMonotonicity;
  import org.apache.calcite.sql.validate.SqlValidator;
  import org.apache.calcite.sql.validate.SqlValidatorImpl;
  import org.apache.calcite.sql.validate.SqlValidatorScope;
  import org.apache.calcite.util.Litmus;
  
  import org.checkerframework.checker.nullness.qual.Nullable;
  import org.checkerframework.dataflow.qual.Pure;
  
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.List;
  
  import static org.apache.calcite.linq4j.Nullness.castNonNull;
  
  import static java.util.Objects.requireNonNull;
  
  /**
   * A <code>SqlCall</code> is a call to an {@link SqlOperator operator}.
   * (Operators can be used to describe any syntactic construct, so in practice,
   * every non-leaf node in a SQL parse tree is a <code>SqlCall</code> of some
   * kind.)
   */
  public abstract class SqlCall extends SqlNode {
    //~ Constructors -----------------------------------------------------------
  
    protected SqlCall(SqlParserPos pos) {
      super(pos);
    }
  
    //~ Methods ----------------------------------------------------------------
  
    /**
     * Whether this call was created by expanding a parentheses-free call to
     * what was syntactically an identifier.
     */
    public boolean isExpanded() {
      return false;
    }
  
    /**
     * Changes the value of an operand. Allows some rewrite by
     * {@link SqlValidator}; use sparingly.
     *
     * @param i Operand index
     * @param operand Operand value
     */
    public void setOperand(int i, @Nullable SqlNode operand) {
      throw new UnsupportedOperationException();
    }
  
    @Override public SqlKind getKind() {
      return getOperator().getKind();
    }
  
    @Pure
    public abstract SqlOperator getOperator();
  
    /**
     * Returns the list of operands. The set and order of operands is
     * call-specific.
     *
     * <p>Note: the proper type would be {@code List<@Nullable SqlNode>}, however,
     * it would trigger too many changes to the current codebase.
     *
     * @return the list of call operands, never null, the operands can be null
     */
    public abstract List</*Nullable*/ SqlNode> getOperandList();
  
    /**
     * Returns i-th operand (0-based).
     *
     * <p>Note: the result might be null, so the proper signature would be
     * {@code <S extends @Nullable SqlNode>}, however, it would trigger to many
     * changes to the current codebase.
     *
     * @param i operand index (0-based)
     * @param <S> type of the result
     * @return i-th operand (0-based), the result might be null
     */
    @SuppressWarnings("unchecked")
    public <S extends /*Nullable*/ SqlNode> S operand(int i) {
      // Note: in general, null elements exist in the list, however, the code
      // assumes operand(..) is non-nullable, so we add a cast here
      return (S) castNonNull(getOperandList().get(i));
    }
  
    public int operandCount() {
      return getOperandList().size();
    }
  
    @Override public SqlNode clone(SqlParserPos pos) {
      return getOperator().createCall(getFunctionQuantifier(), pos,
          getOperandList());
    }
  
    private boolean needsParentheses(SqlWriter writer, int leftPrec, int rightPrec) {
      if (getKind() == SqlKind.SET_SEMANTICS_TABLE) {
        return false;
      }
      final SqlOperator operator = getOperator();
      return leftPrec > operator.getLeftPrec()
          || (operator.getRightPrec() <= rightPrec && (rightPrec != 0))
          || writer.isAlwaysUseParentheses() && isA(SqlKind.EXPRESSION)
          || (operator.getRightPrec() <= rightPrec + 1 && isA(SqlKind.COMPARISON));
    }
  
    @Override public void unparse(
        SqlWriter writer,
        int leftPrec,
        int rightPrec) {
      final SqlDialect dialect = writer.getDialect();
      if (needsParentheses(writer, leftPrec, rightPrec)) {
        final SqlWriter.Frame frame = writer.startList("(", ")");
        dialect.unparseCall(writer, this, 0, 0);
        writer.endList(frame);
      } else {
        dialect.unparseCall(writer, this, leftPrec, rightPrec);
      }
    }
  
    /**
     * Validates this call.
     *
     * <p>The default implementation delegates the validation to the operator's
     * {@link SqlOperator#validateCall}. Derived classes may override (as do,
     * for example {@link SqlSelect} and {@link SqlUpdate}).
     */
    @Override public void validate(SqlValidator validator, SqlValidatorScope scope) {
      validator.validateCall(this, scope);
    }
  
    @Override public void findValidOptions(
        SqlValidator validator,
        SqlValidatorScope scope,
        SqlParserPos pos,
        Collection<SqlMoniker> hintList) {
      for (SqlNode operand : getOperandList()) {
        if (operand instanceof SqlIdentifier) {
          SqlIdentifier id = (SqlIdentifier) operand;
          SqlParserPos idPos = id.getParserPosition();
          if (idPos.toString().equals(pos.toString())) {
            ((SqlValidatorImpl) validator).lookupNameCompletionHints(
                scope, id.names, pos, hintList);
            return;
          }
        }
      }
      // no valid options
    }
  
    @Override public <R> R accept(SqlVisitor<R> visitor) {
      return visitor.visit(this);
    }
  
    @Override public boolean equalsDeep(@Nullable SqlNode node, Litmus litmus) {
      if (node == this) {
        return true;
      }
      if (!(node instanceof SqlCall)) {
        return litmus.fail("{} != {}", this, node);
      }
      SqlCall that = (SqlCall) node;
  
      // Compare operators by name, not identity, because they may not
      // have been resolved yet. Use case insensitive comparison since
      // this may be a case insensitive system.
      if (!this.getOperator().getName().equalsIgnoreCase(that.getOperator().getName())) {
        return litmus.fail("{} != {}", this, node);
      }
      if (!equalDeep(this.getFunctionQuantifier(), that.getFunctionQuantifier(), litmus)) {
        return litmus.fail("{} != {} (function quantifier differs)", this, node);
      }
      return equalDeep(this.getOperandList(), that.getOperandList(), litmus);
    }
  
    /**
     * Returns a string describing the actual argument types of a call, e.g.
     * "SUBSTR(VARCHAR(12), NUMBER(3,2), INTEGER)".
     */
    public String getCallSignature(
        SqlValidator validator,
        @Nullable SqlValidatorScope scope) {
      List<String> signatureList = new ArrayList<>();
      for (final SqlNode operand : getOperandList()) {
        final RelDataType argType =
            validator.deriveType(requireNonNull(scope, "scope"), operand);
        if (null == argType) {
          continue;
        }
        signatureList.add(argType.toString());
      }
      return SqlUtil.getOperatorSignature(getOperator(), signatureList);
    }
  
    @Override public SqlMonotonicity getMonotonicity(SqlValidatorScope scope) {
      // Delegate to operator.
      final SqlCallBinding binding =
          new SqlCallBinding(scope.getValidator(), scope, this);
      return getOperator().getMonotonicity(binding);
    }
  
    /**
     * Returns whether it is the function {@code COUNT(*)}.
     *
     * @return true if function call to COUNT(*)
     */
    public boolean isCountStar() {
      SqlOperator sqlOperator = getOperator();
      if (sqlOperator.getName().equals("COUNT")
          && operandCount() == 1) {
        final SqlNode parm = operand(0);
        if (parm instanceof SqlIdentifier) {
          SqlIdentifier id = (SqlIdentifier) parm;
          if (id.isStar() && id.names.size() == 1) {
            return true;
          }
        }
      }
  
      return false;
    }
  
    @Pure
    public @Nullable SqlLiteral getFunctionQuantifier() {
      return null;
    }
  }
  