001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.compile.ParameterNode
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.impl.sql.compile;
023:
024: import java.sql.Types;
025: import java.util.Enumeration;
026: import java.util.Vector;
027:
028: import org.apache.derby.iapi.error.StandardException;
029: import org.apache.derby.iapi.reference.ClassName;
030: import org.apache.derby.iapi.reference.SQLState;
031: import org.apache.derby.iapi.services.classfile.VMOpcode;
032: import org.apache.derby.iapi.services.compiler.MethodBuilder;
033: import org.apache.derby.iapi.services.sanity.SanityManager;
034: import org.apache.derby.iapi.sql.compile.CompilerContext;
035: import org.apache.derby.iapi.store.access.Qualifier;
036: import org.apache.derby.iapi.types.DataTypeDescriptor;
037: import org.apache.derby.iapi.types.DataValueDescriptor;
038: import org.apache.derby.iapi.types.JSQLType;
039: import org.apache.derby.iapi.types.TypeId;
040:
041: /**
042: * This node type represents a ? parameter.
043: *
044: * @author Jeff Lichtman
045: */
046:
047: public class ParameterNode extends ValueNode {
048:
049: /*
050: ** The parameter number for this parameter. The numbers start at 0.
051: */
052: private int parameterNumber;
053:
054: /*
055: ** Pointer to the array in the DMLStatementNode that holds the
056: ** DataTypeServices for the parameters. When each parameter is
057: ** bound, it fills in its type descriptor in this array. Note that
058: ** the array is allocated in the parser, but the individual elements
059: ** are not filled in until their corresponding parameters are bound.
060: */
061:
062: private DataTypeDescriptor[] typeServices;
063:
064: /*
065: ** The default value for this parameter. Currently, the only
066: ** reason for a parameter having a default value is for a
067: ** stored prepared statement, where they are supplied for
068: ** optimization.
069: */
070: private DataValueDescriptor defaultValue;
071:
072: /**
073: * This ParameterNode may turn up as an argument to a replicated Work Unit.
074: * If so, the remote system will have figured out the type of this node.
075: * That's what this variable is for.
076: */
077: private JSQLType jsqlType;
078:
079: private int orderableVariantType = Qualifier.QUERY_INVARIANT;
080:
081: /**
082: * By default, we assume we are just a normal, harmless
083: * little ole parameter. But sometimes we may be a return
084: * parameter (e.g. ? = CALL myMethod()).
085: */
086: private ValueNode returnOutputParameter;
087:
088: /**
089: * Constructor for use by the NodeFactory
090: */
091: public ParameterNode() {
092: }
093:
094: /**
095: * Initializer for a ParameterNode.
096: *
097: * @param parameterNumber The number of this parameter,
098: * (unique per query starting at 0)
099: * @param defaultValue The default value for this parameter
100: *
101: */
102:
103: public void init(Object parameterNumber, Object defaultValue) {
104: this .defaultValue = (DataValueDescriptor) defaultValue;
105: this .parameterNumber = ((Integer) parameterNumber).intValue();
106: }
107:
108: /**
109: * Get the parameter number
110: *
111: * @return The parameter number
112: */
113:
114: int getParameterNumber() {
115: return parameterNumber;
116: }
117:
118: /**
119: * Set the descriptor array
120: *
121: * @param descriptors The array of DataTypeServices to fill in when the parameters
122: * are bound.
123: */
124:
125: void setDescriptors(DataTypeDescriptor[] descriptors) {
126:
127: // The following is commented out for #3546, for create publication
128: // or target ddl creations there could be multiple statements trying
129: // to bind their own parameters. So the following assumptions does not
130: // hold true.
131:
132: // if (SanityManager.DEBUG)
133: // SanityManager.ASSERT(typeServices == null,
134: // "Attempt to re-set typeServices");
135:
136: typeServices = descriptors;
137: }
138:
139: /**
140: * Set the DataTypeServices for this parameter
141: *
142: * @param descriptor The DataTypeServices to set for this parameter
143: */
144:
145: public void setType(DataTypeDescriptor descriptor)
146: throws StandardException {
147: if (SanityManager.DEBUG)
148: SanityManager.ASSERT(typeServices != null,
149: "typeServices not initialized");
150:
151: /* Make sure the type is nullable. */
152: if (!descriptor.isNullable()) {
153: /*
154: ** Generate a new descriptor with all the same properties as
155: ** the given one, except that it is nullable.
156: */
157: descriptor = new DataTypeDescriptor(descriptor, true);
158: }
159:
160: typeServices[parameterNumber] = descriptor;
161:
162: //make sure we are calling super's setType. We will get into
163: //an infinite loop if this setType ends up calling the local
164: //setType method
165: super .setType(descriptor);
166:
167: if (getJSQLType() == null) {
168: setJSQLType(new JSQLType(descriptor));
169: }
170: }
171:
172: /**
173: * Mark this as a return output parameter (e.g.
174: * ? = CALL myMethod())
175: */
176: public void setReturnOutputParam(ValueNode valueNode) {
177: returnOutputParameter = valueNode;
178: }
179:
180: /**
181: * Is this as a return output parameter (e.g.
182: * ? = CALL myMethod())
183: *
184: * @return true if it is a return param
185: */
186: public boolean isReturnOutputParam() {
187: return returnOutputParameter != null;
188: }
189:
190: /**
191: * Bind this expression. A parameter can't figure out what its type
192: * is without knowing where it appears, so this method does nothing.
193: * It is up to the node that points to this parameter node to figure
194: * out the type of the parameter and set it, using the setType()
195: * method above.
196: *
197: * @param fromList The FROM list for the query this
198: * expression is in, for binding columns.
199: * @param subqueryList The subquery list being built as we find SubqueryNodes
200: * @param aggregateVector The aggregate vector being built as we find AggregateNodes
201: *
202: * @return The new top of the expression tree.
203: *
204: * @exception StandardException Thrown on error
205: */
206:
207: public ValueNode bindExpression(FromList fromList,
208: SubqueryList subqueryList, Vector aggregateVector)
209: throws StandardException {
210: checkReliability("?", CompilerContext.UNNAMED_PARAMETER_ILLEGAL);
211:
212: return this ;
213: }
214:
215: /**
216: * Return whether or not this expression tree represents a constant expression.
217: *
218: * @return Whether or not this expression tree represents a constant expression.
219: */
220: public boolean isConstantExpression() {
221: return true;
222: }
223:
224: /** @see ValueNode#constantExpression */
225: public boolean constantExpression(PredicateList whereClause) {
226: return true;
227: }
228:
229: /**
230: * Return the variant type for the underlying expression.
231: * The variant type can be:
232: * VARIANT - variant within a scan
233: * (method calls and non-static field access)
234: * SCAN_INVARIANT - invariant within a scan
235: * (column references from outer tables)
236: * QUERY_INVARIANT - invariant within the life of a query
237: * (constant expressions)
238: *
239: * @return The variant type for the underlying expression.
240: */
241: protected int getOrderableVariantType() {
242: // Parameters are invariant for the life of the query
243: return orderableVariantType;
244: }
245:
246: /**
247: * In a special circumstance, we want to consider
248: * parameters as constants. For that situation, we
249: * allow a caller to temporarily set us to CONSTANT
250: * and then restore us.
251: */
252: void setOrderableVariantType(int type) {
253: orderableVariantType = type;
254: }
255:
256: ////////////////////////////////////////////////////////////////////////
257: //
258: // OVERRIDE METHODS IN VALUE NODE THAT ARE USED WHILE BINDING REPLICATED
259: // CALL WORK STATEMENTS.
260: //
261: // In this scenario, a JSQLType was replicated along with this parameter.
262: // The JSQLType represents the bind() decision of the remote system, which
263: // we want to reproduce locally.
264: //
265: ////////////////////////////////////////////////////////////////////////
266:
267: /**
268: * Set the JSQLType of this parameter. This supports the unnamed parameters
269: * that we use for replicated work units.
270: *
271: * @param type the JSQLType associated with this parameter
272: */
273: public void setJSQLType(JSQLType type) {
274: jsqlType = type;
275: }
276:
277: /**
278: * Get the JSQLType associated with this parameter. Again, part of method
279: * resolution for replicated work units.
280: *
281: * @return the JSQLType that the remote system assigned
282: */
283: public JSQLType getJSQLType() {
284: return jsqlType;
285: }
286:
287: ////////////////////////////////////////////////////////////////////
288: //
289: // CODE GENERATOR
290: //
291: ////////////////////////////////////////////////////////////////////
292:
293: /**
294: * For a ParameterNode, we generate for the return value:
295: *
296: * (<java type name>)
297: * ( (BaseActivation) this.getParameter(parameterNumber) )
298: *
299: *
300: * @param acb The ExpressionClassBuilder for the class being built
301: * @param mb The method the expression will go into
302: *
303: *
304: * @exception StandardException Thrown on error
305: */
306: public void generateExpression(ExpressionClassBuilder acb,
307: MethodBuilder mb) throws StandardException {
308: DataTypeDescriptor dtd = getTypeServices();
309: if ((dtd != null) && dtd.getTypeId().isXMLTypeId()) {
310: // We're a parameter that corresponds to an XML column/target,
311: // which we don't allow. We throw the error here instead of
312: // in "bindExpression" because at the time of bindExpression,
313: // we don't know yet what the type is going to be (only when
314: // the node that points to this parameter calls
315: // "setType" do we figure out the type).
316: throw StandardException
317: .newException(SQLState.LANG_ATTEMPT_TO_BIND_XML);
318: }
319:
320: /* Generate the return value */
321:
322: mb.pushThis();
323: mb.push(parameterNumber); // arg
324:
325: mb.callMethod(VMOpcode.INVOKEVIRTUAL, ClassName.BaseActivation,
326: "getParameter", ClassName.DataValueDescriptor, 1);
327:
328: // For some types perform host variable checking
329: // to match DB2/JCC where if a host variable is too
330: // big it is not accepted, regardless of any trailing padding.
331:
332: switch (dtd.getJDBCTypeId()) {
333: case Types.BINARY:
334: case Types.VARBINARY:
335: case Types.LONGVARBINARY:
336: case Types.BLOB:
337: mb.dup();
338: mb.push(dtd.getMaximumWidth());
339: mb.callMethod(VMOpcode.INVOKEINTERFACE, (String) null,
340: "checkHostVariable", "void", 1);
341: break;
342:
343: default:
344: break;
345: }
346:
347: /* Cast the result to its specific interface */
348: mb.cast(getTypeCompiler().interfaceName());
349: } // End of generateExpression
350:
351: public TypeId getTypeId() throws StandardException {
352: return (returnOutputParameter != null) ? returnOutputParameter
353: .getTypeId() : super .getTypeId();
354: }
355:
356: ////////////////////////////////////////////////////////////////////
357: //
358: // STATIC ROUTINES
359: //
360: ////////////////////////////////////////////////////////////////////
361:
362: /**
363: * Generate the code to create the ParameterValueSet, if necessary,
364: * when constructing the activation. Also generate the code to call
365: * a method that will throw an exception if we try to execute without
366: * all the parameters being set.
367: *
368: * This generated code goes into the Activation's constructor early on.
369: *
370: * @param acb The ExpressionClassBuilder for the class we're building
371: * @param numberOfParameters number of parameters for this statement
372: * @param parameterList The parameter list for the statement.
373: *
374: * @exception StandardException on error
375: */
376: static public void generateParameterValueSet(
377: ExpressionClassBuilder acb, int numberOfParameters,
378: Vector parameterList) throws StandardException {
379: if (numberOfParameters > 0) {
380: MethodBuilder constructor = acb.getConstructor();
381:
382: /*
383: ** Check the first parameter to see if it is a return
384: ** parameter.
385: */
386: boolean hasReturnParam = ((ParameterNode) parameterList
387: .elementAt(0)).isReturnOutputParam();
388:
389: /*
390: ** Generate the following:
391: **
392: ** pvs =
393: ** getLanguageConnectionContext()
394: ** .getLanguageFactory()
395: ** .getParameterValueSet(numberOfParameters);
396: **
397: ** pvs is a ParameterValueSet that lives in the superclass of
398: ** the activation being generated.
399: */
400:
401: constructor.pushThis(); // for the put field down below
402:
403: /* Generate the call to getContext */
404: //?X constructor.pushThis();
405: //?Xconstructor.callMethod(VMOpcode.INVOKEINTERFACE, ClassName.Activation, "getLanguageConnectionContext",
406: //?X ClassName.LanguageConnectionContext, 0);
407: /*
408: ** Call getLanguageFactory()
409: */
410: //?Xconstructor.callMethod(VMOpcode.INVOKEINTERFACE, (String) null, "getLanguageFactory",
411: //?X ClassName.LanguageFactory, 0);
412: /*
413: ** Call getParameterValueSet(<number of parameters>, <hasReturnParam>)
414: */
415:
416: constructor.push(numberOfParameters); // first arg
417: constructor.push(hasReturnParam); // second arg
418:
419: constructor.callMethod(VMOpcode.INVOKEVIRTUAL,
420: ClassName.BaseActivation, "setParameterValueSet",
421: "void", 2);
422:
423: //?Xconstructor.callMethod(VMOpcode.INVOKEINTERFACE, (String) null, "getParameterValueSet",
424: //?X ClassName.ParameterValueSet, 2);
425:
426: /* Assign the return from getParameterValueSet() to the field */
427: //?Xconstructor.putField(ClassName.BaseActivation, "pvs", ClassName.ParameterValueSet);
428: //?Xconstructor.endStatement();
429: /*
430: ** Add a call to the execute() method to check
431: ** for missing parameters
432: */
433: MethodBuilder executeMethod = acb.getExecuteMethod();
434:
435: executeMethod.pushThis();
436: executeMethod.callMethod(VMOpcode.INVOKEVIRTUAL,
437: ClassName.BaseActivation, "throwIfMissingParms",
438: "void", 0);
439: }
440: }
441:
442: /**
443: * Get the default value for the parameter. Parameters
444: * may get default values for optimization purposes.
445: *
446: * @return the value, may be null
447: */
448: DataValueDescriptor getDefaultValue() {
449: return defaultValue;
450: }
451:
452: /**
453: * @see ValueNode#requiresTypeFromContext
454: */
455: public boolean requiresTypeFromContext() {
456: return true;
457: }
458:
459: /**
460: * @see ValueNode#isParameterNode
461: */
462: public boolean isParameterNode() {
463: return true;
464: }
465:
466: /**
467: * @inheritDoc
468: */
469: protected boolean isEquivalent(ValueNode o) {
470: return false;
471: }
472: }
|