001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.compile.TernaryOperatorNode
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.services.compiler.MethodBuilder;
025: import org.apache.derby.iapi.services.compiler.LocalField;
026: import org.apache.derby.iapi.services.io.StoredFormatIds;
027: import org.apache.derby.iapi.services.sanity.SanityManager;
028: import org.apache.derby.iapi.sql.compile.C_NodeTypes;
029: import org.apache.derby.iapi.sql.compile.Visitable;
030: import org.apache.derby.iapi.sql.compile.Visitor;
031: import org.apache.derby.iapi.sql.dictionary.DataDictionary;
032: import org.apache.derby.iapi.store.access.Qualifier;
033: import org.apache.derby.iapi.error.StandardException;
034:
035: import org.apache.derby.iapi.sql.compile.TypeCompiler;
036: import org.apache.derby.iapi.types.NumberDataValue;
037: import org.apache.derby.iapi.types.StringDataValue;
038: import org.apache.derby.iapi.types.TypeId;
039: import org.apache.derby.iapi.types.DataTypeDescriptor;
040:
041: import org.apache.derby.iapi.store.access.Qualifier;
042: import org.apache.derby.iapi.reference.SQLState;
043: import org.apache.derby.iapi.reference.ClassName;
044: import org.apache.derby.iapi.services.classfile.VMOpcode;
045:
046: import org.apache.derby.impl.sql.compile.ExpressionClassBuilder;
047: import org.apache.derby.iapi.util.JBitSet;
048: import org.apache.derby.iapi.util.ReuseFactory;
049:
050: import java.lang.reflect.Modifier;
051:
052: import java.sql.Types;
053: import java.util.Vector;
054:
055: /**
056: * A TernaryOperatorNode represents a built-in ternary operators.
057: * This covers built-in functions like substr().
058: * Java operators are not represented here: the JSQL language allows Java
059: * methods to be called from expressions, but not Java operators.
060: *
061: * @author Jerry Brenner
062: */
063:
064: public class TernaryOperatorNode extends ValueNode {
065: String operator;
066: String methodName;
067: int operatorType;
068: ValueNode receiver;
069:
070: ValueNode leftOperand;
071: ValueNode rightOperand;
072:
073: String resultInterfaceType;
074: String receiverInterfaceType;
075: String leftInterfaceType;
076: String rightInterfaceType;
077: int trimType;
078:
079: public static final int TRIM = 0;
080: public static final int LOCATE = 1;
081: public static final int SUBSTRING = 2;
082: public static final int LIKE = 3;
083: public static final int TIMESTAMPADD = 4;
084: public static final int TIMESTAMPDIFF = 5;
085: static final String[] TernaryOperators = { "trim", "LOCATE",
086: "substring", "like", "TIMESTAMPADD", "TIMESTAMPDIFF" };
087: static final String[] TernaryMethodNames = { "trim", "locate",
088: "substring", "like", "timestampAdd", "timestampDiff" };
089: static final String[] TernaryResultType = {
090: ClassName.StringDataValue, ClassName.NumberDataValue,
091: ClassName.ConcatableDataValue, ClassName.BooleanDataValue,
092: ClassName.DateTimeDataValue, ClassName.NumberDataValue };
093: static final String[][] TernaryArgType = {
094: { ClassName.StringDataValue, ClassName.StringDataValue,
095: "java.lang.Integer" },
096: { ClassName.StringDataValue, ClassName.StringDataValue,
097: ClassName.NumberDataValue },
098: { ClassName.ConcatableDataValue, ClassName.NumberDataValue,
099: ClassName.NumberDataValue },
100: { ClassName.DataValueDescriptor,
101: ClassName.DataValueDescriptor,
102: ClassName.DataValueDescriptor },
103: { ClassName.DateTimeDataValue, "java.lang.Integer",
104: ClassName.NumberDataValue }, // time.timestampadd( interval, count)
105: { ClassName.DateTimeDataValue, "java.lang.Integer",
106: ClassName.DateTimeDataValue } // time2.timestampDiff( interval, time1)
107: };
108:
109: /**
110: * Initializer for a TernaryOperatorNode
111: *
112: * @param receiver The receiver (eg, string being operated on in substr())
113: * @param leftOperand The left operand of the node
114: * @param rightOperand The right operand of the node
115: * @param operatorType The type of the operand
116: */
117:
118: public void init(Object receiver, Object leftOperand,
119: Object rightOperand, Object operatorType, Object trimType) {
120: this .receiver = (ValueNode) receiver;
121: this .leftOperand = (ValueNode) leftOperand;
122: this .rightOperand = (ValueNode) rightOperand;
123: this .operatorType = ((Integer) operatorType).intValue();
124: this .operator = (String) TernaryOperators[this .operatorType];
125: this .methodName = (String) TernaryMethodNames[this .operatorType];
126: this .resultInterfaceType = (String) TernaryResultType[this .operatorType];
127: this .receiverInterfaceType = (String) TernaryArgType[this .operatorType][0];
128: this .leftInterfaceType = (String) TernaryArgType[this .operatorType][1];
129: this .rightInterfaceType = (String) TernaryArgType[this .operatorType][2];
130: if (trimType != null)
131: this .trimType = ((Integer) trimType).intValue();
132: }
133:
134: /**
135: * Convert this object to a String. See comments in QueryTreeNode.java
136: * for how this should be done for tree printing.
137: *
138: * @return This object as a String
139: */
140:
141: public String toString() {
142: if (SanityManager.DEBUG) {
143: return "operator: " + operator + "\n" + "methodName: "
144: + methodName + "\n" + "resultInterfaceType: "
145: + resultInterfaceType + "\n"
146: + "receiverInterfaceType: " + receiverInterfaceType
147: + "\n" + "leftInterfaceType: " + leftInterfaceType
148: + "\n" + "rightInterfaceType: "
149: + rightInterfaceType + "\n" + super .toString();
150: } else {
151: return "";
152: }
153: }
154:
155: /**
156: * Set the clause that this node appears in.
157: *
158: * @param clause The clause that this node appears in.
159: */
160: public void setClause(int clause) {
161: super .setClause(clause);
162: receiver.setClause(clause);
163: leftOperand.setClause(clause);
164: if (rightOperand != null) {
165: rightOperand.setClause(clause);
166: }
167: }
168:
169: /**
170: * Prints the sub-nodes of this object. See QueryTreeNode.java for
171: * how tree printing is supposed to work.
172: *
173: * @param depth The depth of this node in the tree
174: */
175:
176: public void printSubNodes(int depth) {
177: if (SanityManager.DEBUG) {
178: super .printSubNodes(depth);
179:
180: if (receiver != null) {
181: printLabel(depth, "receiver: ");
182: receiver.treePrint(depth + 1);
183: }
184:
185: if (leftOperand != null) {
186: printLabel(depth, "leftOperand: ");
187: leftOperand.treePrint(depth + 1);
188: }
189:
190: if (rightOperand != null) {
191: printLabel(depth, "rightOperand: ");
192: rightOperand.treePrint(depth + 1);
193: }
194: }
195: }
196:
197: /**
198: * Bind this expression. This means binding the sub-expressions,
199: * as well as figuring out what the return type is for this expression.
200: *
201: * @param fromList The FROM list for the query this
202: * expression is in, for binding columns.
203: * @param subqueryList The subquery list being built as we find SubqueryNodes
204: * @param aggregateVector The aggregate vector being built as we find AggregateNodes
205: *
206: * @return The new top of the expression tree.
207: *
208: * @exception StandardException Thrown on error
209: */
210:
211: public ValueNode bindExpression(FromList fromList,
212: SubqueryList subqueryList, Vector aggregateVector)
213: throws StandardException {
214: receiver = receiver.bindExpression(fromList, subqueryList,
215: aggregateVector);
216: leftOperand = leftOperand.bindExpression(fromList,
217: subqueryList, aggregateVector);
218:
219: if (rightOperand != null) {
220: rightOperand = rightOperand.bindExpression(fromList,
221: subqueryList, aggregateVector);
222: }
223: if (operatorType == TRIM)
224: trimBind();
225: else if (operatorType == LOCATE)
226: locateBind();
227: else if (operatorType == SUBSTRING)
228: substrBind();
229: else if (operatorType == TIMESTAMPADD)
230: timestampAddBind();
231: else if (operatorType == TIMESTAMPDIFF)
232: timestampDiffBind();
233:
234: return this ;
235: }
236:
237: /**
238: * Preprocess an expression tree. We do a number of transformations
239: * here (including subqueries, IN lists, LIKE and BETWEEN) plus
240: * subquery flattening.
241: * NOTE: This is done before the outer ResultSetNode is preprocessed.
242: *
243: * @param numTables Number of tables in the DML Statement
244: * @param outerFromList FromList from outer query block
245: * @param outerSubqueryList SubqueryList from outer query block
246: * @param outerPredicateList PredicateList from outer query block
247: *
248: * @return The modified expression
249: *
250: * @exception StandardException Thrown on error
251: */
252: public ValueNode preprocess(int numTables, FromList outerFromList,
253: SubqueryList outerSubqueryList,
254: PredicateList outerPredicateList) throws StandardException {
255: receiver = receiver.preprocess(numTables, outerFromList,
256: outerSubqueryList, outerPredicateList);
257:
258: leftOperand = leftOperand.preprocess(numTables, outerFromList,
259: outerSubqueryList, outerPredicateList);
260: if (rightOperand != null) {
261: rightOperand = rightOperand.preprocess(numTables,
262: outerFromList, outerSubqueryList,
263: outerPredicateList);
264: }
265: return this ;
266: }
267:
268: /**
269: * Do code generation for this ternary operator.
270: *
271: * @param acb The ExpressionClassBuilder for the class we're generating
272: * @param mb The method the expression will go into
273: *
274: *
275: * @exception StandardException Thrown on error
276: */
277:
278: public void generateExpression(ExpressionClassBuilder acb,
279: MethodBuilder mb) throws StandardException {
280: int nargs = 0;
281: String receiverType = null;
282:
283: /* Allocate an object for re-use to hold the result of the operator */
284: LocalField field = acb.newFieldDeclaration(Modifier.PRIVATE,
285: resultInterfaceType);
286:
287: receiver.generateExpression(acb, mb);
288: if (operatorType == TRIM) {
289: mb.push(trimType);
290: mb.getField(field);
291: nargs = 2;
292: receiverType = receiverInterfaceType;
293: } else if (operatorType == LOCATE) {
294: leftOperand.generateExpression(acb, mb);
295: mb.upCast(leftInterfaceType);
296: rightOperand.generateExpression(acb, mb);
297: mb.upCast(rightInterfaceType);
298: mb.getField(field);
299: nargs = 3;
300:
301: } else if (operatorType == SUBSTRING) {
302: leftOperand.generateExpression(acb, mb);
303: mb.upCast(leftInterfaceType);
304: if (rightOperand != null) {
305: rightOperand.generateExpression(acb, mb);
306: mb.upCast(rightInterfaceType);
307: } else {
308: mb.pushNull(rightInterfaceType);
309: }
310:
311: mb.getField(field); // third arg
312: mb.push(receiver.getTypeServices().getMaximumWidth());
313: nargs = 4;
314: receiverType = receiverInterfaceType;
315: } else if (operatorType == TIMESTAMPADD
316: || operatorType == TIMESTAMPDIFF) {
317: Object intervalType = leftOperand
318: .getConstantValueAsObject();
319: if (SanityManager.DEBUG)
320: SanityManager.ASSERT(intervalType != null
321: && intervalType instanceof Integer,
322: "Invalid interval type used for " + operator);
323: mb.push(((Integer) intervalType).intValue());
324: rightOperand.generateExpression(acb, mb);
325: mb.upCast(TernaryArgType[operatorType][2]);
326: acb.getCurrentDateExpression(mb);
327: mb.getField(field);
328: nargs = 4;
329: receiverType = receiverInterfaceType;
330: }
331:
332: mb.callMethod(VMOpcode.INVOKEINTERFACE, receiverType,
333: methodName, resultInterfaceType, nargs);
334:
335: /*
336: ** Store the result of the method call in the field, so we can re-use
337: ** the object.
338: */
339: mb.putField(field);
340: }
341:
342: /**
343: * Set the leftOperand to the specified ValueNode
344: *
345: * @param newLeftOperand The new leftOperand
346: */
347: public void setLeftOperand(ValueNode newLeftOperand) {
348: leftOperand = newLeftOperand;
349: }
350:
351: /**
352: * Get the leftOperand
353: *
354: * @return The current leftOperand.
355: */
356: public ValueNode getLeftOperand() {
357: return leftOperand;
358: }
359:
360: /**
361: * Set the rightOperand to the specified ValueNode
362: *
363: * @param newRightOperand The new rightOperand
364: */
365: public void setRightOperand(ValueNode newRightOperand) {
366: rightOperand = newRightOperand;
367: }
368:
369: /**
370: * Get the rightOperand
371: *
372: * @return The current rightOperand.
373: */
374: public ValueNode getRightOperand() {
375: return rightOperand;
376: }
377:
378: /**
379: * Categorize this predicate. Initially, this means
380: * building a bit map of the referenced tables for each predicate.
381: * If the source of this ColumnReference (at the next underlying level)
382: * is not a ColumnReference or a VirtualColumnNode then this predicate
383: * will not be pushed down.
384: *
385: * For example, in:
386: * select * from (select 1 from s) a (x) where x = 1
387: * we will not push down x = 1.
388: * NOTE: It would be easy to handle the case of a constant, but if the
389: * inner SELECT returns an arbitrary expression, then we would have to copy
390: * that tree into the pushed predicate, and that tree could contain
391: * subqueries and method calls.
392: * RESOLVE - revisit this issue once we have views.
393: *
394: * @param referencedTabs JBitSet with bit map of referenced FromTables
395: * @param simplePredsOnly Whether or not to consider method
396: * calls, field references and conditional nodes
397: * when building bit map
398: *
399: * @return boolean Whether or not source.expression is a ColumnReference
400: * or a VirtualColumnNode.
401: * @exception StandardException Thrown on error
402: */
403: public boolean categorize(JBitSet referencedTabs,
404: boolean simplePredsOnly) throws StandardException {
405: boolean pushable;
406: pushable = receiver.categorize(referencedTabs, simplePredsOnly);
407: pushable = (leftOperand.categorize(referencedTabs,
408: simplePredsOnly) && pushable);
409: if (rightOperand != null) {
410: pushable = (rightOperand.categorize(referencedTabs,
411: simplePredsOnly) && pushable);
412: }
413: return pushable;
414: }
415:
416: /**
417: * Remap all ColumnReferences in this tree to be clones of the
418: * underlying expression.
419: *
420: * @return ValueNode The remapped expression tree.
421: *
422: * @exception StandardException Thrown on error
423: */
424: public ValueNode remapColumnReferencesToExpressions()
425: throws StandardException {
426: receiver = receiver.remapColumnReferencesToExpressions();
427: leftOperand = leftOperand.remapColumnReferencesToExpressions();
428: if (rightOperand != null) {
429: rightOperand = rightOperand
430: .remapColumnReferencesToExpressions();
431: }
432: return this ;
433: }
434:
435: /**
436: * Return whether or not this expression tree represents a constant expression.
437: *
438: * @return Whether or not this expression tree represents a constant expression.
439: */
440: public boolean isConstantExpression() {
441: return (receiver.isConstantExpression()
442: && leftOperand.isConstantExpression() && (rightOperand == null || rightOperand
443: .isConstantExpression()));
444: }
445:
446: /** @see ValueNode#constantExpression */
447: public boolean constantExpression(PredicateList whereClause) {
448: return (receiver.constantExpression(whereClause)
449: && leftOperand.constantExpression(whereClause) && (rightOperand == null || rightOperand
450: .constantExpression(whereClause)));
451: }
452:
453: /**
454: * Accept a visitor, and call v.visit()
455: * on child nodes as necessary.
456: *
457: * @param v the visitor
458: *
459: * @exception StandardException on error
460: */
461: public Visitable accept(Visitor v) throws StandardException {
462: Visitable returnNode = v.visit(this );
463:
464: if (v.skipChildren(this )) {
465: return returnNode;
466: }
467:
468: if (receiver != null && !v.stopTraversal()) {
469: receiver = (ValueNode) receiver.accept(v);
470: }
471:
472: if (leftOperand != null && !v.stopTraversal()) {
473: leftOperand = (ValueNode) leftOperand.accept(v);
474: }
475:
476: if (rightOperand != null && !v.stopTraversal()) {
477: rightOperand = (ValueNode) rightOperand.accept(v);
478: }
479:
480: return returnNode;
481: }
482:
483: /**
484: * Bind trim expression.
485: * @return The new top of the expression tree.
486: *
487: * @exception StandardException Thrown on error
488: */
489:
490: private ValueNode trimBind() throws StandardException {
491: TypeId receiverType;
492: TypeId resultType = TypeId.getBuiltInTypeId(Types.VARCHAR);
493:
494: // handle parameters here
495:
496: /* Is there a ? parameter for the receiver? */
497: if (receiver.requiresTypeFromContext()) {
498: /*
499: ** According to the SQL standard, if trim has a ? receiver,
500: ** its type is varchar with the implementation-defined maximum length
501: ** for a varchar.
502: */
503:
504: receiver.setType(getVarcharDescriptor());
505: }
506:
507: /* Is there a ? parameter on the left? */
508: if (leftOperand.requiresTypeFromContext()) {
509: /* Set the left operand type to varchar. */
510: leftOperand.setType(getVarcharDescriptor());
511: }
512:
513: bindToBuiltIn();
514:
515: /*
516: ** Check the type of the receiver - this function is allowed only on
517: ** string value types.
518: */
519: receiverType = receiver.getTypeId();
520: if (receiverType.userType())
521: throwBadType("trim", receiverType.getSQLTypeName());
522:
523: receiver = castArgToString(receiver);
524:
525: if ((receiverType.getTypeFormatId() == StoredFormatIds.CLOB_TYPE_ID)
526: || (receiverType.getTypeFormatId() == StoredFormatIds.NCLOB_TYPE_ID)) {
527: // special case for CLOBs: if we start with a CLOB, we have to get
528: // a CLOB as a result (as opposed to a VARCHAR), because we can have a
529: // CLOB that is beyond the max length of VARCHAR (ex. "clob(100k)").
530: // This is okay because CLOBs, like VARCHARs, allow variable-length
531: // values (which is a must for the trim to actually work).
532: resultType = receiverType;
533: }
534:
535: /*
536: ** Check the type of the leftOperand (trimSet).
537: ** The leftOperand should be a string value type.
538: */
539: TypeId leftCTI;
540: leftCTI = leftOperand.getTypeId();
541: if (leftCTI.userType())
542: throwBadType("trim", leftCTI.getSQLTypeName());
543:
544: leftOperand = castArgToString(leftOperand);
545:
546: /*
547: ** The result type of trim is varchar.
548: */
549: setResultType(resultType);
550:
551: return this ;
552: }
553:
554: /*
555: ** set result type for operator
556: */
557: private void setResultType(TypeId resultType)
558: throws StandardException {
559: setType(new DataTypeDescriptor(resultType, true, receiver
560: .getTypeServices().getMaximumWidth()));
561: }
562:
563: /**
564: * Bind locate operator
565: *
566: * @return The new top of the expression tree.
567: *
568: * @exception StandardException Thrown on error
569: */
570:
571: public ValueNode locateBind() throws StandardException {
572: TypeId firstOperandType, secondOperandType, offsetType;
573:
574: /*
575: * Is there a ? parameter for the first arg. Copy the
576: * left/firstOperand's. If the left/firstOperand are both parameters,
577: * both will be max length.
578: */
579: if (receiver.requiresTypeFromContext()) {
580: if (leftOperand.requiresTypeFromContext()) {
581: receiver.setType(getVarcharDescriptor());
582: } else {
583: if (leftOperand.getTypeId().isStringTypeId()) {
584: receiver.setType(leftOperand.getTypeServices());
585: }
586: }
587: }
588:
589: /*
590: * Is there a ? parameter for the second arg. Copy the receiver's.
591: * If the receiver are both parameters, both will be max length.
592: */
593: if (leftOperand.requiresTypeFromContext()) {
594: if (receiver.requiresTypeFromContext()) {
595: leftOperand.setType(getVarcharDescriptor());
596: } else {
597: if (receiver.getTypeId().isStringTypeId()) {
598: leftOperand.setType(receiver.getTypeServices());
599: }
600: }
601: }
602:
603: /*
604: * Is there a ? paramter for the third arg. It will be an int.
605: */
606: if (rightOperand.requiresTypeFromContext()) {
607: rightOperand.setType(new DataTypeDescriptor(
608: TypeId.INTEGER_ID, true));
609: }
610:
611: bindToBuiltIn();
612:
613: /*
614: ** Check the type of the operand - this function is allowed only
615: ** for: receiver = CHAR
616: ** firstOperand = CHAR
617: ** secondOperand = INT
618: */
619: secondOperandType = leftOperand.getTypeId();
620: offsetType = rightOperand.getTypeId();
621: firstOperandType = receiver.getTypeId();
622:
623: if (!firstOperandType.isStringTypeId()
624: || !secondOperandType.isStringTypeId()
625: || offsetType.getJDBCTypeId() != Types.INTEGER)
626: throw StandardException.newException(
627: SQLState.LANG_DB2_FUNCTION_INCOMPATIBLE, "LOCATE",
628: "FUNCTION");
629:
630: /*
631: ** The result type of a LocateFunctionNode is an integer.
632: */
633: setType(new DataTypeDescriptor(TypeId.INTEGER_ID, receiver
634: .getTypeServices().isNullable()));
635:
636: return this ;
637: }
638:
639: /* cast arg to a varchar */
640: protected ValueNode castArgToString(ValueNode vn)
641: throws StandardException {
642: TypeCompiler vnTC = vn.getTypeCompiler();
643: if (!vn.getTypeId().isStringTypeId()) {
644: ValueNode newNode = (ValueNode) getNodeFactory().getNode(
645: C_NodeTypes.CAST_NODE,
646: vn,
647: DataTypeDescriptor.getBuiltInDataTypeDescriptor(
648: Types.VARCHAR, true, vnTC
649: .getCastToCharWidth(vn
650: .getTypeServices())),
651: getContextManager());
652: ((CastNode) newNode).bindCastNodeOnly();
653: return newNode;
654: }
655: return vn;
656: }
657:
658: /**
659: * Bind substr expression.
660: *
661: * @return The new top of the expression tree.
662: *
663: * @exception StandardException Thrown on error
664: */
665:
666: public ValueNode substrBind() throws StandardException {
667: TypeId receiverType;
668: TypeId resultType;
669:
670: // handle parameters here
671:
672: /* Is there a ? parameter for the receiver? */
673: if (receiver.requiresTypeFromContext()) {
674: /*
675: ** According to the SQL standard, if substr has a ? receiver,
676: ** its type is varchar with the implementation-defined maximum length
677: ** for a varchar.
678: */
679:
680: receiver.setType(getVarcharDescriptor());
681: }
682:
683: /* Is there a ? parameter on the left? */
684: if (leftOperand.requiresTypeFromContext()) {
685: /* Set the left operand type to int. */
686: leftOperand.setType(new DataTypeDescriptor(
687: TypeId.INTEGER_ID, true));
688: }
689:
690: /* Is there a ? parameter on the right? */
691: if ((rightOperand != null)
692: && rightOperand.requiresTypeFromContext()) {
693: /* Set the right operand type to int. */
694: rightOperand.setType(new DataTypeDescriptor(
695: TypeId.INTEGER_ID, true));
696: }
697:
698: bindToBuiltIn();
699:
700: if (!leftOperand.getTypeId().isNumericTypeId()
701: || (rightOperand != null && !rightOperand.getTypeId()
702: .isNumericTypeId()))
703: throw StandardException.newException(
704: SQLState.LANG_DB2_FUNCTION_INCOMPATIBLE, "SUBSTR",
705: "FUNCTION");
706:
707: /*
708: ** Check the type of the receiver - this function is allowed only on
709: ** string value types.
710: */
711: resultType = receiverType = receiver.getTypeId();
712: switch (receiverType.getJDBCTypeId()) {
713: case Types.CHAR:
714: case Types.VARCHAR:
715: case Types.LONGVARCHAR:
716: case Types.CLOB:
717: break;
718: default: {
719: throwBadType("SUBSTR", receiverType.getSQLTypeName());
720: }
721: }
722:
723: // Determine the maximum length of the result
724: int resultLen = receiver.getTypeServices().getMaximumWidth();
725:
726: if (rightOperand != null
727: && rightOperand instanceof ConstantNode) {
728: if (((ConstantNode) rightOperand).getValue().getInt() < resultLen)
729: resultLen = ((ConstantNode) rightOperand).getValue()
730: .getInt();
731: }
732:
733: /*
734: ** The result type of substr is a string type
735: */
736: setType(new DataTypeDescriptor(resultType, true, resultLen));
737:
738: return this ;
739: }
740:
741: /**
742: * Bind TIMESTAMPADD expression.
743: *
744: * @return The new top of the expression tree.
745: *
746: * @exception StandardException Thrown on error
747: */
748:
749: private ValueNode timestampAddBind() throws StandardException {
750: if (!bindParameter(rightOperand, Types.INTEGER)) {
751: int jdbcType = rightOperand.getTypeId().getJDBCTypeId();
752: if (jdbcType != Types.TINYINT && jdbcType != Types.SMALLINT
753: && jdbcType != Types.INTEGER
754: && jdbcType != Types.BIGINT)
755: throw StandardException.newException(
756: SQLState.LANG_INVALID_FUNCTION_ARG_TYPE,
757: rightOperand.getTypeId().getSQLTypeName(),
758: ReuseFactory.getInteger(2), operator);
759: }
760: bindDateTimeArg(receiver, 3);
761: setType(DataTypeDescriptor
762: .getBuiltInDataTypeDescriptor(Types.TIMESTAMP));
763: return this ;
764: } // end of timestampAddBind
765:
766: /**
767: * Bind TIMESTAMPDIFF expression.
768: *
769: * @return The new top of the expression tree.
770: *
771: * @exception StandardException Thrown on error
772: */
773:
774: private ValueNode timestampDiffBind() throws StandardException {
775: bindDateTimeArg(rightOperand, 2);
776: bindDateTimeArg(receiver, 3);
777: setType(DataTypeDescriptor
778: .getBuiltInDataTypeDescriptor(Types.INTEGER));
779: return this ;
780: } // End of timestampDiffBind
781:
782: private void bindDateTimeArg(ValueNode arg, int argNumber)
783: throws StandardException {
784: if (!bindParameter(arg, Types.TIMESTAMP)) {
785: if (!arg.getTypeId().isDateTimeTimeStampTypeId())
786: throw StandardException.newException(
787: SQLState.LANG_INVALID_FUNCTION_ARG_TYPE, arg
788: .getTypeId().getSQLTypeName(),
789: ReuseFactory.getInteger(argNumber), operator);
790: }
791: } // end of bindDateTimeArg
792:
793: private boolean bindParameter(ValueNode arg, int jdbcType)
794: throws StandardException {
795: if (arg.requiresTypeFromContext() && arg.getTypeId() == null) {
796: arg.setType(new DataTypeDescriptor(TypeId
797: .getBuiltInTypeId(jdbcType), true));
798: return true;
799: }
800: return false;
801: } // end of bindParameter
802:
803: public ValueNode getReceiver() {
804: return receiver;
805: }
806:
807: /* throw bad type message */
808: private void throwBadType(String funcName, String type)
809: throws StandardException {
810: throw StandardException.newException(
811: SQLState.LANG_UNARY_FUNCTION_BAD_TYPE, funcName, type);
812: }
813:
814: /* bind arguments to built in types */
815: protected void bindToBuiltIn() throws StandardException {
816: /* If the receiver is not a built-in type, then generate a bound conversion
817: * tree to a built-in type.
818: */
819: if (receiver.getTypeId().userType()) {
820: receiver = receiver.genSQLJavaSQLTree();
821: }
822:
823: /* If the left operand is not a built-in type, then generate a bound conversion
824: * tree to a built-in type.
825: */
826: if (leftOperand.getTypeId().userType()) {
827: leftOperand = leftOperand.genSQLJavaSQLTree();
828: }
829:
830: /* If the right operand is not a built-in type, then generate a bound conversion
831: * tree to a built-in type.
832: */
833: if (rightOperand != null) {
834: if (rightOperand.getTypeId().userType()) {
835: rightOperand = rightOperand.genSQLJavaSQLTree();
836: }
837: }
838: }
839:
840: private DataTypeDescriptor getVarcharDescriptor() {
841: return new DataTypeDescriptor(TypeId
842: .getBuiltInTypeId(Types.VARCHAR), true);
843: }
844:
845: protected boolean isEquivalent(ValueNode o)
846: throws StandardException {
847: if (isSameNodeType(o)) {
848: TernaryOperatorNode other = (TernaryOperatorNode) o;
849:
850: /*
851: * SUBSTR function can either have 2 or 3 arguments. In the
852: * 2-args case, rightOperand will be null and thus needs
853: * additional handling in the equivalence check.
854: */
855: return (other.methodName.equals(methodName)
856: && other.receiver.isEquivalent(receiver)
857: && other.leftOperand.isEquivalent(leftOperand) && ((rightOperand == null && other.rightOperand == null) || (other.rightOperand != null && other.rightOperand
858: .isEquivalent(rightOperand))));
859: }
860: return false;
861: }
862: }
|