blob: 683b16e520b67f27fb3539614a3f6fd673260fca [file] [log] [blame]
/*
// Licensed to Julian Hyde under one or more contributor license
// agreements. See the NOTICE file distributed with this work for
// additional information regarding copyright ownership.
//
// Julian Hyde 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.eigenbase.sql;
import java.util.*;
import org.eigenbase.reltype.*;
import org.eigenbase.sql.parser.*;
import org.eigenbase.sql.util.*;
import org.eigenbase.sql.validate.*;
import com.google.common.collect.ImmutableList;
/**
* 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 class SqlCall
extends SqlNode
{
//~ Instance fields --------------------------------------------------------
private SqlOperator operator;
public final SqlNode [] operands;
private final SqlLiteral functionQuantifier;
private final boolean expanded;
//~ Constructors -----------------------------------------------------------
public SqlCall(
SqlOperator operator,
SqlNode [] operands,
SqlParserPos pos)
{
this(operator, operands, pos, false, null);
}
protected SqlCall(
SqlOperator operator,
SqlNode [] operands,
SqlParserPos pos,
boolean expanded,
SqlLiteral functionQualifier)
{
super(pos);
this.operator = operator;
this.operands = operands;
this.expanded = expanded;
this.functionQuantifier = functionQualifier;
}
//~ Methods ----------------------------------------------------------------
public SqlKind getKind()
{
return operator.getKind();
}
/**
* Whether this call was created by expanding a parentheses-free call to
* what was syntactically an identifier.
*/
public boolean isExpanded()
{
return expanded;
}
// REVIEW jvs 10-Sept-2003: I added this to allow for some rewrite by
// SqlValidator. Is mutability OK?
public void setOperand(
int i,
SqlNode operand)
{
operands[i] = operand;
}
public void setOperator(SqlOperator operator)
{
this.operator = operator;
}
public SqlOperator getOperator()
{
return operator;
}
public SqlNode [] getOperands()
{
return operands;
}
public List<SqlNode> getOperandList()
{
return ImmutableList.copyOf(operands);
}
public SqlNode clone(SqlParserPos pos)
{
return operator.createCall(
pos,
SqlNode.cloneArray(operands));
}
public void unparse(
SqlWriter writer,
int leftPrec,
int rightPrec)
{
if ((leftPrec > operator.getLeftPrec())
|| ((operator.getRightPrec() <= rightPrec) && (rightPrec != 0))
|| (writer.isAlwaysUseParentheses() && isA(SqlKind.EXPRESSION)))
{
final SqlWriter.Frame frame = writer.startList("(", ")");
operator.unparse(writer, operands, 0, 0);
writer.endList(frame);
} else {
if (functionQuantifier != null) {
// REVIEW jvs 24-July-2006: This is currently the only
// way to get the quantifier through to the unparse
SqlUtil.unparseFunctionSyntax(
operator,
writer,
operands,
true,
functionQuantifier);
} else {
operator.unparse(writer, operands, 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}).
*/
public void validate(SqlValidator validator, SqlValidatorScope scope)
{
validator.validateCall(this, scope);
}
public void findValidOptions(
SqlValidator validator,
SqlValidatorScope scope,
SqlParserPos pos,
List<SqlMoniker> hintList)
{
for (SqlNode operand : getOperands()) {
if (operand instanceof SqlIdentifier) {
SqlIdentifier id = (SqlIdentifier) operand;
SqlParserPos idPos = id.getParserPosition();
if (idPos.toString().equals(pos.toString())) {
((SqlValidatorImpl) validator).lookupNameCompletionHints(
scope,
Arrays.asList(id.names),
pos,
hintList);
return;
}
}
}
// no valid options
}
public <R> R accept(SqlVisitor<R> visitor)
{
return visitor.visit(this);
}
public boolean equalsDeep(SqlNode node, boolean fail)
{
if (!(node instanceof SqlCall)) {
assert !fail : this + "!=" + node;
return false;
}
SqlCall that = (SqlCall) node;
// Compare operators by name, not identity, because they may not
// have been resolved yet.
if (!this.operator.getName().equals(that.operator.getName())) {
assert !fail : this + "!=" + node;
return false;
}
if (this.operands.length != that.operands.length) {
assert !fail : this + "!=" + node;
return false;
}
for (int i = 0; i < this.operands.length; i++) {
if (!SqlNode.equalDeep(this.operands[i], that.operands[i], fail)) {
return false;
}
}
return true;
}
/**
* Returns a string describing the actual argument types of a call, e.g.
* "SUBSTR(VARCHAR(12), NUMBER(3,2), INTEGER)".
*/
protected String getCallSignature(
SqlValidator validator,
SqlValidatorScope scope)
{
List<String> signatureList = new ArrayList<String>();
for (int i = 0; i < operands.length; i++) {
final SqlNode operand = operands[i];
final RelDataType argType = validator.deriveType(scope, operand);
if (null == argType) {
continue;
}
signatureList.add(argType.toString());
}
return SqlUtil.getOperatorSignature(operator, signatureList);
}
public SqlMonotonicity getMonotonicity(SqlValidatorScope scope)
{
// Delegate to operator.
return operator.getMonotonicity(this, scope);
}
/**
* Tests whether operator name matches supplied value.
*
* @param name Test string
*
* @return whether operator name matches parameter
*/
public boolean isName(String name)
{
return operator.isName(name);
}
/**
* Test to see if it is the function COUNT(*)
*
* @return boolean true if function call to COUNT(*)
*/
public boolean isCountStar()
{
if (operator.isName("COUNT") && (operands.length == 1)) {
final SqlNode parm = operands[0];
if (parm instanceof SqlIdentifier) {
SqlIdentifier id = (SqlIdentifier) parm;
if (id.isStar() && (id.names.length == 1)) {
return true;
}
}
}
return false;
}
public SqlLiteral getFunctionQuantifier()
{
return functionQuantifier;
}
}
// End SqlCall.java