Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/apache/derby/impl/sql/compile/StaticMethodCallNode.java


1   /*
2   
3      Derby - Class org.apache.derby.impl.sql.compile.StaticMethodCallNode
4   
5      Copyright 1997, 2004 The Apache Software Foundation or its licensors, as applicable.
6   
7      Licensed under the Apache License, Version 2.0 (the "License");
8      you may not use this file except in compliance with the License.
9      You may obtain a copy of the License at
10  
11        http://www.apache.org/licenses/LICENSE-2.0
12  
13     Unless required by applicable law or agreed to in writing, software
14     distributed under the License is distributed on an "AS IS" BASIS,
15     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16     See the License for the specific language governing permissions and
17     limitations under the License.
18  
19   */
20  
21  package  org.apache.derby.impl.sql.compile;
22  
23  import org.apache.derby.iapi.services.compiler.MethodBuilder;
24  
25  import org.apache.derby.iapi.services.sanity.SanityManager;
26  
27  import org.apache.derby.iapi.sql.compile.CompilerContext;
28  import org.apache.derby.iapi.sql.compile.TypeCompiler;
29  import org.apache.derby.iapi.sql.compile.C_NodeTypes;
30  import org.apache.derby.iapi.types.JSQLType;
31  import org.apache.derby.iapi.types.DataTypeDescriptor;
32  import org.apache.derby.iapi.types.TypeId;
33  
34  import org.apache.derby.iapi.sql.dictionary.AliasDescriptor;
35  import org.apache.derby.iapi.sql.dictionary.DataDictionary;
36  import org.apache.derby.iapi.sql.dictionary.SchemaDescriptor;
37  
38  import org.apache.derby.iapi.reference.ClassName;
39  import org.apache.derby.iapi.reference.SQLState;
40  import org.apache.derby.iapi.reference.JDBC30Translation;
41  import org.apache.derby.iapi.error.StandardException;
42  
43  import org.apache.derby.impl.sql.compile.ExpressionClassBuilder;
44  import org.apache.derby.iapi.services.loader.ClassInspector;
45  import org.apache.derby.iapi.services.compiler.LocalField;
46  
47  import org.apache.derby.iapi.util.JBitSet;
48  import org.apache.derby.iapi.services.classfile.VMOpcode;
49  
50  import org.apache.derby.iapi.sql.conn.Authorizer;
51  
52  import org.apache.derby.catalog.AliasInfo;
53  import org.apache.derby.catalog.TypeDescriptor;
54  import org.apache.derby.catalog.types.RoutineAliasInfo;
55  import org.apache.derby.impl.sql.compile.ActivationClassBuilder;
56  
57  import org.apache.derby.catalog.UUID;
58  
59  import java.util.Vector;
60  import java.lang.reflect.Modifier;
61  
62  /**
63   * A StaticMethodCallNode represents a static method call from a Class
64   * (as opposed to from an Object).
65  
66     For a procedure the call requires that the arguments be ? parameters.
67     The parameter is *logically* passed into the method call a number of different ways.
68  
69     <P>
70     For a application call like CALL MYPROC(?) the logically Java method call is
71     (in psuedo Java/SQL code) (examples with CHAR(10) parameter)
72     <BR>
73     Fixed length IN parameters - com.acme.MyProcedureMethod(?)
74     <BR>
75     Variable length IN parameters - com.acme.MyProcedureMethod(CAST (? AS CHAR(10))
76     <BR>
77     Fixed length INOUT parameter -
78      String[] holder = new String[] {?}; com.acme.MyProcedureMethod(holder); ? = holder[0]
79     <BR>
80     Variable length INOUT parameter -
81      String[] holder = new String[] {CAST (? AS CHAR(10)}; com.acme.MyProcedureMethod(holder); ? = CAST (holder[0] AS CHAR(10))
82  
83     <BR>
84     Fixed length OUT parameter -
85      String[] holder = new String[1]; com.acme.MyProcedureMethod(holder); ? = holder[0]
86  
87     <BR>
88     Variable length INOUT parameter -
89      String[] holder = new String[1]; com.acme.MyProcedureMethod(holder); ? = CAST (holder[0] AS CHAR(10))
90  
91  
92      <P>
93    For static method calls there is no pre-definition of an IN or INOUT parameter, so a call to CallableStatement.registerOutParameter()
94    makes the parameter an INOUT parameter, provided:
95      - the parameter is passed directly to the method call (no casts or expressions).
96      - the method's parameter type is a Java array type.
97  
98      Since this is a dynmaic decision we compile in code to take both paths, based upon a boolean isINOUT which is dervied from the
99    ParameterValueSet. Code is logically (only single parameter String[] shown here). Note, no casts can exist here.
100 
101   boolean isINOUT = getParameterValueSet().getParameterMode(0) == PARAMETER_IN_OUT;
102   if (isINOUT) {
103     String[] holder = new String[] {?}; com.acme.MyProcedureMethod(holder); ? = holder[0]
104      
105   } else {
106     com.acme.MyProcedureMethod(?)
107   }
108 
109  *
110  * @author Jerry Brenner
111  */
112 public class StaticMethodCallNode extends MethodCallNode
113 {
114   private TableName procedureName;
115 
116   private LocalField[] outParamArrays;
117   private int[]     applicationParameterNumbers; 
118 
119   private boolean    isSystemCode;
120   private boolean    alreadyBound;
121 
122   private LocalField  returnsNullOnNullState;
123 
124 
125   AliasDescriptor  ad;
126 
127 
128   /**
129    * Intializer for a NonStaticMethodCallNode
130    *
131    * @param methodName    The name of the method to call
132    * @param javaClassName    The name of the java class that the static method belongs to.
133    */
134   public void init(Object methodName, Object javaClassName)
135   {
136     if (methodName instanceof String)
137       init(methodName);
138     else {
139       procedureName = (TableName) methodName;
140       init(procedureName.getTableName());
141     }
142 
143     this.javaClassName = (String) javaClassName;
144   }
145 
146   /**
147    * Bind this expression.  This means binding the sub-expressions,
148    * as well as figuring out what the return type is for this expression.
149    *
150    * @param fromList    The FROM list for the query this
151    *        expression is in, for binding columns.
152    * @param subqueryList    The subquery list being built as we find SubqueryNodes
153    * @param aggregateVector  The aggregate vector being built as we find AggregateNodes
154    *
155    * @return  this or an AggregateNode
156    *
157    * @exception StandardException    Thrown on error
158    */
159 
160   public JavaValueNode bindExpression(
161     FromList fromList, SubqueryList subqueryList,
162     Vector  aggregateVector) 
163       throws StandardException
164   {
165     // for a function we can get called recursively
166     if (alreadyBound)
167       return this;
168 
169 
170     bindParameters(fromList, subqueryList, aggregateVector);
171 
172     
173     /* If javaClassName is null then we assume that the current methodName
174      * is an alias and we must go to sysmethods to
175      * get the real method and java class names for this alias.
176      */
177     if (javaClassName == null)
178     {
179       CompilerContext cc = getCompilerContext();
180 
181       // look for a routine
182       if (ad == null) {
183 
184         String schemaName = procedureName != null ?
185                   procedureName.getSchemaName() : null;
186 
187         SchemaDescriptor sd = getSchemaDescriptor(schemaName, schemaName != null);
188 
189 
190         if (sd.getUUID() != null) {
191 
192         java.util.List list = getDataDictionary().getRoutineList(
193           sd.getUUID().toString(), methodName,
194           forCallStatement ? AliasInfo.ALIAS_NAME_SPACE_PROCEDURE_AS_CHAR : AliasInfo.ALIAS_NAME_SPACE_FUNCTION_AS_CHAR
195           );
196 
197         for (int i = list.size() - 1; i >= 0; i--) {
198 
199           AliasDescriptor proc = (AliasDescriptor) list.get(i);
200 
201           RoutineAliasInfo routineInfo = (RoutineAliasInfo) proc.getAliasInfo();
202           int parameterCount = routineInfo.getParameterCount();
203           if (parameterCount != methodParms.length)
204             continue;
205 
206           // pre-form the method signature. If it is a dynamic result set procedure
207           // then we need to add in the ResultSet array
208 
209           TypeDescriptor[] parameterTypes = routineInfo.getParameterTypes();
210 
211           int sigParameterCount = parameterCount;
212           if (routineInfo.getMaxDynamicResultSets() > 0)
213             sigParameterCount++;
214 
215           signature = new JSQLType[sigParameterCount];
216           for (int p = 0; p < parameterCount; p++) {
217 
218             // find the declared type.
219 
220             TypeDescriptor td = parameterTypes[p];
221 
222             TypeId typeId = TypeId.getBuiltInTypeId(td.getJDBCTypeId());
223 
224             TypeId parameterTypeId = typeId;
225 
226 
227             // if it's an OUT or INOUT parameter we need an array.
228             int parameterMode = routineInfo.getParameterModes()[p];
229 
230             if (parameterMode != JDBC30Translation.PARAMETER_MODE_IN) {
231 
232               String arrayType;
233               switch (typeId.getJDBCTypeId()) {
234                 case java.sql.Types.SMALLINT:
235                 case java.sql.Types.INTEGER:
236                 case java.sql.Types.BIGINT:
237                 case java.sql.Types.REAL:
238                 case java.sql.Types.DOUBLE:
239                   arrayType = getTypeCompiler(typeId).getCorrespondingPrimitiveTypeName().concat("[]");
240                   break;
241                 default:
242                   arrayType = typeId.getCorrespondingJavaTypeName().concat("[]");
243                   break;
244               }
245 
246               typeId = TypeId.getUserDefinedTypeId(arrayType, false);
247             }
248 
249             // this is the type descriptor of the require method parameter
250             DataTypeDescriptor methoddtd = new DataTypeDescriptor(
251                 typeId,
252                 td.getPrecision(),
253                 td.getScale(),
254                 td.isNullable(),
255                 td.getMaximumWidth()
256               );
257 
258             signature[p] = new JSQLType(methoddtd);
259 
260             // check parameter is a ? node for INOUT and OUT parameters.
261 
262             ValueNode sqlParamNode = null;
263 
264             if (methodParms[p] instanceof SQLToJavaValueNode) {
265               SQLToJavaValueNode sql2j = (SQLToJavaValueNode) methodParms[p];
266               sqlParamNode = sql2j.getSQLValueNode();
267             }
268             else
269             {
270             }
271 
272             boolean isParameterMarker = true;
273             if ((sqlParamNode == null) || !sqlParamNode.isParameterNode())
274             {
275               if (parameterMode != JDBC30Translation.PARAMETER_MODE_IN) {
276                
277                 throw StandardException.newException(SQLState.LANG_DB2_PARAMETER_NEEDS_MARKER,
278                   RoutineAliasInfo.parameterMode(parameterMode),
279                   routineInfo.getParameterNames()[p]);
280               }
281               isParameterMarker = false;
282             }
283             else
284             {
285               if (applicationParameterNumbers == null)
286                 applicationParameterNumbers = new int[parameterCount];
287               applicationParameterNumbers[p] = ((ParameterNode) sqlParamNode).getParameterNumber();
288             }
289 
290             // this is the SQL type of the procedure parameter.
291             DataTypeDescriptor paramdtd = new DataTypeDescriptor(
292               parameterTypeId,
293               td.getPrecision(),
294               td.getScale(),
295               td.isNullable(),
296               td.getMaximumWidth()
297             );
298 
299             boolean needCast = false;
300             if (!isParameterMarker)
301             {
302 
303               // can only be an IN parameter.
304               // check that the value can be assigned to the
305               // type of the procedure parameter.
306               if (sqlParamNode instanceof UntypedNullConstantNode)
307               {
308                 sqlParamNode.setDescriptor(paramdtd);
309               }
310               else
311               {
312 
313 
314                 DataTypeDescriptor dts;
315                 TypeId argumentTypeId;
316 
317                 if (sqlParamNode != null)
318                 {
319                   // a node from the SQL world
320                   argumentTypeId = sqlParamNode.getTypeId();
321                   dts = sqlParamNode.getTypeServices();
322                 }
323                 else
324                 {
325                   // a node from the Java world
326                   dts = DataTypeDescriptor.getSQLDataTypeDescriptor(methodParms[p].getJavaTypeName());
327                   if (dts == null)
328                   {
329                     throw StandardException.newException(SQLState.LANG_NO_CORRESPONDING_S_Q_L_TYPE, 
330                       methodParms[p].getJavaTypeName());
331                   }
332 
333                   argumentTypeId = dts.getTypeId();
334                 }
335 
336                 if (! getTypeCompiler(parameterTypeId).storable(argumentTypeId, getClassFactory()))
337                     throw StandardException.newException(SQLState.LANG_NOT_STORABLE, 
338                       parameterTypeId.getSQLTypeName(),
339                       argumentTypeId.getSQLTypeName() );
340 
341                 // if it's not an exact length match then some cast will be needed.
342                 if (!paramdtd.isExactTypeAndLengthMatch(dts))
343                   needCast = true;
344               }
345             }
346             else
347             {
348               // any variable length type will need a cast from the
349               // Java world (the ? parameter) to the SQL type. This
350               // ensures values like CHAR(10) are passed into the procedure
351               // correctly as 10 characters long.
352               if (parameterTypeId.variableLength()) {
353 
354                 if (parameterMode != JDBC30Translation.PARAMETER_MODE_OUT)
355                   needCast = true;
356               }
357             }
358             
359 
360             if (needCast)
361             {
362               // push a cast node to ensure the
363               // correct type is passed to the method
364               // this gets tacky because before we knew
365               // it was a procedure call we ensured all the
366               // parameter are JavaNodeTypes. Now we need to
367               // push them back to the SQL domain, cast them
368               // and then push them back to the Java domain.
369 
370               if (sqlParamNode == null) {
371 
372                 sqlParamNode = (ValueNode) getNodeFactory().getNode(
373                   C_NodeTypes.JAVA_TO_SQL_VALUE_NODE,
374                   methodParms[p], 
375                   getContextManager());
376               }
377 
378               ValueNode castNode = (ValueNode) getNodeFactory().getNode(
379                 C_NodeTypes.CAST_NODE,
380                 sqlParamNode, 
381                 paramdtd,
382                 getContextManager());
383 
384 
385               methodParms[p] = (JavaValueNode) getNodeFactory().getNode(
386                   C_NodeTypes.SQL_TO_JAVA_VALUE_NODE,
387                   castNode, 
388                   getContextManager());
389 
390               methodParms[p] = methodParms[p].bindExpression(fromList, subqueryList, aggregateVector);
391             }
392 
393             // only force the type for a ? so that the correct type shows up
394             // in parameter meta data
395             if (isParameterMarker)
396               sqlParamNode.setDescriptor(paramdtd);
397           }
398 
399           if (sigParameterCount != parameterCount) {
400 
401             TypeId typeId = TypeId.getUserDefinedTypeId("java.sql.ResultSet[]", false);
402 
403             DataTypeDescriptor dtd = new DataTypeDescriptor(
404                 typeId,
405                 0,
406                 0,
407                 false,
408                 -1
409               );
410 
411             signature[parameterCount] = new JSQLType(dtd);
412 
413           }
414 
415           this.routineInfo = routineInfo;
416           ad = proc;
417 
418           // If a procedure is in the system schema and defined as executing
419           // SQL do we set we are in system code.
420           if (sd.isSystemSchema() && (routineInfo.getReturnType() == null) && routineInfo.getSQLAllowed() != RoutineAliasInfo.NO_SQL)
421             isSystemCode = true;
422 
423           break;
424         }
425       }
426   
427       }
428 
429       /* Throw exception if no alias found */
430       if (ad == null)
431       {
432         Object errName;
433         if (procedureName == null)
434           errName = methodName;
435         else
436           errName = procedureName;
437 
438         throw StandardException.newException(SQLState.LANG_NO_SUCH_METHOD_ALIAS, errName);
439       }
440   
441 
442 
443       /* Query is dependent on the AliasDescriptor */
444       cc.createDependency(ad);
445 
446 
447       methodName = ad.getAliasInfo().getMethodName();
448       javaClassName = ad.getJavaClassName();
449     }
450 
451 
452     javaClassName = verifyClassExist(javaClassName, true);
453 
454     /* Resolve the method call */
455     resolveMethodCall(javaClassName, true);
456 
457 
458     alreadyBound = true;
459 
460     // If this is a function call with a variable length
461     // return type, then we need to push a CAST node.
462     if (routineInfo != null)
463     {
464       TypeDescriptor returnType = routineInfo.getReturnType();
465       if (returnType != null)
466       {
467         TypeId returnTypeId = TypeId.getBuiltInTypeId(returnType.getJDBCTypeId());
468 
469         if (returnTypeId.variableLength()) {
470           // Cast the return using a cast node, but have to go
471           // into the SQL domain, and back to the Java domain.
472 
473           DataTypeDescriptor returnValueDtd = new DataTypeDescriptor(
474                 returnTypeId,
475                 returnType.getPrecision(),
476                 returnType.getScale(),
477                 returnType.isNullable(),
478                 returnType.getMaximumWidth()
479               );
480 
481 
482           ValueNode returnValueToSQL = (ValueNode) getNodeFactory().getNode(
483                 C_NodeTypes.JAVA_TO_SQL_VALUE_NODE,
484                 this, 
485                 getContextManager());
486 
487           ValueNode returnValueCastNode = (ValueNode) getNodeFactory().getNode(
488                   C_NodeTypes.CAST_NODE,
489                   returnValueToSQL, 
490                   returnValueDtd,
491                   getContextManager());
492 
493 
494           JavaValueNode returnValueToJava = (JavaValueNode) getNodeFactory().getNode(
495                     C_NodeTypes.SQL_TO_JAVA_VALUE_NODE,
496                     returnValueCastNode, 
497                     getContextManager());
498 
499           return returnValueToJava.bindExpression(fromList, subqueryList, aggregateVector);
500         }
501 
502       }
503     }
504 
505     return this;
506   }
507 
508   /**
509     Push extra code to generate the casts within the
510     arrays for the parameters passed as arrays.
511   */
512   public  void generateOneParameter(ExpressionClassBuilder acb,
513                       MethodBuilder mb,
514                       int parameterNumber )
515       throws StandardException
516   {
517     int parameterMode;
518 
519 
520     SQLToJavaValueNode sql2j = null;
521     if (methodParms[parameterNumber] instanceof SQLToJavaValueNode)
522       sql2j = (SQLToJavaValueNode) methodParms[parameterNumber];
523     
524     if (routineInfo != null) {
525       parameterMode = routineInfo.getParameterModes()[parameterNumber];
526     } else {
527       // for a static method call the parameter always starts out as a in parameter, but
528       // may be registered as an IN OUT parameter. For a static method argument to be
529       // a dynmaically registered out parameter it must be a simple ? parameter
530 
531       parameterMode = JDBC30Translation.PARAMETER_MODE_IN;
532 
533       if (sql2j != null) {
534         if (sql2j.getSQLValueNode().isParameterNode()) {
535 
536           // applicationParameterNumbers is only set up for a procedure.
537           int applicationParameterNumber = ((ParameterNode) (sql2j.getSQLValueNode())).getParameterNumber();
538 
539           String parameterType = methodParameterTypes[parameterNumber];
540 
541           if (parameterType.endsWith("[]")) {
542 
543             // constructor  - setting up correct paramter type info
544             MethodBuilder constructor = acb.getConstructor();
545             acb.pushThisAsActivation(constructor);
546             constructor.callMethod(VMOpcode.INVOKEINTERFACE, null,
547                       "getParameterValueSet", ClassName.ParameterValueSet, 0);
548 
549             constructor.push(applicationParameterNumber);
550             constructor.push(JDBC30Translation.PARAMETER_MODE_UNKNOWN);
551             constructor.callMethod(VMOpcode.INVOKEINTERFACE, null,
552                       "setParameterMode", "void", 2);
553             constructor.endStatement();
554           }
555         }
556       } 
557     }
558 
559     switch (parameterMode) {
560     case JDBC30Translation.PARAMETER_MODE_IN:
561     case JDBC30Translation.PARAMETER_MODE_IN_OUT:
562     case JDBC30Translation.PARAMETER_MODE_UNKNOWN:
563       if (sql2j != null)
564         sql2j.returnsNullOnNullState = returnsNullOnNullState;
565       super.generateOneParameter(acb, mb, parameterNumber);
566       break;
567 
568     case JDBC30Translation.PARAMETER_MODE_OUT:
569       // For an OUT parameter we require nothing to be pushed into the
570       // method call from the parameter node.
571       break;
572     }
573 
574     switch (parameterMode) {
575     case JDBC30Translation.PARAMETER_MODE_IN:
576     case JDBC30Translation.PARAMETER_MODE_UNKNOWN:
577       break;
578 
579     case JDBC30Translation.PARAMETER_MODE_IN_OUT:
580     case JDBC30Translation.PARAMETER_MODE_OUT:
581     {
582       // Create the array used to pass into the method. We create a
583       // new array for each call as there is a small chance the
584       // application could retain a reference to it and corrupt
585       // future calls with the same CallableStatement object.
586 
587       String methodParameterType = methodParameterTypes[parameterNumber];
588       String arrayType = methodParameterType.substring(0, methodParameterType.length() - 2);
589       LocalField lf = acb.newFieldDeclaration(Modifier.PRIVATE, methodParameterType);
590 
591       if (outParamArrays == null)
592         outParamArrays = new LocalField[methodParms.length];
593 
594       outParamArrays[parameterNumber] = lf;
595 
596       mb.pushNewArray(arrayType, 1);
597       mb.putField(lf);
598 
599       // set the IN part of the parameter into the INOUT parameter.
600       if (parameterMode != JDBC30Translation.PARAMETER_MODE_OUT) {
601         mb.swap();
602         mb.setArrayElement(0);
603         mb.getField(lf);
604       }
605       break;
606       }
607     }
608 
609   }
610 
611   /**
612    * Categorize this predicate.  Initially, this means
613    * building a bit map of the referenced tables for each predicate.
614    * If the source of this ColumnReference (at the next underlying level) 
615    * is not a ColumnReference or a VirtualColumnNode then this predicate
616    * will not be pushed down.
617    *
618    * For example, in:
619    *    select * from (select 1 from s) a (x) where x = 1
620    * we will not push down x = 1.
621    * NOTE: It would be easy to handle the case of a constant, but if the
622    * inner SELECT returns an arbitrary expression, then we would have to copy
623    * that tree into the pushed predicate, and that tree could contain
624    * subqueries and method calls.
625    * RESOLVE - revisit this issue once we have views.
626    *
627    * @param referencedTabs  JBitSet with bit map of referenced FromTables
628    * @param simplePredsOnly  Whether or not to consider method
629    *              calls, field references and conditional nodes
630    *              when building bit map
631    *
632    * @return boolean    Whether or not source.expression is a ColumnReference
633    *            or a VirtualColumnNode.
634    *
635    * @exception StandardException    Thrown on error
636    */
637   public boolean categorize(JBitSet referencedTabs, boolean simplePredsOnly)
638     throws StandardException
639   {
640     /* We stop here when only considering simple predicates
641      *  as we don't consider method calls when looking
642      * for null invariant predicates.
643      */
644     if (simplePredsOnly)
645     {
646       return false;
647     }
648 
649     boolean pushable = true;
650 
651     pushable = pushable && super.categorize(referencedTabs, simplePredsOnly);
652 
653     return pushable;
654   }
655 
656   /**
657    * Convert this object to a String.  See comments in QueryTreeNode.java
658    * for how this should be done for tree printing.
659    *
660    * @return  This object as a String
661    */
662 
663   public String toString()
664   {
665     if (SanityManager.DEBUG)
666     {
667       return "javaClassName: " +
668         (javaClassName != null ? javaClassName : "null") + "\n" +
669         super.toString();
670     }
671     else
672     {
673       return "";
674     }
675   }
676 
677   /**
678    * Do code generation for this method call
679    *
680    * @param acb  The ExpressionClassBuilder for the class we're generating
681    * @param mb  The method the expression will go into
682    *
683    *
684    * @exception StandardException    Thrown on error
685    */
686 
687   public void generateExpression(ExpressionClassBuilder acb,
688                       MethodBuilder mb)
689                   throws StandardException
690   {
691     if (routineInfo != null) {
692 
693       if (!routineInfo.calledOnNullInput() && routineInfo.getParameterCount() != 0)
694         returnsNullOnNullState = acb.newFieldDeclaration(Modifier.PRIVATE, "boolean");
695 
696     }
697 
698     // reset the parameters are null indicator.
699     if (returnsNullOnNullState != null) {
700       mb.push(false);
701       mb.setField(returnsNullOnNullState);
702 
703       // for the call to the generated method below.
704       mb.pushThis();
705     }
706 
707     int nargs = generateParameters(acb, mb);
708 
709     LocalField functionEntrySQLAllowed = null;
710 
711     if (routineInfo != null) {
712 
713       short sqlAllowed = routineInfo.getSQLAllowed();
714 
715       // Before we set up our authorization level, add a check to see if this
716       // method can be called. If the routine is NO SQL or CONTAINS SQL 
717       // then there is no need for a check. As follows:
718       //
719       // Current Level = NO_SQL - CALL will be rejected when getting CALL result set
720       // Current Level = anything else - calls to procedures defined as NO_SQL and CONTAINS SQL both allowed.
721 
722 
723       if (sqlAllowed != RoutineAliasInfo.NO_SQL)
724       {
725         
726         int sqlOperation;
727         
728         if (sqlAllowed == RoutineAliasInfo.READS_SQL_DATA)
729           sqlOperation = Authorizer.SQL_SELECT_OP;
730         else if (sqlAllowed == RoutineAliasInfo.MODIFIES_SQL_DATA)
731           sqlOperation = Authorizer.SQL_WRITE_OP;
732         else
733           sqlOperation = Authorizer.SQL_ARBITARY_OP;
734         
735         generateAuthorizeCheck((ActivationClassBuilder) acb, mb, sqlOperation);
736       }
737 
738       int statmentContextReferences = isSystemCode ? 2 : 1;
739       
740       boolean isFunction = routineInfo.getReturnType() != null;
741 
742       if (isFunction)
743         statmentContextReferences++;
744 
745 
746       if (statmentContextReferences != 0) {
747         acb.pushThisAsActivation(mb);
748         mb.callMethod(VMOpcode.INVOKEINTERFACE, null,
749                   "getLanguageConnectionContext", ClassName.LanguageConnectionContext, 0);
750         mb.callMethod(VMOpcode.INVOKEINTERFACE, null,
751                   "getStatementContext", "org.apache.derby.iapi.sql.conn.StatementContext", 0);
752 
753         for (int scc = 1; scc < statmentContextReferences; scc++)
754           mb.dup();
755       }
756 
757       /**
758         Set the statement context to reflect we are running
759         System procedures, so that we can execute non-standard SQL.
760       */
761       if (isSystemCode) {
762         mb.callMethod(VMOpcode.INVOKEINTERFACE, null,
763                   "setSystemCode", "void", 0);
764       }
765 
766       // for a function we need to fetch the current SQL control
767       // so that we can reset it once the function is complete.
768       // 
769       if (isFunction)
770       {
771         functionEntrySQLAllowed = acb.newFieldDeclaration(Modifier.PRIVATE, "short");
772         mb.callMethod(VMOpcode.INVOKEINTERFACE, null,
773                   "getSQLAllowed", "short", 0);
774         mb.setField(functionEntrySQLAllowed);
775 
776       }
777       
778       
779       // Set up the statement context to reflect the
780       // restricted SQL execution allowed by this routine.
781 
782       mb.push(sqlAllowed);
783       mb.push(false);
784       mb.callMethod(VMOpcode.INVOKEINTERFACE, null,
785                 "setSQLAllowed", "void", 2);
786 
787     }
788 
789     // add in the ResultSet arrays.
790     if (routineInfo != null) {
791 
792       int compiledResultSets = methodParameterTypes.length - methodParms.length;
793 
794       if (compiledResultSets != 0) {
795 
796         // Add a method that indicates the maxium number of dynamic result sets.
797         int maxDynamicResults = routineInfo.getMaxDynamicResultSets();
798         if (maxDynamicResults > 0) {
799           MethodBuilder gdr = acb.getClassBuilder().newMethodBuilder(Modifier.PUBLIC, "int", "getMaxDynamicResults");
800           gdr.push(maxDynamicResults);
801           gdr.methodReturn();
802           gdr.complete();
803         }
804 
805         // add a method to return all the dynamic result sets (unordered)
806         MethodBuilder gdr = acb.getClassBuilder().newMethodBuilder(Modifier.PUBLIC, "java.sql.ResultSet[][]", "getDynamicResults");
807 
808         MethodBuilder cons = acb.getConstructor();
809         // if (procDef.getParameterStyle() == RoutineAliasInfo.PS_JAVA)
810         {
811           // PARAMETER STYLE JAVA
812 
813           LocalField procedureResultSetsHolder = acb.newFieldDeclaration(Modifier.PRIVATE, "java.sql.ResultSet[][]");
814 
815           // getDynamicResults body
816           gdr.getField(procedureResultSetsHolder);
817 
818           // create the holder of all the ResultSet arrays, new java.sql.ResultSet[][compiledResultSets]
819           cons.pushNewArray("java.sql.ResultSet[]", compiledResultSets);
820           cons.setField(procedureResultSetsHolder);
821 
822 
823           // arguments for the dynamic result sets
824           for (int i = 0; i < compiledResultSets; i++) {
825 
826             mb.pushNewArray("java.sql.ResultSet", 1);
827             mb.dup();
828 
829             mb.getField(procedureResultSetsHolder);
830             mb.swap();
831 
832             mb.setArrayElement(i);
833           }
834         } 
835 
836         // complete the method that returns the ResultSet[][] to the 
837         gdr.methodReturn();
838         gdr.complete();
839 
840         nargs += compiledResultSets;
841       }
842 
843     }
844 
845     String javaReturnType = getJavaTypeName();
846 
847     MethodBuilder mbnc = null;
848     MethodBuilder mbcm = mb;
849 
850 
851     // If any of the parameters are null then
852     // do not call the method, just return null.
853     if (returnsNullOnNullState != null)
854     {
855       mbnc = acb.newGeneratedFun(javaReturnType, Modifier.PRIVATE, methodParameterTypes);
856 
857       // add the throws clause for the public static method we are going to call.
858       Class[] throwsSet = ((java.lang.reflect.Method) method).getExceptionTypes();
859       for (int te = 0; te < throwsSet.length; te++)
860       {
861         mbnc.addThrownException(throwsSet[te].getName());
862       }
863 
864       mbnc.getField(returnsNullOnNullState);
865       mbnc.conditionalIf();
866 
867       // set up for a null!!
868       // for objects is easy.
869       mbnc.pushNull(javaReturnType);
870 
871       mbnc.startElseCode();  
872 
873       if (!actualMethodReturnType.equals(javaReturnType))
874         mbnc.pushNewStart(javaReturnType);
875 
876       // fetch all the arguments
877       for (int pa = 0; pa < nargs; pa++)
878       {
879         mbnc.getParameter(pa);
880       }
881 
882       mbcm = mbnc;
883     }
884 
885     mbcm.callMethod(VMOpcode.INVOKESTATIC, method.getDeclaringClass().getName(), methodName,
886           actualMethodReturnType, nargs);
887 
888 
889     if (returnsNullOnNullState != null)
890     {
891       if (!actualMethodReturnType.equals(javaReturnType))
892         mbnc.pushNewComplete(1);
893 
894       mbnc.completeConditional();
895 
896       mbnc.methodReturn();
897       mbnc.complete();
898 
899       // now call the wrapper method
900       mb.callMethod(VMOpcode.INVOKEVIRTUAL, acb.getClassBuilder().getFullName(), mbnc.getName(),
901           javaReturnType, nargs);
902       mbnc = null;
903     }
904 
905 
906     if (routineInfo != null) {
907 
908       // reset the SQL allowed setting that we set upon
909       // entry to the method.
910       if (functionEntrySQLAllowed != null) {
911         acb.pushThisAsActivation(mb);
912         mb.callMethod(VMOpcode.INVOKEINTERFACE, null,
913                   "getLanguageConnectionContext", ClassName.LanguageConnectionContext, 0);
914         mb.callMethod(VMOpcode.INVOKEINTERFACE, null,
915                   "getStatementContext", "org.apache.derby.iapi.sql.conn.StatementContext", 0);
916         mb.getField(functionEntrySQLAllowed);
917         mb.push(true); // override as we are ending the control set by this function all.
918         mb.callMethod(VMOpcode.INVOKEINTERFACE, null,
919                   "setSQLAllowed", "void", 2);
920 
921       }
922 
923       if (outParamArrays != null) {
924 
925         MethodBuilder constructor = acb.getConstructor();
926 
927         // constructor  - setting up correct paramter type info
928         acb.pushThisAsActivation(constructor);
929         constructor.callMethod(VMOpcode.INVOKEINTERFACE, null,
930                   "getParameterValueSet", ClassName.ParameterValueSet, 0);
931 
932         // execute  - passing out parameters back.
933         acb.pushThisAsActivation(mb);
934         mb.callMethod(VMOpcode.INVOKEINTERFACE, null,
935                   "getParameterValueSet", ClassName.ParameterValueSet, 0);
936 
937         int[] parameterModes = routineInfo.getParameterModes();
938         for (int i = 0; i < outParamArrays.length; i++) {
939 
940           int parameterMode = parameterModes[i];
941           if (parameterMode != JDBC30Translation.PARAMETER_MODE_IN) {
942 
943             // must be a parameter if it is INOUT or OUT.
944             ValueNode sqlParamNode = ((SQLToJavaValueNode) methodParms[i]).getSQLValueNode();
945 
946 
947             int applicationParameterNumber = applicationParameterNumbers[i];
948 
949             // Set the correct parameter nodes in the ParameterValueSet at constructor time.
950             constructor.dup();
951             constructor.push(applicationParameterNumber);
952             constructor.push(parameterMode);
953             constructor.callMethod(VMOpcode.INVOKEINTERFACE, null,
954                     "setParameterMode", "void", 2);
955 
956             // Pass the value of the outparameters back to the calling code
957             LocalField lf = outParamArrays[i];
958 
959             mb.dup(); 
960             mb.push(applicationParameterNumber);
961             mb.callMethod(VMOpcode.INVOKEINTERFACE, null,
962                   "getParameter", ClassName.DataValueDescriptor, 1);
963 
964             // see if we need to set the desired length/scale/precision of the type
965             DataTypeDescriptor paramdtd = sqlParamNode.getTypeServices();
966 
967             boolean isNumericType = paramdtd.getTypeId().isNumericTypeId();
968 
969             // is the underlying type for the OUT/INOUT parameter primitive.
970             boolean isPrimitive = ((java.lang.reflect.Method) method).getParameterTypes()[i].getComponentType().isPrimitive();
971 
972             if (isNumericType) {
973               // need to up-cast as the setValue(Number) method only exists on NumberDataValue
974 
975               if (!isPrimitive)
976                 mb.cast(ClassName.NumberDataValue);
977             }
978             else if (paramdtd.getTypeId().isBooleanTypeId())
979             {
980               // need to cast as the setValue(Boolean) method only exists on BooleanDataValue
981               if (!isPrimitive)
982                 mb.cast(ClassName.BooleanDataValue);
983             }
984 
985             if (paramdtd.getTypeId().variableLength()) {
986               // need another DVD reference for the set width below.
987               mb.dup();
988             }
989 
990 
991             mb.getField(lf); // pvs, dvd, array
992             mb.getArrayElement(0); // pvs, dvd, value
993 
994             // The value needs to be set thorugh the setValue(Number) method.
995             if (isNumericType && !isPrimitive)
996             {
997               mb.upCast("java.lang.Number");
998             }
999 
1000            mb.callMethod(VMOpcode.INVOKEINTERFACE, null, "setValue", "void", 1);
1001
1002            if (paramdtd.getTypeId().variableLength()) {
1003              mb.push(isNumericType ? paramdtd.getPrecision() : paramdtd.getMaximumWidth());
1004              mb.push(paramdtd.getScale());
1005              mb.push(isNumericType);
1006              mb.callMethod(VMOpcode.INVOKEINTERFACE, ClassName.VariableSizeDataValue, "setWidth", ClassName.DataValueDescriptor, 3);
1007              mb.endStatement();
1008            }
1009          }
1010        }
1011        constructor.endStatement();
1012        mb.endStatement();
1013      }
1014
1015    }
1016  }
1017}