KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > derby > impl > sql > compile > RowResultSetNode


1 /*
2
3    Derby - Class org.apache.derby.impl.sql.compile.RowResultSetNode
4
5    Licensed to the Apache Software Foundation (ASF) under one or more
6    contributor license agreements. See the NOTICE file distributed with
7    this work for additional information regarding copyright ownership.
8    The ASF licenses this file to you under the Apache License, Version 2.0
9    (the "License"); you may not use this file except in compliance with
10    the License. You may obtain a copy of the License at
11
12       http://www.apache.org/licenses/LICENSE-2.0
13
14    Unless required by applicable law or agreed to in writing, software
15    distributed under the License is distributed on an "AS IS" BASIS,
16    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17    See the License for the specific language governing permissions and
18    limitations under the License.
19
20  */

21
22 package org.apache.derby.impl.sql.compile;
23
24 import org.apache.derby.iapi.services.context.ContextManager;
25
26 import org.apache.derby.iapi.sql.compile.CompilerContext;
27 import org.apache.derby.iapi.sql.compile.CostEstimate;
28 import org.apache.derby.iapi.sql.compile.Optimizer;
29 import org.apache.derby.iapi.sql.compile.OptimizableList;
30 import org.apache.derby.iapi.sql.compile.Optimizable;
31 import org.apache.derby.iapi.sql.compile.OptimizablePredicate;
32 import org.apache.derby.iapi.sql.compile.OptimizablePredicateList;
33 import org.apache.derby.iapi.sql.compile.RequiredRowOrdering;
34 import org.apache.derby.iapi.sql.compile.RowOrdering;
35 import org.apache.derby.iapi.sql.compile.C_NodeTypes;
36
37 import org.apache.derby.iapi.sql.dictionary.DataDictionary;
38 import org.apache.derby.iapi.sql.dictionary.ConglomerateDescriptor;
39 import org.apache.derby.iapi.sql.dictionary.TableDescriptor;
40
41 import org.apache.derby.iapi.sql.Activation;
42 import org.apache.derby.iapi.sql.ResultSet;
43 import org.apache.derby.iapi.sql.Row;
44 import org.apache.derby.iapi.error.StandardException;
45
46 import org.apache.derby.impl.sql.compile.ActivationClassBuilder;
47 import org.apache.derby.iapi.services.compiler.MethodBuilder;
48
49 import org.apache.derby.iapi.store.access.Qualifier;
50
51 import org.apache.derby.iapi.services.sanity.SanityManager;
52
53 import org.apache.derby.iapi.util.JBitSet;
54 import org.apache.derby.iapi.reference.SQLState;
55 import org.apache.derby.iapi.reference.ClassName;
56 import org.apache.derby.iapi.services.classfile.VMOpcode;
57
58 import java.util.Enumeration JavaDoc;
59 import java.util.Properties JavaDoc;
60 import java.util.Vector JavaDoc;
61
62 /**
63  * A RowResultSetNode represents the result set for a VALUES clause.
64  *
65  * @author Jerry Brenner
66  */

