KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2
3    Derby - Class org.apache.derby.impl.sql.compile.StaticMethodCallNode
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
26 import org.apache.derby.iapi.services.sanity.SanityManager;
27
28 import org.apache.derby.iapi.sql.compile.CompilerContext;
29 import org.apache.derby.iapi.sql.compile.TypeCompiler;
30 import org.apache.derby.iapi.sql.compile.C_NodeTypes;
31 import org.apache.derby.iapi.types.JSQLType;
32 import org.apache.derby.iapi.types.DataTypeDescriptor;
33 import org.apache.derby.iapi.types.TypeId;
34
35 import org.apache.derby.iapi.sql.dictionary.AliasDescriptor;
36 import org.apache.derby.iapi.sql.dictionary.DataDictionary;
37 import org.apache.derby.iapi.sql.dictionary.SchemaDescriptor;
38
39 import org.apache.derby.iapi.reference.ClassName;
40 import org.apache.derby.iapi.reference.SQLState;
41 import org.apache.derby.iapi.reference.JDBC30Translation;
42 import org.apache.derby.iapi.error.StandardException;
43
44 import org.apache.derby.impl.sql.compile.ExpressionClassBuilder;
45 import org.apache.derby.iapi.services.loader.ClassInspector;
46 import org.apache.derby.iapi.services.compiler.LocalField;
47
48 import org.apache.derby.iapi.util.JBitSet;
49 import org.apache.derby.iapi.services.classfile.VMOpcode;
50
51 import org.apache.derby.iapi.sql.conn.Authorizer;
52
53 import org.apache.derby.catalog.AliasInfo;
54 import org.apache.derby.catalog.TypeDescriptor;
55 import org.apache.derby.catalog.types.RoutineAliasInfo;
56 import org.apache.derby.impl.sql.compile.ActivationClassBuilder;
57
58 import org.apache.derby.catalog.UUID;
59
60 import java.util.Vector JavaDoc;
61 import java.lang.reflect.Modifier JavaDoc;
62
63 /**
64  * A StaticMethodCallNode represents a static method call from a Class
65  * (as opposed to from an Object).
66
67    For a procedure the call requires that the arguments be ? parameters.
68    The parameter is *logically* passed into the method call a number of different ways.
69
70    <P>
71    For a application call like CALL MYPROC(?) the logically Java method call is
72    (in psuedo Java/SQL code) (examples with CHAR(10) parameter)
73    <BR>
74    Fixed length IN parameters - com.acme.MyProcedureMethod(?)
75    <BR>
76    Variable length IN parameters - com.acme.MyProcedureMethod(CAST (? AS CHAR(10))
77    <BR>
78    Fixed length INOUT parameter -
79         String[] holder = new String[] {?}; com.acme.MyProcedureMethod(holder); ? = holder[0]
80    <BR>
81    Variable length INOUT parameter -
82         String[] holder = new String[] {CAST (? AS CHAR(10)}; com.acme.MyProcedureMethod(holder); ? = CAST (holder[0] AS CHAR(10))
83
84    <BR>
85    Fixed length OUT parameter -
86         String[] holder = new String[1]; com.acme.MyProcedureMethod(holder); ? = holder[0]
87
88    <BR>
89    Variable length INOUT parameter -
90         String[] holder = new String[1]; com.acme.MyProcedureMethod(holder); ? = CAST (holder[0] AS CHAR(10))
91
92
93     <P>
94     For static method calls there is no pre-definition of an IN or INOUT parameter, so a call to CallableStatement.registerOutParameter()
95     makes the parameter an INOUT parameter, provided:
96         - the parameter is passed directly to the method call (no casts or expressions).
97         - the method's parameter type is a Java array type.
98
99     Since this is a dynmaic decision we compile in code to take both paths, based upon a boolean isINOUT which is dervied from the
100     ParameterValueSet. Code is logically (only single parameter String[] shown here). Note, no casts can exist here.
101
102     boolean isINOUT = getParameterValueSet().getParameterMode(0) == PARAMETER_IN_OUT;
103     if (isINOUT) {
104         String[] holder = new String[] {?}; com.acme.MyProcedureMethod(holder); ? = holder[0]
105        
106     } else {
107         com.acme.MyProcedureMethod(?)
108     }
109
110  *
111  * @author Jerry Brenner
112  */

