0001: /*
0002:
0003: Derby - Class org.apache.derby.impl.sql.compile.StaticMethodCallNode
0004:
0005: Licensed to the Apache Software Foundation (ASF) under one or more
0006: contributor license agreements. See the NOTICE file distributed with
0007: this work for additional information regarding copyright ownership.
0008: The ASF licenses this file to you under the Apache License, Version 2.0
0009: (the "License"); you may not use this file except in compliance with
0010: the License. You may obtain a copy of the License at
0011:
0012: http://www.apache.org/licenses/LICENSE-2.0
0013:
0014: Unless required by applicable law or agreed to in writing, software
0015: distributed under the License is distributed on an "AS IS" BASIS,
0016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0017: See the License for the specific language governing permissions and
0018: limitations under the License.
0019:
0020: */
0021:
0022: package org.apache.derby.impl.sql.compile;
0023:
0024: import org.apache.derby.iapi.services.compiler.MethodBuilder;
0025:
0026: import org.apache.derby.iapi.services.sanity.SanityManager;
0027:
0028: import org.apache.derby.iapi.sql.compile.CompilerContext;
0029: import org.apache.derby.iapi.sql.compile.TypeCompiler;
0030: import org.apache.derby.iapi.sql.compile.C_NodeTypes;
0031: import org.apache.derby.iapi.types.JSQLType;
0032: import org.apache.derby.iapi.types.DataTypeDescriptor;
0033: import org.apache.derby.iapi.types.TypeId;
0034:
0035: import org.apache.derby.iapi.sql.dictionary.AliasDescriptor;
0036: import org.apache.derby.iapi.sql.dictionary.DataDictionary;
0037: import org.apache.derby.iapi.sql.dictionary.SchemaDescriptor;
0038:
0039: import org.apache.derby.iapi.reference.ClassName;
0040: import org.apache.derby.iapi.reference.SQLState;
0041: import org.apache.derby.iapi.reference.JDBC30Translation;
0042: import org.apache.derby.iapi.error.StandardException;
0043:
0044: import org.apache.derby.impl.sql.compile.ExpressionClassBuilder;
0045: import org.apache.derby.iapi.services.loader.ClassInspector;
0046: import org.apache.derby.iapi.services.compiler.LocalField;
0047:
0048: import org.apache.derby.iapi.util.JBitSet;
0049: import org.apache.derby.iapi.services.classfile.VMOpcode;
0050:
0051: import org.apache.derby.iapi.sql.conn.Authorizer;
0052:
0053: import org.apache.derby.catalog.AliasInfo;
0054: import org.apache.derby.catalog.TypeDescriptor;
0055: import org.apache.derby.catalog.types.RoutineAliasInfo;
0056: import org.apache.derby.impl.sql.compile.ActivationClassBuilder;
0057:
0058: import org.apache.derby.catalog.UUID;
0059:
0060: import java.util.Vector;
0061: import java.lang.reflect.Modifier;
0062:
0063: /**
0064: * A StaticMethodCallNode represents a static method call from a Class
0065: * (as opposed to from an Object).
0066:
0067: For a procedure the call requires that the arguments be ? parameters.
0068: The parameter is *logically* passed into the method call a number of different ways.
0069:
0070: <P>
0071: For a application call like CALL MYPROC(?) the logically Java method call is
0072: (in psuedo Java/SQL code) (examples with CHAR(10) parameter)
0073: <BR>
0074: Fixed length IN parameters - com.acme.MyProcedureMethod(?)
0075: <BR>
0076: Variable length IN parameters - com.acme.MyProcedureMethod(CAST (? AS CHAR(10))
0077: <BR>
0078: Fixed length INOUT parameter -
0079: String[] holder = new String[] {?}; com.acme.MyProcedureMethod(holder); ? = holder[0]
0080: <BR>
0081: Variable length INOUT parameter -
0082: String[] holder = new String[] {CAST (? AS CHAR(10)}; com.acme.MyProcedureMethod(holder); ? = CAST (holder[0] AS CHAR(10))
0083:
0084: <BR>
0085: Fixed length OUT parameter -
0086: String[] holder = new String[1]; com.acme.MyProcedureMethod(holder); ? = holder[0]
0087:
0088: <BR>
0089: Variable length INOUT parameter -
0090: String[] holder = new String[1]; com.acme.MyProcedureMethod(holder); ? = CAST (holder[0] AS CHAR(10))
0091:
0092:
0093: <P>
0094: For static method calls there is no pre-definition of an IN or INOUT parameter, so a call to CallableStatement.registerOutParameter()
0095: makes the parameter an INOUT parameter, provided:
0096: - the parameter is passed directly to the method call (no casts or expressions).
0097: - the method's parameter type is a Java array type.
0098:
0099: Since this is a dynmaic decision we compile in code to take both paths, based upon a boolean isINOUT which is dervied from the
0100: ParameterValueSet. Code is logically (only single parameter String[] shown here). Note, no casts can exist here.
0101:
0102: boolean isINOUT = getParameterValueSet().getParameterMode(0) == PARAMETER_IN_OUT;
0103: if (isINOUT) {
0104: String[] holder = new String[] {?}; com.acme.MyProcedureMethod(holder); ? = holder[0]
0105:
0106: } else {
0107: com.acme.MyProcedureMethod(?)
0108: }
0109:
0110: *
0111: * @author Jerry Brenner
0112: */
0113: public class StaticMethodCallNode extends MethodCallNode {
0114: private TableName procedureName;
0115:
0116: private LocalField[] outParamArrays;
0117: private int[] applicationParameterNumbers;
0118:
0119: private boolean isSystemCode;
0120: private boolean alreadyBound;
0121:
0122: private LocalField returnsNullOnNullState;
0123:
0124: AliasDescriptor ad;
0125:
0126: /**
0127: * Intializer for a NonStaticMethodCallNode
0128: *
0129: * @param methodName The name of the method to call
0130: * @param javaClassName The name of the java class that the static method belongs to.
0131: */
0132: public void init(Object methodName, Object javaClassName) {
0133: if (methodName instanceof String)
0134: init(methodName);
0135: else {
0136: procedureName = (TableName) methodName;
0137: init(procedureName.getTableName());
0138: }
0139:
0140: this .javaClassName = (String) javaClassName;
0141: }
0142:
0143: /**
0144: * Bind this expression. This means binding the sub-expressions,
0145: * as well as figuring out what the return type is for this expression.
0146: *
0147: * @param fromList The FROM list for the query this
0148: * expression is in, for binding columns.
0149: * @param subqueryList The subquery list being built as we find SubqueryNodes
0150: * @param aggregateVector The aggregate vector being built as we find AggregateNodes
0151: *
0152: * @return this or an AggregateNode
0153: *
0154: * @exception StandardException Thrown on error
0155: */
0156:
0157: public JavaValueNode bindExpression(FromList fromList,
0158: SubqueryList subqueryList, Vector aggregateVector)
0159: throws StandardException {
0160: // for a function we can get called recursively
0161: if (alreadyBound)
0162: return this ;
0163:
0164: bindParameters(fromList, subqueryList, aggregateVector);
0165:
0166: /* If javaClassName is null then we assume that the current methodName
0167: * is an alias and we must go to sysmethods to
0168: * get the real method and java class names for this alias.
0169: */
0170: if (javaClassName == null) {
0171: CompilerContext cc = getCompilerContext();
0172:
0173: // look for a routine
0174: if (ad == null) {
0175:
0176: String schemaName = procedureName != null ? procedureName
0177: .getSchemaName()
0178: : null;
0179:
0180: boolean noSchema = schemaName == null;
0181:
0182: SchemaDescriptor sd = getSchemaDescriptor(schemaName,
0183: schemaName != null);
0184:
0185: resolveRoutine(fromList, subqueryList, aggregateVector,
0186: sd);
0187:
0188: if (ad == null && noSchema && !forCallStatement) {
0189: // Resolve to a built-in SYSFUN function but only
0190: // if this is a function call and the call
0191: // was not qualified. E.g. COS(angle). The
0192: // SYSFUN functions are not in SYSALIASES but
0193: // an in-memory table, set up in DataDictioanryImpl.
0194: sd = getSchemaDescriptor("SYSFUN", true);
0195:
0196: resolveRoutine(fromList, subqueryList,
0197: aggregateVector, sd);
0198: }
0199:
0200: }
0201:
0202: /* Throw exception if no alias found */
0203: if (ad == null) {
0204: Object errName;
0205: if (procedureName == null)
0206: errName = methodName;
0207: else
0208: errName = procedureName;
0209:
0210: throw StandardException.newException(
0211: SQLState.LANG_NO_SUCH_METHOD_ALIAS, errName);
0212: }
0213:
0214: /* Query is dependent on the AliasDescriptor */
0215: cc.createDependency(ad);
0216:
0217: methodName = ad.getAliasInfo().getMethodName();
0218: javaClassName = ad.getJavaClassName();
0219: }
0220:
0221: javaClassName = verifyClassExist(javaClassName, true);
0222:
0223: /* Resolve the method call */
0224: resolveMethodCall(javaClassName, true);
0225:
0226: alreadyBound = true;
0227: if (isPrivilegeCollectionRequired())
0228: getCompilerContext().addRequiredRoutinePriv(ad);
0229:
0230: // If this is a function call with a variable length
0231: // return type, then we need to push a CAST node.
0232: if (routineInfo != null) {
0233: if (methodParms != null)
0234: optimizeDomainValueConversion();
0235:
0236: TypeDescriptor returnType = routineInfo.getReturnType();
0237: if (returnType != null) {
0238: TypeId returnTypeId = TypeId
0239: .getBuiltInTypeId(returnType.getJDBCTypeId());
0240:
0241: if (returnTypeId.variableLength()) {
0242: // Cast the return using a cast node, but have to go
0243: // into the SQL domain, and back to the Java domain.
0244:
0245: DataTypeDescriptor returnValueDtd = new DataTypeDescriptor(
0246: returnTypeId, returnType.getPrecision(),
0247: returnType.getScale(), returnType
0248: .isNullable(), returnType
0249: .getMaximumWidth());
0250:
0251: ValueNode returnValueToSQL = (ValueNode) getNodeFactory()
0252: .getNode(
0253: C_NodeTypes.JAVA_TO_SQL_VALUE_NODE,
0254: this , getContextManager());
0255:
0256: ValueNode returnValueCastNode = (ValueNode) getNodeFactory()
0257: .getNode(C_NodeTypes.CAST_NODE,
0258: returnValueToSQL, returnValueDtd,
0259: getContextManager());
0260:
0261: JavaValueNode returnValueToJava = (JavaValueNode) getNodeFactory()
0262: .getNode(
0263: C_NodeTypes.SQL_TO_JAVA_VALUE_NODE,
0264: returnValueCastNode,
0265: getContextManager());
0266:
0267: return returnValueToJava.bindExpression(fromList,
0268: subqueryList, aggregateVector);
0269: }
0270:
0271: }
0272: }
0273:
0274: return this ;
0275: }
0276:
0277: /**
0278: * If this SQL function has parameters which are SQLToJavaValueNode over
0279: * JavaToSQLValueNode and the java value node underneath is a SQL function
0280: * defined with CALLED ON NULL INPUT, then we can get rid of the wrapper
0281: * nodes over the java value node for such parameters. This is because
0282: * SQL functions defined with CALLED ON NULL INPUT need access to only
0283: * java domain values.
0284: * This can't be done for parameters which are wrappers over SQL function
0285: * defined with RETURN NULL ON NULL INPUT because such functions need
0286: * access to both sql domain value and java domain value. - Derby479
0287: */
0288: private void optimizeDomainValueConversion()
0289: throws StandardException {
0290: int count = methodParms.length;
0291: for (int parm = 0; parm < count; parm++) {
0292: if (methodParms[parm] instanceof SQLToJavaValueNode
0293: && ((SQLToJavaValueNode) methodParms[parm])
0294: .getSQLValueNode() instanceof JavaToSQLValueNode) {
0295: //If we are here, then it means that the parameter is
0296: //SQLToJavaValueNode on top of JavaToSQLValueNode
0297: JavaValueNode paramIsJavaValueNode = ((JavaToSQLValueNode) ((SQLToJavaValueNode) methodParms[parm])
0298: .getSQLValueNode()).getJavaValueNode();
0299: if (paramIsJavaValueNode instanceof StaticMethodCallNode) {
0300: //If we are here, then it means that the parameter has
0301: //a MethodCallNode underneath it.
0302: StaticMethodCallNode paramIsMethodCallNode = (StaticMethodCallNode) paramIsJavaValueNode;
0303: //If the MethodCallNode parameter is defined as
0304: //CALLED ON NULL INPUT, then we can remove the wrappers
0305: //for the param and just set the parameter to the
0306: //java value node.
0307: if (paramIsMethodCallNode.routineInfo != null
0308: && paramIsMethodCallNode.routineInfo
0309: .calledOnNullInput())
0310: methodParms[parm] = ((JavaToSQLValueNode) ((SQLToJavaValueNode) methodParms[parm])
0311: .getSQLValueNode()).getJavaValueNode();
0312: }
0313: }
0314: }
0315: }
0316:
0317: /**
0318: * Resolve a routine. Obtain a list of routines from the data dictionary
0319: * of the correct type (functions or procedures) and name.
0320: * Pick the best routine from the list. Currently only a single routine
0321: * with a given type and name is allowed, thus if changes are made to
0322: * support overloaded routines, careful code inspection and testing will
0323: * be required.
0324: * @param fromList
0325: * @param subqueryList
0326: * @param aggregateVector
0327: * @param sd
0328: * @throws StandardException
0329: */
0330: private void resolveRoutine(FromList fromList,
0331: SubqueryList subqueryList, Vector aggregateVector,
0332: SchemaDescriptor sd) throws StandardException {
0333: if (sd.getUUID() != null) {
0334:
0335: java.util.List list = getDataDictionary()
0336: .getRoutineList(
0337: sd.getUUID().toString(),
0338: methodName,
0339: forCallStatement ? AliasInfo.ALIAS_NAME_SPACE_PROCEDURE_AS_CHAR
0340: : AliasInfo.ALIAS_NAME_SPACE_FUNCTION_AS_CHAR);
0341:
0342: for (int i = list.size() - 1; i >= 0; i--) {
0343:
0344: AliasDescriptor proc = (AliasDescriptor) list.get(i);
0345:
0346: RoutineAliasInfo routineInfo = (RoutineAliasInfo) proc
0347: .getAliasInfo();
0348: int parameterCount = routineInfo.getParameterCount();
0349: if (parameterCount != methodParms.length)
0350: continue;
0351:
0352: // pre-form the method signature. If it is a dynamic result set procedure
0353: // then we need to add in the ResultSet array
0354:
0355: TypeDescriptor[] parameterTypes = routineInfo
0356: .getParameterTypes();
0357:
0358: int sigParameterCount = parameterCount;
0359: if (routineInfo.getMaxDynamicResultSets() > 0)
0360: sigParameterCount++;
0361:
0362: signature = new JSQLType[sigParameterCount];
0363: for (int p = 0; p < parameterCount; p++) {
0364:
0365: // find the declared type.
0366:
0367: TypeDescriptor td = parameterTypes[p];
0368:
0369: TypeId typeId = TypeId.getBuiltInTypeId(td
0370: .getJDBCTypeId());
0371:
0372: TypeId parameterTypeId = typeId;
0373:
0374: // if it's an OUT or INOUT parameter we need an array.
0375: int parameterMode = routineInfo.getParameterModes()[p];
0376:
0377: if (parameterMode != JDBC30Translation.PARAMETER_MODE_IN) {
0378:
0379: String arrayType;
0380: switch (typeId.getJDBCTypeId()) {
0381: case java.sql.Types.SMALLINT:
0382: case java.sql.Types.INTEGER:
0383: case java.sql.Types.BIGINT:
0384: case java.sql.Types.REAL:
0385: case java.sql.Types.DOUBLE:
0386: arrayType = getTypeCompiler(typeId)
0387: .getCorrespondingPrimitiveTypeName()
0388: .concat("[]");
0389: break;
0390: default:
0391: arrayType = typeId
0392: .getCorrespondingJavaTypeName()
0393: .concat("[]");
0394: break;
0395: }
0396:
0397: typeId = TypeId.getUserDefinedTypeId(arrayType,
0398: false);
0399: }
0400:
0401: // this is the type descriptor of the require method parameter
0402: DataTypeDescriptor methoddtd = new DataTypeDescriptor(
0403: typeId, td.getPrecision(), td.getScale(),
0404: td.isNullable(), td.getMaximumWidth());
0405:
0406: signature[p] = new JSQLType(methoddtd);
0407:
0408: // check parameter is a ? node for INOUT and OUT parameters.
0409:
0410: ValueNode sqlParamNode = null;
0411:
0412: if (methodParms[p] instanceof SQLToJavaValueNode) {
0413: SQLToJavaValueNode sql2j = (SQLToJavaValueNode) methodParms[p];
0414: sqlParamNode = sql2j.getSQLValueNode();
0415: } else {
0416: }
0417:
0418: boolean isParameterMarker = true;
0419: if ((sqlParamNode == null)
0420: || !sqlParamNode.requiresTypeFromContext()) {
0421: if (parameterMode != JDBC30Translation.PARAMETER_MODE_IN) {
0422:
0423: throw StandardException
0424: .newException(
0425: SQLState.LANG_DB2_PARAMETER_NEEDS_MARKER,
0426: RoutineAliasInfo
0427: .parameterMode(parameterMode),
0428: routineInfo
0429: .getParameterNames()[p]);
0430: }
0431: isParameterMarker = false;
0432: } else {
0433: if (applicationParameterNumbers == null)
0434: applicationParameterNumbers = new int[parameterCount];
0435: if (sqlParamNode instanceof UnaryOperatorNode) {
0436: ParameterNode pn = ((UnaryOperatorNode) sqlParamNode)
0437: .getParameterOperand();
0438: applicationParameterNumbers[p] = pn
0439: .getParameterNumber();
0440: } else
0441: applicationParameterNumbers[p] = ((ParameterNode) sqlParamNode)
0442: .getParameterNumber();
0443: }
0444:
0445: // this is the SQL type of the procedure parameter.
0446: DataTypeDescriptor paramdtd = new DataTypeDescriptor(
0447: parameterTypeId, td.getPrecision(), td
0448: .getScale(), td.isNullable(), td
0449: .getMaximumWidth());
0450:
0451: boolean needCast = false;
0452: if (!isParameterMarker) {
0453:
0454: // can only be an IN parameter.
0455: // check that the value can be assigned to the
0456: // type of the procedure parameter.
0457: if (sqlParamNode instanceof UntypedNullConstantNode) {
0458: sqlParamNode.setType(paramdtd);
0459: } else {
0460:
0461: DataTypeDescriptor dts;
0462: TypeId argumentTypeId;
0463:
0464: if (sqlParamNode != null) {
0465: // a node from the SQL world
0466: argumentTypeId = sqlParamNode
0467: .getTypeId();
0468: dts = sqlParamNode.getTypeServices();
0469: } else {
0470: // a node from the Java world
0471: dts = DataTypeDescriptor
0472: .getSQLDataTypeDescriptor(methodParms[p]
0473: .getJavaTypeName());
0474: if (dts == null) {
0475: throw StandardException
0476: .newException(
0477: SQLState.LANG_NO_CORRESPONDING_S_Q_L_TYPE,
0478: methodParms[p]
0479: .getJavaTypeName());
0480: }
0481:
0482: argumentTypeId = dts.getTypeId();
0483: }
0484:
0485: if (!getTypeCompiler(parameterTypeId)
0486: .storable(argumentTypeId,
0487: getClassFactory()))
0488: throw StandardException
0489: .newException(
0490: SQLState.LANG_NOT_STORABLE,
0491: parameterTypeId
0492: .getSQLTypeName(),
0493: argumentTypeId
0494: .getSQLTypeName());
0495:
0496: // if it's not an exact length match then some cast will be needed.
0497: if (!paramdtd
0498: .isExactTypeAndLengthMatch(dts))
0499: needCast = true;
0500: }
0501: } else {
0502: // any variable length type will need a cast from the
0503: // Java world (the ? parameter) to the SQL type. This
0504: // ensures values like CHAR(10) are passed into the procedure
0505: // correctly as 10 characters long.
0506: if (parameterTypeId.variableLength()) {
0507:
0508: if (parameterMode != JDBC30Translation.PARAMETER_MODE_OUT)
0509: needCast = true;
0510: }
0511: }
0512:
0513: if (needCast) {
0514: // push a cast node to ensure the
0515: // correct type is passed to the method
0516: // this gets tacky because before we knew
0517: // it was a procedure call we ensured all the
0518: // parameter are JavaNodeTypes. Now we need to
0519: // push them back to the SQL domain, cast them
0520: // and then push them back to the Java domain.
0521:
0522: if (sqlParamNode == null) {
0523:
0524: sqlParamNode = (ValueNode) getNodeFactory()
0525: .getNode(
0526: C_NodeTypes.JAVA_TO_SQL_VALUE_NODE,
0527: methodParms[p],
0528: getContextManager());
0529: }
0530:
0531: ValueNode castNode = (ValueNode) getNodeFactory()
0532: .getNode(C_NodeTypes.CAST_NODE,
0533: sqlParamNode, paramdtd,
0534: getContextManager());
0535:
0536: methodParms[p] = (JavaValueNode) getNodeFactory()
0537: .getNode(
0538: C_NodeTypes.SQL_TO_JAVA_VALUE_NODE,
0539: castNode, getContextManager());
0540:
0541: methodParms[p] = methodParms[p]
0542: .bindExpression(fromList, subqueryList,
0543: aggregateVector);
0544: }
0545:
0546: // only force the type for a ? so that the correct type shows up
0547: // in parameter meta data
0548: if (isParameterMarker)
0549: sqlParamNode.setType(paramdtd);
0550: }
0551:
0552: if (sigParameterCount != parameterCount) {
0553:
0554: TypeId typeId = TypeId.getUserDefinedTypeId(
0555: "java.sql.ResultSet[]", false);
0556:
0557: DataTypeDescriptor dtd = new DataTypeDescriptor(
0558: typeId, 0, 0, false, -1);
0559:
0560: signature[parameterCount] = new JSQLType(dtd);
0561:
0562: }
0563:
0564: this .routineInfo = routineInfo;
0565: ad = proc;
0566:
0567: // If a procedure is in the system schema and defined as executing
0568: // SQL do we set we are in system code.
0569: if (sd.isSystemSchema()
0570: && (routineInfo.getReturnType() == null)
0571: && routineInfo.getSQLAllowed() != RoutineAliasInfo.NO_SQL)
0572: isSystemCode = true;
0573:
0574: break;
0575: }
0576: }
0577: }
0578:
0579: /**
0580: Push extra code to generate the casts within the
0581: arrays for the parameters passed as arrays.
0582: */
0583: public void generateOneParameter(ExpressionClassBuilder acb,
0584: MethodBuilder mb, int parameterNumber)
0585: throws StandardException {
0586: int parameterMode;
0587:
0588: SQLToJavaValueNode sql2j = null;
0589: if (methodParms[parameterNumber] instanceof SQLToJavaValueNode)
0590: sql2j = (SQLToJavaValueNode) methodParms[parameterNumber];
0591:
0592: if (routineInfo != null) {
0593: parameterMode = routineInfo.getParameterModes()[parameterNumber];
0594: } else {
0595: // for a static method call the parameter always starts out as a in parameter, but
0596: // may be registered as an IN OUT parameter. For a static method argument to be
0597: // a dynmaically registered out parameter it must be a simple ? parameter
0598:
0599: parameterMode = JDBC30Translation.PARAMETER_MODE_IN;
0600:
0601: if (sql2j != null) {
0602: if (sql2j.getSQLValueNode().requiresTypeFromContext()) {
0603: ParameterNode pn;
0604: if (sql2j.getSQLValueNode() instanceof UnaryOperatorNode)
0605: pn = ((UnaryOperatorNode) sql2j
0606: .getSQLValueNode())
0607: .getParameterOperand();
0608: else
0609: pn = (ParameterNode) (sql2j.getSQLValueNode());
0610:
0611: // applicationParameterNumbers is only set up for a procedure.
0612: int applicationParameterNumber = pn
0613: .getParameterNumber();
0614:
0615: String parameterType = methodParameterTypes[parameterNumber];
0616:
0617: if (parameterType.endsWith("[]")) {
0618:
0619: // constructor - setting up correct paramter type info
0620: MethodBuilder constructor = acb
0621: .getConstructor();
0622: acb.pushThisAsActivation(constructor);
0623: constructor.callMethod(
0624: VMOpcode.INVOKEINTERFACE, null,
0625: "getParameterValueSet",
0626: ClassName.ParameterValueSet, 0);
0627:
0628: constructor.push(applicationParameterNumber);
0629: constructor
0630: .push(JDBC30Translation.PARAMETER_MODE_UNKNOWN);
0631: constructor.callMethod(
0632: VMOpcode.INVOKEINTERFACE, null,
0633: "setParameterMode", "void", 2);
0634: constructor.endStatement();
0635: }
0636: }
0637: }
0638: }
0639:
0640: switch (parameterMode) {
0641: case JDBC30Translation.PARAMETER_MODE_IN:
0642: case JDBC30Translation.PARAMETER_MODE_IN_OUT:
0643: case JDBC30Translation.PARAMETER_MODE_UNKNOWN:
0644: if (sql2j != null)
0645: sql2j.returnsNullOnNullState = returnsNullOnNullState;
0646: super .generateOneParameter(acb, mb, parameterNumber);
0647: break;
0648:
0649: case JDBC30Translation.PARAMETER_MODE_OUT:
0650: // For an OUT parameter we require nothing to be pushed into the
0651: // method call from the parameter node.
0652: break;
0653: }
0654:
0655: switch (parameterMode) {
0656: case JDBC30Translation.PARAMETER_MODE_IN:
0657: case JDBC30Translation.PARAMETER_MODE_UNKNOWN:
0658: break;
0659:
0660: case JDBC30Translation.PARAMETER_MODE_IN_OUT:
0661: case JDBC30Translation.PARAMETER_MODE_OUT: {
0662: // Create the array used to pass into the method. We create a
0663: // new array for each call as there is a small chance the
0664: // application could retain a reference to it and corrupt
0665: // future calls with the same CallableStatement object.
0666:
0667: String methodParameterType = methodParameterTypes[parameterNumber];
0668: String arrayType = methodParameterType.substring(0,
0669: methodParameterType.length() - 2);
0670: LocalField lf = acb.newFieldDeclaration(Modifier.PRIVATE,
0671: methodParameterType);
0672:
0673: if (outParamArrays == null)
0674: outParamArrays = new LocalField[methodParms.length];
0675:
0676: outParamArrays[parameterNumber] = lf;
0677:
0678: mb.pushNewArray(arrayType, 1);
0679: mb.putField(lf);
0680:
0681: // set the IN part of the parameter into the INOUT parameter.
0682: if (parameterMode != JDBC30Translation.PARAMETER_MODE_OUT) {
0683: mb.swap();
0684: mb.setArrayElement(0);
0685: mb.getField(lf);
0686: }
0687: break;
0688: }
0689: }
0690:
0691: }
0692:
0693: /**
0694: * Categorize this predicate. Initially, this means
0695: * building a bit map of the referenced tables for each predicate.
0696: * If the source of this ColumnReference (at the next underlying level)
0697: * is not a ColumnReference or a VirtualColumnNode then this predicate
0698: * will not be pushed down.
0699: *
0700: * For example, in:
0701: * select * from (select 1 from s) a (x) where x = 1
0702: * we will not push down x = 1.
0703: * NOTE: It would be easy to handle the case of a constant, but if the
0704: * inner SELECT returns an arbitrary expression, then we would have to copy
0705: * that tree into the pushed predicate, and that tree could contain
0706: * subqueries and method calls.
0707: * RESOLVE - revisit this issue once we have views.
0708: *
0709: * @param referencedTabs JBitSet with bit map of referenced FromTables
0710: * @param simplePredsOnly Whether or not to consider method
0711: * calls, field references and conditional nodes
0712: * when building bit map
0713: *
0714: * @return boolean Whether or not source.expression is a ColumnReference
0715: * or a VirtualColumnNode.
0716: *
0717: * @exception StandardException Thrown on error
0718: */
0719: public boolean categorize(JBitSet referencedTabs,
0720: boolean simplePredsOnly) throws StandardException {
0721: /* We stop here when only considering simple predicates
0722: * as we don't consider method calls when looking
0723: * for null invariant predicates.
0724: */
0725: if (simplePredsOnly) {
0726: return false;
0727: }
0728:
0729: boolean pushable = true;
0730:
0731: pushable = pushable
0732: && super .categorize(referencedTabs, simplePredsOnly);
0733:
0734: return pushable;
0735: }
0736:
0737: /**
0738: * Convert this object to a String. See comments in QueryTreeNode.java
0739: * for how this should be done for tree printing.
0740: *
0741: * @return This object as a String
0742: */
0743:
0744: public String toString() {
0745: if (SanityManager.DEBUG) {
0746: return "javaClassName: "
0747: + (javaClassName != null ? javaClassName : "null")
0748: + "\n" + super .toString();
0749: } else {
0750: return "";
0751: }
0752: }
0753:
0754: /**
0755: * Do code generation for this method call
0756: *
0757: * @param acb The ExpressionClassBuilder for the class we're generating
0758: * @param mb The method the expression will go into
0759: *
0760: *
0761: * @exception StandardException Thrown on error
0762: */
0763:
0764: public void generateExpression(ExpressionClassBuilder acb,
0765: MethodBuilder mb) throws StandardException {
0766: if (routineInfo != null) {
0767:
0768: if (!routineInfo.calledOnNullInput()
0769: && routineInfo.getParameterCount() != 0)
0770: returnsNullOnNullState = acb.newFieldDeclaration(
0771: Modifier.PRIVATE, "boolean");
0772:
0773: }
0774:
0775: // reset the parameters are null indicator.
0776: if (returnsNullOnNullState != null) {
0777: mb.push(false);
0778: mb.setField(returnsNullOnNullState);
0779:
0780: // for the call to the generated method below.
0781: mb.pushThis();
0782: }
0783:
0784: int nargs = generateParameters(acb, mb);
0785:
0786: LocalField functionEntrySQLAllowed = null;
0787:
0788: if (routineInfo != null) {
0789:
0790: short sqlAllowed = routineInfo.getSQLAllowed();
0791:
0792: // Before we set up our authorization level, add a check to see if this
0793: // method can be called. If the routine is NO SQL or CONTAINS SQL
0794: // then there is no need for a check. As follows:
0795: //
0796: // Current Level = NO_SQL - CALL will be rejected when getting CALL result set
0797: // Current Level = anything else - calls to procedures defined as NO_SQL and CONTAINS SQL both allowed.
0798:
0799: if (sqlAllowed != RoutineAliasInfo.NO_SQL) {
0800:
0801: int sqlOperation;
0802:
0803: if (sqlAllowed == RoutineAliasInfo.READS_SQL_DATA)
0804: sqlOperation = Authorizer.SQL_SELECT_OP;
0805: else if (sqlAllowed == RoutineAliasInfo.MODIFIES_SQL_DATA)
0806: sqlOperation = Authorizer.SQL_WRITE_OP;
0807: else
0808: sqlOperation = Authorizer.SQL_ARBITARY_OP;
0809:
0810: generateAuthorizeCheck((ActivationClassBuilder) acb,
0811: mb, sqlOperation);
0812: }
0813:
0814: int statmentContextReferences = isSystemCode ? 2 : 1;
0815:
0816: boolean isFunction = routineInfo.getReturnType() != null;
0817:
0818: if (isFunction)
0819: statmentContextReferences++;
0820:
0821: if (statmentContextReferences != 0) {
0822: acb.pushThisAsActivation(mb);
0823: mb.callMethod(VMOpcode.INVOKEINTERFACE, null,
0824: "getLanguageConnectionContext",
0825: ClassName.LanguageConnectionContext, 0);
0826: mb
0827: .callMethod(
0828: VMOpcode.INVOKEINTERFACE,
0829: null,
0830: "getStatementContext",
0831: "org.apache.derby.iapi.sql.conn.StatementContext",
0832: 0);
0833:
0834: for (int scc = 1; scc < statmentContextReferences; scc++)
0835: mb.dup();
0836: }
0837:
0838: /**
0839: Set the statement context to reflect we are running
0840: System procedures, so that we can execute non-standard SQL.
0841: */
0842: if (isSystemCode) {
0843: mb.callMethod(VMOpcode.INVOKEINTERFACE, null,
0844: "setSystemCode", "void", 0);
0845: }
0846:
0847: // for a function we need to fetch the current SQL control
0848: // so that we can reset it once the function is complete.
0849: //
0850: if (isFunction) {
0851: functionEntrySQLAllowed = acb.newFieldDeclaration(
0852: Modifier.PRIVATE, "short");
0853: mb.callMethod(VMOpcode.INVOKEINTERFACE, null,
0854: "getSQLAllowed", "short", 0);
0855: mb.setField(functionEntrySQLAllowed);
0856:
0857: }
0858:
0859: // Set up the statement context to reflect the
0860: // restricted SQL execution allowed by this routine.
0861:
0862: mb.push(sqlAllowed);
0863: mb.push(false);
0864: mb.callMethod(VMOpcode.INVOKEINTERFACE, null,
0865: "setSQLAllowed", "void", 2);
0866:
0867: }
0868:
0869: // add in the ResultSet arrays.
0870: if (routineInfo != null) {
0871:
0872: int compiledResultSets = methodParameterTypes.length
0873: - methodParms.length;
0874:
0875: if (compiledResultSets != 0) {
0876:
0877: // Add a method that indicates the maxium number of dynamic result sets.
0878: int maxDynamicResults = routineInfo
0879: .getMaxDynamicResultSets();
0880: if (maxDynamicResults > 0) {
0881: MethodBuilder gdr = acb.getClassBuilder()
0882: .newMethodBuilder(Modifier.PUBLIC, "int",
0883: "getMaxDynamicResults");
0884: gdr.push(maxDynamicResults);
0885: gdr.methodReturn();
0886: gdr.complete();
0887: }
0888:
0889: // add a method to return all the dynamic result sets (unordered)
0890: MethodBuilder gdr = acb.getClassBuilder()
0891: .newMethodBuilder(Modifier.PUBLIC,
0892: "java.sql.ResultSet[][]",
0893: "getDynamicResults");
0894:
0895: MethodBuilder cons = acb.getConstructor();
0896: // if (procDef.getParameterStyle() == RoutineAliasInfo.PS_JAVA)
0897: {
0898: // PARAMETER STYLE JAVA
0899:
0900: LocalField procedureResultSetsHolder = acb
0901: .newFieldDeclaration(Modifier.PRIVATE,
0902: "java.sql.ResultSet[][]");
0903:
0904: // getDynamicResults body
0905: gdr.getField(procedureResultSetsHolder);
0906:
0907: // create the holder of all the ResultSet arrays, new java.sql.ResultSet[][compiledResultSets]
0908: cons.pushNewArray("java.sql.ResultSet[]",
0909: compiledResultSets);
0910: cons.setField(procedureResultSetsHolder);
0911:
0912: // arguments for the dynamic result sets
0913: for (int i = 0; i < compiledResultSets; i++) {
0914:
0915: mb.pushNewArray("java.sql.ResultSet", 1);
0916: mb.dup();
0917:
0918: mb.getField(procedureResultSetsHolder);
0919: mb.swap();
0920:
0921: mb.setArrayElement(i);
0922: }
0923: }
0924:
0925: // complete the method that returns the ResultSet[][] to the
0926: gdr.methodReturn();
0927: gdr.complete();
0928:
0929: nargs += compiledResultSets;
0930: }
0931:
0932: }
0933:
0934: String javaReturnType = getJavaTypeName();
0935:
0936: MethodBuilder mbnc = null;
0937: MethodBuilder mbcm = mb;
0938:
0939: // If any of the parameters are null then
0940: // do not call the method, just return null.
0941: if (returnsNullOnNullState != null) {
0942: mbnc = acb.newGeneratedFun(javaReturnType,
0943: Modifier.PRIVATE, methodParameterTypes);
0944:
0945: // add the throws clause for the public static method we are going to call.
0946: Class[] throwsSet = ((java.lang.reflect.Method) method)
0947: .getExceptionTypes();
0948: for (int te = 0; te < throwsSet.length; te++) {
0949: mbnc.addThrownException(throwsSet[te].getName());
0950: }
0951:
0952: mbnc.getField(returnsNullOnNullState);
0953: mbnc.conditionalIf();
0954:
0955: // set up for a null!!
0956: // for objects is easy.
0957: mbnc.pushNull(javaReturnType);
0958:
0959: mbnc.startElseCode();
0960:
0961: if (!actualMethodReturnType.equals(javaReturnType))
0962: mbnc.pushNewStart(javaReturnType);
0963:
0964: // fetch all the arguments
0965: for (int pa = 0; pa < nargs; pa++) {
0966: mbnc.getParameter(pa);
0967: }
0968:
0969: mbcm = mbnc;
0970: }
0971:
0972: mbcm.callMethod(VMOpcode.INVOKESTATIC, method
0973: .getDeclaringClass().getName(), methodName,
0974: actualMethodReturnType, nargs);
0975:
0976: if (returnsNullOnNullState != null) {
0977: if (!actualMethodReturnType.equals(javaReturnType))
0978: mbnc.pushNewComplete(1);
0979:
0980: mbnc.completeConditional();
0981:
0982: mbnc.methodReturn();
0983: mbnc.complete();
0984:
0985: // now call the wrapper method
0986: mb.callMethod(VMOpcode.INVOKEVIRTUAL, acb.getClassBuilder()
0987: .getFullName(), mbnc.getName(), javaReturnType,
0988: nargs);
0989: mbnc = null;
0990: }
0991:
0992: if (routineInfo != null) {
0993:
0994: // reset the SQL allowed setting that we set upon
0995: // entry to the method.
0996: if (functionEntrySQLAllowed != null) {
0997: acb.pushThisAsActivation(mb);
0998: mb.callMethod(VMOpcode.INVOKEINTERFACE, null,
0999: "getLanguageConnectionContext",
1000: ClassName.LanguageConnectionContext, 0);
1001: mb
1002: .callMethod(
1003: VMOpcode.INVOKEINTERFACE,
1004: null,
1005: "getStatementContext",
1006: "org.apache.derby.iapi.sql.conn.StatementContext",
1007: 0);
1008: mb.getField(functionEntrySQLAllowed);
1009: mb.push(true); // override as we are ending the control set by this function all.
1010: mb.callMethod(VMOpcode.INVOKEINTERFACE, null,
1011: "setSQLAllowed", "void", 2);
1012:
1013: }
1014:
1015: if (outParamArrays != null) {
1016:
1017: MethodBuilder constructor = acb.getConstructor();
1018:
1019: // constructor - setting up correct paramter type info
1020: acb.pushThisAsActivation(constructor);
1021: constructor.callMethod(VMOpcode.INVOKEINTERFACE, null,
1022: "getParameterValueSet",
1023: ClassName.ParameterValueSet, 0);
1024:
1025: // execute - passing out parameters back.
1026: acb.pushThisAsActivation(mb);
1027: mb.callMethod(VMOpcode.INVOKEINTERFACE, null,
1028: "getParameterValueSet",
1029: ClassName.ParameterValueSet, 0);
1030:
1031: int[] parameterModes = routineInfo.getParameterModes();
1032: for (int i = 0; i < outParamArrays.length; i++) {
1033:
1034: int parameterMode = parameterModes[i];
1035: if (parameterMode != JDBC30Translation.PARAMETER_MODE_IN) {
1036:
1037: // must be a parameter if it is INOUT or OUT.
1038: ValueNode sqlParamNode = ((SQLToJavaValueNode) methodParms[i])
1039: .getSQLValueNode();
1040:
1041: int applicationParameterNumber = applicationParameterNumbers[i];
1042:
1043: // Set the correct parameter nodes in the ParameterValueSet at constructor time.
1044: constructor.dup();
1045: constructor.push(applicationParameterNumber);
1046: constructor.push(parameterMode);
1047: constructor.callMethod(
1048: VMOpcode.INVOKEINTERFACE, null,
1049: "setParameterMode", "void", 2);
1050:
1051: // Pass the value of the outparameters back to the calling code
1052: LocalField lf = outParamArrays[i];
1053:
1054: mb.dup();
1055: mb.push(applicationParameterNumber);
1056: mb.callMethod(VMOpcode.INVOKEINTERFACE, null,
1057: "getParameter",
1058: ClassName.DataValueDescriptor, 1);
1059:
1060: // see if we need to set the desired length/scale/precision of the type
1061: DataTypeDescriptor paramdtd = sqlParamNode
1062: .getTypeServices();
1063:
1064: boolean isNumericType = paramdtd.getTypeId()
1065: .isNumericTypeId();
1066:
1067: // is the underlying type for the OUT/INOUT parameter primitive.
1068: boolean isPrimitive = ((java.lang.reflect.Method) method)
1069: .getParameterTypes()[i]
1070: .getComponentType().isPrimitive();
1071:
1072: if (isNumericType) {
1073: // need to up-cast as the setValue(Number) method only exists on NumberDataValue
1074:
1075: if (!isPrimitive)
1076: mb.cast(ClassName.NumberDataValue);
1077: } else if (paramdtd.getTypeId()
1078: .isBooleanTypeId()) {
1079: // need to cast as the setValue(Boolean) method only exists on BooleanDataValue
1080: if (!isPrimitive)
1081: mb.cast(ClassName.BooleanDataValue);
1082: }
1083:
1084: if (paramdtd.getTypeId().variableLength()) {
1085: // need another DVD reference for the set width below.
1086: mb.dup();
1087: }
1088:
1089: mb.getField(lf); // pvs, dvd, array
1090: mb.getArrayElement(0); // pvs, dvd, value
1091:
1092: // The value needs to be set thorugh the setValue(Number) method.
1093: if (isNumericType && !isPrimitive) {
1094: mb.upCast("java.lang.Number");
1095: }
1096:
1097: mb.callMethod(VMOpcode.INVOKEINTERFACE, null,
1098: "setValue", "void", 1);
1099:
1100: if (paramdtd.getTypeId().variableLength()) {
1101: mb.push(isNumericType ? paramdtd
1102: .getPrecision() : paramdtd
1103: .getMaximumWidth());
1104: mb.push(paramdtd.getScale());
1105: mb.push(isNumericType);
1106: mb.callMethod(VMOpcode.INVOKEINTERFACE,
1107: ClassName.VariableSizeDataValue,
1108: "setWidth", "void", 3);
1109: // mb.endStatement();
1110: }
1111: }
1112: }
1113: constructor.endStatement();
1114: mb.endStatement();
1115: }
1116:
1117: }
1118: }
1119:
1120: /**
1121: * Set default privilege of EXECUTE for this node.
1122: */
1123: int getPrivType() {
1124: return Authorizer.EXECUTE_PRIV;
1125: }
1126: }
|