001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.compile.ExtractOperatorNode
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.impl.sql.compile;
023:
024: import org.apache.derby.iapi.sql.compile.C_NodeTypes;
025:
026: import org.apache.derby.iapi.sql.dictionary.DataDictionary;
027:
028: import org.apache.derby.iapi.types.TypeId;
029: import org.apache.derby.iapi.types.DateTimeDataValue;
030: import org.apache.derby.iapi.types.DataTypeDescriptor;
031:
032: import org.apache.derby.iapi.sql.compile.TypeCompiler;
033:
034: import org.apache.derby.iapi.reference.SQLState;
035: import org.apache.derby.iapi.error.StandardException;
036:
037: import org.apache.derby.iapi.services.sanity.SanityManager;
038:
039: import java.sql.Types;
040:
041: import java.util.Vector;
042:
043: /**
044: * This node represents a unary extract operator, used to extract
045: * a field from a date/time. The field value is returned as an integer.
046: *
047: * @author ames
048: */
049: public class ExtractOperatorNode extends UnaryOperatorNode {
050:
051: static private final String fieldName[] = { "YEAR", "MONTH", "DAY",
052: "HOUR", "MINUTE", "SECOND" };
053: static private final String fieldMethod[] = { "getYear",
054: "getMonth", "getDate", "getHours", "getMinutes",
055: "getSeconds" };
056:
057: private int extractField;
058:
059: /**
060: * Initializer for a ExtractOperatorNode
061: *
062: * @param field The field to extract
063: * @param operand The operand
064: */
065: public void init(Object field, Object operand) {
066: extractField = ((Integer) field).intValue();
067: super .init(operand, "EXTRACT " + fieldName[extractField],
068: fieldMethod[extractField]);
069: }
070:
071: /**
072: * Bind this operator
073: *
074: * @param fromList The query's FROM list
075: * @param subqueryList The subquery list being built as we find SubqueryNodes
076: * @param aggregateVector The aggregate vector being built as we find AggregateNodes
077: *
078: * @return The new top of the expression tree.
079: *
080: * @exception StandardException Thrown on error
081: */
082:
083: public ValueNode bindExpression(FromList fromList,
084: SubqueryList subqueryList, Vector aggregateVector)
085: throws StandardException {
086: int operandType;
087: TypeId opTypeId;
088:
089: super .bindExpression(fromList, subqueryList, aggregateVector);
090:
091: opTypeId = operand.getTypeId();
092: operandType = opTypeId.getJDBCTypeId();
093: TypeCompiler tc = operand.getTypeCompiler();
094:
095: /*
096: ** Cast the operand, if necessary, - this function is allowed only on
097: ** date/time types. By default, we cast to DATE if extracting
098: ** YEAR, MONTH or DAY and to TIME if extracting HOUR, MINUTE or
099: ** SECOND.
100: */
101: if (opTypeId.isStringTypeId()) {
102: int castType = (extractField < 3) ? Types.DATE : Types.TIME;
103: operand = (ValueNode) getNodeFactory().getNode(
104: C_NodeTypes.CAST_NODE,
105: operand,
106: DataTypeDescriptor.getBuiltInDataTypeDescriptor(
107: castType, true, tc
108: .getCastToCharWidth(operand
109: .getTypeServices())),
110: getContextManager());
111: ((CastNode) operand).bindCastNodeOnly();
112:
113: opTypeId = operand.getTypeId();
114: operandType = opTypeId.getJDBCTypeId();
115: }
116:
117: if (!((operandType == Types.DATE)
118: || (operandType == Types.TIME) || (operandType == Types.TIMESTAMP))) {
119: throw StandardException.newException(
120: SQLState.LANG_UNARY_FUNCTION_BAD_TYPE, "EXTRACT "
121: + fieldName[extractField], opTypeId
122: .getSQLTypeName());
123: }
124:
125: /*
126: If the type is DATE, ensure the field is okay.
127: */
128: if ((operandType == Types.DATE)
129: && (extractField > DateTimeDataValue.DAY_FIELD)) {
130: throw StandardException.newException(
131: SQLState.LANG_UNARY_FUNCTION_BAD_TYPE, "EXTRACT "
132: + fieldName[extractField], opTypeId
133: .getSQLTypeName());
134: }
135:
136: /*
137: If the type is TIME, ensure the field is okay.
138: */
139: if ((operandType == Types.TIME)
140: && (extractField < DateTimeDataValue.HOUR_FIELD)) {
141: throw StandardException.newException(
142: SQLState.LANG_UNARY_FUNCTION_BAD_TYPE, "EXTRACT "
143: + fieldName[extractField], opTypeId
144: .getSQLTypeName());
145: }
146:
147: /*
148: ** The result type of extract is int,
149: ** unless it is TIMESTAMP and SECOND, in which case
150: ** for now it is DOUBLE but eventually it will need to
151: ** be DECIMAL(11,9).
152: */
153: if ((operandType == Types.TIMESTAMP)
154: && (extractField == DateTimeDataValue.SECOND_FIELD)) {
155: setType(new DataTypeDescriptor(TypeId
156: .getBuiltInTypeId(Types.DOUBLE), operand
157: .getTypeServices().isNullable()));
158: } else {
159: setType(new DataTypeDescriptor(TypeId.INTEGER_ID, operand
160: .getTypeServices().isNullable()));
161: }
162:
163: return this ;
164: }
165:
166: public String toString() {
167: if (SanityManager.DEBUG) {
168: return super .toString() + "field is "
169: + fieldName[extractField] + "\n";
170: } else {
171: return "";
172: }
173: }
174: }
|