KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > derby > impl > sql > compile > TernaryOperatorNode


1 /*
2
3    Derby - Class org.apache.derby.impl.sql.compile.TernaryOperatorNode
4
5    Licensed to the Apache Software Foundation (ASF) under one or more
6    contributor license agreements. See the NOTICE file distributed with
7    this work for additional information regarding copyright ownership.
8    The ASF licenses this file to you under the Apache License, Version 2.0
9    (the "License"); you may not use this file except in compliance with
10    the License. You may obtain a copy of the License at
11
12       http://www.apache.org/licenses/LICENSE-2.0
13
14    Unless required by applicable law or agreed to in writing, software
15    distributed under the License is distributed on an "AS IS" BASIS,
16    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17    See the License for the specific language governing permissions and
18    limitations under the License.
19
20  */

21
22 package org.apache.derby.impl.sql.compile;
23
24 import org.apache.derby.iapi.services.compiler.MethodBuilder;
25 import org.apache.derby.iapi.services.compiler.LocalField;
26 import org.apache.derby.iapi.services.io.StoredFormatIds;
27 import org.apache.derby.iapi.services.sanity.SanityManager;
28 import org.apache.derby.iapi.sql.compile.C_NodeTypes;
29 import org.apache.derby.iapi.sql.compile.Visitable;
30 import org.apache.derby.iapi.sql.compile.Visitor;
31 import org.apache.derby.iapi.sql.dictionary.DataDictionary;
32 import org.apache.derby.iapi.store.access.Qualifier;
33 import org.apache.derby.iapi.error.StandardException;
34
35 import org.apache.derby.iapi.sql.compile.TypeCompiler;
36 import org.apache.derby.iapi.types.NumberDataValue;
37 import org.apache.derby.iapi.types.StringDataValue;
38 import org.apache.derby.iapi.types.TypeId;
39 import org.apache.derby.iapi.types.DataTypeDescriptor;
40
41 import org.apache.derby.iapi.store.access.Qualifier;
42 import org.apache.derby.iapi.reference.SQLState;
43 import org.apache.derby.iapi.reference.ClassName;
44 import org.apache.derby.iapi.services.classfile.VMOpcode;
45
46 import org.apache.derby.impl.sql.compile.ExpressionClassBuilder;
47 import org.apache.derby.iapi.util.JBitSet;
48 import org.apache.derby.iapi.util.ReuseFactory;
49
50 import java.lang.reflect.Modifier JavaDoc;
51
52 import java.sql.Types JavaDoc;
53 import java.util.Vector JavaDoc;
54 /**
55  * A TernaryOperatorNode represents a built-in ternary operators.
56  * This covers built-in functions like substr().
57  * Java operators are not represented here: the JSQL language allows Java
58  * methods to be called from expressions, but not Java operators.
59  *
60  * @author Jerry Brenner
61  */

