001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.compile.NonStaticMethodCallNode
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 org.apache.derby.iapi.reference.SQLState;
025:
026: import org.apache.derby.iapi.services.context.ContextManager;
027:
028: import org.apache.derby.iapi.sql.compile.CompilerContext;
029:
030: import org.apache.derby.iapi.sql.dictionary.DataDictionary;
031: import org.apache.derby.iapi.reference.SQLState;
032: import org.apache.derby.iapi.error.StandardException;
033: import org.apache.derby.iapi.store.access.Qualifier;
034:
035: import org.apache.derby.iapi.sql.compile.Visitable;
036: import org.apache.derby.iapi.sql.compile.Visitor;
037: import org.apache.derby.iapi.sql.compile.C_NodeTypes;
038:
039: import org.apache.derby.iapi.services.loader.ClassInspector;
040:
041: import org.apache.derby.iapi.services.compiler.MethodBuilder;
042: import org.apache.derby.iapi.services.sanity.SanityManager;
043:
044: import org.apache.derby.iapi.types.JSQLType;
045:
046: import org.apache.derby.impl.sql.compile.ExpressionClassBuilder;
047: import org.apache.derby.impl.sql.compile.ActivationClassBuilder;
048: import org.apache.derby.iapi.error.ExceptionSeverity;
049: import org.apache.derby.iapi.util.JBitSet;
050: import org.apache.derby.iapi.services.classfile.VMOpcode;
051:
052: import org.apache.derby.catalog.AliasInfo;
053:
054: import java.lang.reflect.Modifier;
055:
056: import java.util.Vector;
057:
058: /**
059: * A NonStaticMethodCallNode is really a node to represent a (static or non-static)
060: * method call from an object (as opposed to a static method call from a class.
061: */
062: public class NonStaticMethodCallNode extends MethodCallNode {
063: /*
064: ** The receiver for a non-static method call is an object, represented
065: ** by a ValueNode.
066: */
067: JavaValueNode receiver;
068:
069: /* Is this a static method call? Assume non-static call */
070: private boolean isStatic;
071:
072: /**
073: * Initializer for a NonStaticMethodCallNode
074: *
075: * @param methodName The name of the method to call
076: * @param receiver A JavaValueNode representing the receiving object
077: * @exception StandardException Thrown on error
078: */
079: public void init(Object methodName, Object receiver)
080: throws StandardException {
081: super .init(methodName);
082:
083: /*
084: ** If the receiver is a Java value that has been converted to a
085: ** SQL value, get rid of the conversion and just use the Java value
086: ** as-is. If the receiver is a "normal" SQL value, then convert
087: ** it to a Java value to use as the receiver.
088: */
089: if (receiver instanceof JavaToSQLValueNode) {
090: this .receiver = ((JavaToSQLValueNode) receiver)
091: .getJavaValueNode();
092: } else {
093: this .receiver = (JavaValueNode) getNodeFactory().getNode(
094: C_NodeTypes.SQL_TO_JAVA_VALUE_NODE, receiver,
095: getContextManager());
096: // System.out.println("NonStaticMethodCallNode.init() receiver = "+receiver);
097: // get nulpointer because not .bind?
098: // System.out.println("\ttypecompiler = "+((ValueNode)receiver).getTypeCompiler());
099: // System.out.println("\tdtd = "+((ValueNode)receiver).getTypeServices());
100: // System.out.println("\ttypeid = "+((ValueNode)receiver).getTypeServices().getTypeId());
101: }
102: }
103:
104: /**
105: * Bind this expression. This means binding the sub-expressions,
106: * as well as figuring out what the return type is for this expression.
107: *
108: * @param fromList The FROM list for the query this
109: * expression is in, for binding columns.
110: * @param subqueryList The subquery list being built as we find SubqueryNodes
111: * @param aggregateVector The aggregate vector being built as we find AggregateNodes
112: *
113: * @return this
114: *
115: * @exception StandardException Thrown on error
116: */
117:
118: public JavaValueNode bindExpression(FromList fromList,
119: SubqueryList subqueryList, Vector aggregateVector)
120: throws StandardException {
121: boolean nullParameter = false;
122: String[] parmTypeNames;
123:
124: /* NULL and ? not allowed as receivers */
125: if (receiver instanceof SQLToJavaValueNode) {
126: ValueNode SQLValue = ((SQLToJavaValueNode) receiver)
127: .getSQLValueNode();
128:
129: if (SanityManager.DEBUG)
130: SanityManager
131: .ASSERT(
132: !(SQLValue instanceof UntypedNullConstantNode),
133: "UntypedNullConstantNode found as a receiver of a non-static method call");
134:
135: //
136: // We don't allow methods off of naked unnamed "?" parameters.
137: // This is because we have no way of knowing the data type of
138: // a naked "?" parameter.
139: //
140: // However, if this "?" has actually been associated with a
141: // named "?paramName" parameter in a COPY PUBLICATION statement,
142: // then we have a type for it. Binding can continue.
143: //
144:
145: if (SQLValue.requiresTypeFromContext()) {
146: if (SQLValue.getTypeServices() == null) {
147: throw StandardException.newException(
148: SQLState.LANG_PARAMETER_RECEIVER,
149: methodName);
150: }
151: }
152: }
153:
154: bindParameters(fromList, subqueryList, aggregateVector);
155:
156: /* Now we don't allow an alias static method call here (that has to
157: * use :: sign for any static call). If it gets here, it can't be
158: * alias static method call.
159: */
160: receiver = receiver.bindExpression(fromList, subqueryList,
161: aggregateVector);
162:
163: // Don't allow LOB types to be used as a method receiver
164: String type = receiver.getJSQLType().getSQLType().getTypeId()
165: .getSQLTypeName();
166: if (type.equals("BLOB") || type.equals("CLOB")
167: || type.equals("NCLOB")) {
168: throw StandardException
169: .newException(SQLState.LOB_AS_METHOD_ARGUMENT_OR_RECEIVER);
170: }
171:
172: javaClassName = receiver.getJavaTypeName();
173:
174: /* Not allowed to use a primitive type as a method receiver */
175: if (ClassInspector.primitiveType(javaClassName)) {
176: throw StandardException.newException(
177: SQLState.LANG_PRIMITIVE_RECEIVER, methodName,
178: javaClassName);
179: }
180:
181: /* Resolve the method call */
182: resolveMethodCall(javaClassName, false);
183:
184: /* Remember if method is static */
185: isStatic = Modifier.isStatic(method.getModifiers());
186:
187: return this ;
188: }
189:
190: /**
191: * Categorize this predicate. Initially, this means
192: * building a bit map of the referenced tables for each predicate.
193: * If the source of this ColumnReference (at the next underlying level)
194: * is not a ColumnReference or a VirtualColumnNode then this predicate
195: * will not be pushed down.
196: *
197: * For example, in:
198: * select * from (select 1 from s) a (x) where x = 1
199: * we will not push down x = 1.
200: * NOTE: It would be easy to handle the case of a constant, but if the
201: * inner SELECT returns an arbitrary expression, then we would have to copy
202: * that tree into the pushed predicate, and that tree could contain
203: * subqueries and method calls.
204: * RESOLVE - revisit this issue once we have views.
205: *
206: * @param referencedTabs JBitSet with bit map of referenced FromTables
207: * @param simplePredsOnly Whether or not to consider method
208: * calls, field references and conditional nodes
209: * when building bit map
210: *
211: * @return boolean Whether or not source.expression is a ColumnReference
212: * or a VirtualColumnNode.
213: * @exception StandardException Thrown on error
214: */
215: public boolean categorize(JBitSet referencedTabs,
216: boolean simplePredsOnly) throws StandardException {
217: /* We stop here when only considering simple predicates
218: * as we don't consider method calls when looking
219: * for null invariant predicates.
220: */
221: if (simplePredsOnly) {
222: return false;
223: }
224:
225: boolean pushable = true;
226:
227: pushable = pushable
228: && super .categorize(referencedTabs, simplePredsOnly);
229:
230: if (receiver != null) {
231: pushable = pushable
232: && receiver.categorize(referencedTabs,
233: simplePredsOnly);
234: }
235:
236: return pushable;
237: }
238:
239: /**
240: * Return the variant type for the underlying expression.
241: * The variant type can be:
242: * VARIANT - variant within a scan
243: * (non-static field access)
244: * SCAN_INVARIANT - invariant within a scan
245: * (column references from outer tables)
246: * QUERY_INVARIANT - invariant within the life of a query
247: * (constant expressions)
248: *
249: * @return The variant type for the underlying expression.
250: */
251: protected int getOrderableVariantType() throws StandardException {
252: int receiverVariant = receiver.getOrderableVariantType();
253:
254: if (receiverVariant > Qualifier.SCAN_INVARIANT) {
255:
256: // If the method call is related to a trigger then the return
257: // values are SCAN_INVARIANT even though their calls look QUERY_INVARIANT
258: // because they take no parameters.
259: if (receiver.getJavaTypeName().equals(
260: "org.apache.derby.iapi.db.TriggerExecutionContext"))
261: receiverVariant = Qualifier.SCAN_INVARIANT;
262: }
263:
264: int this Variant = super .getOrderableVariantType();
265: if (receiverVariant < this Variant) //return the more variant one
266: return receiverVariant;
267: return this Variant;
268: }
269:
270: /**
271: * Remap all ColumnReferences in this tree to be clones of the
272: * underlying expression.
273: *
274: * @return JavaValueNode The remapped expression tree.
275: *
276: * @exception StandardException Thrown on error
277: */
278: public JavaValueNode remapColumnReferencesToExpressions()
279: throws StandardException {
280: if (receiver != null) {
281: receiver.remapColumnReferencesToExpressions();
282: }
283:
284: return super .remapColumnReferencesToExpressions();
285: }
286:
287: /**
288: * Prints the sub-nodes of this object. See QueryTreeNode.java for
289: * how tree printing is supposed to work.
290: *
291: * @param depth The depth of this node in the tree
292: */
293:
294: public void printSubNodes(int depth) {
295: if (SanityManager.DEBUG) {
296: int parm;
297:
298: super .printSubNodes(depth);
299: if (receiver != null) {
300: printLabel(depth, "receiver :");
301: receiver.treePrint(depth + 1);
302: }
303: }
304: }
305:
306: /**
307: * Preprocess an expression tree. We do a number of transformations
308: * here (including subqueries, IN lists, LIKE and BETWEEN) plus
309: * subquery flattening.
310: * NOTE: This is done before the outer ResultSetNode is preprocessed.
311: *
312: * @param numTables Number of tables in the DML Statement
313: * @param outerFromList FromList from outer query block
314: * @param outerSubqueryList SubqueryList from outer query block
315: * @param outerPredicateList PredicateList from outer query block
316: *
317: * @exception StandardException Thrown on error
318: */
319: public void preprocess(int numTables, FromList outerFromList,
320: SubqueryList outerSubqueryList,
321: PredicateList outerPredicateList) throws StandardException {
322: super .preprocess(numTables, outerFromList, outerSubqueryList,
323: outerPredicateList);
324: receiver.preprocess(numTables, outerFromList,
325: outerSubqueryList, outerPredicateList);
326:
327: }
328:
329: /**
330: * Do code generation for this method call
331: *
332: * @param acb The ExpressionClassBuilder for the class we're generating
333: * @param mb The method the expression will go into
334: *
335: *
336: * @exception StandardException Thrown on error
337: */
338:
339: public void generateExpression(ExpressionClassBuilder acb,
340: MethodBuilder mb) throws StandardException {
341: boolean inConditional = false;
342: /*
343: ** If this method returns its value to the Java domain,
344: ** generate the receiver and put the value in a field (only if
345: ** this method does not return a primitive type). If it
346: ** returns its value to the SQL domain, it's up to the JavaToSQLNode
347: ** to call generateReceiver().
348: **
349: ** Also, don't do this if the return value from this method
350: ** call will be discarded. This is the case for a CALL statement.
351: ** One reason we don't want to do this for a CALL statement
352: ** is that the ?: operator cannot be made into a statement.
353: */
354: if ((!valueReturnedToSQLDomain()) && (!returnValueDiscarded())) {
355: if (generateReceiver(acb, mb, receiver)) {
356: /*
357: ** If the above did generate the expression, let's test it for
358: ** a null value.
359: */
360: /*
361: ** Generate the following to test for null:
362: **
363: ** (receiverExpression == null) ?
364: */
365:
366: inConditional = true;
367: mb.conditionalIfNull();
368: mb.pushNull(getJavaTypeName());
369: mb.startElseCode();
370: }
371: }
372:
373: /*
374: ** Generate the following:
375: **
376: ** <receiver>.<method name>(<param> (, <param> )* )
377: **
378: ** for non-static calls.
379: **
380: ** Refer to the field holding the receiver, if there is any.
381: */
382:
383: Class declaringClass = method.getDeclaringClass();
384:
385: /*
386: ** If it's an interface, generate an interface method call, if it's a static,
387: ** generate a static method call, otherwise generate a regular method call.
388: */
389:
390: short methodType;
391:
392: if (declaringClass.isInterface())
393: methodType = VMOpcode.INVOKEINTERFACE;
394: else if (isStatic)
395: methodType = VMOpcode.INVOKESTATIC;
396: else
397: methodType = VMOpcode.INVOKEVIRTUAL;
398:
399: getReceiverExpression(acb, mb, receiver);
400: if (isStatic)
401: mb.endStatement(); // PUSHCOMPILER ???
402:
403: int nargs = generateParameters(acb, mb);
404:
405: mb.callMethod(methodType, declaringClass.getName(), methodName,
406: getJavaTypeName(), nargs);
407:
408: if (inConditional)
409: mb.completeConditional();
410: }
411:
412: /**
413: * Generate the expression that evaluates to the receiver. This is
414: * for the case where a java expression is being returned to the SQL
415: * domain, and we need to check whether the receiver is null (if so,
416: * the SQL value should be set to null, and this Java expression
417: * not evaluated). Instance method calls and field references have
418: * receivers, while class method calls and calls to constructors do
419: * not. If this Java expression does not have a receiver, this method
420: * returns null.
421: *
422: * Only generate the receiver once and cache it in a field. This is
423: * because there will be two references to the receiver, and we want
424: * to evaluate it only once.
425: *
426: *
427: * @param acb The ExpressionClassBuilder for the class being built
428: * @param mb The method the expression will go into
429: *
430: * @return true if compiled receiver, false otherwise.
431: *
432: * @exception StandardException Thrown on error
433: */
434: protected boolean generateReceiver(ExpressionClassBuilder acb,
435: MethodBuilder mb) throws StandardException {
436: /*
437: ** Let's pretend that a call to a static method doesn't have a
438: ** receiver, since the method call is actually to the class,
439: ** and can be made even if the receiver is null (that is, we
440: ** always want to call a static method, even if the receiver is null).
441: */
442: if (isStatic)
443: return false;
444:
445: return generateReceiver(acb, mb, receiver);
446: }
447:
448: /**
449: * Accept a visitor, and call v.visit()
450: * on child nodes as necessary.
451: *
452: * @param v the visitor
453: *
454: * @exception StandardException on error
455: */
456: public Visitable accept(Visitor v) throws StandardException {
457: if (v.skipChildren(this )) {
458: return v.visit(this );
459: }
460:
461: Visitable returnNode = super .accept(v);
462:
463: if (receiver != null && !v.stopTraversal()) {
464: receiver = (JavaValueNode) receiver.accept(v);
465: }
466:
467: return returnNode;
468: }
469: }
|