| /* |
| * 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 java.util.Objects; |
| |
| import static org.apache.calcite.linq4j.Nullness.castNonNull; |
| |
| /** |
| * 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()); |
| } |
| |
| @Override public void unparse( |
| SqlWriter writer, |
| int leftPrec, |
| int rightPrec) { |
| final SqlOperator operator = getOperator(); |
| final SqlDialect dialect = writer.getDialect(); |
| if (leftPrec > operator.getLeftPrec() |
| || (operator.getRightPrec() <= rightPrec && (rightPrec != 0)) |
| || writer.isAlwaysUseParentheses() && isA(SqlKind.EXPRESSION)) { |
| 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(Objects.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; |
| } |
| } |