113 public class StaticMethodCallNode extends MethodCallNode
114 {
115     private TableName procedureName;
116
117     private LocalField[] outParamArrays;
118     private int[] applicationParameterNumbers;
119
120     private boolean isSystemCode;
121     private boolean alreadyBound;
122
123     private LocalField returnsNullOnNullState;
124
125
126     AliasDescriptor ad;
127
128
129     /**
130      * Intializer for a NonStaticMethodCallNode
131      *
132      * @param methodName The name of the method to call
133      * @param javaClassName The name of the java class that the static method belongs to.
134      */

135     public void init(Object JavaDoc methodName, Object JavaDoc javaClassName)
136     {
137         if (methodName instanceof String JavaDoc)
138             init(methodName);
139         else {
140             procedureName = (TableName) methodName;
141             init(procedureName.getTableName());
142         }
143
144         this.javaClassName = (String JavaDoc) javaClassName;
145     }
146
147     /**
148      * Bind this expression. This means binding the sub-expressions,
149      * as well as figuring out what the return type is for this expression.
150      *
151      * @param fromList The FROM list for the query this
152      * expression is in, for binding columns.
153      * @param subqueryList The subquery list being built as we find SubqueryNodes
154      * @param aggregateVector The aggregate vector being built as we find AggregateNodes
155      *
156      * @return this or an AggregateNode
157      *
158      * @exception StandardException Thrown on error
159      */

160
161     public JavaValueNode bindExpression(
162         FromList fromList, SubqueryList subqueryList,
163         Vector JavaDoc aggregateVector)
164             throws StandardException
165     {
166         // for a function we can get called recursively
167
if (alreadyBound)
168             return this;
169
170
171         bindParameters(fromList, subqueryList, aggregateVector);
172
173         
174         /* If javaClassName is null then we assume that the current methodName
175          * is an alias and we must go to sysmethods to
176          * get the real method and java class names for this alias.
177          */

178         if (javaClassName == null)
179         {
180             CompilerContext cc = getCompilerContext();
181
182             // look for a routine
183
if (ad == null) {
184
185                 String JavaDoc schemaName = procedureName != null ?
186                                     procedureName.getSchemaName() : null;
187                                     
188                 boolean noSchema = schemaName == null;
189
190                 SchemaDescriptor sd = getSchemaDescriptor(schemaName, schemaName != null);
191
192
193                 resolveRoutine(fromList, subqueryList, aggregateVector, sd);
194                 
195                 if (ad == null && noSchema && !forCallStatement)
196                 {
197                     // Resolve to a built-in SYSFUN function but only
198
// if this is a function call and the call
199
// was not qualified. E.g. COS(angle). The
200
// SYSFUN functions are not in SYSALIASES but
201
// an in-memory table, set up in DataDictioanryImpl.
202
sd = getSchemaDescriptor("SYSFUN", true);
203                     
204                     resolveRoutine(fromList, subqueryList, aggregateVector, sd);
205                 }
206     
207             }
208
209             /* Throw exception if no alias found */
210             if (ad == null)
211             {
212                 Object JavaDoc errName;
213                 if (procedureName == null)
214                     errName = methodName;
215                 else
216                     errName = procedureName;
217
218                 throw StandardException.newException(SQLState.LANG_NO_SUCH_METHOD_ALIAS, errName);
219             }
220     
221
222
223             /* Query is dependent on the AliasDescriptor */
224             cc.createDependency(ad);
225
226
227             methodName = ad.getAliasInfo().getMethodName();
228             javaClassName = ad.getJavaClassName();
229         }
230
231
232         javaClassName = verifyClassExist(javaClassName, true);
233
234         /* Resolve the method call */
235         resolveMethodCall(javaClassName, true);
236
237
238         alreadyBound = true;
239         if (isPrivilegeCollectionRequired())
240             getCompilerContext().addRequiredRoutinePriv(ad);
241
242         // If this is a function call with a variable length
243
// return type, then we need to push a CAST node.
244
if (routineInfo != null)
245         {
246             if (methodParms != null)
247                 optimizeDomainValueConversion();
248             
249             TypeDescriptor returnType = routineInfo.getReturnType();
250             if (returnType != null)
251             {
252                 TypeId returnTypeId = TypeId.getBuiltInTypeId(returnType.getJDBCTypeId());
253
254                 if (returnTypeId.variableLength()) {
255                     // Cast the return using a cast node, but have to go
256
// into the SQL domain, and back to the Java domain.
257

258                     DataTypeDescriptor returnValueDtd = new DataTypeDescriptor(
259                                 returnTypeId,
260                                 returnType.getPrecision(),
261                                 returnType.getScale(),
262                                 returnType.isNullable(),
263                                 returnType.getMaximumWidth()
264                             );
265
266
267                     ValueNode returnValueToSQL = (ValueNode) getNodeFactory().getNode(
268                                 C_NodeTypes.JAVA_TO_SQL_VALUE_NODE,
269                                 this,
270                                 getContextManager());
271
272                     ValueNode returnValueCastNode = (ValueNode) getNodeFactory().getNode(
273                                     C_NodeTypes.CAST_NODE,
274                                     returnValueToSQL,
275                                     returnValueDtd,
276                                     getContextManager());
277
278
279                     JavaValueNode returnValueToJava = (JavaValueNode) getNodeFactory().getNode(
280                                         C_NodeTypes.SQL_TO_JAVA_VALUE_NODE,
281                                         returnValueCastNode,
282                                         getContextManager());
283
284                     return returnValueToJava.bindExpression(fromList, subqueryList, aggregateVector);
285                 }
286
287             }
288         }
289
290         return this;
291     }
292
293     /**
294      * If this SQL function has parameters which are SQLToJavaValueNode over
295      * JavaToSQLValueNode and the java value node underneath is a SQL function
296      * defined with CALLED ON NULL INPUT, then we can get rid of the wrapper
297      * nodes over the java value node for such parameters. This is because
298      * SQL functions defined with CALLED ON NULL INPUT need access to only
299      * java domain values.
300      * This can't be done for parameters which are wrappers over SQL function
301      * defined with RETURN NULL ON NULL INPUT because such functions need
302      * access to both sql domain value and java domain value. - Derby479
303      */

304     private void optimizeDomainValueConversion() throws StandardException {
305         int count = methodParms.length;
306         for (int parm = 0; parm < count; parm++)
307         {
308             if (methodParms[parm] instanceof SQLToJavaValueNode &&
309                 ((SQLToJavaValueNode)methodParms[parm]).getSQLValueNode() instanceof
310                 JavaToSQLValueNode)
311             {
312                 //If we are here, then it means that the parameter is
313
//SQLToJavaValueNode on top of JavaToSQLValueNode
314
JavaValueNode paramIsJavaValueNode =
315                     ((JavaToSQLValueNode)((SQLToJavaValueNode)methodParms[parm]).getSQLValueNode()).getJavaValueNode();
316                 if (paramIsJavaValueNode instanceof StaticMethodCallNode)
317                 {
318                     //If we are here, then it means that the parameter has
319
//a MethodCallNode underneath it.
320
StaticMethodCallNode paramIsMethodCallNode = (StaticMethodCallNode)paramIsJavaValueNode;
321                     //If the MethodCallNode parameter is defined as
322
//CALLED ON NULL INPUT, then we can remove the wrappers
323
//for the param and just set the parameter to the
324
//java value node.
325
if (paramIsMethodCallNode.routineInfo != null &&
326                             paramIsMethodCallNode.routineInfo.calledOnNullInput())
327                         methodParms[parm] =
328                             ((JavaToSQLValueNode)((SQLToJavaValueNode)methodParms[parm]).getSQLValueNode()).getJavaValueNode();
329                 }
330             }
331         }
332     }
333
334     /**
335      * Resolve a routine. Obtain a list of routines from the data dictionary
336      * of the correct type (functions or procedures) and name.
337      * Pick the best routine from the list. Currently only a single routine
338      * with a given type and name is allowed, thus if changes are made to
339      * support overloaded routines, careful code inspection and testing will
340      * be required.
341      * @param fromList
342      * @param subqueryList
343      * @param aggregateVector
344      * @param sd
345      * @throws StandardException
346      */

347     private void resolveRoutine(FromList fromList, SubqueryList subqueryList, Vector JavaDoc aggregateVector, SchemaDescriptor sd) throws StandardException {
348         if (sd.getUUID() != null) {
349
350         java.util.List JavaDoc list = getDataDictionary().getRoutineList(
351             sd.getUUID().toString(), methodName,
352             forCallStatement ? AliasInfo.ALIAS_NAME_SPACE_PROCEDURE_AS_CHAR : AliasInfo.ALIAS_NAME_SPACE_FUNCTION_AS_CHAR
353             );
354
355         for (int i = list.size() - 1; i >= 0; i--) {
356
357             AliasDescriptor proc = (AliasDescriptor) list.get(i);
358
359             RoutineAliasInfo routineInfo = (RoutineAliasInfo) proc.getAliasInfo();
360             int parameterCount = routineInfo.getParameterCount();
361             if (parameterCount != methodParms.length)
362                 continue;
363
364             // pre-form the method signature. If it is a dynamic result set procedure
365
// then we need to add in the ResultSet array
366

367             TypeDescriptor[] parameterTypes = routineInfo.getParameterTypes();
368
369             int sigParameterCount = parameterCount;
370             if (routineInfo.getMaxDynamicResultSets() > 0)
371                 sigParameterCount++;
372
373             signature = new JSQLType[sigParameterCount];
374             for (int p = 0; p < parameterCount; p++) {
375
376                 // find the declared type.
377

378                 TypeDescriptor td = parameterTypes[p];
379
380                 TypeId typeId = TypeId.getBuiltInTypeId(td.getJDBCTypeId());
381
382                 TypeId parameterTypeId = typeId;
383
384
385                 // if it's an OUT or INOUT parameter we need an array.
386
int parameterMode = routineInfo.getParameterModes()[p];
387
388                 if (parameterMode != JDBC30Translation.PARAMETER_MODE_IN) {
389
390                     String JavaDoc arrayType;
391                     switch (typeId.getJDBCTypeId()) {
392                         case java.sql.Types.SMALLINT:
393                         case java.sql.Types.INTEGER:
394                         case java.sql.Types.BIGINT:
395                         case java.sql.Types.REAL:
396                         case java.sql.Types.DOUBLE:
397                             arrayType = getTypeCompiler(typeId).getCorrespondingPrimitiveTypeName().concat("[]");
398                             break;
399                         default:
400                             arrayType = typeId.getCorrespondingJavaTypeName().concat("[]");
401                             break;
402                     }
403
404                     typeId = TypeId.getUserDefinedTypeId(arrayType, false);
405                 }
406
407                 // this is the type descriptor of the require method parameter
408
DataTypeDescriptor methoddtd = new DataTypeDescriptor(
409                         typeId,
410                         td.getPrecision(),
411                         td.getScale(),
412                         td.isNullable(),
413                         td.getMaximumWidth()
414                     );
415
416                 signature[p] = new JSQLType(methoddtd);
417
418                 // check parameter is a ? node for INOUT and OUT parameters.
419

420                 ValueNode sqlParamNode = null;
421
422                 if (methodParms[p] instanceof SQLToJavaValueNode) {
423                     SQLToJavaValueNode sql2j = (SQLToJavaValueNode) methodParms[p];
424                     sqlParamNode = sql2j.getSQLValueNode();
425                 }
426                 else
427                 {
428                 }
429
430                 boolean isParameterMarker = true;
431                 if ((sqlParamNode == null) || !sqlParamNode.requiresTypeFromContext())
432                 {
433                     if (parameterMode != JDBC30Translation.PARAMETER_MODE_IN) {
434                      
435                         throw StandardException.newException(SQLState.LANG_DB2_PARAMETER_NEEDS_MARKER,
436                             RoutineAliasInfo.parameterMode(parameterMode),
437                             routineInfo.getParameterNames()[p]);
438                     }
439                     isParameterMarker = false;
440                 }
441                 else
442                 {
443                     if (applicationParameterNumbers == null)
444                         applicationParameterNumbers = new int[parameterCount];
445                     if (sqlParamNode instanceof UnaryOperatorNode) {
446                         ParameterNode pn = ((UnaryOperatorNode)sqlParamNode).getParameterOperand();
447                         applicationParameterNumbers[p] = pn.getParameterNumber();
448                     } else
449                         applicationParameterNumbers[p] = ((ParameterNode) sqlParamNode).getParameterNumber();
450                 }
451
452                 // this is the SQL type of the procedure parameter.
453
DataTypeDescriptor paramdtd = new DataTypeDescriptor(
454                     parameterTypeId,
455                     td.getPrecision(),
456                     td.getScale(),
457                     td.isNullable(),
458                     td.getMaximumWidth()
459                 );
460
461                 boolean needCast = false;
462                 if (!isParameterMarker)
463                 {
464
465                     // can only be an IN parameter.
466
// check that the value can be assigned to the
467
// type of the procedure parameter.
468
if (sqlParamNode instanceof UntypedNullConstantNode)
469                     {
470                         sqlParamNode.setType(paramdtd);
471                     }
472                     else
473                     {
474
475
476                         DataTypeDescriptor dts;
477                         TypeId argumentTypeId;
478
479                         if (sqlParamNode != null)
480                         {
481                             // a node from the SQL world
482
argumentTypeId = sqlParamNode.getTypeId();
483                             dts = sqlParamNode.getTypeServices();
484                         }
485                         else
486                         {
487                             // a node from the Java world
488
dts = DataTypeDescriptor.getSQLDataTypeDescriptor(methodParms[p].getJavaTypeName());
489                             if (dts == null)
490                             {
491                                 throw StandardException.newException(SQLState.LANG_NO_CORRESPONDING_S_Q_L_TYPE,
492                                     methodParms[p].getJavaTypeName());
493                             }
494
495                             argumentTypeId = dts.getTypeId();
496                         }
497
498                         if (! getTypeCompiler(parameterTypeId).storable(argumentTypeId, getClassFactory()))
499                                 throw StandardException.newException(SQLState.LANG_NOT_STORABLE,
500                                     parameterTypeId.getSQLTypeName(),
501                                     argumentTypeId.getSQLTypeName() );
502
503                         // if it's not an exact length match then some cast will be needed.
504
if (!paramdtd.isExactTypeAndLengthMatch(dts))
505                             needCast = true;
506                     }
507                 }
508                 else
509                 {
510                     // any variable length type will need a cast from the
511
// Java world (the ? parameter) to the SQL type. This
512
// ensures values like CHAR(10) are passed into the procedure
513
// correctly as 10 characters long.
514
if (parameterTypeId.variableLength()) {
515
516                         if (parameterMode != JDBC30Translation.PARAMETER_MODE_OUT)
517                             needCast = true;
518                     }
519                 }
520                 
521
522                 if (needCast)
523                 {
524                     // push a cast node to ensure the
525
// correct type is passed to the method
526
// this gets tacky because before we knew
527
// it was a procedure call we ensured all the
528
// parameter are JavaNodeTypes. Now we need to
529
// push them back to the SQL domain, cast them
530
// and then push them back to the Java domain.
531

532                     if (sqlParamNode == null) {
533
534                         sqlParamNode = (ValueNode) getNodeFactory().getNode(
535                             C_NodeTypes.JAVA_TO_SQL_VALUE_NODE,
536                             methodParms[p],
537                             getContextManager());
538                     }
539
540                     ValueNode castNode = (ValueNode) getNodeFactory().getNode(
541                         C_NodeTypes.CAST_NODE,
542                         sqlParamNode,
543                         paramdtd,
544                         getContextManager());
545
546
547                     methodParms[p] = (JavaValueNode) getNodeFactory().getNode(
548                             C_NodeTypes.SQL_TO_JAVA_VALUE_NODE,
549                             castNode,
550                             getContextManager());
551
552                     methodParms[p] = methodParms[p].bindExpression(fromList, subqueryList, aggregateVector);
553                 }
554
555                 // only force the type for a ? so that the correct type shows up
556
// in parameter meta data
557
if (isParameterMarker)
558                     sqlParamNode.setType(paramdtd);
559             }
560
561             if (sigParameterCount != parameterCount) {
562
563                 TypeId typeId = TypeId.getUserDefinedTypeId("java.sql.ResultSet[]", false);
564
565                 DataTypeDescriptor dtd = new DataTypeDescriptor(
566                         typeId,
567                         0,
568                         0,
569                         false,
570                         -1
571                     );
572
573                 signature[parameterCount] = new JSQLType(dtd);
574
575             }
576
577             this.routineInfo = routineInfo;
578             ad = proc;
579
580             // If a procedure is in the system schema and defined as executing
581
// SQL do we set we are in system code.
582
if (sd.isSystemSchema() && (routineInfo.getReturnType() == null) && routineInfo.getSQLAllowed() != RoutineAliasInfo.NO_SQL)
583                 isSystemCode = true;
584
585             break;
586         }
587 }
588     }
589
590     /**
591         Push extra code to generate the casts within the
592         arrays for the parameters passed as arrays.
593     */

594     public void generateOneParameter(ExpressionClassBuilder acb,
595                                             MethodBuilder mb,
596                                             int parameterNumber )
597             throws StandardException
598     {
599         int parameterMode;
600
601         SQLToJavaValueNode sql2j = null;
602         if (methodParms[parameterNumber] instanceof SQLToJavaValueNode)
603             sql2j = (SQLToJavaValueNode) methodParms[parameterNumber];
604         
605         if (routineInfo != null) {
606             parameterMode = routineInfo.getParameterModes()[parameterNumber];
607         } else {
608             // for a static method call the parameter always starts out as a in parameter, but
609
// may be registered as an IN OUT parameter. For a static method argument to be
610
// a dynmaically registered out parameter it must be a simple ? parameter
611

612             parameterMode = JDBC30Translation.PARAMETER_MODE_IN;
613
614             if (sql2j != null) {
615                 if (sql2j.getSQLValueNode().requiresTypeFromContext()) {
616                     ParameterNode pn;
617                     if (sql2j.getSQLValueNode() instanceof UnaryOperatorNode)
618                         pn = ((UnaryOperatorNode)sql2j.getSQLValueNode()).getParameterOperand();
619                     else
620                         pn = (ParameterNode) (sql2j.getSQLValueNode());
621
622                     // applicationParameterNumbers is only set up for a procedure.
623
int applicationParameterNumber = pn.getParameterNumber();
624
625                     String JavaDoc parameterType = methodParameterTypes[parameterNumber];
626
627                     if (parameterType.endsWith("[]")) {
628
629                         // constructor - setting up correct paramter type info
630
MethodBuilder constructor = acb.getConstructor();
631                         acb.pushThisAsActivation(constructor);
632                         constructor.callMethod(VMOpcode.INVOKEINTERFACE, null,
633                                             "getParameterValueSet", ClassName.ParameterValueSet, 0);
634
635                         constructor.push(applicationParameterNumber);
636                         constructor.push(JDBC30Translation.PARAMETER_MODE_UNKNOWN);
637                         constructor.callMethod(VMOpcode.INVOKEINTERFACE, null,
638                                             "setParameterMode", "void", 2);
639                         constructor.endStatement();
640                     }
641                 }
642             }
643         }
644
645         switch (parameterMode) {
646         case JDBC30Translation.PARAMETER_MODE_IN:
647         case JDBC30Translation.PARAMETER_MODE_IN_OUT:
648         case JDBC30Translation.PARAMETER_MODE_UNKNOWN:
649             if (sql2j != null)
650                 sql2j.returnsNullOnNullState = returnsNullOnNullState;
651             super.generateOneParameter(acb, mb, parameterNumber);
652             break;
653
654         case JDBC30Translation.PARAMETER_MODE_OUT:
655             // For an OUT parameter we require nothing to be pushed into the
656
// method call from the parameter node.
657
break;
658         }
659
660         switch (parameterMode) {
661         case JDBC30Translation.PARAMETER_MODE_IN:
662         case JDBC30Translation.PARAMETER_MODE_UNKNOWN:
663             break;
664
665         case JDBC30Translation.PARAMETER_MODE_IN_OUT:
666         case JDBC30Translation.PARAMETER_MODE_OUT:
667         {
668             // Create the array used to pass into the method. We create a
669
// new array for each call as there is a small chance the
670
// application could retain a reference to it and corrupt
671
// future calls with the same CallableStatement object.
672

673             String JavaDoc methodParameterType = methodParameterTypes[parameterNumber];
674             String JavaDoc arrayType = methodParameterType.substring(0, methodParameterType.length() - 2);
675             LocalField lf = acb.newFieldDeclaration(Modifier.PRIVATE, methodParameterType);
676
677             if (outParamArrays == null)
678                 outParamArrays = new LocalField[methodParms.length];
679
680             outParamArrays[parameterNumber] = lf;
681
682             mb.pushNewArray(arrayType, 1);
683             mb.putField(lf);
684
685             // set the IN part of the parameter into the INOUT parameter.
686
if (parameterMode != JDBC30Translation.PARAMETER_MODE_OUT) {
687                 mb.swap();
688                 mb.setArrayElement(0);
689                 mb.getField(lf);
690             }
691             break;
692             }
693         }
694
695     }
696
697     /**
698      * Categorize this predicate. Initially, this means
699      * building a bit map of the referenced tables for each predicate.
700      * If the source of this ColumnReference (at the next underlying level)
701      * is not a ColumnReference or a VirtualColumnNode then this predicate
702      * will not be pushed down.
703      *
704      * For example, in:
705      * select * from (select 1 from s) a (x) where x = 1
706      * we will not push down x = 1.
707      * NOTE: It would be easy to handle the case of a constant, but if the
708      * inner SELECT returns an arbitrary expression, then we would have to copy
709      * that tree into the pushed predicate, and that tree could contain
710      * subqueries and method calls.
711      * RESOLVE - revisit this issue once we have views.
712      *
713      * @param referencedTabs JBitSet with bit map of referenced FromTables
714      * @param simplePredsOnly Whether or not to consider method
715      * calls, field references and conditional nodes
716      * when building bit map
717      *
718      * @return boolean Whether or not source.expression is a ColumnReference
719      * or a VirtualColumnNode.
720      *
721      * @exception StandardException Thrown on error
722      */

723     public boolean categorize(JBitSet referencedTabs, boolean simplePredsOnly)
724         throws StandardException
725     {
726         /* We stop here when only considering simple predicates
727          * as we don't consider method calls when looking
728          * for null invariant predicates.
729          */

730         if (simplePredsOnly)
731         {
732             return false;
733         }
734
735         boolean pushable = true;
736
737         pushable = pushable && super.categorize(referencedTabs, simplePredsOnly);
738
739         return pushable;
740     }
741
742     /**
743      * Convert this object to a String. See comments in QueryTreeNode.java
744      * for how this should be done for tree printing.
745      *
746      * @return This object as a String
747      */

748
749     public String JavaDoc toString()
750     {
751         if (SanityManager.DEBUG)
752         {
753             return "javaClassName: " +
754                 (javaClassName != null ? javaClassName : "null") + "\n" +
755                 super.toString();
756         }
757         else
758         {
759             return "";
760         }
761     }
762
763     /**
764      * Do code generation for this method call
765      *
766      * @param acb The ExpressionClassBuilder for the class we're generating
767      * @param mb The method the expression will go into
768      *
769      *
770      * @exception StandardException Thrown on error
771      */

772
773     public void generateExpression(ExpressionClassBuilder acb,
774                                             MethodBuilder mb)
775                                     throws StandardException
776     {
777         if (routineInfo != null) {
778
779             if (!routineInfo.calledOnNullInput() && routineInfo.getParameterCount() != 0)
780                 returnsNullOnNullState = acb.newFieldDeclaration(Modifier.PRIVATE, "boolean");
781
782         }
783
784         // reset the parameters are null indicator.
785
if (returnsNullOnNullState != null) {
786             mb.push(false);
787             mb.setField(returnsNullOnNullState);
788
789             // for the call to the generated method below.
790
mb.pushThis();
791         }
792
793         int nargs = generateParameters(acb, mb);
794
795         LocalField functionEntrySQLAllowed = null;
796
797         if (routineInfo != null) {
798
799             short sqlAllowed = routineInfo.getSQLAllowed();
800
801             // Before we set up our authorization level, add a check to see if this
802
// method can be called. If the routine is NO SQL or CONTAINS SQL
803
// then there is no need for a check. As follows:
804
//
805
// Current Level = NO_SQL - CALL will be rejected when getting CALL result set
806
// Current Level = anything else - calls to procedures defined as NO_SQL and CONTAINS SQL both allowed.
807

808
809             if (sqlAllowed != RoutineAliasInfo.NO_SQL)
810             {
811                 
812                 int sqlOperation;
813                 
814                 if (sqlAllowed == RoutineAliasInfo.READS_SQL_DATA)
815                     sqlOperation = Authorizer.SQL_SELECT_OP;
816                 else if (sqlAllowed == RoutineAliasInfo.MODIFIES_SQL_DATA)
817                     sqlOperation = Authorizer.SQL_WRITE_OP;
818                 else
819                     sqlOperation = Authorizer.SQL_ARBITARY_OP;
820                 
821                 generateAuthorizeCheck((ActivationClassBuilder) acb, mb, sqlOperation);
822             }
823
824             int statmentContextReferences = isSystemCode ? 2 : 1;
825             
826             boolean isFunction = routineInfo.getReturnType() != null;
827
828             if (isFunction)
829                 statmentContextReferences++;
830
831
832             if (statmentContextReferences != 0) {
833                 acb.pushThisAsActivation(mb);
834                 mb.callMethod(VMOpcode.INVOKEINTERFACE, null,
835                                     "getLanguageConnectionContext", ClassName.LanguageConnectionContext, 0);
836                 mb.callMethod(VMOpcode.INVOKEINTERFACE, null,
837                                     "getStatementContext", "org.apache.derby.iapi.sql.conn.StatementContext", 0);
838
839                 for (int scc = 1; scc < statmentContextReferences; scc++)
840                     mb.dup();
841             }
842
843             /**
844                 Set the statement context to reflect we are running
845                 System procedures, so that we can execute non-standard SQL.
846             */

847             if (isSystemCode) {
848                 mb.callMethod(VMOpcode.INVOKEINTERFACE, null,
849                                     "setSystemCode", "void", 0);
850             }
851
852             // for a function we need to fetch the current SQL control
853
// so that we can reset it once the function is complete.
854
//
855
if (isFunction)
856             {
857                 functionEntrySQLAllowed = acb.newFieldDeclaration(Modifier.PRIVATE, "short");
858                 mb.callMethod(VMOpcode.INVOKEINTERFACE, null,
859                                     "getSQLAllowed", "short", 0);
860                 mb.setField(functionEntrySQLAllowed);
861
862             }
863             
864             
865             // Set up the statement context to reflect the
866
// restricted SQL execution allowed by this routine.
867

868             mb.push(sqlAllowed);
869             mb.push(false);
870             mb.callMethod(VMOpcode.INVOKEINTERFACE, null,
871                                 "setSQLAllowed", "void", 2);
872
873         }
874
875         // add in the ResultSet arrays.
876
if (routineInfo != null) {
877
878             int compiledResultSets = methodParameterTypes.length - methodParms.length;
879
880             if (compiledResultSets != 0) {
881
882                 // Add a method that indicates the maxium number of dynamic result sets.
883
int maxDynamicResults = routineInfo.getMaxDynamicResultSets();
884                 if (maxDynamicResults > 0) {
885                     MethodBuilder gdr = acb.getClassBuilder().newMethodBuilder(Modifier.PUBLIC, "int", "getMaxDynamicResults");
886                     gdr.push(maxDynamicResults);
887                     gdr.methodReturn();
888                     gdr.complete();
889                 }
890
891                 // add a method to return all the dynamic result sets (unordered)
892
MethodBuilder gdr = acb.getClassBuilder().newMethodBuilder(Modifier.PUBLIC, "java.sql.ResultSet[][]", "getDynamicResults");
893
894                 MethodBuilder cons = acb.getConstructor();
895                 // if (procDef.getParameterStyle() == RoutineAliasInfo.PS_JAVA)
896
{
897                     // PARAMETER STYLE JAVA
898

899                     LocalField procedureResultSetsHolder = acb.newFieldDeclaration(Modifier.PRIVATE, "java.sql.ResultSet[][]");
900
901                     // getDynamicResults body
902
gdr.getField(procedureResultSetsHolder);
903
904                     // create the holder of all the ResultSet arrays, new java.sql.ResultSet[][compiledResultSets]
905
cons.pushNewArray("java.sql.ResultSet[]", compiledResultSets);
906                     cons.setField(procedureResultSetsHolder);
907
908
909                     // arguments for the dynamic result sets
910
for (int i = 0; i < compiledResultSets; i++) {
911
912                         mb.pushNewArray("java.sql.ResultSet", 1);
913                         mb.dup();
914
915                         mb.getField(procedureResultSetsHolder);
916                         mb.swap();
917
918                         mb.setArrayElement(i);
919                     }
920                 }
921
922                 // complete the method that returns the ResultSet[][] to the
923
gdr.methodReturn();
924                 gdr.complete();
925
926                 nargs += compiledResultSets;
927             }
928
929         }
930
931         String JavaDoc javaReturnType = getJavaTypeName();
932
933         MethodBuilder mbnc = null;
934         MethodBuilder mbcm = mb;
935
936
937         // If any of the parameters are null then
938
// do not call the method, just return null.
939
if (returnsNullOnNullState != null)
940         {
941             mbnc = acb.newGeneratedFun(javaReturnType, Modifier.PRIVATE, methodParameterTypes);
942
943             // add the throws clause for the public static method we are going to call.
944
Class JavaDoc[] throwsSet = ((java.lang.reflect.Method JavaDoc) method).getExceptionTypes();
945             for (int te = 0; te < throwsSet.length; te++)
946             {
947                 mbnc.addThrownException(throwsSet[te].getName());
948             }
949
950             mbnc.getField(returnsNullOnNullState);
951             mbnc.conditionalIf();
952
953             // set up for a null!!
954
// for objects is easy.
955
mbnc.pushNull(javaReturnType);
956
957             mbnc.startElseCode();
958
959             if (!actualMethodReturnType.equals(javaReturnType))
960                 mbnc.pushNewStart(javaReturnType);
961
962             // fetch all the arguments
963
for (int pa = 0; pa < nargs; pa++)
964             {
965                 mbnc.getParameter(pa);
966             }
967
968             mbcm = mbnc;
969         }
970
971         mbcm.callMethod(VMOpcode.INVOKESTATIC, method.getDeclaringClass().getName(), methodName,
972                     actualMethodReturnType, nargs);
973
974
975         if (returnsNullOnNullState != null)
976         {
977             if (!actualMethodReturnType.equals(javaReturnType))
978                 mbnc.pushNewComplete(1);
979
980             mbnc.completeConditional();
981
982             mbnc.methodReturn();
983             mbnc.complete();
984
985             // now call the wrapper method
986
mb.callMethod(VMOpcode.INVOKEVIRTUAL, acb.getClassBuilder().getFullName(), mbnc.getName(),
987                     javaReturnType, nargs);
988             mbnc = null;
989         }
990
991
992         if (routineInfo != null) {
993
994             // reset the SQL allowed setting that we set upon
995
// entry to the method.
996
if (functionEntrySQLAllowed != null) {
997                 acb.pushThisAsActivation(mb);
998                 mb.callMethod(VMOpcode.INVOKEINTERFACE, null,
999                                     "getLanguageConnectionContext", ClassName.LanguageConnectionContext, 0);
1000                mb.callMethod(VMOpcode.INVOKEINTERFACE, null,
1001                                    "getStatementContext", "org.apache.derby.iapi.sql.conn.StatementContext", 0);
1002                mb.getField(functionEntrySQLAllowed);
1003                mb.push(true); // override as we are ending the control set by this function all.
1004
mb.callMethod(VMOpcode.INVOKEINTERFACE, null,
1005                                    "setSQLAllowed", "void", 2);
1006
1007            }
1008
1009            if (outParamArrays != null) {
1010
1011                MethodBuilder constructor = acb.getConstructor();
1012
1013                // constructor - setting up correct paramter type info
1014
acb.pushThisAsActivation(constructor);
1015                constructor.callMethod(VMOpcode.INVOKEINTERFACE, null,
1016                                    "getParameterValueSet", ClassName.ParameterValueSet, 0);
1017
1018                // execute - passing out parameters back.
1019
acb.pushThisAsActivation(mb);
1020                mb.callMethod(VMOpcode.INVOKEINTERFACE, null,
1021                                    "getParameterValueSet", ClassName.ParameterValueSet, 0);
1022
1023                int[] parameterModes = routineInfo.getParameterModes();
1024                for (int i = 0; i < outParamArrays.length; i++) {
1025
1026                    int parameterMode = parameterModes[i];
1027                    if (parameterMode != JDBC30Translation.PARAMETER_MODE_IN) {
1028
1029                        // must be a parameter if it is INOUT or OUT.
1030
ValueNode sqlParamNode = ((SQLToJavaValueNode) methodParms[i]).getSQLValueNode();
1031
1032
1033                        int applicationParameterNumber = applicationParameterNumbers[i];
1034
1035                        // Set the correct parameter nodes in the ParameterValueSet at constructor time.
1036
constructor.dup();
1037                        constructor.push(applicationParameterNumber);
1038                        constructor.push(parameterMode);
1039                        constructor.callMethod(VMOpcode.INVOKEINTERFACE, null,
1040                                        "setParameterMode", "void", 2);
1041
1042                        // Pass the value of the outparameters back to the calling code
1043
LocalField lf = outParamArrays[i];
1044
1045                        mb.dup();
1046                        mb.push(applicationParameterNumber);
1047                        mb.callMethod(VMOpcode.INVOKEINTERFACE, null,
1048                                    "getParameter", ClassName.DataValueDescriptor, 1);
1049
1050                        // see if we need to set the desired length/scale/precision of the type
1051
DataTypeDescriptor paramdtd = sqlParamNode.getTypeServices();
1052
1053                        boolean isNumericType = paramdtd.getTypeId().isNumericTypeId();
1054
1055                        // is the underlying type for the OUT/INOUT parameter primitive.
1056
boolean isPrimitive = ((java.lang.reflect.Method JavaDoc) method).getParameterTypes()[i].getComponentType().isPrimitive();
1057
1058                        if (isNumericType) {
1059                            // need to up-cast as the setValue(Number) method only exists on NumberDataValue
1060

1061                            if (!isPrimitive)
1062                                mb.cast(ClassName.NumberDataValue);
1063                        }
1064                        else if (paramdtd.getTypeId().isBooleanTypeId())
1065                        {
1066                            // need to cast as the setValue(Boolean) method only exists on BooleanDataValue
1067
if (!isPrimitive)
1068                                mb.cast(ClassName.BooleanDataValue);
1069                        }
1070
1071                        if (paramdtd.getTypeId().variableLength()) {
1072                            // need another DVD reference for the set width below.
1073
mb.dup();
1074                        }
1075
1076
1077                        mb.getField(lf); // pvs, dvd, array
1078
mb.getArrayElement(0); // pvs, dvd, value
1079

1080                        // The value needs to be set thorugh the setValue(Number) method.
1081
if (isNumericType && !isPrimitive)
1082                        {
1083                            mb.upCast("java.lang.Number");
1084                        }
1085
1086                        mb.callMethod(VMOpcode.INVOKEINTERFACE, null, "setValue", "void", 1);
1087
1088                        if (paramdtd.getTypeId().variableLength()) {
1089                            mb.push(isNumericType ? paramdtd.getPrecision() : paramdtd.getMaximumWidth());
1090                            mb.push(paramdtd.getScale());
1091                            mb.push(isNumericType);
1092                            mb.callMethod(VMOpcode.INVOKEINTERFACE, ClassName.VariableSizeDataValue, "setWidth", "void", 3);
1093                            // mb.endStatement();
1094
}
1095                    }
1096                }
1097                constructor.endStatement();
1098                mb.endStatement();
1099            }
1100
1101        }
1102    }
1103
1104    /**
1105     * Set default privilege of EXECUTE for this node.
1106     */

1107    int getPrivType()
1108    {
1109        return Authorizer.EXECUTE_PRIV;
1110    }
1111}
1112
Popular Tags