KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2
3    Derby - Class org.apache.derby.impl.sql.compile.MethodCallNode
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.loader.ClassInspector;
25
26 import org.apache.derby.iapi.services.compiler.MethodBuilder;
27
28 import org.apache.derby.iapi.services.sanity.SanityManager;
29
30 import org.apache.derby.iapi.error.StandardException;
31
32 import org.apache.derby.iapi.types.TypeId;
33 import org.apache.derby.iapi.types.JSQLType;
34
35 import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
36
37 import org.apache.derby.iapi.sql.compile.Visitable;
38 import org.apache.derby.iapi.sql.compile.Visitor;
39 import org.apache.derby.iapi.sql.compile.C_NodeTypes;
40 import org.apache.derby.iapi.sql.compile.CompilerContext;
41
42 import org.apache.derby.iapi.types.DataTypeDescriptor;
43
44 import org.apache.derby.iapi.sql.compile.TypeCompiler;
45 import org.apache.derby.catalog.TypeDescriptor;
46
47 import org.apache.derby.iapi.reference.SQLState;
48 import org.apache.derby.iapi.reference.JDBC30Translation;
49
50 import org.apache.derby.iapi.store.access.Qualifier;
51
52 import org.apache.derby.iapi.util.JBitSet;
53
54 import org.apache.derby.impl.sql.compile.ExpressionClassBuilder;
55 import org.apache.derby.catalog.types.RoutineAliasInfo;
56
57 import java.lang.reflect.Modifier JavaDoc;
58 import java.lang.reflect.Member JavaDoc;
59
60 import java.util.StringTokenizer JavaDoc;
61 import java.util.Vector JavaDoc;
62
63 /**
64  * A MethodCallNode represents a Java method call. Method calls can be done
65  * through DML (as expressions) or through the CALL statement.
66  *
67  * @author Jeff Lichtman
68  */

