001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.compile.DMLStatementNode
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: import org.apache.derby.iapi.sql.compile.CompilerContext;
028: import org.apache.derby.iapi.sql.compile.Visitable;
029: import org.apache.derby.iapi.sql.compile.Visitor;
030: import org.apache.derby.iapi.sql.compile.C_NodeTypes;
031:
032: import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
033: import org.apache.derby.iapi.sql.conn.Authorizer;
034: import org.apache.derby.iapi.sql.execute.ExecutionContext;
035: import org.apache.derby.iapi.sql.ResultColumnDescriptor;
036: import org.apache.derby.iapi.sql.ParameterValueSet;
037: import org.apache.derby.iapi.sql.ResultDescription;
038:
039: import org.apache.derby.iapi.sql.dictionary.DataDictionaryContext;
040: import org.apache.derby.iapi.sql.dictionary.DataDictionary;
041: import org.apache.derby.iapi.sql.dictionary.TableDescriptor;
042: import org.apache.derby.iapi.sql.dictionary.ConglomerateDescriptor;
043: import org.apache.derby.iapi.sql.dictionary.IndexRowGenerator;
044:
045: import org.apache.derby.iapi.services.monitor.Monitor;
046:
047: import org.apache.derby.iapi.services.compiler.MethodBuilder;
048:
049: import org.apache.derby.iapi.services.sanity.SanityManager;
050:
051: import org.apache.derby.iapi.util.JBitSet;
052:
053: import org.apache.derby.impl.sql.compile.ActivationClassBuilder;
054:
055: import java.util.Enumeration;
056: import java.util.Properties;
057: import java.util.Vector;
058:
059: /**
060: * A DMLStatementNode represents any type of DML statement: a cursor declaration,
061: * an INSERT statement, and UPDATE statement, or a DELETE statement. All DML
062: * statements have result sets, but they do different things with them. A
063: * SELECT statement sends its result set to the client, an INSERT statement
064: * inserts its result set into a table, a DELETE statement deletes from a
065: * table the rows corresponding to the rows in its result set, and an UPDATE
066: * statement updates the rows in a base table corresponding to the rows in its
067: * result set.
068: *
069: * @author Jeff Lichtman
070: */
071:
072: abstract class DMLStatementNode extends StatementNode {
073:
074: /**
075: * The result set is the rows that result from running the
076: * statement. What this means for SELECT statements is fairly obvious.
077: * For a DELETE, there is one result column representing the
078: * key of the row to be deleted (most likely, the location of the
079: * row in the underlying heap). For an UPDATE, the row consists of
080: * the key of the row to be updated plus the updated columns. For
081: * an INSERT, the row consists of the new column values to be
082: * inserted, with no key (the system generates a key).
083: *
084: * The parser doesn't know anything about keys, so the columns
085: * representing the keys will be added after parsing (perhaps in
086: * the binding phase?).
087: *
088: */
089: ResultSetNode resultSet;
090:
091: /**
092: * Initializer for a DMLStatementNode
093: *
094: * @param resultSet A ResultSetNode for the result set of the
095: * DML statement
096: */
097:
098: public void init(Object resultSet) {
099: this .resultSet = (ResultSetNode) resultSet;
100: }
101:
102: /**
103: * Prints the sub-nodes of this object. See QueryTreeNode.java for
104: * how tree printing is supposed to work.
105: *
106: * @param depth The depth of this node in the tree
107: */
108:
109: public void printSubNodes(int depth) {
110: if (SanityManager.DEBUG) {
111: super .printSubNodes(depth);
112: if (resultSet != null) {
113: printLabel(depth, "resultSet: ");
114: resultSet.treePrint(depth + 1);
115: }
116: }
117: }
118:
119: /**
120: * Get the ResultSetNode from this DML Statement.
121: * (Useful for view resolution after parsing the view definition.)
122: *
123: * @return ResultSetNode The ResultSetNode from this DMLStatementNode.
124: */
125: public ResultSetNode getResultSetNode() {
126: return resultSet;
127: }
128:
129: /**
130: * Bind this DMLStatementNode. This means looking up tables and columns and
131: * getting their types, and figuring out the result types of all
132: * expressions, as well as doing view resolution, permissions checking,
133: * etc.
134: *
135: * @param dataDictionary The DataDictionary to use to look up
136: * columns, tables, etc.
137: *
138: * @return The bound query tree
139: *
140: * @exception StandardException Thrown on error
141: */
142:
143: public QueryTreeNode bind(DataDictionary dataDictionary)
144: throws StandardException {
145: // We just need select privilege on most columns and tables
146: getCompilerContext().pushCurrentPrivType(getPrivType());
147: try {
148: /*
149: ** Bind the tables before binding the expressions, so we can
150: ** use the results of table binding to look up columns.
151: */
152: bindTables(dataDictionary);
153:
154: /* Bind the expressions */
155: bindExpressions();
156: } finally {
157: getCompilerContext().popCurrentPrivType();
158: }
159:
160: return this ;
161: }
162:
163: /**
164: * Bind only the underlying ResultSets with tables. This is necessary for
165: * INSERT, where the binding order depends on the underlying ResultSets.
166: * This means looking up tables and columns and
167: * getting their types, and figuring out the result types of all
168: * expressions, as well as doing view resolution, permissions checking,
169: * etc.
170: *
171: * @param dataDictionary The DataDictionary to use to look up
172: * columns, tables, etc.
173: *
174: * @return The bound query tree
175: *
176: * @exception StandardException Thrown on error
177: */
178:
179: public QueryTreeNode bindResultSetsWithTables(
180: DataDictionary dataDictionary) throws StandardException {
181: /* Okay to bindly bind the tables, since ResultSets without tables
182: * know to handle the call.
183: */
184: bindTables(dataDictionary);
185:
186: /* Bind the expressions in the underlying ResultSets with tables */
187: bindExpressionsWithTables();
188:
189: return this ;
190: }
191:
192: /**
193: * Bind the tables in this DML statement.
194: *
195: * @param dataDictionary The data dictionary to use to look up the tables
196: *
197: * @exception StandardException Thrown on error
198: */
199:
200: protected void bindTables(DataDictionary dataDictionary)
201: throws StandardException {
202: /* Bind the tables in the resultSet
203: * (DMLStatementNode is above all ResultSetNodes, so table numbering
204: * will begin at 0.)
205: * In case of referential action on delete , the table numbers can be
206: * > 0 because the nodes are create for dependent tables also in the
207: * the same context.
208: */
209:
210: resultSet = resultSet.bindNonVTITables(dataDictionary,
211: (FromList) getNodeFactory().getNode(
212: C_NodeTypes.FROM_LIST,
213: getNodeFactory().doJoinOrderOptimization(),
214: getContextManager()));
215: resultSet = resultSet.bindVTITables((FromList) getNodeFactory()
216: .getNode(C_NodeTypes.FROM_LIST,
217: getNodeFactory().doJoinOrderOptimization(),
218: getContextManager()));
219: }
220:
221: /**
222: * Bind the expressions in this DML statement.
223: *
224: * @exception StandardException Thrown on error
225: */
226:
227: protected void bindExpressions() throws StandardException {
228: FromList fromList = (FromList) getNodeFactory().getNode(
229: C_NodeTypes.FROM_LIST,
230: getNodeFactory().doJoinOrderOptimization(),
231: getContextManager());
232:
233: /* Bind the expressions under the resultSet */
234: resultSet.bindExpressions(fromList);
235:
236: /* Verify that all underlying ResultSets reclaimed their FromList */
237: if (SanityManager.DEBUG)
238: SanityManager.ASSERT(fromList.size() == 0,
239: "fromList.size() is expected to be 0, not "
240: + fromList.size()
241: + " on return from RS.bindExpressions()");
242: }
243:
244: /**
245: * Bind the expressions in the underlying ResultSets with tables.
246: *
247: * @exception StandardException Thrown on error
248: */
249:
250: protected void bindExpressionsWithTables() throws StandardException {
251: FromList fromList = (FromList) getNodeFactory().getNode(
252: C_NodeTypes.FROM_LIST,
253: getNodeFactory().doJoinOrderOptimization(),
254: getContextManager());
255:
256: /* Bind the expressions under the resultSet */
257: resultSet.bindExpressionsWithTables(fromList);
258:
259: /* Verify that all underlying ResultSets reclaimed their FromList */
260: if (SanityManager.DEBUG)
261: SanityManager.ASSERT(fromList.size() == 0,
262: "fromList.size() is expected to be 0, not "
263: + fromList.size()
264: + " on return from RS.bindExpressions()");
265: }
266:
267: /**
268: * Returns the type of activation this class
269: * generates.
270: *
271: * @return either (NEED_ROW_ACTIVATION | NEED_PARAM_ACTIVATION) or
272: * (NEED_ROW_ACTIVATION) depending on params
273: *
274: */
275: int activationKind() {
276: Vector parameterList = getCompilerContext().getParameterList();
277: /*
278: ** We need rows for all types of DML activations. We need parameters
279: ** only for those that have parameters.
280: */
281: if (parameterList != null && parameterList.size() > 0) {
282: return StatementNode.NEED_PARAM_ACTIVATION;
283: } else {
284: return StatementNode.NEED_ROW_ACTIVATION;
285: }
286: }
287:
288: /**
289: * Optimize a DML statement (which is the only type of statement that
290: * should need optimizing, I think). This method over-rides the one
291: * in QueryTreeNode.
292: *
293: * This method takes a bound tree, and returns an optimized tree.
294: * It annotates the bound tree rather than creating an entirely
295: * new tree.
296: *
297: * Throws an exception if the tree is not bound, or if the binding
298: * is out of date.
299: *
300: * @return An optimized QueryTree
301: *
302: * @exception StandardException Thrown on error
303: */
304:
305: public QueryTreeNode optimize() throws StandardException {
306: resultSet = resultSet.preprocess(getCompilerContext()
307: .getNumTables(), null, (FromList) null);
308: resultSet = resultSet.optimize(getDataDictionary(), null, 1.0d);
309:
310: resultSet = resultSet.modifyAccessPaths();
311:
312: /* If this is a cursor, then we
313: * need to generate a new ResultSetNode to enable the scrolling
314: * on top of the tree before modifying the access paths.
315: */
316: if (this instanceof CursorNode) {
317: ResultColumnList siRCList;
318: ResultColumnList childRCList;
319: ResultSetNode siChild = resultSet;
320:
321: /* We get a shallow copy of the ResultColumnList and its
322: * ResultColumns. (Copy maintains ResultColumn.expression for now.)
323: */
324: siRCList = resultSet.getResultColumns();
325: childRCList = siRCList.copyListAndObjects();
326: resultSet.setResultColumns(childRCList);
327:
328: /* Replace ResultColumn.expression with new VirtualColumnNodes
329: * in the ScrollInsensitiveResultSetNode's ResultColumnList. (VirtualColumnNodes include
330: * pointers to source ResultSetNode, this, and source ResultColumn.)
331: */
332: siRCList.genVirtualColumnNodes(resultSet, childRCList);
333:
334: /* Finally, we create the new ScrollInsensitiveResultSetNode */
335: resultSet = (ResultSetNode) getNodeFactory().getNode(
336: C_NodeTypes.SCROLL_INSENSITIVE_RESULT_SET_NODE,
337: resultSet, siRCList, null, getContextManager());
338: // Propagate the referenced table map if it's already been created
339: if (siChild.getReferencedTableMap() != null) {
340: resultSet.setReferencedTableMap((JBitSet) siChild
341: .getReferencedTableMap().clone());
342: }
343: }
344:
345: return this ;
346: }
347:
348: /**
349: * Make a ResultDescription for use in a PreparedStatement.
350: *
351: * ResultDescriptions are visible to JDBC only for cursor statements.
352: * For other types of statements, they are only used internally to
353: * get descriptions of the base tables being affected. For example,
354: * for an INSERT statement, the ResultDescription describes the
355: * rows in the table being inserted into, which is useful when
356: * the values being inserted are of a different type or length
357: * than the columns in the base table.
358: *
359: * @return A ResultDescription for this DML statement
360: */
361:
362: public ResultDescription makeResultDescription() {
363: ExecutionContext ec = (ExecutionContext) getContextManager()
364: .getContext(ExecutionContext.CONTEXT_ID);
365: ResultColumnDescriptor[] colDescs = resultSet
366: .makeResultDescriptors(ec);
367: String statementType = statementToString();
368:
369: return ec.getExecutionFactory().getResultDescription(colDescs,
370: statementType);
371: }
372:
373: /**
374: * Generate the code to create the ParameterValueSet, if necessary,
375: * when constructing the activation. Also generate the code to call
376: * a method that will throw an exception if we try to execute without
377: * all the parameters being set.
378: *
379: * @param acb The ActivationClassBuilder for the class we're building
380: */
381:
382: void generateParameterValueSet(ActivationClassBuilder acb)
383: throws StandardException {
384: Vector parameterList = getCompilerContext().getParameterList();
385: int numberOfParameters = (parameterList == null) ? 0
386: : parameterList.size();
387:
388: if (numberOfParameters <= 0)
389: return;
390:
391: ParameterNode.generateParameterValueSet(acb,
392: numberOfParameters, parameterList);
393: }
394:
395: /**
396: * A read statement is atomic (DMLMod overrides us) if there
397: * are no work units, and no SELECT nodes, or if its SELECT nodes
398: * are all arguments to a function. This is admittedly
399: * a bit simplistic, what if someone has: <pre>
400: * VALUES myfunc(SELECT max(c.commitFunc()) FROM T)
401: * </pre>
402: * but we aren't going too far out of our way to
403: * catch every possible wierd case. We basically
404: * want to be permissive w/o allowing someone to partially
405: * commit a write.
406: *
407: * @return true if the statement is atomic
408: *
409: * @exception StandardException on error
410: */
411: public boolean isAtomic() throws StandardException {
412: /*
413: ** If we have a FromBaseTable then we have
414: ** a SELECT, so we want to consider ourselves
415: ** atomic. Don't drill below StaticMethodCallNodes
416: ** to allow a SELECT in an argument to a method
417: ** call that can be atomic.
418: */
419: HasNodeVisitor visitor = new HasNodeVisitor(
420: FromBaseTable.class, StaticMethodCallNode.class);
421:
422: this .accept(visitor);
423: if (visitor.hasNode()) {
424: return true;
425: }
426:
427: return false;
428: }
429:
430: /**
431: * Accept a visitor, and call v.visit()
432: * on child nodes as necessary.
433: *
434: * @param v the visitor
435: *
436: * @exception StandardException on error
437: */
438: public Visitable accept(Visitor v) throws StandardException {
439: if (v.skipChildren(this )) {
440: return v.visit(this );
441: }
442:
443: if (resultSet != null && !v.stopTraversal()) {
444: resultSet = (ResultSetNode) resultSet.accept(v);
445: }
446:
447: return this ;
448: }
449:
450: /**
451: * Return default privilege needed for this node. Other DML nodes can override
452: * this method to set their own default privilege.
453: *
454: * @return true if the statement is atomic
455: */
456: int getPrivType() {
457: return Authorizer.SELECT_PRIV;
458: }
459: }
|