62
63 public class TernaryOperatorNode extends ValueNode
64 {
65     String JavaDoc operator;
66     String JavaDoc methodName;
67     int operatorType;
68     ValueNode receiver;
69
70     ValueNode leftOperand;
71     ValueNode rightOperand;
72
73     String JavaDoc resultInterfaceType;
74     String JavaDoc receiverInterfaceType;
75     String JavaDoc leftInterfaceType;
76     String JavaDoc rightInterfaceType;
77     int trimType;
78
79     public static final int TRIM = 0;
80     public static final int LOCATE = 1;
81     public static final int SUBSTRING = 2;
82     public static final int LIKE = 3;
83     public static final int TIMESTAMPADD = 4;
84     public static final int TIMESTAMPDIFF = 5;
85     static final String JavaDoc[] TernaryOperators = {"trim", "LOCATE", "substring", "like", "TIMESTAMPADD", "TIMESTAMPDIFF"};
86     static final String JavaDoc[] TernaryMethodNames = {"trim", "locate", "substring", "like", "timestampAdd", "timestampDiff"};
87     static final String JavaDoc[] TernaryResultType = {ClassName.StringDataValue,
88             ClassName.NumberDataValue,
89             ClassName.ConcatableDataValue,
90             ClassName.BooleanDataValue,
91             ClassName.DateTimeDataValue,
92             ClassName.NumberDataValue};
93     static final String JavaDoc[][] TernaryArgType = {
94     {ClassName.StringDataValue, ClassName.StringDataValue, "java.lang.Integer"},
95     {ClassName.StringDataValue, ClassName.StringDataValue, ClassName.NumberDataValue},
96     {ClassName.ConcatableDataValue, ClassName.NumberDataValue, ClassName.NumberDataValue},
97     {ClassName.DataValueDescriptor, ClassName.DataValueDescriptor, ClassName.DataValueDescriptor},
98     {ClassName.DateTimeDataValue, "java.lang.Integer", ClassName.NumberDataValue}, // time.timestampadd( interval, count)
99
{ClassName.DateTimeDataValue, "java.lang.Integer", ClassName.DateTimeDataValue}// time2.timestampDiff( interval, time1)
100
};
101
102     /**
103      * Initializer for a TernaryOperatorNode
104      *
105      * @param receiver The receiver (eg, string being operated on in substr())
106      * @param leftOperand The left operand of the node
107      * @param rightOperand The right operand of the node
108      * @param operatorType The type of the operand
109      */

110
111     public void init(
112                     Object JavaDoc receiver,
113                     Object JavaDoc leftOperand,
114                     Object JavaDoc rightOperand,
115                     Object JavaDoc operatorType,
116                     Object JavaDoc trimType)
117     {
118         this.receiver = (ValueNode) receiver;
119         this.leftOperand = (ValueNode) leftOperand;
120         this.rightOperand = (ValueNode) rightOperand;
121         this.operatorType = ((Integer JavaDoc) operatorType).intValue();
122         this.operator = (String JavaDoc) TernaryOperators[this.operatorType];
123         this.methodName = (String JavaDoc) TernaryMethodNames[this.operatorType];
124         this.resultInterfaceType = (String JavaDoc) TernaryResultType[this.operatorType];
125         this.receiverInterfaceType = (String JavaDoc) TernaryArgType[this.operatorType][0];
126         this.leftInterfaceType = (String JavaDoc) TernaryArgType[this.operatorType][1];
127         this.rightInterfaceType = (String JavaDoc) TernaryArgType[this.operatorType][2];
128         if (trimType != null)
129                 this.trimType = ((Integer JavaDoc) trimType).intValue();
130     }
131
132     /**
133      * Convert this object to a String. See comments in QueryTreeNode.java
134      * for how this should be done for tree printing.
135      *
136      * @return This object as a String
137      */

138
139     public String JavaDoc toString()
140     {
141         if (SanityManager.DEBUG)
142         {
143             return "operator: " + operator + "\n" +
144                 "methodName: " + methodName + "\n" +
145                 "resultInterfaceType: " + resultInterfaceType + "\n" +
146                 "receiverInterfaceType: " + receiverInterfaceType + "\n" +
147                 "leftInterfaceType: " + leftInterfaceType + "\n" +
148                 "rightInterfaceType: " + rightInterfaceType + "\n" +
149                 super.toString();
150         }
151         else
152         {
153             return "";
154         }
155     }
156
157     /**
158      * Set the clause that this node appears in.
159      *
160      * @param clause The clause that this node appears in.
161      */

162     public void setClause(int clause)
163     {
164         super.setClause(clause);
165         receiver.setClause(clause);
166         leftOperand.setClause(clause);
167         if (rightOperand != null)
168         {
169             rightOperand.setClause(clause);
170         }
171     }
172
173     /**
174      * Prints the sub-nodes of this object. See QueryTreeNode.java for
175      * how tree printing is supposed to work.
176      *
177      * @param depth The depth of this node in the tree
178      */

179
180     public void printSubNodes(int depth)
181     {
182         if (SanityManager.DEBUG)
183         {
184             super.printSubNodes(depth);
185
186             if (receiver != null)
187             {
188                 printLabel(depth, "receiver: ");
189                 receiver.treePrint(depth + 1);
190             }
191
192             if (leftOperand != null)
193             {
194                 printLabel(depth, "leftOperand: ");
195                 leftOperand.treePrint(depth + 1);
196             }
197
198             if (rightOperand != null)
199             {
200                 printLabel(depth, "rightOperand: ");
201                 rightOperand.treePrint(depth + 1);
202             }
203         }
204     }
205
206     /**
207      * Bind this expression. This means binding the sub-expressions,
208      * as well as figuring out what the return type is for this expression.
209      *
210      * @param fromList The FROM list for the query this
211      * expression is in, for binding columns.
212      * @param subqueryList The subquery list being built as we find SubqueryNodes
213      * @param aggregateVector The aggregate vector being built as we find AggregateNodes
214      *
215      * @return The new top of the expression tree.
216      *
217      * @exception StandardException Thrown on error
218      */

219
220     public ValueNode bindExpression(FromList fromList, SubqueryList subqueryList,
221         Vector JavaDoc aggregateVector)
222             throws StandardException
223     {
224         receiver = receiver.bindExpression(fromList, subqueryList,
225             aggregateVector);
226         leftOperand = leftOperand.bindExpression(fromList, subqueryList,
227             aggregateVector);
228
229         if (rightOperand != null)
230         {
231             rightOperand = rightOperand.bindExpression(fromList, subqueryList,
232                 aggregateVector);
233         }
234         if (operatorType == TRIM)
235             trimBind();
236         else if (operatorType == LOCATE)
237             locateBind();
238         else if (operatorType == SUBSTRING)
239             substrBind();
240         else if (operatorType == TIMESTAMPADD)
241             timestampAddBind();
242         else if (operatorType == TIMESTAMPDIFF)
243             timestampDiffBind();
244
245         return this;
246     }
247
248     /**
249      * Preprocess an expression tree. We do a number of transformations
250      * here (including subqueries, IN lists, LIKE and BETWEEN) plus
251      * subquery flattening.
252      * NOTE: This is done before the outer ResultSetNode is preprocessed.
253      *
254      * @param numTables Number of tables in the DML Statement
255      * @param outerFromList FromList from outer query block
256      * @param outerSubqueryList SubqueryList from outer query block
257      * @param outerPredicateList PredicateList from outer query block
258      *
259      * @return The modified expression
260      *
261      * @exception StandardException Thrown on error
262      */

263     public ValueNode preprocess(int numTables,
264                                 FromList outerFromList,
265                                 SubqueryList outerSubqueryList,
266                                 PredicateList outerPredicateList)
267                     throws StandardException
268     {
269         receiver = receiver.preprocess(numTables,
270                                              outerFromList, outerSubqueryList,
271                                              outerPredicateList);
272
273         leftOperand = leftOperand.preprocess(numTables,
274                                              outerFromList, outerSubqueryList,
275                                              outerPredicateList);
276         if (rightOperand != null)
277         {
278             rightOperand = rightOperand.preprocess(numTables,
279                                                    outerFromList, outerSubqueryList,
280                                                    outerPredicateList);
281         }
282         return this;
283     }
284     /**
285      * Do code generation for this ternary operator.
286      *
287      * @param acb The ExpressionClassBuilder for the class we're generating
288      * @param mb The method the expression will go into
289      *
290      *
291      * @exception StandardException Thrown on error
292      */

293
294     public void generateExpression(ExpressionClassBuilder acb,
295                                             MethodBuilder mb)
296         throws StandardException
297     {
298         int nargs = 0;
299         String JavaDoc receiverType = null;
300
301         /* Allocate an object for re-use to hold the result of the operator */
302         LocalField field = acb.newFieldDeclaration(Modifier.PRIVATE, resultInterfaceType);
303
304         receiver.generateExpression(acb, mb);
305         if (operatorType == TRIM)
306         {
307             mb.push(trimType);
308             mb.getField(field);
309             nargs = 2;
310             receiverType = receiverInterfaceType;
311         }
312         else if (operatorType == LOCATE)
313         {
314             leftOperand.generateExpression(acb, mb);
315             mb.upCast(leftInterfaceType);
316             rightOperand.generateExpression(acb, mb);
317             mb.upCast(rightInterfaceType);
318             mb.getField(field);
319             nargs = 3;
320         
321         }
322         else if (operatorType == SUBSTRING)
323         {
324             leftOperand.generateExpression(acb, mb);
325             mb.upCast(leftInterfaceType);
326             if (rightOperand != null)
327             {
328                 rightOperand.generateExpression(acb, mb);
329                 mb.upCast(rightInterfaceType);
330             }
331             else
332             {
333                 mb.pushNull(rightInterfaceType);
334             }
335
336             mb.getField(field); // third arg
337
mb.push(receiver.getTypeServices().getMaximumWidth());
338             nargs = 4;
339             receiverType = receiverInterfaceType;
340         }
341         else if (operatorType == TIMESTAMPADD || operatorType == TIMESTAMPDIFF)
342         {
343             Object JavaDoc intervalType = leftOperand.getConstantValueAsObject();
344             if( SanityManager.DEBUG)
345                 SanityManager.ASSERT( intervalType != null && intervalType instanceof Integer JavaDoc,
346                                       "Invalid interval type used for " + operator);
347             mb.push( ((Integer JavaDoc) intervalType).intValue());
348             rightOperand.generateExpression( acb, mb);
349             mb.upCast( TernaryArgType[ operatorType][2]);
350             acb.getCurrentDateExpression( mb);
351             mb.getField(field);
352             nargs = 4;
353             receiverType = receiverInterfaceType;
354         }
355             
356         mb.callMethod(VMOpcode.INVOKEINTERFACE, receiverType, methodName, resultInterfaceType, nargs);
357
358         /*
359         ** Store the result of the method call in the field, so we can re-use
360         ** the object.
361         */

362         mb.putField(field);
363     }
364
365     /**
366      * Set the leftOperand to the specified ValueNode
367      *
368      * @param newLeftOperand The new leftOperand
369      */

370     public void setLeftOperand(ValueNode newLeftOperand)
371     {
372         leftOperand = newLeftOperand;
373     }
374
375     /**
376      * Get the leftOperand
377      *
378      * @return The current leftOperand.
379      */

380     public ValueNode getLeftOperand()
381     {
382         return leftOperand;
383     }
384
385     /**
386      * Set the rightOperand to the specified ValueNode
387      *
388      * @param newRightOperand The new rightOperand
389      */

390     public void setRightOperand(ValueNode newRightOperand)
391     {
392         rightOperand = newRightOperand;
393     }
394
395     /**
396      * Get the rightOperand
397      *
398      * @return The current rightOperand.
399      */

400     public ValueNode getRightOperand()
401     {
402         return rightOperand;
403     }
404
405     /**
406      * Categorize this predicate. Initially, this means
407      * building a bit map of the referenced tables for each predicate.
408      * If the source of this ColumnReference (at the next underlying level)
409      * is not a ColumnReference or a VirtualColumnNode then this predicate
410      * will not be pushed down.
411      *
412      * For example, in:
413      * select * from (select 1 from s) a (x) where x = 1
414      * we will not push down x = 1.
415      * NOTE: It would be easy to handle the case of a constant, but if the
416      * inner SELECT returns an arbitrary expression, then we would have to copy
417      * that tree into the pushed predicate, and that tree could contain
418      * subqueries and method calls.
419      * RESOLVE - revisit this issue once we have views.
420      *
421      * @param referencedTabs JBitSet with bit map of referenced FromTables
422      * @param simplePredsOnly Whether or not to consider method
423      * calls, field references and conditional nodes
424      * when building bit map
425      *
426      * @return boolean Whether or not source.expression is a ColumnReference
427      * or a VirtualColumnNode.
428      * @exception StandardException Thrown on error
429      */

430     public boolean categorize(JBitSet referencedTabs, boolean simplePredsOnly)
431         throws StandardException
432     {
433         boolean pushable;
434         pushable = receiver.categorize(referencedTabs, simplePredsOnly);
435         pushable = (leftOperand.categorize(referencedTabs, simplePredsOnly) && pushable);
436         if (rightOperand != null)
437         {
438             pushable = (rightOperand.categorize(referencedTabs, simplePredsOnly) && pushable);
439         }
440         return pushable;
441     }
442
443     /**
444      * Remap all ColumnReferences in this tree to be clones of the
445      * underlying expression.
446      *
447      * @return ValueNode The remapped expression tree.
448      *
449      * @exception StandardException Thrown on error
450      */

451     public ValueNode remapColumnReferencesToExpressions()
452         throws StandardException
453     {
454         receiver = receiver.remapColumnReferencesToExpressions();
455         leftOperand = leftOperand.remapColumnReferencesToExpressions();
456         if (rightOperand != null)
457         {
458             rightOperand = rightOperand.remapColumnReferencesToExpressions();
459         }
460         return this;
461     }
462
463     /**
464      * Return whether or not this expression tree represents a constant expression.
465      *
466      * @return Whether or not this expression tree represents a constant expression.
467      */

468     public boolean isConstantExpression()
469     {
470         return (receiver.isConstantExpression() &&
471                 leftOperand.isConstantExpression() &&
472                 (rightOperand == null || rightOperand.isConstantExpression()));
473     }
474
475     /** @see ValueNode#constantExpression */
476     public boolean constantExpression(PredicateList whereClause)
477     {
478         return (receiver.constantExpression(whereClause) &&
479                 leftOperand.constantExpression(whereClause) &&
480                 (rightOperand == null ||
481                     rightOperand.constantExpression(whereClause)));
482     }
483
484     /**
485      * Accept a visitor, and call v.visit()
486      * on child nodes as necessary.
487      *
488      * @param v the visitor
489      *
490      * @exception StandardException on error
491      */

492     public Visitable accept(Visitor v)
493         throws StandardException
494     {
495         Visitable returnNode = v.visit(this);
496     
497         if (v.skipChildren(this))
498         {
499             return returnNode;
500         }
501
502         if (receiver != null && !v.stopTraversal())
503         {
504             receiver = (ValueNode)receiver.accept(v);
505         }
506
507         if (leftOperand != null && !v.stopTraversal())
508         {
509             leftOperand = (ValueNode)leftOperand.accept(v);
510         }
511
512         if (rightOperand != null && !v.stopTraversal())
513         {
514             rightOperand = (ValueNode)rightOperand.accept(v);
515         }
516         
517         return returnNode;
518     }
519     /**
520      * Bind trim expression.
521      * @return The new top of the expression tree.
522      *
523      * @exception StandardException Thrown on error
524      */

525
526     private ValueNode trimBind()
527             throws StandardException
528     {
529         TypeId receiverType;
530         TypeId resultType = TypeId.getBuiltInTypeId(Types.VARCHAR);
531
532         // handle parameters here
533

534         /* Is there a ? parameter for the receiver? */
535         if (receiver.requiresTypeFromContext())
536         {
537             /*
538             ** According to the SQL standard, if trim has a ? receiver,
539             ** its type is varchar with the implementation-defined maximum length
540             ** for a varchar.
541             */

542     
543             receiver.setType(getVarcharDescriptor());
544         }
545
546         /* Is there a ? parameter on the left? */
547         if (leftOperand.requiresTypeFromContext())
548         {
549             /* Set the left operand type to varchar. */
550             leftOperand.setType(getVarcharDescriptor());
551         }
552
553         bindToBuiltIn();
554
555         /*
556         ** Check the type of the receiver - this function is allowed only on
557         ** string value types.
558         */

559         receiverType = receiver.getTypeId();
560         if (receiverType.userType())
561             throwBadType("trim", receiverType.getSQLTypeName());
562
563         receiver = castArgToString(receiver);
564
565         if ((receiverType.getTypeFormatId() == StoredFormatIds.CLOB_TYPE_ID) ||
566            (receiverType.getTypeFormatId() == StoredFormatIds.NCLOB_TYPE_ID)) {
567         // special case for CLOBs: if we start with a CLOB, we have to get
568
// a CLOB as a result (as opposed to a VARCHAR), because we can have a
569
// CLOB that is beyond the max length of VARCHAR (ex. "clob(100k)").
570
// This is okay because CLOBs, like VARCHARs, allow variable-length
571
// values (which is a must for the trim to actually work).
572
resultType = receiverType;
573         }
574
575         /*
576         ** Check the type of the leftOperand (trimSet).
577         ** The leftOperand should be a string value type.
578         */

579         TypeId leftCTI;
580         leftCTI = leftOperand.getTypeId();
581         if (leftCTI.userType())
582             throwBadType("trim", leftCTI.getSQLTypeName());
583
584         leftOperand = castArgToString(leftOperand);
585
586         /*
587         ** The result type of trim is varchar.
588         */

589         setResultType(resultType);
590
591         return this;
592     }
593     /*
594     ** set result type for operator
595     */

596     private void setResultType(TypeId resultType) throws StandardException
597     {
598         setType(new DataTypeDescriptor(
599                         resultType,
600                         true,
601                         receiver.getTypeServices().getMaximumWidth()
602                     )
603                 );
604     }
605     /**
606      * Bind locate operator
607      *
608      * @return The new top of the expression tree.
609      *
610      * @exception StandardException Thrown on error
611      */

612
613     public ValueNode locateBind() throws StandardException
614     {
615         TypeId firstOperandType, secondOperandType, offsetType;
616
617         /*
618          * Is there a ? parameter for the first arg. Copy the
619          * left/firstOperand's. If the left/firstOperand are both parameters,
620          * both will be max length.
621          */

622         if( receiver.requiresTypeFromContext())
623         {
624             if( leftOperand.requiresTypeFromContext())
625             {
626                 receiver.setType(getVarcharDescriptor());
627             }
628             else
629             {
630                 if( leftOperand.getTypeId().isStringTypeId() )
631                 {
632                     receiver.setType(
633                                      leftOperand.getTypeServices());
634                 }
635             }
636         }
637                                                         
638         /*
639          * Is there a ? parameter for the second arg. Copy the receiver's.
640          * If the receiver are both parameters, both will be max length.
641          */

642         if(leftOperand.requiresTypeFromContext())
643         {
644             if(receiver.requiresTypeFromContext())
645             {
646                 leftOperand.setType(getVarcharDescriptor());
647             }
648             else
649             {
650                 if( receiver.getTypeId().isStringTypeId() )
651                 {
652                     leftOperand.setType(
653                                      receiver.getTypeServices());
654                 }
655             }
656         }
657
658         /*
659          * Is there a ? paramter for the third arg. It will be an int.
660          */

661         if( rightOperand.requiresTypeFromContext())
662         {
663             rightOperand.setType(
664                 new DataTypeDescriptor(TypeId.INTEGER_ID, true));
665         }
666
667         bindToBuiltIn();
668
669         /*
670         ** Check the type of the operand - this function is allowed only
671         ** for: receiver = CHAR
672         ** firstOperand = CHAR
673         ** secondOperand = INT
674         */

675         secondOperandType = leftOperand.getTypeId();
676         offsetType = rightOperand.getTypeId();
677         firstOperandType = receiver.getTypeId();
678
679         if (!firstOperandType.isStringTypeId() ||
680             !secondOperandType.isStringTypeId() ||
681             offsetType.getJDBCTypeId() != Types.INTEGER)
682             throw StandardException.newException(SQLState.LANG_DB2_FUNCTION_INCOMPATIBLE,
683                     "LOCATE", "FUNCTION");
684
685         /*
686         ** The result type of a LocateFunctionNode is an integer.
687         */

688         setType(new DataTypeDescriptor(TypeId.INTEGER_ID,
689                 receiver.getTypeServices().isNullable()));
690
691         return this;
692     }
693
694     /* cast arg to a varchar */
695     protected ValueNode castArgToString(ValueNode vn) throws StandardException
696     {
697         TypeCompiler vnTC = vn.getTypeCompiler();
698         if (! vn.getTypeId().isStringTypeId())
699         {
700             ValueNode newNode = (ValueNode)
701                         getNodeFactory().getNode(
702                             C_NodeTypes.CAST_NODE,
703                             vn,
704                             DataTypeDescriptor.getBuiltInDataTypeDescriptor(Types.VARCHAR, true,
705                                             vnTC.getCastToCharWidth(
706                                                 vn.getTypeServices())),
707                             getContextManager());
708             ((CastNode) newNode).bindCastNodeOnly();
709             return newNode;
710         }
711         return vn;
712     }
713
714     /**
715      * Bind substr expression.
716      *
717      * @return The new top of the expression tree.
718      *
719      * @exception StandardException Thrown on error
720      */

721
722     public ValueNode substrBind()
723             throws StandardException
724     {
725         TypeId receiverType;
726         TypeId resultType;
727
728         // handle parameters here
729

730         /* Is there a ? parameter for the receiver? */
731         if (receiver.requiresTypeFromContext())
732         {
733             /*
734             ** According to the SQL standard, if substr has a ? receiver,
735             ** its type is varchar with the implementation-defined maximum length
736             ** for a varchar.
737             */

738     
739             receiver.setType(getVarcharDescriptor());
740         }
741
742         /* Is there a ? parameter on the left? */
743         if (leftOperand.requiresTypeFromContext())
744         {
745             /* Set the left operand type to int. */
746             leftOperand.setType(
747                 new DataTypeDescriptor(TypeId.INTEGER_ID, true));
748         }
749
750         /* Is there a ? parameter on the right? */
751         if ((rightOperand != null) && rightOperand.requiresTypeFromContext())
752         {
753             /* Set the right operand type to int. */
754             rightOperand.setType(
755                 new DataTypeDescriptor(TypeId.INTEGER_ID, true));
756         }
757
758         bindToBuiltIn();
759
760         if (!leftOperand.getTypeId().isNumericTypeId() ||
761             (rightOperand != null && !rightOperand.getTypeId().isNumericTypeId()))
762             throw StandardException.newException(SQLState.LANG_DB2_FUNCTION_INCOMPATIBLE, "SUBSTR", "FUNCTION");
763
764         /*
765         ** Check the type of the receiver - this function is allowed only on
766         ** string value types.
767         */

768         resultType = receiverType = receiver.getTypeId();
769         switch (receiverType.getJDBCTypeId())
770         {
771             case Types.CHAR:
772             case Types.VARCHAR:
773             case Types.LONGVARCHAR:
774             case Types.CLOB:
775                 break;
776             default:
777             {
778                 throwBadType("SUBSTR", receiverType.getSQLTypeName());
779             }
780         }
781
782         // Determine the maximum length of the result
783
int resultLen = receiver.getTypeServices().getMaximumWidth();
784
785         if (rightOperand != null && rightOperand instanceof ConstantNode)
786         {
787             if (((ConstantNode)rightOperand).getValue().getInt() < resultLen)
788                 resultLen = ((ConstantNode)rightOperand).getValue().getInt();
789         }
790
791         /*
792         ** The result type of substr is a string type
793         */

794         setType(new DataTypeDescriptor(
795                         resultType,
796                         true,
797                         resultLen
798                     ));
799
800         return this;
801     }
802
803
804     /**
805      * Bind TIMESTAMPADD expression.
806      *
807      * @return The new top of the expression tree.
808      *
809      * @exception StandardException Thrown on error
810      */

811
812     private ValueNode timestampAddBind()
813             throws StandardException
814     {
815         if( ! bindParameter( rightOperand, Types.INTEGER))
816         {
817             int jdbcType = rightOperand.getTypeId().getJDBCTypeId();
818             if( jdbcType != Types.TINYINT && jdbcType != Types.SMALLINT &&
819                 jdbcType != Types.INTEGER && jdbcType != Types.BIGINT)
820                 throw StandardException.newException(SQLState.LANG_INVALID_FUNCTION_ARG_TYPE,
821                                                      rightOperand.getTypeId().getSQLTypeName(),
822                                                      ReuseFactory.getInteger( 2),
823                                                      operator);
824         }
825         bindDateTimeArg( receiver, 3);
826         setType(DataTypeDescriptor.getBuiltInDataTypeDescriptor( Types.TIMESTAMP));
827         return this;
828     } // end of timestampAddBind
829

830     /**
831      * Bind TIMESTAMPDIFF expression.
832      *
833      * @return The new top of the expression tree.
834      *
835      * @exception StandardException Thrown on error
836      */

837
838     private ValueNode timestampDiffBind()
839             throws StandardException
840     {
841         bindDateTimeArg( rightOperand, 2);
842         bindDateTimeArg( receiver, 3);
843         setType(DataTypeDescriptor.getBuiltInDataTypeDescriptor( Types.INTEGER));
844         return this;
845     } // End of timestampDiffBind
846

847     private void bindDateTimeArg( ValueNode arg, int argNumber) throws StandardException
848     {
849         if( ! bindParameter( arg, Types.TIMESTAMP))
850         {
851             if( ! arg.getTypeId().isDateTimeTimeStampTypeId())
852                 throw StandardException.newException(SQLState.LANG_INVALID_FUNCTION_ARG_TYPE,
853                                                      arg.getTypeId().getSQLTypeName(),
854                                                      ReuseFactory.getInteger( argNumber),
855                                                      operator);
856         }
857     } // end of bindDateTimeArg
858

859     private boolean bindParameter( ValueNode arg, int jdbcType) throws StandardException
860     {
861         if( arg.requiresTypeFromContext() && arg.getTypeId() == null)
862         {
863             arg.setType( new DataTypeDescriptor(TypeId.getBuiltInTypeId( jdbcType), true));
864             return true;
865         }
866         return false;
867     } // end of bindParameter
868

869     public ValueNode getReceiver()
870     {
871         return receiver;
872     }
873
874     /* throw bad type message */
875     private void throwBadType(String JavaDoc funcName, String JavaDoc type)
876         throws StandardException
877     {
878         throw StandardException.newException(SQLState.LANG_UNARY_FUNCTION_BAD_TYPE,
879                                         funcName,
880                                         type);
881     }
882
883     /* bind arguments to built in types */
884     protected void bindToBuiltIn()
885         throws StandardException
886     {
887         /* If the receiver is not a built-in type, then generate a bound conversion
888          * tree to a built-in type.
889          */

890         if (receiver.getTypeId().userType())
891         {
892             receiver = receiver.genSQLJavaSQLTree();
893         }
894
895         /* If the left operand is not a built-in type, then generate a bound conversion
896          * tree to a built-in type.
897          */

898         if (leftOperand.getTypeId().userType())
899         {
900             leftOperand = leftOperand.genSQLJavaSQLTree();
901         }
902
903         /* If the right operand is not a built-in type, then generate a bound conversion
904          * tree to a built-in type.
905          */

906         if (rightOperand != null)
907         {
908             if (rightOperand.getTypeId().userType())
909             {
910                 rightOperand = rightOperand.genSQLJavaSQLTree();
911             }
912         }
913     }
914
915     private DataTypeDescriptor getVarcharDescriptor() {
916         return new DataTypeDescriptor(TypeId.getBuiltInTypeId(Types.VARCHAR), true);
917     }
918         
919     protected boolean isEquivalent(ValueNode o) throws StandardException
920     {
921         if (isSameNodeType(o))
922     {
923         TernaryOperatorNode other = (TernaryOperatorNode)o;
924         
925             /*
926              * SUBSTR function can either have 2 or 3 arguments. In the
927              * 2-args case, rightOperand will be null and thus needs
928              * additional handling in the equivalence check.
929              */

930             return (other.methodName.equals(methodName)
931                 && other.receiver.isEquivalent(receiver)
932                     && other.leftOperand.isEquivalent(leftOperand)
933                     && ( (rightOperand == null && other.rightOperand == null) ||
934                          (other.rightOperand != null &&
935                             other.rightOperand.isEquivalent(rightOperand)) ) );
936         }
937         return false;
938     }
939 }
940
Popular Tags