69
70 abstract class MethodCallNode extends JavaValueNode
71 {
72     /*
73     ** Name of the method.
74     */

75      String JavaDoc methodName;
76
77     /** The name of the class containing the method. May not be known until bindExpression() has been called.
78      * @see #bindExpression
79      * @see #getJavaClassName()
80      */

81     String JavaDoc javaClassName;
82     
83     /**
84         For a procedure or function call
85     */

86     RoutineAliasInfo routineInfo;
87
88
89     /**
90         True if this is an internal call, just used to set up a generated method call.
91     */

92     boolean internalCall;
93
94     /**
95         For resolution of procedure INOUT/OUT parameters to the primitive
96         form, such as int[]. May be null.
97     */

98     private String JavaDoc[] procedurePrimitiveArrayType;
99
100     // bound signature of arguments, stated in universal types (JSQLType)
101
protected JSQLType[] signature;
102
103     /*
104     ** Parameters to the method, if any. No elements if no parameters.
105     */

106     protected JavaValueNode[] methodParms;
107
108     /* The method call */
109     protected Member JavaDoc method;
110
111     protected String JavaDoc actualMethodReturnType;
112
113     /**
114       * Gets the signature of JSQLTypes needed to propagate a work unit from
115       * target to source.
116       *
117       * @return the JSQLType signature
118       */

119     public JSQLType[] getSignature()
120     {
121         return signature;
122     }
123
124     /**
125         The parameter types for the resolved method.
126     */

127     String JavaDoc[] methodParameterTypes;
128
129     /**
130      * Initializer for a MethodCallNode
131      *
132      * @param methodName The name of the method to call
133      */

134     public void init(Object JavaDoc methodName)
135     {
136         this.methodName = (String JavaDoc) methodName;
137     }
138
139     public String JavaDoc getMethodName()
140     {
141         return methodName;
142     }
143
144     /**
145      * @return the name of the class that contains the method, null if not known. It may not be known
146      * until this node has been bound.
147      */

148     public String JavaDoc getJavaClassName()
149     {
150         return javaClassName;
151     }
152     
153     /**
154      * Set the clause that this node appears in.
155      *
156      * @param clause The clause that this node appears in.
157      */

158     public void setClause(int clause)
159     {
160         super.setClause(clause);
161         if (methodParms != null)
162         {
163             for (int parm = 0; parm < methodParms.length; parm++)
164             {
165                 if (methodParms[parm] != null)
166                 {
167                     methodParms[parm].setClause(clause);
168                 }
169             }
170         }
171     }
172
173     /**
174      * Add the parameter list.
175      * (This flavor is useful when transforming a non-static method call node
176      * to a static method call node.)
177      *
178      * @param methodParms JavaValueNode[]
179      */

180
181     public void addParms(JavaValueNode[] methodParms)
182     {
183         this.methodParms = methodParms;
184     }
185
186     /**
187      * Add the parameter list
188      *
189      * @param parameterList A Vector of the parameters
190      *
191      * @exception StandardException Thrown on error
192      */

193     public void addParms(Vector JavaDoc parameterList) throws StandardException
194     {
195         methodParms = new JavaValueNode[parameterList.size()];
196
197         int plSize = parameterList.size();
198         for (int index = 0; index < plSize; index++)
199         {
200             QueryTreeNode qt;
201
202             qt = (QueryTreeNode) parameterList.elementAt(index);
203
204             /*
205             ** Since we need the parameter to be in Java domain format, put a
206             ** SQLToJavaValueNode on top of the parameter node if it is a
207             ** SQLValueNode. But if the parameter is already in Java domain
208             ** format, then we don't need to do anything.
209             */

210             if ( ! (qt instanceof JavaValueNode))
211             {
212                 qt = (SQLToJavaValueNode) getNodeFactory().getNode(
213                         C_NodeTypes.SQL_TO_JAVA_VALUE_NODE,
214                         qt,
215                         getContextManager());
216             }
217
218             methodParms[index] = (JavaValueNode) qt;
219         }
220     }
221
222     /**
223      * Prints the sub-nodes of this object. See QueryTreeNode.java for
224      * how tree printing is supposed to work.
225      *
226      * @param depth The depth of this node in the tree
227      */

228
229     public void printSubNodes(int depth)
230     {
231         if (SanityManager.DEBUG)
232         {
233             int parm;
234
235             super.printSubNodes(depth);
236             if (methodParms != null)
237             {
238                 for (parm = 0; parm < methodParms.length; parm++)
239                 {
240                     if (methodParms[parm] != null)
241                     {
242                         printLabel(depth, "methodParms[" + parm + "] :");
243                         methodParms[parm].treePrint(depth + 1);
244                     }
245                 }
246             }
247         }
248     }
249
250     /**
251      * Convert this object to a String. See comments in QueryTreeNode.java
252      * for how this should be done for tree printing.
253      *
254      * @return This object as a String
255      */

256
257     public String JavaDoc toString()
258     {
259         if (SanityManager.DEBUG)
260         {
261             return "methodName: " +
262                     (methodName != null ? methodName : "null") + "\n" +
263                     super.toString();
264         }
265         else
266         {
267             return "";
268         }
269     }
270
271     /**
272      * Bind this expression. This means binding the sub-expressions,
273      * as well as figuring out what the return type is for this expression.
274      *
275      * @param fromList The FROM list for the query this
276      * expression is in, for binding columns.
277      * @param subqueryList The subquery list being built as we find SubqueryNodes
278      * @param aggregateVector The aggregate vector being built as we find AggregateNodes
279      *
280      * @exception StandardException Thrown on error
281      */

282
283     final void bindParameters(
284         FromList fromList, SubqueryList subqueryList,
285         Vector JavaDoc aggregateVector)
286             throws StandardException
287     {
288         /* Bind the parameters */
289         if (methodParms != null)
290         {
291             int count = methodParms.length;
292
293             // with a procedure call the signature
294
// is preformed in StaticMethodCall from
295
// the procedures signature.
296
if (signature == null)
297                 signature = new JSQLType[ count ];
298
299             for (int parm = 0; parm < count; parm++)
300             {
301                 if (methodParms[parm] != null)
302                 {
303                     methodParms[parm] =
304                         methodParms[parm].bindExpression(
305                             fromList, subqueryList, aggregateVector);
306
307                     if (routineInfo == null)
308                         signature[ parm ] = methodParms[ parm ].getJSQLType();
309                     
310                     // prohibit LOB columns/types
311
if (signature[parm] != null) {
312                         String JavaDoc type = signature[parm].getSQLType().getTypeId().getSQLTypeName();
313                         if (type.equals("BLOB") || type.equals("CLOB") || type.equals("NCLOB")) {
314                             throw StandardException.newException(SQLState.LOB_AS_METHOD_ARGUMENT_OR_RECEIVER);
315                         }
316                     }
317                 }
318             }
319         }
320     }
321
322     /**
323      * Return whether or not all of the parameters to this node are
324      * QUERY_INVARIANT or CONSTANT. This is useful for VTIs - a VTI is a candidate
325      * for materialization if all of its parameters are QUERY_INVARIANT or CONSTANT
326      *
327      * @return Whether or not all of the parameters to this node are QUERY_INVARIANT or CONSTANT
328      * @exception StandardException thrown on error
329      */

330      protected boolean areParametersQueryInvariant() throws StandardException
331      {
332         return (getVariantTypeOfParams() == Qualifier.QUERY_INVARIANT);
333      }
334
335     /**
336      * Build parameters for error message and throw the exception when there
337      * is no matching signature found.
338      *
339      * @param receiverTypeName Type name for receiver
340      * @param parmTypeNames Type names for parameters as object types
341      * @param primParmTypeNames Type names for parameters as primitive types
342      *
343      * @exception StandardException Thrown on error
344      */

345     void throwNoMethodFound(String JavaDoc receiverTypeName,
346                                       String JavaDoc[] parmTypeNames,
347                                       String JavaDoc[] primParmTypeNames)
348         throws StandardException
349     {
350         /* Put the parameter type names into a single string */
351         StringBuffer JavaDoc parmTypes = new StringBuffer JavaDoc();
352         for (int i = 0; i < parmTypeNames.length; i++)
353         {
354             if (i != 0)
355                 parmTypes.append(", ");
356             /* RESOLVE - shouldn't be using hard coded strings for output */
357             parmTypes.append( (parmTypeNames[i].length() != 0 ?
358                                 parmTypeNames[i] :
359                                 "UNTYPED"));
360             if ((primParmTypeNames != null) &&
361                 ! primParmTypeNames[i].equals(parmTypeNames[i])) // has primitive
362
parmTypes.append("(" + primParmTypeNames[i] + ")");
363         }
364
365         throw StandardException.newException(SQLState.LANG_NO_METHOD_FOUND,
366                                                 receiverTypeName,
367                                                 methodName,
368                                                 parmTypes);
369     }
370
371     /**
372      * Preprocess an expression tree. We do a number of transformations
373      * here (including subqueries, IN lists, LIKE and BETWEEN) plus
374      * subquery flattening.
375      * NOTE: This is done before the outer ResultSetNode is preprocessed.
376      *
377      * @param numTables Number of tables in the DML Statement
378      * @param outerFromList FromList from outer query block
379      * @param outerSubqueryList SubqueryList from outer query block
380      * @param outerPredicateList PredicateList from outer query block
381      *
382      * @exception StandardException Thrown on error
383      */

384     public void preprocess(int numTables,
385                             FromList outerFromList,
386                             SubqueryList outerSubqueryList,
387                             PredicateList outerPredicateList)
388                     throws StandardException
389     {
390         int parm;
391
392         /* Preprocess the parameters */
393         if (methodParms != null)
394         {
395             for (parm = 0; parm < methodParms.length; parm++)
396             {
397                 if (methodParms[parm] != null)
398                 {
399                     methodParms[parm].preprocess(numTables,
400                                                  outerFromList,
401                                                  outerSubqueryList,
402                                                  outerPredicateList);
403                 }
404             }
405         }
406     }
407
408     /**
409      * Categorize this predicate. Initially, this means
410      * building a bit map of the referenced tables for each predicate.
411      * If the source of this ColumnReference (at the next underlying level)
412      * is not a ColumnReference or a VirtualColumnNode then this predicate
413      * will not be pushed down.
414      *
415      * For example, in:
416      * select * from (select 1 from s) a (x) where x = 1
417      * we will not push down x = 1.
418      * NOTE: It would be easy to handle the case of a constant, but if the
419      * inner SELECT returns an arbitrary expression, then we would have to copy
420      * that tree into the pushed predicate, and that tree could contain
421      * subqueries and method calls.
422      * RESOLVE - revisit this issue once we have views.
423      *
424      * @param referencedTabs JBitSet with bit map of referenced FromTables
425      * @param simplePredsOnly Whether or not to consider method
426      * calls, field references and conditional nodes
427      * when building bit map
428      *
429      * @return boolean Whether or not source.expression is a ColumnReference
430      * or a VirtualColumnNode.
431      * @exception StandardException Thrown on error
432      */

433     public boolean categorize(JBitSet referencedTabs, boolean simplePredsOnly)
434         throws StandardException
435     {
436         /* We stop here when only considering simple predicates
437          * as we don't consider method calls when looking
438          * for null invariant predicates.
439          */

440         if (simplePredsOnly)
441         {
442             return false;
443         }
444
445         boolean pushable = true;
446         int param;
447
448         if (methodParms != null)
449         {
450             for (param = 0; param < methodParms.length; param++)
451             {
452                 if (methodParms[param] != null)
453                 {
454                     pushable = methodParms[param].categorize(referencedTabs, simplePredsOnly) &&
455                                pushable;
456                 }
457             }
458         }
459
460         /* We need to push down method call. Then the predicate can be used for start/stop
461          * key for index scan. The fact that method call's cost is not predictable and can
462          * be expensive doesn't mean we shouldn't push it down. Beetle 4826.
463          */

464         return pushable;
465     }
466
467     /**
468      * Remap all ColumnReferences in this tree to be clones of the
469      * underlying expression.
470      *
471      * @return JavaValueNode The remapped expression tree.
472      *
473      * @exception StandardException Thrown on error
474      */

475     public JavaValueNode remapColumnReferencesToExpressions()
476         throws StandardException
477     {
478         int param;
479
480         if (methodParms != null)
481         {
482             for (param = 0; param < methodParms.length; param++)
483             {
484                 if (methodParms[param] != null)
485                 {
486                     methodParms[param] =
487                         methodParms[param].remapColumnReferencesToExpressions();
488                 }
489             }
490         }
491         return this;
492     }
493
494     /**
495      * Generate the parameters to the given method call
496      *
497      * @param acb The ExpressionClassBuilder for the class we're generating
498      * @param mb the method the expression will go into
499      *
500      * @return Count of arguments to the method.
501      *
502      * @exception StandardException Thrown on error
503      */

504
505     public int generateParameters(ExpressionClassBuilder acb,
506                                             MethodBuilder mb)
507             throws StandardException
508     {
509         int param;
510
511         String JavaDoc[] expectedTypes = methodParameterTypes;
512
513         ClassInspector classInspector = getClassFactory().getClassInspector();
514
515         /* Generate the code for each user parameter, generating the appropriate
516          * cast when the passed type needs to get widened to the expected type.
517          */

518         for (param = 0; param < methodParms.length; param++)
519         {
520             generateOneParameter( acb, mb, param );
521
522             // type from the SQL-J expression
523
String JavaDoc argumentType = getParameterTypeName( methodParms[param] );
524
525             // type of the method
526
String JavaDoc parameterType = expectedTypes[param];
527
528             if (!parameterType.equals(argumentType))
529             {
530                 // since we reached here through method resolution
531
// casts are only required for primitive types.
532
// In any other case the expression type must be assignable
533
// to the parameter type.
534
if (classInspector.primitiveType(parameterType)) {
535                     mb.cast(parameterType);
536                 } else {
537
538                     // for a prodcedure
539
if (routineInfo != null) {
540                         continue; // probably should be only for INOUT/OUT parameters.
541
}
542
543                     if (SanityManager.DEBUG) {
544                         SanityManager.ASSERT(classInspector.assignableTo(argumentType, parameterType),
545                             "Argument type " + argumentType + " is not assignable to parameter " + parameterType);
546                     }
547
548                     /*
549                     ** Set the parameter type in case the argument type is narrower
550                     ** than the parameter type.
551                     */

552                     mb.upCast(parameterType);
553
554                 }
555             }
556
557         }
558
559         return methodParms.length;
560     }
561
562     static public String JavaDoc getParameterTypeName( JavaValueNode param )
563         throws StandardException
564     {
565         String JavaDoc argumentType;
566
567         // RESOLVE - shouldn't this logic be inside JavaValueNode ??
568
// I.e. once the value is primitive then its java type name is its
569
// primitive type name.
570
if (param.isPrimitiveType()) { argumentType = param.getPrimitiveTypeName(); }
571         else { argumentType = param.getJavaTypeName(); }
572
573         return argumentType;
574     }
575
576     /**
577      * Generate one parameter to the given method call. This method is overriden by
578      * RepStaticMethodCallNode.
579      *
580      * @param acb The ExpressionClassBuilder for the class we're generating
581      * @param mb the method the expression will go into
582      * @param parameterNumber Identifies which parameter to generate. 0 based.
583      *
584      * @exception StandardException Thrown on error
585      */

586
587     public void generateOneParameter(ExpressionClassBuilder acb,
588                                             MethodBuilder mb,
589                                             int parameterNumber )
590             throws StandardException
591     {
592         methodParms[parameterNumber].generateExpression(acb, mb);
593     }
594
595     /**
596      * Set the appropriate type information for a null passed as a parameter.
597      * This method is called after method resolution, when a signature was
598      * successfully matched.
599      *
600      * @param parmTypeNames String[] with the java type names for the parameters
601      * as declared by the method
602      *
603      * @exception StandardException Thrown on error
604      */

605     public void setNullParameterInfo(String JavaDoc[] parmTypeNames)
606             throws StandardException
607     {
608         for (int i = 0; i < methodParms.length; i++)
609         {
610             /* null parameters are represented by a java type name of "" */
611             if (methodParms[i].getJavaTypeName().equals(""))
612             {
613                 /* Set the type information in the null constant node */
614                 DataTypeDescriptor dts = DataTypeDescriptor.getSQLDataTypeDescriptor(parmTypeNames[i]);
615                 ((SQLToJavaValueNode)methodParms[i]).value.setType(dts);
616
617                 /* Set the correct java type name */
618                 methodParms[i].setJavaTypeName(parmTypeNames[i]);
619                 signature[i] = methodParms[i].getJSQLType();
620             }
621         }
622     }
623
624     protected void resolveMethodCall(String JavaDoc javaClassName,
625                                      boolean staticMethod)
626                 throws StandardException
627     {
628         // only allow direct method calls through routines and internal SQL.
629
if (routineInfo == null && !internalCall)
630         {
631             // See if we are being executed in an internal context
632
if ((getCompilerContext().getReliability() & CompilerContext.INTERNAL_SQL_ILLEGAL) != 0) {
633                 throw StandardException.newException(SQLState.LANG_SYNTAX_ERROR, javaClassName + (staticMethod ? "::" : ".") + methodName);
634             }
635         }
636
637         int count = signature.length;
638
639         ClassInspector classInspector = getClassFactory().getClassInspector();
640
641         
642         String JavaDoc[] parmTypeNames;
643         String JavaDoc[] primParmTypeNames = null;
644         boolean[] isParam = getIsParam();
645
646         boolean hasDynamicResultSets = (routineInfo != null) && (count != 0) && (count != methodParms.length);
647
648         /*
649         ** Find the matching method that is public.
650         */

651
652             int signatureOffset = methodName.indexOf('(');
653             
654             // support Java signatures by checking if the method name contains a '('
655
if (signatureOffset != -1) {
656                 parmTypeNames = parseValidateSignature(methodName, signatureOffset, hasDynamicResultSets);
657                methodName = methodName.substring(0, signatureOffset);
658                
659                // If the signature is specified then Derby resolves to exactly
660
// that method. Setting this flag to false disables the method
661
// resolution from automatically optionally repeating the last
662
// parameter as needed.
663
hasDynamicResultSets = false;
664                  
665             }
666             else
667             {
668                 parmTypeNames = getObjectSignature();
669             }
670         try
671         {
672                 method = classInspector.findPublicMethod(javaClassName,
673                                                     methodName,
674                                                     parmTypeNames,
675                                                     null,
676                                                     isParam,
677                                                     staticMethod,
678                                                     hasDynamicResultSets);
679
680
681                 // DB2 LUW does not support Java object types for SMALLINT, INTEGER, BIGINT, REAL, DOUBLE
682
// and these are the only types that can map to a primitive or an object type according
683
// to SQL part 13. So we never have a second chance match.
684
// Also if the DDL specified a signature, then no alternate resolution
685
if (signatureOffset == -1 && routineInfo == null) {
686
687                     /* If no match, then retry with combinations of object and
688                      * primitive types.
689                      */

690                     if (method == null)
691                     {
692                         primParmTypeNames = getPrimitiveSignature(false);
693
694                         method = classInspector.findPublicMethod(javaClassName,
695                                                     methodName,
696                                                     parmTypeNames,
697                                                     primParmTypeNames,
698                                                     isParam,
699                                                     staticMethod,
700                                                     hasDynamicResultSets);
701                     }
702                 }
703         }
704         catch (ClassNotFoundException JavaDoc e)
705         {
706             /*
707             ** If one of the classes couldn't be found, just act like the
708             ** method couldn't be found. The error lists all the class names,
709             ** which should give the user enough info to diagnose the problem.
710             */

711             method = null;
712         }
713         /* Throw exception if no matching signature found */
714         if (method == null)
715         {
716             throwNoMethodFound(javaClassName, parmTypeNames, primParmTypeNames);
717         }
718
719         String JavaDoc typeName = classInspector.getType(method);
720         actualMethodReturnType = typeName;
721
722         if (routineInfo == null) {
723
724             /* void methods are only okay for CALL Statements */
725             if (typeName.equals("void"))
726             {
727                 if (!forCallStatement)
728                     throw StandardException.newException(SQLState.LANG_VOID_METHOD_CALL);
729             }
730         }
731         else
732         {
733             String JavaDoc promoteName = null;
734             TypeDescriptor returnType = routineInfo.getReturnType();
735             String JavaDoc requiredType;
736             if (returnType == null)
737             {
738                 // must have a void method for a procedure call.
739
requiredType = "void";
740             }
741             else
742             {
743
744
745                 // DB2 LUW does not support Java object types for SMALLINT, INTEGER, BIGINT, REAL, DOUBLE
746
// and these are the only types that can map to a primitive or an object type according
747
// to SQL part 13. So always map to the primitive type. We can not use the getPrimitiveSignature()
748
// as it (incorrectly but historically always has) maps a DECIMAL to a double.
749

750                 
751                 TypeId returnTypeId = TypeId.getBuiltInTypeId(returnType.getJDBCTypeId());
752                 switch (returnType.getJDBCTypeId()) {
753                 case java.sql.Types.SMALLINT:
754                 case java.sql.Types.INTEGER:
755                 case java.sql.Types.BIGINT:
756                 case java.sql.Types.REAL:
757                 case java.sql.Types.DOUBLE:
758                     TypeCompiler tc = getTypeCompiler(returnTypeId);
759                     requiredType = tc.getCorrespondingPrimitiveTypeName();
760                     if (!routineInfo.calledOnNullInput() && routineInfo.getParameterCount() != 0)
761                     {
762                         promoteName = returnTypeId.getCorrespondingJavaTypeName();
763                     }
764
765                     break;
766                 default:
767                     requiredType = returnTypeId.getCorrespondingJavaTypeName();
768                     break;
769                 }
770
771             }
772
773             if (!requiredType.equals(typeName))
774             {
775                 throwNoMethodFound(requiredType + " " + javaClassName, parmTypeNames, primParmTypeNames);
776             }
777
778             // for a returns null on null input with a primitive
779
// type we need to promote to an object so we can return null.
780
if (promoteName != null)
781                 typeName = promoteName;
782         }
783         setJavaTypeName( typeName );
784
785         methodParameterTypes = classInspector.getParameterTypes(method);
786
787         for (int i = 0; i < methodParameterTypes.length; i++)
788         {
789             String JavaDoc methodParameter = methodParameterTypes[i];
790
791             if (routineInfo != null) {
792                 if (i < routineInfo.getParameterCount()) {
793                     int parameterMode = routineInfo.getParameterModes()[i];
794
795                     switch (parameterMode) {
796                     case JDBC30Translation.PARAMETER_MODE_IN:
797                         break;
798                     case JDBC30Translation.PARAMETER_MODE_IN_OUT:
799                         // we need to see if the type of the array is
800
// primitive, not the array itself.
801
methodParameter = methodParameter.substring(0, methodParameter.length() - 2);
802                         break;
803
804                     case JDBC30Translation.PARAMETER_MODE_OUT:
805                         // value is not obtained *from* parameter.
806
continue;
807                     }
808                 }
809             }
810
811             if (ClassInspector.primitiveType(methodParameter))
812                 methodParms[i].castToPrimitive(true);
813         }
814
815         /* Set type info for any null parameters */
816         if ( someParametersAreNull() )
817         {
818             setNullParameterInfo(methodParameterTypes);
819         }
820
821
822     
823         /* bug 4450 - if the callable statement is ? = call form, generate the metadata
824         infor for the return parameter. We don't really need that info in order to
825         execute the callable statement. But with jdbc3.0, this information should be
826         made available for return parameter through ParameterMetaData class.
827         Parser sets a flag in compilercontext if ? = call. If the flag is set,
828         we generate the metadata info for the return parameter and reset the flag
829         in the compilercontext for future call statements*/

830         DataTypeDescriptor dts = DataTypeDescriptor.getSQLDataTypeDescriptor(typeName);
831         if (getCompilerContext().getReturnParameterFlag()) {
832             getCompilerContext().getParameterTypes()[0] = dts;
833         }
834   }
835     
836     /**
837      * Parse the user supplied signature for a method and validate
838      * it, need to match the number of parameters passed in and match
839      * the valid types for the parameter.
840      * @param offset Character offset of first paren
841      * @param hasDynamicResultSets Can ResultSet[] parameters be specified.
842      * @return The valid array of types for resolution.
843      * @throws StandardException
844      */

845     private String JavaDoc[] parseValidateSignature(String JavaDoc externalName, int offset,
846             boolean hasDynamicResultSets)
847         throws StandardException
848     {
849         int siglen = externalName.length();
850
851         // Ensure the opening paren is not the last
852
// character and that the last character is a close paren
853
if (((offset + 1) == siglen)
854             || (externalName.charAt(siglen - 1) != ')'))
855             throw StandardException.newException(SQLState.SQLJ_SIGNATURE_INVALID); // invalid
856

857         StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(externalName.substring(offset + 1, siglen - 1), ",", true);
858         
859         String JavaDoc[] signatureTypes = new String JavaDoc[signature.length];
860         int count;
861         boolean seenClass = false;
862         for (count = 0; st.hasMoreTokens();)
863         {
864             String JavaDoc type = st.nextToken().trim();
865  
866             // check sequence is <class><comma>class> etc.
867
if (",".equals(type))
868             {
869                 if (!seenClass)
870                     throw StandardException.newException(SQLState.SQLJ_SIGNATURE_INVALID); // invalid
871
seenClass = false;
872                 continue;
873             }
874             else
875             {
876                 if (type.length() == 0)
877                     throw StandardException.newException(SQLState.SQLJ_SIGNATURE_INVALID); // invalid
878
seenClass = true;
879                 count++;
880             }
881                                                
882             if (count > signature.length)
883             {
884                 if (hasDynamicResultSets)
885                 {
886                     // Allow any number of dynamic result set holders
887
// but they must match the exact type.
888
String JavaDoc rsType = signature[signature.length - 1].getSQLType().
889                         getTypeId().getCorrespondingJavaTypeName();
890                     
891                     if (!type.equals(rsType))
892                         throw StandardException.newException(SQLState.LANG_DATA_TYPE_GET_MISMATCH,
893                                 type, rsType);
894
895                     if (signatureTypes.length == signature.length)
896                     {
897                         // expand once
898
String JavaDoc[] sigs = new String JavaDoc[st.countTokens()];
899                         System.arraycopy(signatureTypes, 0, sigs, 0, signatureTypes.length);
900                         signatureTypes = sigs;
901                     }
902                     
903                     signatureTypes[count - 1] = type;
904                     continue;
905                 
906                 }
907                 throw StandardException.newException(SQLState.SQLJ_SIGNATURE_PARAMETER_COUNT,
908                         Integer.toString(count),
909                         Integer.toString(signature.length)); // too many types
910
}
911
912                     
913             TypeId paramTypeId = signature[count - 1].getSQLType().getTypeId();
914                         
915             // Does it match the object name
916
if (type.equals(paramTypeId.getCorrespondingJavaTypeName()))
917             {
918                 signatureTypes[count - 1] = type;
919                 continue;
920             }
921         
922             // how about the primitive name
923
if ((paramTypeId.isNumericTypeId() && !paramTypeId.isDecimalTypeId())
924                     || paramTypeId.isBooleanTypeId())
925             {
926                 TypeCompiler tc = getTypeCompiler(paramTypeId);
927                 if (type.equals(tc.getCorrespondingPrimitiveTypeName()))
928                 {
929                     signatureTypes[count - 1] = type;
930                     continue;
931                 }
932             }
933             throw StandardException.newException(SQLState.LANG_DATA_TYPE_GET_MISMATCH,
934                         type, paramTypeId.getSQLTypeName()); // type conversion error
935
}
936         
937         // Did signature end with trailing comma?
938
if (count != 0 && !seenClass)
939             throw StandardException.newException(SQLState.SQLJ_SIGNATURE_INVALID); // invalid
940

941         if (count < signatureTypes.length)
942         {
943             if (hasDynamicResultSets)
944             {
945                 // we can tolerate a count of one less than the
946
// expected count, which means the procedure is declared
947
// to have dynamic result sets, but the explict signature
948
// doesn't have any ResultSet[] types.
949
// So accept, and procedure will automatically have 0
950
// dynamic results at runtime
951
if (count == (signature.length - 1))
952                 {
953                     String JavaDoc[] sigs = new String JavaDoc[count];
954                     System.arraycopy(signatureTypes, 0, sigs, 0, count);
955                     return sigs;
956                 }
957             }
958             throw StandardException.newException(SQLState.SQLJ_SIGNATURE_PARAMETER_COUNT,
959                     Integer.toString(count),
960                     Integer.toString(signature.length)); // too few types
961
}
962
963         return signatureTypes;
964     }
965
966     /**
967       * Return true if some parameters are null, false otherwise.
968       */

969     protected boolean someParametersAreNull()
970     {
971         int count = signature.length;
972         
973         for ( int ictr = 0; ictr < count; ictr++ )
974         {
975             if ( signature[ictr] == null )
976             {
977                 return true;
978             }
979         }
980
981         return false;
982     }
983
984     /**
985       * Build an array of names of the argument types. These types are biased toward
986       * Java objects. That is, if an argument is of SQLType, then we map it to the
987       * corresponding Java synonym class (e.g., SQLINT is mapped to 'java.lang.Integer').
988       *
989       *
990       * @return array of type names
991       *
992       * @exception StandardException Thrown on error
993       */

994     protected String JavaDoc[] getObjectSignature( )
995         throws StandardException
996     {
997         int count = signature.length;
998         String JavaDoc parmTypeNames[] = new String JavaDoc[ count ];
999
1000        for ( int i = 0; i < count; i++ ) { parmTypeNames[i] = getObjectTypeName( signature[ i ] ); }
1001
1002        return parmTypeNames;
1003    }
1004
1005    /**
1006     * Build an array of booleans denoting whether or not a given method
1007     * parameter is a ?.
1008     *
1009     * @return array of booleans denoting wheter or not a given method
1010     * parameter is a ?.
1011     */

1012    protected boolean[] getIsParam()
1013    {
1014        if (methodParms == null)
1015        {
1016            return new boolean[0];
1017        }
1018        
1019        boolean[] isParam = new boolean[methodParms.length];
1020
1021        for (int index = 0; index < methodParms.length; index++)
1022        {
1023            if (methodParms[index] instanceof SQLToJavaValueNode)
1024            {
1025                SQLToJavaValueNode stjvn = (SQLToJavaValueNode) methodParms[index];
1026                if (stjvn.value.requiresTypeFromContext())
1027                {
1028                    isParam[index] = true;
1029                }
1030            }
1031        }
1032
1033        return isParam;
1034    }
1035
1036    private String JavaDoc getObjectTypeName( JSQLType jsqlType )
1037        throws StandardException
1038    {
1039        if ( jsqlType != null )
1040        {
1041            switch( jsqlType.getCategory() )
1042            {
1043                case JSQLType.SQLTYPE:
1044
1045                    TypeId ctid = mapToTypeID( jsqlType );
1046
1047                    if ( ctid == null ) { return null; }
1048                    else {
1049                        // DB2 LUW does not support Java object types for SMALLINT, INTEGER, BIGINT, REAL, DOUBLE
1050
// and these are the only types that can map to a primitive or an object type according
1051
// to SQL part 13. So always map to the primitive type. We can not use the getPrimitiveSignature()
1052
// as it (incorrectly but historically always has) maps a DECIMAL to a double.
1053

1054                        switch (ctid.getJDBCTypeId()) {
1055                        case java.sql.Types.SMALLINT:
1056                        case java.sql.Types.INTEGER:
1057                        case java.sql.Types.BIGINT:
1058                        case java.sql.Types.REAL:
1059                        case java.sql.Types.DOUBLE:
1060                            if (routineInfo != null) {
1061                                TypeCompiler tc = getTypeCompiler(ctid);
1062                                return tc.getCorrespondingPrimitiveTypeName();
1063                            }
1064                            // fall through
1065
default:
1066                            return ctid.getCorrespondingJavaTypeName();
1067                        }
1068                    }
1069
1070                case JSQLType.JAVA_CLASS: return jsqlType.getJavaClassName();
1071
1072                case JSQLType.JAVA_PRIMITIVE: return JSQLType.primitiveNames[ jsqlType.getPrimitiveKind() ];
1073
1074                default:
1075
1076                    if (SanityManager.DEBUG)
1077                    { SanityManager.THROWASSERT( "Unknown JSQLType: " + jsqlType ); }
1078
1079            }
1080        }
1081
1082        return "";
1083    }
1084
1085    String JavaDoc[] getPrimitiveSignature( boolean castToPrimitiveAsNecessary )
1086        throws StandardException
1087    {
1088        int count = signature.length;
1089        String JavaDoc[] primParmTypeNames = new String JavaDoc[ count ];
1090        JSQLType jsqlType;
1091
1092        for (int i = 0; i < count; i++)
1093        {
1094            jsqlType = signature[ i ];
1095
1096            if ( jsqlType == null ) { primParmTypeNames[i] = ""; }
1097            else
1098            {
1099                switch( jsqlType.getCategory() )
1100                {
1101                    case JSQLType.SQLTYPE:
1102
1103                        if ((procedurePrimitiveArrayType != null)
1104                            && (i < procedurePrimitiveArrayType.length)
1105                            && (procedurePrimitiveArrayType[i] != null)) {
1106
1107                            primParmTypeNames[i] = procedurePrimitiveArrayType[i];
1108
1109                        } else {
1110
1111
1112                            TypeId ctid = mapToTypeID( jsqlType );
1113
1114                            if ((ctid.isNumericTypeId() && !ctid.isDecimalTypeId()) || ctid.isBooleanTypeId())
1115                            {
1116                                TypeCompiler tc = getTypeCompiler(ctid);
1117                                primParmTypeNames[i] = tc.getCorrespondingPrimitiveTypeName();
1118                                if ( castToPrimitiveAsNecessary) { methodParms[i].castToPrimitive(true); }
1119                            }
1120                            else { primParmTypeNames[i] = ctid.getCorrespondingJavaTypeName(); }
1121                        }
1122
1123                        break;
1124
1125                    case JSQLType.JAVA_CLASS:
1126
1127                        primParmTypeNames[i] = jsqlType.getJavaClassName();
1128                        break;
1129
1130                    case JSQLType.JAVA_PRIMITIVE:
1131
1132                        primParmTypeNames[i] = JSQLType.primitiveNames[ jsqlType.getPrimitiveKind() ];
1133                        if ( castToPrimitiveAsNecessary) { methodParms[i].castToPrimitive(true); }
1134                        break;
1135
1136                    default:
1137
1138                        if (SanityManager.DEBUG)
1139                            { SanityManager.THROWASSERT( "Unknown JSQLType: " + jsqlType ); }
1140
1141                } // end switch
1142

1143            } // end if
1144

1145        } // end for
1146

1147        return primParmTypeNames;
1148    }
1149
1150    /**
1151     * Return the variant type for the underlying expression.
1152     * The variant type can be:
1153     * VARIANT - variant within a scan
1154     * (non-static field access)
1155     * SCAN_INVARIANT - invariant within a scan
1156     * (column references from outer tables)
1157     * QUERY_INVARIANT - invariant within the life of a query
1158     * (constant expressions)
1159     *
1160     * @return The variant type for the underlying expression.
1161     */

1162    protected int getOrderableVariantType() throws StandardException
1163    {
1164        // beetle 4880. We return the most variant type of the parameters. If no
1165
// params then query-invariant. This makes more sense, and we can evaluate
1166
// only once per query (good for performance) because method call could be
1167
// expensive. And if we push down method qualifier to store, language
1168
// can pre-evaluate the method call. This avoids letting store evaluate
1169
// the method while holding page latch, causing deadlock.
1170

1171        return getVariantTypeOfParams();
1172    }
1173
1174    private int getVariantTypeOfParams() throws StandardException
1175    {
1176        int variance = Qualifier.QUERY_INVARIANT;
1177
1178        if (methodParms != null)
1179        {
1180            for (int parm = 0; parm < methodParms.length; parm++)
1181            {
1182                if (methodParms[parm] != null)
1183                {
1184                    int paramVariantType =
1185                        methodParms[parm].getOrderableVariantType();
1186                    if (paramVariantType < variance) //return the most variant type
1187
variance = paramVariantType;
1188                }
1189                else
1190                {
1191                    variance = Qualifier.VARIANT;
1192                }
1193            }
1194        }
1195
1196        return variance;
1197    }
1198
1199
1200    /////////////////////////////////////////////////////////////////////
1201
//
1202
// ACCESSORS
1203
//
1204
/////////////////////////////////////////////////////////////////////
1205
/**
1206     * Get the method parameters.
1207     *
1208     * @return The method parameters
1209     */

1210    public JavaValueNode[] getMethodParms()
1211    {
1212        return methodParms;
1213    }
1214
1215    /**
1216     * Accept a visitor, and call v.visit()
1217     * on child nodes as necessary.
1218     *
1219     * @param v the visitor
1220     *
1221     * @exception StandardException on error
1222     */

1223    public Visitable accept(Visitor v)
1224        throws StandardException
1225    {
1226        Visitable returnNode = v.visit(this);
1227
1228        if (v.skipChildren(this))
1229        {
1230            return returnNode;
1231        }
1232
1233        for (int parm = 0;
1234            !v.stopTraversal() && parm < methodParms.length;
1235            parm++)
1236        {
1237            if (methodParms[parm] != null)
1238            {
1239                methodParms[parm] = (JavaValueNode)methodParms[parm].accept(v);
1240            }
1241        }
1242
1243        return returnNode;
1244    }
1245}
1246
Popular Tags