001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.compile.RowResultSetNode
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.sql.compile.CompilerContext;
027: import org.apache.derby.iapi.sql.compile.CostEstimate;
028: import org.apache.derby.iapi.sql.compile.Optimizer;
029: import org.apache.derby.iapi.sql.compile.OptimizableList;
030: import org.apache.derby.iapi.sql.compile.Optimizable;
031: import org.apache.derby.iapi.sql.compile.OptimizablePredicate;
032: import org.apache.derby.iapi.sql.compile.OptimizablePredicateList;
033: import org.apache.derby.iapi.sql.compile.RequiredRowOrdering;
034: import org.apache.derby.iapi.sql.compile.RowOrdering;
035: import org.apache.derby.iapi.sql.compile.C_NodeTypes;
036:
037: import org.apache.derby.iapi.sql.dictionary.DataDictionary;
038: import org.apache.derby.iapi.sql.dictionary.ConglomerateDescriptor;
039: import org.apache.derby.iapi.sql.dictionary.TableDescriptor;
040:
041: import org.apache.derby.iapi.sql.Activation;
042: import org.apache.derby.iapi.sql.ResultSet;
043: import org.apache.derby.iapi.sql.Row;
044: import org.apache.derby.iapi.error.StandardException;
045:
046: import org.apache.derby.impl.sql.compile.ActivationClassBuilder;
047: import org.apache.derby.iapi.services.compiler.MethodBuilder;
048:
049: import org.apache.derby.iapi.store.access.Qualifier;
050:
051: import org.apache.derby.iapi.services.sanity.SanityManager;
052:
053: import org.apache.derby.iapi.util.JBitSet;
054: import org.apache.derby.iapi.reference.SQLState;
055: import org.apache.derby.iapi.reference.ClassName;
056: import org.apache.derby.iapi.services.classfile.VMOpcode;
057:
058: import java.util.Enumeration;
059: import java.util.Properties;
060: import java.util.Vector;
061:
062: /**
063: * A RowResultSetNode represents the result set for a VALUES clause.
064: *
065: * @author Jerry Brenner
066: */
067:
068: public class RowResultSetNode extends FromTable {
069: SubqueryList subquerys;
070: Vector aggregateVector;
071: OrderByList orderByList;
072:
073: /**
074: * Initializer for a RowResultSetNode.
075: *
076: * @param valuesClause The result column list for the VALUES clause.
077: * @param tableProperties Properties list associated with the table
078: */
079: public void init(Object valuesClause, Object tableProperties) {
080: super .init(null, tableProperties);
081: resultColumns = (ResultColumnList) valuesClause;
082: if (resultColumns != null)
083: resultColumns.markInitialSize();
084: }
085:
086: /**
087: * Convert this object to a String. See comments in QueryTreeNode.java
088: * for how this should be done for tree printing.
089: *
090: * @return This object as a String
091: */
092:
093: public String toString() {
094: if (SanityManager.DEBUG) {
095: return "orderByList: "
096: + (orderByList != null ? orderByList.toString()
097: : "null") + "\n" + super .toString();
098: } else {
099: return "";
100: }
101: }
102:
103: public String statementToString() {
104: return "VALUES";
105: }
106:
107: /**
108: * Prints the sub-nodes of this object. See QueryTreeNode.java for
109: * how tree printing is supposed to work.
110: *
111: * @param depth The depth of this node in the tree
112: */
113:
114: public void printSubNodes(int depth) {
115: if (SanityManager.DEBUG) {
116: super .printSubNodes(depth);
117:
118: if (subquerys != null) {
119: printLabel(depth, "subquerys: ");
120: subquerys.treePrint(depth + 1);
121: }
122: }
123: }
124:
125: /*
126: * Optimizable interface
127: */
128:
129: /**
130: * @see Optimizable#estimateCost
131: *
132: * @exception StandardException Thrown on error
133: */
134: public CostEstimate estimateCost(OptimizablePredicateList predList,
135: ConglomerateDescriptor cd, CostEstimate outerCost,
136: Optimizer optimizer, RowOrdering rowOrdering)
137: throws StandardException {
138: /*
139: ** Assume for now that the cost of a VALUES clause is zero, with one row
140: ** fetched. Is this true, and if not, does it make a difference?
141: ** There's nothing to optimize in this case.
142: */
143: if (costEstimate == null) {
144: costEstimate = optimizer.newCostEstimate();
145: }
146:
147: costEstimate.setCost(0.0d, 1.0d, 1.0d);
148:
149: /* A single row is always ordered */
150: rowOrdering.optimizableAlwaysOrdered(this );
151:
152: return costEstimate;
153: }
154:
155: /**
156: * Bind the non VTI tables in this ResultSetNode. This includes getting their
157: * descriptors from the data dictionary and numbering them.
158: *
159: * @param dataDictionary The DataDictionary to use for binding
160: * @param fromListParam FromList to use/append to.
161: *
162: * @return ResultSetNode
163: *
164: * @exception StandardException Thrown on error
165: */
166:
167: public ResultSetNode bindNonVTITables(
168: DataDictionary dataDictionary, FromList fromListParam)
169: throws StandardException {
170: /* Assign the tableNumber */
171: if (tableNumber == -1) // allow re-bind, in which case use old number
172: tableNumber = getCompilerContext().getNextTableNumber();
173:
174: /* VALUES clause has no tables, so nothing to do */
175: return this ;
176: }
177:
178: /**
179: * Bind the expressions in this RowResultSetNode. This means binding the
180: * sub-expressions, as well as figuring out what the return type is
181: * for each expression.
182: *
183: * @exception StandardException Thrown on error
184: */
185:
186: public void bindExpressions(FromList fromListParam)
187: throws StandardException {
188: int nestingLevel;
189:
190: subquerys = (SubqueryList) getNodeFactory().getNode(
191: C_NodeTypes.SUBQUERY_LIST, getContextManager());
192:
193: aggregateVector = new Vector();
194:
195: /* Verify that there are no DEFAULTs in the RCL.
196: * DEFAULT is only valid for an insert, and it has
197: * already been coverted into the tree by the time we get here.
198: * The grammar allows:
199: * VALUES DEFAULT;
200: * so we need to check for that here and throw an exception if found.
201: */
202: resultColumns.checkForInvalidDefaults();
203:
204: /* Believe it or not, a values clause can contain correlated column references
205: * and subqueries. In order to get correlated column resolution working
206: * correctly, we need to set our nesting level to be 1 deeper than the current
207: * level and push ourselves into the FROM list.
208: */
209:
210: /* Set the nesting level in this node */
211: if (fromListParam.size() == 0) {
212: nestingLevel = 0;
213: } else {
214: nestingLevel = ((FromTable) fromListParam.elementAt(0))
215: .getLevel() + 1;
216: }
217: setLevel(nestingLevel);
218: fromListParam.insertElementAt(this , 0);
219: resultColumns.bindExpressions(fromListParam, subquerys,
220: aggregateVector);
221: // Pop ourselves back out of the FROM list
222: fromListParam.removeElementAt(0);
223:
224: if (aggregateVector.size() > 0) {
225: throw StandardException
226: .newException(SQLState.LANG_NO_AGGREGATES_IN_WHERE_CLAUSE);
227: }
228: }
229:
230: /**
231: * Bind the expressions in this ResultSetNode if it has tables. This means binding the
232: * sub-expressions, as well as figuring out what the return type is for
233: * each expression.
234: *
235: * @param fromListParam FromList to use/append to.
236: *
237: * @exception StandardException Thrown on error
238: */
239: public void bindExpressionsWithTables(FromList fromListParam)
240: throws StandardException {
241: /* We don't have any tables, so just return */
242: return;
243: }
244:
245: /**
246: * Bind the expressions in the target list. This means binding the
247: * sub-expressions, as well as figuring out what the return type is
248: * for each expression. This is useful for EXISTS subqueries, where we
249: * need to validate the target list before blowing it away and replacing
250: * it with a SELECT true.
251: *
252: * @exception StandardException Thrown on error
253: */
254:
255: public void bindTargetExpressions(FromList fromListParam)
256: throws StandardException {
257: bindExpressions(fromListParam);
258: }
259:
260: /**
261: * Bind any untyped null nodes to the types in the given ResultColumnList.
262: *
263: * @param bindingRCL The ResultColumnList with the types to bind to.
264: *
265: * @exception StandardException Thrown on error
266: */
267: public void bindUntypedNullsToResultColumns(
268: ResultColumnList bindingRCL) throws StandardException {
269: /*
270: ** If bindingRCL is null, then we are
271: ** under a cursor node that is inferring
272: ** its RCL from us. It passes null to
273: ** get union to use both sides of the union
274: ** for the check. Anyway, since there is
275: ** nothing under us but an RCL, just pass
276: ** in our RCL.
277: */
278: if (bindingRCL == null)
279: bindingRCL = resultColumns;
280:
281: resultColumns.bindUntypedNullsToResultColumns(bindingRCL);
282: }
283:
284: /**
285: * Try to find a ResultColumn in the table represented by this FromTable
286: * that matches the name in the given ColumnReference.
287: *
288: * @param columnReference The columnReference whose name we're looking
289: * for in the given table.
290: *
291: * @return A ResultColumn whose expression is the ColumnNode
292: * that matches the ColumnReference.
293: * Returns null if there is no match.
294: *
295: * @exception StandardException Thrown on error
296: */
297:
298: public ResultColumn getMatchingColumn(
299: ColumnReference columnReference) throws StandardException {
300: return null;
301: }
302:
303: /**
304: * Get the exposed name for this table, which is the name that can
305: * be used to refer to it in the rest of the query.
306: *
307: * @return The exposed name of this table.
308: *
309: * @exception StandardException Thrown on error
310: */
311: public String getExposedName() throws StandardException {
312: return null;
313: }
314:
315: /**
316: * Verify that a SELECT * is valid for this type of subquery.
317: *
318: * @param outerFromList The FromList from the outer query block(s)
319: * @param subqueryType The subquery type
320: *
321: * @exception StandardException Thrown on error
322: */
323: public void verifySelectStarSubquery(FromList outerFromList,
324: int subqueryType) throws StandardException {
325: return;
326: }
327:
328: /**
329: * Push the order by list down from the cursor node
330: * into its child result set so that the optimizer
331: * has all of the information that it needs to
332: * consider sort avoidance.
333: *
334: * @param orderByList The order by list
335: */
336: void pushOrderByList(OrderByList orderByList) {
337: this .orderByList = orderByList;
338: }
339:
340: /**
341: * Put a ProjectRestrictNode on top of each FromTable in the FromList.
342: * ColumnReferences must continue to point to the same ResultColumn, so
343: * that ResultColumn must percolate up to the new PRN. However,
344: * that ResultColumn will point to a new expression, a VirtualColumnNode,
345: * which points to the FromTable and the ResultColumn that is the source for
346: * the ColumnReference.
347: * (The new PRN will have the original of the ResultColumnList and
348: * the ResultColumns from that list. The FromTable will get shallow copies
349: * of the ResultColumnList and its ResultColumns. ResultColumn.expression
350: * will remain at the FromTable, with the PRN getting a new
351: * VirtualColumnNode for each ResultColumn.expression.)
352: * We then project out the non-referenced columns. If there are no referenced
353: * columns, then the PRN's ResultColumnList will consist of a single ResultColumn
354: * whose expression is 1.
355: *
356: * @param numTables Number of tables in the DML Statement
357: * @param gbl The group by list, if any
358: * @param fromList The from list, if any
359: *
360: * @return The generated ProjectRestrictNode atop the original FromTable.
361: *
362: * @exception StandardException Thrown on error
363: */
364:
365: public ResultSetNode preprocess(int numTables, GroupByList gbl,
366: FromList fromList) throws StandardException {
367:
368: if (subquerys.size() > 0) {
369: subquerys.preprocess(numTables, (FromList) getNodeFactory()
370: .getNode(C_NodeTypes.FROM_LIST,
371: getNodeFactory().doJoinOrderOptimization(),
372: getContextManager()),
373: (SubqueryList) getNodeFactory().getNode(
374: C_NodeTypes.SUBQUERY_LIST,
375: getContextManager()),
376: (PredicateList) getNodeFactory().getNode(
377: C_NodeTypes.PREDICATE_LIST,
378: getContextManager()));
379: }
380:
381: /* Allocate a dummy referenced table map */
382: referencedTableMap = new JBitSet(numTables);
383: referencedTableMap.set(tableNumber);
384: return this ;
385: }
386:
387: /**
388: * Ensure that the top of the RSN tree has a PredicateList.
389: *
390: * @param numTables The number of tables in the query.
391: * @return ResultSetNode A RSN tree with a node which has a PredicateList on top.
392: *
393: * @exception StandardException Thrown on error
394: */
395: public ResultSetNode ensurePredicateList(int numTables)
396: throws StandardException {
397: return genProjectRestrict(numTables);
398: }
399:
400: /**
401: * Add a new predicate to the list. This is useful when doing subquery
402: * transformations, when we build a new predicate with the left side of
403: * the subquery operator and the subquery's result column.
404: *
405: * @param predicate The predicate to add
406: *
407: * @return ResultSetNode The new top of the tree.
408: *
409: * @exception StandardException Thrown on error
410: */
411: public ResultSetNode addNewPredicate(Predicate predicate)
412: throws StandardException {
413: PredicateList predList;
414: ResultColumnList prRCList;
415: ResultSetNode newPRN;
416:
417: /* We are the body of a quantified predicate subquery. We
418: * need to generate (and return) a PRN above us so that there will be
419: * a place to attach the new predicate.
420: */
421:
422: /* We get a shallow copy of the ResultColumnList and its
423: * ResultColumns. (Copy maintains ResultColumn.expression for now.)
424: */
425: prRCList = resultColumns;
426: resultColumns = resultColumns.copyListAndObjects();
427:
428: /* Replace ResultColumn.expression with new VirtualColumnNodes
429: * in the ProjectRestrictNode's ResultColumnList. (VirtualColumnNodes include
430: * pointers to source ResultSetNode, this, and source ResultColumn.)
431: */
432: prRCList.genVirtualColumnNodes(this , resultColumns);
433:
434: /* Put the new predicate in a list */
435: predList = (PredicateList) getNodeFactory().getNode(
436: C_NodeTypes.PREDICATE_LIST, getContextManager());
437: predList.addPredicate(predicate);
438:
439: /* Finally, we create the new ProjectRestrictNode */
440: return (ResultSetNode) getNodeFactory().getNode(
441: C_NodeTypes.PROJECT_RESTRICT_NODE, this , prRCList,
442: null, /* Restriction */
443: predList, /* Restriction as PredicateList */
444: null, /* Project subquery list */
445: null, /* Restrict subquery list */
446: tableProperties, getContextManager());
447: }
448:
449: /**
450: * Evaluate whether or not the subquery in a FromSubquery is flattenable.
451: * Currently, a FSqry is flattenable if all of the following are true:
452: * o Subquery is a SelectNode or a RowResultSetNode (not a UnionNode)
453: * o It contains no top level subqueries. (RESOLVE - we can relax this)
454: * o It does not contain a group by or having clause
455: * o It does not contain aggregates.
456: * o There is at least one result set in the from list that is
457: * not a RowResultSetNode (the reason is to avoid having
458: * an outer SelectNode with an empty FromList.
459: *
460: * @param fromList The outer from list
461: *
462: * @return boolean Whether or not the FromSubquery is flattenable.
463: */
464: public boolean flattenableInFromSubquery(FromList fromList) {
465: if ((subquerys != null) && (subquerys.size() > 0)) {
466: return false;
467: }
468:
469: if ((aggregateVector != null) && (aggregateVector.size() > 0)) {
470: return false;
471: }
472:
473: /*
474: ** Don't flatten if select list contains something
475: ** that isn't clonable
476: */
477: if (!resultColumns.isCloneable()) {
478: return false;
479: }
480:
481: boolean nonRowResultSetFound = false;
482: int flSize = fromList.size();
483: for (int index = 0; index < flSize; index++) {
484: FromTable ft = (FromTable) fromList.elementAt(index);
485:
486: if (ft instanceof FromSubquery) {
487: ResultSetNode subq = ((FromSubquery) ft).getSubquery();
488: if (!(subq instanceof RowResultSetNode)) {
489: nonRowResultSetFound = true;
490: break;
491: }
492: } else {
493: nonRowResultSetFound = true;
494: break;
495: }
496: }
497:
498: return nonRowResultSetFound;
499: }
500:
501: /**
502: * Optimize this SelectNode. This means choosing the best access path
503: * for each table, among other things.
504: *
505: * @param dataDictionary The DataDictionary to use for optimization
506: * @param predicateList The predicate list to optimize against
507: * @param outerRows The number of outer joining rows
508: *
509: * @return ResultSetNode The top of the optimized tree
510: *
511: * @exception StandardException Thrown on error
512: */
513: public ResultSetNode optimize(DataDictionary dataDictionary,
514: PredicateList predicateList, double outerRows)
515: throws StandardException {
516: /*
517: ** Get an optimizer. The only reason we need one is to get a
518: ** CostEstimate object, so we can represent the cost of this node.
519: ** This seems like overkill, but it's just an object allocation...
520: */
521: Optimizer optimizer = getOptimizer((FromList) getNodeFactory()
522: .getNode(C_NodeTypes.FROM_LIST,
523: getNodeFactory().doJoinOrderOptimization(),
524: getContextManager()), predicateList,
525: dataDictionary, (RequiredRowOrdering) null);
526: costEstimate = optimizer.newCostEstimate();
527:
528: // RESOLVE: THE COST SHOULD TAKE SUBQUERIES INTO ACCOUNT
529: costEstimate.setCost(0.0d, outerRows, outerRows);
530:
531: subquerys.optimize(dataDictionary, outerRows);
532: return this ;
533: }
534:
535: /**
536: * @see Optimizable#modifyAccessPath
537: *
538: * @exception StandardException Thrown on error
539: */
540: public Optimizable modifyAccessPath(JBitSet outerTables)
541: throws StandardException {
542: /* For most types of Optimizable, do nothing */
543: return (Optimizable) modifyAccessPaths();
544: }
545:
546: /**
547: * @see ResultSetNode#modifyAccessPaths
548: *
549: * @exception StandardException Thrown on error
550: */
551: public ResultSetNode modifyAccessPaths() throws StandardException {
552: ResultSetNode treeTop = this ;
553:
554: subquerys.modifyAccessPaths();
555:
556: /* Generate the OrderByNode if a sort is still required for
557: * the order by.
558: */
559: if (orderByList != null) {
560: treeTop = (ResultSetNode) getNodeFactory().getNode(
561: C_NodeTypes.ORDER_BY_NODE, treeTop, orderByList,
562: tableProperties, getContextManager());
563: }
564: return treeTop;
565: }
566:
567: /**
568: * Return whether or not this ResultSet tree is guaranteed to return
569: * at most 1 row based on heuristics. (A RowResultSetNode and a
570: * SELECT with a non-grouped aggregate will return at most 1 row.)
571: *
572: * @return Whether or not this ResultSet tree is guaranteed to return
573: * at most 1 row based on heuristics.
574: */
575: boolean returnsAtMostOneRow() {
576: return true;
577: }
578:
579: /**
580: * The generated ResultSet will be:
581: *
582: * RowResultSet -- for the VALUES clause
583: *
584: *
585: * @exception StandardException Thrown on error
586: */
587: public void generate(ActivationClassBuilder acb, MethodBuilder mb)
588: throws StandardException {
589: if (SanityManager.DEBUG)
590: SanityManager.ASSERT(resultColumns != null,
591: "Tree structure bad");
592:
593: // Get our final cost estimate.
594: costEstimate = getFinalCostEstimate();
595:
596: /*
597: ** Check and see if everything below us is a constant or not.
598: ** If so, we'll let execution know that it can do some caching.
599: ** Before we do the check, we are going to temporarily set
600: */
601: boolean canCache = canWeCacheResults();
602:
603: /* Get the next ResultSet #, so that we can number this ResultSetNode, its
604: * ResultColumnList and ResultSet.
605: */
606: assignResultSetNumber();
607:
608: // we are dealing with
609: // VALUES(value1, value2, value3)
610: // so we generate a RowResultSet to return the values listed.
611:
612: // we can reduce the tree to one RowResultSet
613: // since there is nothing but the resultColumns
614:
615: // RowResultSet takes the row-generating function
616: // so we generate one and get back the expression
617: // pointing to it.
618: //
619: // generate the expression to return, which is:
620: // ResultSetFactory.getRowResultSet(this, planX.exprN)
621: // [planX is the name of the class being generated,
622: // exprN is the name of the function being generated.]
623:
624: acb.pushGetResultSetFactoryExpression(mb);
625:
626: acb.pushThisAsActivation(mb);
627: resultColumns.generate(acb, mb);
628: mb.push(canCache);
629: mb.push(resultSetNumber);
630: mb.push(costEstimate.rowCount());
631: mb.push(costEstimate.getEstimatedCost());
632: mb.callMethod(VMOpcode.INVOKEINTERFACE, (String) null,
633: "getRowResultSet", ClassName.NoPutResultSet, 6);
634: }
635:
636: /**
637: * Replace any DEFAULTs with the associated tree for the default.
638: *
639: * @param ttd The TableDescriptor for the target table.
640: * @param tcl The RCL for the target table.
641: *
642: * @exception StandardException Thrown on error
643: */
644: void replaceDefaults(TableDescriptor ttd, ResultColumnList tcl)
645: throws StandardException {
646: resultColumns.replaceDefaults(ttd, tcl);
647: }
648:
649: /**
650: * Optimize any subqueries that haven't been optimized any where
651: * else. This is useful for a RowResultSetNode as a derived table
652: * because it doesn't get optimized otherwise.
653: *
654: * @exception StandardException Thrown on error
655: */
656: void optimizeSubqueries(DataDictionary dd, double rowCount)
657: throws StandardException {
658: subquerys.optimize(dd, rowCount);
659: }
660:
661: /**
662: * Notify the underlying result set tree that the result is
663: * ordering dependent. (For example, no bulk fetch on an index
664: * if under an IndexRowToBaseRow.)
665: */
666: void markOrderingDependent() {
667: }
668:
669: /*
670: ** Check and see if everything below us is a constant or not.
671: ** If so, we'll let execution know that it can do some caching.
672: ** Before we do the check, we are going to temporarily set
673: ** ParameterNodes to CONSTANT. We do this because we know
674: ** that we can cache a row with a parameter value and get
675: ** the param column reset by the user setting a param, so
676: ** we can skip over parameter nodes. We are doing this
677: ** extra work to optimize inserts of the form:
678: **
679: ** prepare: insert into mytab values (?,?);
680: ** setParam
681: ** execute()
682: ** setParam
683: ** execute()
684: */
685: private boolean canWeCacheResults() throws StandardException {
686:
687: /*
688: ** Check the tree below us
689: */
690: HasVariantValueNodeVisitor visitor = new HasVariantValueNodeVisitor(
691: Qualifier.QUERY_INVARIANT, true);
692:
693: super .accept(visitor);
694: boolean canCache = !visitor.hasVariant();
695:
696: return canCache;
697: }
698: }
|