001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.compile.CallStatementNode
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.services.context.ContextManager;
025:
026: import org.apache.derby.iapi.error.StandardException;
027:
028: import org.apache.derby.iapi.sql.dictionary.DataDictionary;
029: import org.apache.derby.iapi.sql.dictionary.DataDictionaryContext;
030:
031: import org.apache.derby.iapi.sql.ResultSet;
032: import org.apache.derby.iapi.sql.Activation;
033: import org.apache.derby.iapi.sql.ResultDescription;
034:
035: import org.apache.derby.iapi.sql.compile.CompilerContext;
036: import org.apache.derby.iapi.sql.compile.Visitable;
037: import org.apache.derby.iapi.sql.compile.Visitor;
038: import org.apache.derby.iapi.sql.compile.C_NodeTypes;
039:
040: import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
041: import org.apache.derby.iapi.sql.conn.Authorizer;
042:
043: import org.apache.derby.iapi.services.loader.GeneratedMethod;
044:
045: import org.apache.derby.iapi.services.sanity.SanityManager;
046:
047: import org.apache.derby.iapi.services.compiler.MethodBuilder;
048:
049: import org.apache.derby.impl.sql.compile.ActivationClassBuilder;
050: import org.apache.derby.iapi.reference.ClassName;
051: import org.apache.derby.iapi.services.classfile.VMOpcode;
052:
053: import org.apache.derby.catalog.types.RoutineAliasInfo;
054: import org.apache.derby.iapi.reference.SQLState;
055:
056: import java.lang.reflect.Modifier;
057:
058: import java.util.Vector;
059:
060: /**
061: * An CallStatementNode represents a CALL <procedure> statement.
062: * It is the top node of the query tree for that statement.
063: * A procedure call is very simple.
064: *
065: * CALL [<schema>.]<procedure>(<args>)
066: *
067: * <args> are either constants or parameter markers.
068: * This implementation assumes that no subqueries or aggregates
069: * can be in the argument list.
070: *
071: * A procedure is always represented by a MethodCallNode.
072: *
073: * @author Jerry Brenner
074: */
075: public class CallStatementNode extends DMLStatementNode {
076: /**
077: * The method call for the Java procedure. Guaranteed to be
078: * a JavaToSQLValueNode wrapping a MethodCallNode by checks
079: * in the parser.
080: */
081: private JavaToSQLValueNode methodCall;
082:
083: /**
084: * Initializer for a CallStatementNode.
085: *
086: * @param methodCall The expression to "call"
087: */
088:
089: public void init(Object methodCall) {
090: super .init(null);
091: this .methodCall = (JavaToSQLValueNode) methodCall;
092: this .methodCall.getJavaValueNode().markForCallStatement();
093: }
094:
095: /**
096: * Convert this object to a String. See comments in QueryTreeNode.java
097: * for how this should be done for tree printing.
098: *
099: * @return This object as a String
100: */
101:
102: public String toString() {
103: if (SanityManager.DEBUG) {
104: return "CALL " + methodCall.toString() + "\n"
105: + super .toString();
106: } else {
107: return "";
108: }
109: }
110:
111: public String statementToString() {
112: return "CALL";
113: }
114:
115: /**
116: * Prints the sub-nodes of this object. See QueryTreeNode.java for
117: * how tree printing is supposed to work.
118: *
119: * @param depth The depth of this node in the tree
120: */
121:
122: public void printSubNodes(int depth) {
123: if (SanityManager.DEBUG) {
124: super .printSubNodes(depth);
125:
126: if (methodCall != null) {
127: printLabel(depth, "methodCall: ");
128: methodCall.treePrint(depth + 1);
129: }
130: }
131: }
132:
133: /**
134: * Bind this UpdateNode. This means looking up tables and columns and
135: * getting their types, and figuring out the result types of all
136: * expressions, as well as doing view resolution, permissions checking,
137: * etc.
138: * <p>
139: * Binding an update will also massage the tree so that
140: * the ResultSetNode has a single column, the RID.
141: *
142: * @return The bound query tree
143: *
144: * @exception StandardException Thrown on error
145: */
146:
147: public QueryTreeNode bind() throws StandardException {
148: DataDictionary dd = getDataDictionary();
149:
150: if (SanityManager.DEBUG)
151: SanityManager.ASSERT((dd != null),
152: "Failed to get data dictionary");
153:
154: getCompilerContext().pushCurrentPrivType(getPrivType());
155: methodCall = (JavaToSQLValueNode) methodCall.bindExpression(
156: (FromList) getNodeFactory().getNode(
157: C_NodeTypes.FROM_LIST,
158: getNodeFactory().doJoinOrderOptimization(),
159: getContextManager()), null, null);
160:
161: // Disallow creation of BEFORE triggers which contain calls to
162: // procedures that modify SQL data.
163: checkReliability();
164:
165: getCompilerContext().popCurrentPrivType();
166: return this ;
167: }
168:
169: /**
170: * Optimize a DML statement (which is the only type of statement that
171: * should need optimizing, I think). This method over-rides the one
172: * in QueryTreeNode.
173: *
174: * This method takes a bound tree, and returns an optimized tree.
175: * It annotates the bound tree rather than creating an entirely
176: * new tree.
177: *
178: * Throws an exception if the tree is not bound, or if the binding
179: * is out of date.
180: *
181: * @return An optimized QueryTree
182: *
183: * @exception StandardException Thrown on error
184: */
185:
186: public QueryTreeNode optimize() throws StandardException {
187: DataDictionary dd = getDataDictionary();
188:
189: if (SanityManager.DEBUG)
190: SanityManager.ASSERT((dd != null),
191: "Failed to get data dictionary");
192:
193: /* Preprocess the method call tree */
194: methodCall = (JavaToSQLValueNode) methodCall.preprocess(
195: getCompilerContext().getNumTables(),
196: (FromList) getNodeFactory().getNode(
197: C_NodeTypes.FROM_LIST,
198: getNodeFactory().doJoinOrderOptimization(),
199: getContextManager()), (SubqueryList) null,
200: (PredicateList) null);
201:
202: return this ;
203: }
204:
205: /**
206: * Code generation for CallStatementNode.
207: * The generated code will contain:
208: * o A generated void method for the user's method call.
209: *
210: * @param acb The ActivationClassBuilder for the class being built
211: * @param mb The method for the execute() method to be built
212: *
213: * @exception StandardException Thrown on error
214: */
215: public void generate(ActivationClassBuilder acb, MethodBuilder mb)
216: throws StandardException {
217: JavaValueNode methodCallBody;
218:
219: /* generate the parameters */
220: generateParameterValueSet(acb);
221:
222: /*
223: * Skip over the JavaToSQLValueNode and call generate() for the JavaValueNode.
224: * (This skips over generated code which is unnecessary since we are throwing
225: * away any return value and which won't work with void methods.)
226: * generates:
227: * <methodCall.generate(acb)>;
228: * and adds it to userExprFun
229: */
230: methodCallBody = methodCall.getJavaValueNode();
231:
232: /*
233: ** Tell the method call that its return value (if any) will be
234: ** discarded. This is so it doesn't generate the ?: operator
235: ** that would return null if the receiver is null. This is
236: ** important because the ?: operator cannot be made into a statement.
237: */
238: methodCallBody.markReturnValueDiscarded();
239:
240: // this sets up the method
241: // generates:
242: // void userExprFun {
243: // method_call(<args>);
244: // }
245: //
246: // An expression function is used to avoid reflection.
247: // Since the arguments to a procedure are simple, this
248: // will be the only expression function and so it will
249: // be executed directly as e0.
250: MethodBuilder userExprFun = acb.newGeneratedFun("void",
251: Modifier.PUBLIC);
252: userExprFun.addThrownException("java.lang.Exception");
253: methodCallBody.generate(acb, userExprFun);
254: userExprFun.endStatement();
255: userExprFun.methodReturn();
256: userExprFun.complete();
257:
258: acb.pushGetResultSetFactoryExpression(mb);
259: acb.pushMethodReference(mb, userExprFun); // first arg
260: acb.pushThisAsActivation(mb); // arg 2
261: mb.callMethod(VMOpcode.INVOKEINTERFACE, (String) null,
262: "getCallStatementResultSet", ClassName.ResultSet, 2);
263: }
264:
265: public ResultDescription makeResultDescription() {
266: return null;
267: }
268:
269: /**
270: * Accept a visitor, and call v.visit()
271: * on child nodes as necessary.
272: *
273: * @param v the visitor
274: *
275: * @exception StandardException on error
276: */
277: public Visitable accept(Visitor v) throws StandardException {
278: if (v.skipChildren(this )) {
279: return v.visit(this );
280: }
281:
282: Visitable returnNode = super .accept(v);
283:
284: if (!v.stopTraversal()) {
285: methodCall = (JavaToSQLValueNode) methodCall.accept(v);
286: }
287:
288: return returnNode;
289: }
290:
291: /**
292: * Set default privilege of EXECUTE for this node.
293: */
294: int getPrivType() {
295: return Authorizer.EXECUTE_PRIV;
296: }
297:
298: /**
299: * This method checks if the called procedure allows modification of SQL
300: * data. If yes, it cannot be compiled if the reliability is
301: * <code>CompilerContext.MODIFIES_SQL_DATA_PROCEDURE_ILLEGAL</code>. This
302: * reliability is set for BEFORE triggers in the create trigger node. This
303: * check thus disallows creation of BEFORE triggers which contain calls to
304: * procedures that modify SQL data in the trigger action statement.
305: *
306: * @throws StandardException
307: */
308: private void checkReliability() throws StandardException {
309: if (getSQLAllowedInProcedure() == RoutineAliasInfo.MODIFIES_SQL_DATA
310: && getCompilerContext().getReliability() == CompilerContext.MODIFIES_SQL_DATA_PROCEDURE_ILLEGAL)
311: throw StandardException
312: .newException(SQLState.LANG_UNSUPPORTED_TRIGGER_PROC);
313: }
314:
315: /**
316: * This method checks the SQL allowed by the called procedure. This method
317: * should be called only after the procedure has been resolved.
318: *
319: * @return SQL allowed by the procedure
320: */
321: private short getSQLAllowedInProcedure() {
322: RoutineAliasInfo routineInfo = ((MethodCallNode) methodCall
323: .getJavaValueNode()).routineInfo;
324:
325: // If this method is called before the routine has been resolved, routineInfo will be null
326: if (SanityManager.DEBUG)
327: SanityManager.ASSERT((routineInfo != null),
328: "Failed to get routineInfo");
329:
330: return routineInfo.getSQLAllowed();
331: }
332: }
|