67
68 public class RowResultSetNode extends FromTable
69 {
70     SubqueryList subquerys;
71     Vector JavaDoc aggregateVector;
72     OrderByList orderByList;
73
74     /**
75      * Initializer for a RowResultSetNode.
76      *
77      * @param valuesClause The result column list for the VALUES clause.
78      * @param tableProperties Properties list associated with the table
79      */

80     public void init(Object JavaDoc valuesClause, Object JavaDoc tableProperties)
81     {
82         super.init(null, tableProperties);
83         resultColumns = (ResultColumnList) valuesClause;
84         if (resultColumns != null)
85             resultColumns.markInitialSize();
86     }
87
88     /**
89      * Convert this object to a String. See comments in QueryTreeNode.java
90      * for how this should be done for tree printing.
91      *
92      * @return This object as a String
93      */

94
95     public String JavaDoc toString()
96     {
97         if (SanityManager.DEBUG)
98         {
99             return "orderByList: " +
100                 (orderByList != null ? orderByList.toString() : "null") + "\n" +
101                 super.toString();
102         }
103         else
104         {
105             return "";
106         }
107     }
108
109     public String JavaDoc statementToString()
110     {
111         return "VALUES";
112     }
113
114     /**
115      * Prints the sub-nodes of this object. See QueryTreeNode.java for
116      * how tree printing is supposed to work.
117      *
118      * @param depth The depth of this node in the tree
119      */

120
121     public void printSubNodes(int depth)
122     {
123         if (SanityManager.DEBUG)
124         {
125             super.printSubNodes(depth);
126
127             if (subquerys != null)
128             {
129                 printLabel(depth, "subquerys: ");
130                 subquerys.treePrint(depth + 1);
131             }
132         }
133     }
134
135
136     /*
137      * Optimizable interface
138      */

139
140     /**
141      * @see Optimizable#estimateCost
142      *
143      * @exception StandardException Thrown on error
144      */

145     public CostEstimate estimateCost(OptimizablePredicateList predList,
146                                         ConglomerateDescriptor cd,
147                                         CostEstimate outerCost,
148                                         Optimizer optimizer,
149                                         RowOrdering rowOrdering)
150                                 throws StandardException
151     {
152         /*
153         ** Assume for now that the cost of a VALUES clause is zero, with one row
154         ** fetched. Is this true, and if not, does it make a difference?
155         ** There's nothing to optimize in this case.
156         */

157         if (costEstimate == null)
158         {
159             costEstimate = optimizer.newCostEstimate();
160         }
161
162         costEstimate.setCost(0.0d, 1.0d, 1.0d);
163
164         /* A single row is always ordered */
165         rowOrdering.optimizableAlwaysOrdered(this);
166
167         return costEstimate;
168     }
169
170     /**
171      * Bind the non VTI tables in this ResultSetNode. This includes getting their
172      * descriptors from the data dictionary and numbering them.
173      *
174      * @param dataDictionary The DataDictionary to use for binding
175      * @param fromListParam FromList to use/append to.
176      *
177      * @return ResultSetNode
178      *
179      * @exception StandardException Thrown on error
180      */

181
182     public ResultSetNode bindNonVTITables(DataDictionary dataDictionary,
183                             FromList fromListParam)
184                     throws StandardException
185     {
186         /* Assign the tableNumber */
187         if (tableNumber == -1) // allow re-bind, in which case use old number
188
tableNumber = getCompilerContext().getNextTableNumber();
189
190         /* VALUES clause has no tables, so nothing to do */
191         return this;
192     }
193
194     /**
195      * Bind the expressions in this RowResultSetNode. This means binding the
196      * sub-expressions, as well as figuring out what the return type is
197      * for each expression.
198      *
199      * @exception StandardException Thrown on error
200      */

201
202     public void bindExpressions(FromList fromListParam)
203                     throws StandardException
204     {
205         int nestingLevel;
206
207         subquerys = (SubqueryList) getNodeFactory().getNode(
208                                         C_NodeTypes.SUBQUERY_LIST,
209                                         getContextManager());
210
211         aggregateVector = new Vector JavaDoc();
212
213         /* Verify that there are no DEFAULTs in the RCL.
214          * DEFAULT is only valid for an insert, and it has
215          * already been coverted into the tree by the time we get here.
216          * The grammar allows:
217          * VALUES DEFAULT;
218          * so we need to check for that here and throw an exception if found.
219          */

220         resultColumns.checkForInvalidDefaults();
221
222         /* Believe it or not, a values clause can contain correlated column references
223          * and subqueries. In order to get correlated column resolution working
224          * correctly, we need to set our nesting level to be 1 deeper than the current
225          * level and push ourselves into the FROM list.
226          */

227
228         /* Set the nesting level in this node */
229         if (fromListParam.size() == 0)
230         {
231             nestingLevel = 0;
232         }
233         else
234         {
235             nestingLevel = ((FromTable) fromListParam.elementAt(0)).getLevel() + 1;
236         }
237         setLevel(nestingLevel);
238         fromListParam.insertElementAt(this, 0);
239         resultColumns.bindExpressions(fromListParam, subquerys,
240                                       aggregateVector);
241         // Pop ourselves back out of the FROM list
242
fromListParam.removeElementAt(0);
243
244         if (aggregateVector.size() > 0)
245         {
246             throw StandardException.newException(SQLState.LANG_NO_AGGREGATES_IN_WHERE_CLAUSE);
247         }
248     }
249
250     /**
251      * Bind the expressions in this ResultSetNode if it has tables. This means binding the
252      * sub-expressions, as well as figuring out what the return type is for
253      * each expression.
254      *
255      * @param fromListParam FromList to use/append to.
256      *
257      * @exception StandardException Thrown on error
258      */

259     public void bindExpressionsWithTables(FromList fromListParam)
260                     throws StandardException
261     {
262         /* We don't have any tables, so just return */
263         return;
264     }
265
266     /**
267      * Bind the expressions in the target list. This means binding the
268      * sub-expressions, as well as figuring out what the return type is
269      * for each expression. This is useful for EXISTS subqueries, where we
270      * need to validate the target list before blowing it away and replacing
271      * it with a SELECT true.
272      *
273      * @exception StandardException Thrown on error
274      */

275
276     public void bindTargetExpressions(FromList fromListParam)
277                     throws StandardException
278     {
279         bindExpressions(fromListParam);
280     }
281
282     /**
283      * Bind any untyped null nodes to the types in the given ResultColumnList.
284      *
285      * @param bindingRCL The ResultColumnList with the types to bind to.
286      *
287      * @exception StandardException Thrown on error
288      */

289     public void bindUntypedNullsToResultColumns(ResultColumnList bindingRCL)
290                 throws StandardException
291     {
292         /*
293         ** If bindingRCL is null, then we are
294         ** under a cursor node that is inferring
295         ** its RCL from us. It passes null to
296         ** get union to use both sides of the union
297         ** for the check. Anyway, since there is
298         ** nothing under us but an RCL, just pass
299         ** in our RCL.
300         */

301         if (bindingRCL == null)
302             bindingRCL = resultColumns;
303
304         resultColumns.bindUntypedNullsToResultColumns(bindingRCL);
305     }
306
307     /**
308      * Try to find a ResultColumn in the table represented by this FromTable
309      * that matches the name in the given ColumnReference.
310      *
311      * @param columnReference The columnReference whose name we're looking
312      * for in the given table.
313      *
314      * @return A ResultColumn whose expression is the ColumnNode
315      * that matches the ColumnReference.
316      * Returns null if there is no match.
317      *
318      * @exception StandardException Thrown on error
319      */

320
321     public ResultColumn getMatchingColumn(
322                         ColumnReference columnReference)
323                         throws StandardException
324     {
325         return null;
326     }
327
328     /**
329      * Get the exposed name for this table, which is the name that can
330      * be used to refer to it in the rest of the query.
331      *
332      * @return The exposed name of this table.
333      *
334      * @exception StandardException Thrown on error
335      */

336     public String JavaDoc getExposedName() throws StandardException
337     {
338         return null;
339     }
340
341     /**
342      * Verify that a SELECT * is valid for this type of subquery.
343      *
344      * @param outerFromList The FromList from the outer query block(s)
345      * @param subqueryType The subquery type
346      *
347      * @exception StandardException Thrown on error
348      */

349     public void verifySelectStarSubquery(FromList outerFromList, int subqueryType)
350                     throws StandardException
351     {
352         return;
353     }
354
355     /**
356      * Push the order by list down from the cursor node
357      * into its child result set so that the optimizer
358      * has all of the information that it needs to
359      * consider sort avoidance.
360      *
361      * @param orderByList The order by list
362      */

363     void pushOrderByList(OrderByList orderByList)
364     {
365         this.orderByList = orderByList;
366     }
367
368     /**
369      * Put a ProjectRestrictNode on top of each FromTable in the FromList.
370      * ColumnReferences must continue to point to the same ResultColumn, so
371      * that ResultColumn must percolate up to the new PRN. However,
372      * that ResultColumn will point to a new expression, a VirtualColumnNode,
373      * which points to the FromTable and the ResultColumn that is the source for
374      * the ColumnReference.
375      * (The new PRN will have the original of the ResultColumnList and
376      * the ResultColumns from that list. The FromTable will get shallow copies
377      * of the ResultColumnList and its ResultColumns. ResultColumn.expression
378      * will remain at the FromTable, with the PRN getting a new
379      * VirtualColumnNode for each ResultColumn.expression.)
380      * We then project out the non-referenced columns. If there are no referenced
381      * columns, then the PRN's ResultColumnList will consist of a single ResultColumn
382      * whose expression is 1.
383      *
384      * @param numTables Number of tables in the DML Statement
385      * @param gbl The group by list, if any
386      * @param fromList The from list, if any
387      *
388      * @return The generated ProjectRestrictNode atop the original FromTable.
389      *
390      * @exception StandardException Thrown on error
391      */

392
393     public ResultSetNode preprocess(int numTables,
394                                     GroupByList gbl,
395                                     FromList fromList)
396                                 throws StandardException
397     {
398
399         if (subquerys.size() > 0)
400         {
401             subquerys.preprocess(
402                                 numTables,
403                                 (FromList) getNodeFactory().getNode(
404                                     C_NodeTypes.FROM_LIST,
405                                     getNodeFactory().doJoinOrderOptimization(),
406                                     getContextManager()),
407                                 (SubqueryList) getNodeFactory().getNode(
408                                                     C_NodeTypes.SUBQUERY_LIST,
409                                                     getContextManager()),
410                                 (PredicateList) getNodeFactory().getNode(
411                                                     C_NodeTypes.PREDICATE_LIST,
412                                                     getContextManager()));
413         }
414
415         /* Allocate a dummy referenced table map */
416         referencedTableMap = new JBitSet(numTables);
417         referencedTableMap.set(tableNumber);
418         return this;
419     }
420     
421     /**
422      * Ensure that the top of the RSN tree has a PredicateList.
423      *
424      * @param numTables The number of tables in the query.
425      * @return ResultSetNode A RSN tree with a node which has a PredicateList on top.
426      *
427      * @exception StandardException Thrown on error
428      */

429     public ResultSetNode ensurePredicateList(int numTables)
430         throws StandardException
431     {
432         return genProjectRestrict(numTables);
433     }
434
435     /**
436      * Add a new predicate to the list. This is useful when doing subquery
437      * transformations, when we build a new predicate with the left side of
438      * the subquery operator and the subquery's result column.
439      *
440      * @param predicate The predicate to add
441      *
442      * @return ResultSetNode The new top of the tree.
443      *
444      * @exception StandardException Thrown on error
445      */

446     public ResultSetNode addNewPredicate(Predicate predicate)
447             throws StandardException
448     {
449         PredicateList predList;
450         ResultColumnList prRCList;
451         ResultSetNode newPRN;
452         
453         /* We are the body of a quantified predicate subquery. We
454          * need to generate (and return) a PRN above us so that there will be
455          * a place to attach the new predicate.
456          */

457
458         /* We get a shallow copy of the ResultColumnList and its
459          * ResultColumns. (Copy maintains ResultColumn.expression for now.)
460          */

461         prRCList = resultColumns;
462         resultColumns = resultColumns.copyListAndObjects();
463
464         /* Replace ResultColumn.expression with new VirtualColumnNodes
465          * in the ProjectRestrictNode's ResultColumnList. (VirtualColumnNodes include
466          * pointers to source ResultSetNode, this, and source ResultColumn.)
467          */

468         prRCList.genVirtualColumnNodes(this, resultColumns);
469
470         /* Put the new predicate in a list */
471         predList = (PredicateList) getNodeFactory().getNode(
472                                         C_NodeTypes.PREDICATE_LIST,
473                                         getContextManager());
474         predList.addPredicate(predicate);
475
476         /* Finally, we create the new ProjectRestrictNode */
477         return (ResultSetNode) getNodeFactory().getNode(
478                                 C_NodeTypes.PROJECT_RESTRICT_NODE,
479                                 this,
480                                 prRCList,
481                                 null, /* Restriction */
482                                 predList, /* Restriction as PredicateList */
483                                 null, /* Project subquery list */
484                                 null, /* Restrict subquery list */
485                                 tableProperties,
486                                 getContextManager() );
487     }
488
489     /**
490      * Evaluate whether or not the subquery in a FromSubquery is flattenable.
491      * Currently, a FSqry is flattenable if all of the following are true:
492      * o Subquery is a SelectNode or a RowResultSetNode (not a UnionNode)
493      * o It contains no top level subqueries. (RESOLVE - we can relax this)
494      * o It does not contain a group by or having clause
495      * o It does not contain aggregates.
496      * o There is at least one result set in the from list that is
497      * not a RowResultSetNode (the reason is to avoid having
498      * an outer SelectNode with an empty FromList.
499      *
500      * @param fromList The outer from list
501      *
502      * @return boolean Whether or not the FromSubquery is flattenable.
503      */

504     public boolean flattenableInFromSubquery(FromList fromList)
505     {
506         if ((subquerys != null) &&
507             (subquerys.size() > 0))
508         {
509             return false;
510         }
511
512         if ((aggregateVector != null) &&
513             (aggregateVector.size() > 0))
514         {
515             return false;
516         }
517
518         /*
519         ** Don't flatten if select list contains something
520         ** that isn't clonable
521         */

522         if ( ! resultColumns.isCloneable())
523         {
524             return false;
525         }
526
527         boolean nonRowResultSetFound = false;
528         int flSize = fromList.size();
529         for (int index = 0; index < flSize; index++)
530         {
531             FromTable ft = (FromTable) fromList.elementAt(index);
532
533             if (ft instanceof FromSubquery)
534             {
535                 ResultSetNode subq = ((FromSubquery) ft).getSubquery();
536                 if ( ! (subq instanceof RowResultSetNode))
537                 {
538                     nonRowResultSetFound = true;
539                     break;
540                 }
541             }
542             else
543             {
544                 nonRowResultSetFound = true;
545                 break;
546             }
547         }
548
549         return nonRowResultSetFound;
550     }
551
552     /**
553      * Optimize this SelectNode. This means choosing the best access path
554      * for each table, among other things.
555      *
556      * @param dataDictionary The DataDictionary to use for optimization
557      * @param predicateList The predicate list to optimize against
558      * @param outerRows The number of outer joining rows
559      *
560      * @return ResultSetNode The top of the optimized tree
561      *
562      * @exception StandardException Thrown on error
563      */

564     public ResultSetNode optimize(DataDictionary dataDictionary,
565                                   PredicateList predicateList,
566                                   double outerRows)
567             throws StandardException
568     {
569         /*
570         ** Get an optimizer. The only reason we need one is to get a
571         ** CostEstimate object, so we can represent the cost of this node.
572         ** This seems like overkill, but it's just an object allocation...
573         */

574         Optimizer optimizer =
575                     getOptimizer(
576                                 (FromList) getNodeFactory().getNode(
577                                     C_NodeTypes.FROM_LIST,
578                                     getNodeFactory().doJoinOrderOptimization(),
579                                     getContextManager()),
580                                 predicateList,
581                                 dataDictionary,
582                                 (RequiredRowOrdering) null);
583         costEstimate = optimizer.newCostEstimate();
584
585         // RESOLVE: THE COST SHOULD TAKE SUBQUERIES INTO ACCOUNT
586
costEstimate.setCost(0.0d, outerRows, outerRows);
587
588         subquerys.optimize(dataDictionary, outerRows);
589         return this;
590     }
591
592     /**
593      * @see Optimizable#modifyAccessPath
594      *
595      * @exception StandardException Thrown on error
596      */

597     public Optimizable modifyAccessPath(JBitSet outerTables) throws StandardException
598     {
599         /* For most types of Optimizable, do nothing */
600         return (Optimizable) modifyAccessPaths();
601     }
602
603     /**
604      * @see ResultSetNode#modifyAccessPaths
605      *
606      * @exception StandardException Thrown on error
607      */

608     public ResultSetNode modifyAccessPaths() throws StandardException
609     {
610         ResultSetNode treeTop = this;
611
612         subquerys.modifyAccessPaths();
613
614         /* Generate the OrderByNode if a sort is still required for
615          * the order by.
616          */

617         if (orderByList != null)
618         {
619             treeTop = (ResultSetNode) getNodeFactory().getNode(
620                                             C_NodeTypes.ORDER_BY_NODE,
621                                             treeTop,
622                                             orderByList,
623                                             tableProperties,
624                                             getContextManager());
625         }
626         return treeTop;
627     }
628
629     /**
630      * Return whether or not this ResultSet tree is guaranteed to return
631      * at most 1 row based on heuristics. (A RowResultSetNode and a
632      * SELECT with a non-grouped aggregate will return at most 1 row.)
633      *
634      * @return Whether or not this ResultSet tree is guaranteed to return
635      * at most 1 row based on heuristics.
636      */

637     boolean returnsAtMostOneRow()
638     {
639         return true;
640     }
641
642     /**
643      * The generated ResultSet will be:
644      *
645      * RowResultSet -- for the VALUES clause
646      *
647      *
648      * @exception StandardException Thrown on error
649      */

650     public void generate(ActivationClassBuilder acb,
651                                 MethodBuilder mb)
652                             throws StandardException
653     {
654         if (SanityManager.DEBUG)
655         SanityManager.ASSERT(resultColumns != null, "Tree structure bad");
656
657         // Get our final cost estimate.
658
costEstimate = getFinalCostEstimate();
659
660         /*
661         ** Check and see if everything below us is a constant or not.
662         ** If so, we'll let execution know that it can do some caching.
663         ** Before we do the check, we are going to temporarily set
664         */

665         boolean canCache = canWeCacheResults();
666
667         /* Get the next ResultSet #, so that we can number this ResultSetNode, its
668          * ResultColumnList and ResultSet.
669          */

670         assignResultSetNumber();
671
672         // we are dealing with
673
// VALUES(value1, value2, value3)
674
// so we generate a RowResultSet to return the values listed.
675

676         // we can reduce the tree to one RowResultSet
677
// since there is nothing but the resultColumns
678

679         // RowResultSet takes the row-generating function
680
// so we generate one and get back the expression
681
// pointing to it.
682
//
683
// generate the expression to return, which is:
684
// ResultSetFactory.getRowResultSet(this, planX.exprN)
685
// [planX is the name of the class being generated,
686
// exprN is the name of the function being generated.]
687

688         acb.pushGetResultSetFactoryExpression(mb);
689
690         acb.pushThisAsActivation(mb);
691         resultColumns.generate(acb, mb);
692         mb.push(canCache);
693         mb.push(resultSetNumber);
694         mb.push(costEstimate.rowCount());
695         mb.push(costEstimate.getEstimatedCost());
696         mb.callMethod(VMOpcode.INVOKEINTERFACE, (String JavaDoc) null, "getRowResultSet",
697                 ClassName.NoPutResultSet, 6);
698     }
699
700     /**
701      * Replace any DEFAULTs with the associated tree for the default.
702      *
703      * @param ttd The TableDescriptor for the target table.
704      * @param tcl The RCL for the target table.
705      *
706      * @exception StandardException Thrown on error
707      */

708     void replaceDefaults(TableDescriptor ttd, ResultColumnList tcl)
709         throws StandardException
710     {
711         resultColumns.replaceDefaults(ttd, tcl);
712     }
713
714     /**
715      * Optimize any subqueries that haven't been optimized any where
716      * else. This is useful for a RowResultSetNode as a derived table
717      * because it doesn't get optimized otherwise.
718      *
719      * @exception StandardException Thrown on error
720      */

721     void optimizeSubqueries(DataDictionary dd, double rowCount)
722         throws StandardException
723     {
724         subquerys.optimize(dd, rowCount);
725     }
726
727     /**
728      * Notify the underlying result set tree that the result is
729      * ordering dependent. (For example, no bulk fetch on an index
730      * if under an IndexRowToBaseRow.)
731      */

732     void markOrderingDependent()
733     {
734     }
735
736     /*
737     ** Check and see if everything below us is a constant or not.
738     ** If so, we'll let execution know that it can do some caching.
739     ** Before we do the check, we are going to temporarily set
740     ** ParameterNodes to CONSTANT. We do this because we know
741     ** that we can cache a row with a parameter value and get
742     ** the param column reset by the user setting a param, so
743     ** we can skip over parameter nodes. We are doing this
744     ** extra work to optimize inserts of the form:
745     **
746     ** prepare: insert into mytab values (?,?);
747     ** setParam
748     ** execute()
749     ** setParam
750     ** execute()
751     */

752     private boolean canWeCacheResults() throws StandardException
753     {
754
755         /*
756         ** Check the tree below us
757         */

758         HasVariantValueNodeVisitor visitor =
759             new HasVariantValueNodeVisitor(Qualifier.QUERY_INVARIANT, true);
760
761         super.accept(visitor);
762         boolean canCache = !visitor.hasVariant();
763
764         return canCache;
765     }
766 }
767
Popular Tags