blob: f4591e47875f39c50ac7fb4bb67d5ca6409dec48 [file] [log] [blame]
/*
Derby - Class org.apache.derby.impl.sql.compile.UnaryArithmeticOperatorNode
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.derby.impl.sql.compile;
import java.sql.Types;
import java.util.List;
import org.apache.derby.iapi.error.StandardException;
import org.apache.derby.iapi.reference.SQLState;
import org.apache.derby.iapi.services.compiler.MethodBuilder;
import org.apache.derby.iapi.services.context.ContextManager;
import org.apache.derby.iapi.types.DataTypeDescriptor;
import org.apache.derby.iapi.types.TypeId;
/**
* This node represents a unary arithmetic operator
*
*/
class UnaryArithmeticOperatorNode extends UnaryOperatorNode
{
private final static String[] UNARY_OPERATORS = {"+","-","SQRT", "ABS/ABSVAL"};
private final static String[] UNARY_METHODS = {"plus","minus","sqrt", "absolute"};
// Allowed kinds
final static int K_PLUS = 0;
final static int K_MINUS = 1;
final static int K_SQRT = 2;
final static int K_ABS = 3;
/**
* This class is used to hold logically different objects for
* space efficiency. {@code kind} represents the logical object
* type. See also {@link ValueNode#isSameNodeKind}.
*/
final int kind;
/**
* @param operand The operand of the node
* @param kind unary operator identity
* @param cm context manager
* @throws StandardException
*/
UnaryArithmeticOperatorNode(
ValueNode operand,
int kind,
ContextManager cm) throws StandardException {
super(operand,
UNARY_OPERATORS[kind],
UNARY_METHODS[kind],
cm);
this.kind = kind;
}
/**
* Unary + and - require their type to be set if
* they wrap another node (e.g. a parameter) that
* requires type from its context.
* @see ValueNode#requiresTypeFromContext
*/
@Override
public boolean requiresTypeFromContext()
{
if (kind == K_PLUS ||
kind == K_MINUS) {
return operand.requiresTypeFromContext();
}
return false;
}
/**
* A +? or a -? is considered a parameter.
*/
@Override
public boolean isParameterNode()
{
if (kind == K_PLUS ||
kind == K_MINUS) {
return operand.isParameterNode();
}
return false;
}
/**
* For SQRT and ABS the parameter becomes a DOUBLE.
* For unary + and - no change is made to the
* underlying node. Once this node's type is set
* using setType, then the underlying node will have
* its type set.
*
* @exception StandardException Thrown if ? parameter doesn't
* have a type bound to it yet.
* ? parameter where it isn't allowed.
*/
@Override
void bindParameter() throws StandardException
{
if (kind == K_SQRT ||
kind == K_ABS)
{
operand.setType(
new DataTypeDescriptor(TypeId.getBuiltInTypeId(Types.DOUBLE), true));
return;
}
//Derby-582 add support for dynamic parameter for unary plus and minus
if (kind == K_MINUS ||
kind == K_PLUS)
return;
// Not expected to get here since only the above types are supported
// but the super-class method will throw an exception
super.bindParameter();
}
/**
* Bind this operator
*
* @param fromList The query's FROM list
* @param subqueryList The subquery list being built as we find SubqueryNodes
* @param aggregates The aggregate list being built as we find AggregateNodes
*
* @return The new top of the expression tree.
*
* @exception StandardException Thrown on error
*/
@Override
ValueNode bindExpression(
FromList fromList, SubqueryList subqueryList, List<AggregateNode> aggregates)
throws StandardException
{
//Return with no binding, if the type of unary minus/plus parameter is not set yet.
if (operand.requiresTypeFromContext() &&
((kind == K_PLUS ||
kind == K_MINUS))
&& operand.getTypeServices() == null)
return this;
bindOperand(fromList, subqueryList, aggregates);
if (kind == K_SQRT ||
kind == K_ABS)
{
bindSQRTABS();
}
else if (kind == K_PLUS ||
kind == K_MINUS)
{
checkOperandIsNumeric(operand.getTypeId());
}
/*
** The result type of a +, -, SQRT, ABS is the same as its operand.
*/
super.setType(operand.getTypeServices());
return this;
}
/**
* Only called for Unary +/-.
*
*/
private void checkOperandIsNumeric(TypeId operandType) throws StandardException
{
if (!operandType.isNumericTypeId())
{
throw StandardException.newException(
SQLState.LANG_UNARY_ARITHMETIC_BAD_TYPE,
(kind == K_PLUS) ? "+" : "-",
operandType.getSQLTypeName());
}
}
/**
* Do code generation for this unary plus operator
*
* @param acb The ExpressionClassBuilder for the class we're generating
* @param mb The method the expression will go into
*
* @exception StandardException Thrown on error
*/
@Override
void generateExpression(ExpressionClassBuilder acb, MethodBuilder mb)
throws StandardException
{
/* Unary + doesn't do anything. Just return the operand */
if (kind == K_PLUS)
operand.generateExpression(acb, mb);
else
super.generateExpression(acb, mb);
}
/**
* Bind SQRT or ABS
*
* @exception StandardException Thrown on error
*/
private void bindSQRTABS()
throws StandardException
{
TypeId operandType;
int jdbcType;
/*
** Check the type of the operand
*/
operandType = operand.getTypeId();
/*
* If the operand is not a build-in type, generate a bound conversion
* tree to build-in types.
*/
if (operandType.userType() )
{
operand = operand.genSQLJavaSQLTree();
}
/* DB2 doesn't cast string types to numeric types for numeric functions */
jdbcType = operandType.getJDBCTypeId();
/* Both SQRT and ABS are only allowed on numeric types */
if (!operandType.isNumericTypeId())
throw StandardException.newException(
SQLState.LANG_UNARY_FUNCTION_BAD_TYPE,
getOperatorString(), operandType.getSQLTypeName());
/* For SQRT, if operand is not a DOUBLE, convert it to DOUBLE */
if (kind == K_SQRT &&
jdbcType != Types.DOUBLE)
{
operand = new CastNode(
operand,
new DataTypeDescriptor(TypeId.getBuiltInTypeId(Types.DOUBLE), true),
getContextManager());
((CastNode) operand).bindCastNodeOnly();
}
}
/** We are overwriting this method here because for -?/+?, we now know
the type of these dynamic parameters and hence we can do the parameter
binding. The setType method will call the binding code after setting
the type of the parameter*/
@Override
void setType(DataTypeDescriptor descriptor) throws StandardException
{
if (operand.requiresTypeFromContext() && operand.getTypeServices() == null)
{
checkOperandIsNumeric(descriptor.getTypeId());
operand.setType(descriptor);
}
super.setType(descriptor);
}
@Override
boolean isSameNodeKind(ValueNode o) {
return super.isSameNodeKind(o) &&
((UnaryArithmeticOperatorNode)o).kind == kind;
}
}