KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2
3    Derby - Class org.apache.derby.impl.sql.compile.SetOperatorNode
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.sanity.SanityManager;
25
26 import org.apache.derby.iapi.error.StandardException;
27
28 import org.apache.derby.iapi.sql.compile.AccessPath;
29 import org.apache.derby.iapi.sql.compile.C_NodeTypes;
30 import org.apache.derby.iapi.sql.compile.CostEstimate;
31 import org.apache.derby.iapi.sql.compile.Optimizable;
32 import org.apache.derby.iapi.sql.compile.OptimizablePredicate;
33 import org.apache.derby.iapi.sql.compile.OptimizablePredicateList;
34
35 import org.apache.derby.iapi.sql.dictionary.DataDictionary;
36 import org.apache.derby.iapi.sql.dictionary.TableDescriptor;
37
38 import org.apache.derby.iapi.reference.SQLState;
39 import org.apache.derby.iapi.types.DataTypeDescriptor;
40
41 import org.apache.derby.iapi.util.JBitSet;
42
43 import java.util.HashMap JavaDoc;
44
45 /**
46  * A SetOperatorNode represents a UNION, INTERSECT, or EXCEPT in a DML statement. Binding and optimization
47  * preprocessing is the same for all of these operations, so they share bind methods in this abstract class.
48  *
49  * The class contains a boolean telling whether the operation should eliminate
50  * duplicate rows.
51  *
52  * @author Jeff Lichtman
53  */

54
55 abstract class SetOperatorNode extends TableOperatorNode
56 {
57     /**
58     ** Tells whether to eliminate duplicate rows. all == TRUE means do
59     ** not eliminate duplicates, all == FALSE means eliminate duplicates.
60     */

61     boolean all;
62
63     OrderByList orderByList;
64
65     // List of scoped predicates for pushing during optimization.
66
private PredicateList leftOptPredicates;
67     private PredicateList rightOptPredicates;
68
69     // List of original (unscoped) predicates that we tried to push
70
// during the most recent phase of optimization.
71
private PredicateList pushedPredicates;
72
73     // Mapping of original predicates to scoped predicates, used to
74
// avoid re-scoping predicates unnecessarily.
75
private HashMap JavaDoc leftScopedPreds;
76     private HashMap JavaDoc rightScopedPreds;
77
78     /**
79      * Initializer for a SetOperatorNode.
80      *
81      * @param leftResult The ResultSetNode on the left side of this union
82      * @param rightResult The ResultSetNode on the right side of this union
83      * @param all Whether or not this is an ALL.
84      * @param tableProperties Properties list associated with the table
85      *
86      * @exception StandardException Thrown on error
87      */

88
89     public void init(
90                     Object JavaDoc leftResult,
91                     Object JavaDoc rightResult,
92                     Object JavaDoc all,
93                     Object JavaDoc tableProperties)
94             throws StandardException
95     {
96         super.init(leftResult, rightResult, tableProperties);
97
98         this.all = ((Boolean JavaDoc) all).booleanValue();
99
100         /* resultColumns cannot be null, so we make a copy of the left RCL
101          * for now. At bind() time, we need to recopy the list because there
102          * may have been a "*" in the list. (We will set the names and
103          * column types at that time, as expected.)
104          */

105         resultColumns = leftResultSet.getResultColumns().copyListAndObjects();
106     }
107
108     /**
109      * @see Optimizable#modifyAccessPath
110      *
111      * @exception StandardException Thrown on error
112      */

113     public Optimizable modifyAccessPath(JBitSet outerTables,
114         PredicateList predList) throws StandardException
115     {
116         // When we optimized this node we attempted to push predicates down to
117
// the children, which means the best access path for the children
118
// might depend on those predicates. So now that we're preparing
119
// to generate the best paths, we have to push those same predicates
120
// down again (this is the last time) so that the children can use
121
// them as appropriate. NOTE: If our final choice for join strategy
122
// is a hash join, then we do not push the predicates because we'll
123
// need them to be at this level in order to find out which of them
124
// is the equijoin predicate that is required by hash join.
125
if ((predList != null) &&
126             !getTrulyTheBestAccessPath().getJoinStrategy().isHashJoin())
127         {
128             for (int i = predList.size() - 1; i >= 0; i--)
129                 if (pushOptPredicate(predList.getOptPredicate(i)))
130                     predList.removeOptPredicate(i);
131         }
132
133         /*
134          * It's possible that we tried to push a predicate down to this node's
135          * children but failed to do so. This can happen if this node's
136          * children both match the criteria for pushing a predicate (namely,
137          * they reference base tables) but the children's children do not.
138          * Ex.
139          * select * from
140          * (select i,j from t2 UNION
141          * values (1,1),(2,2),(3,3),(4,4) UNION
142          * select i,j from t1
143          * ) x0 (i,j),
144          * t5 where x0.i = t5.i;
145          *
146          * This will yield a tree resembling the following:
147          *
148          * UNION
149          * / \
150          * UNION SELECT (T1)
151          * / \
152          * SELECT (T2) VALUES
153          *
154          * In this case the top UNION ("this") will push the predicate down,
155          * but the second UNION will _not_ push the predicate because
156          * it can't be pushed to the VALUES clause. This means that
157          * after we're done modifying the paths for "this" node (the top
158          * UNION), the predicate will still be sitting in our leftOptPredicates
159          * list. If that's the case, then we have to make sure the predicate,
160          * which was _not_ enforced in the left child, is enforced at this
161          * level. We do that by generating a ProjectRestrictNode above this
162          * node. Yes, this means the predicate will actually be applied
163          * twice to the right child (in this case), but that's okay as it
164          * won't affect the results.
165          */

166
167         // Get the cost estimate for this node so that we can put it in
168
// the new ProjectRestrictNode, if one is needed.
169
CostEstimate ce = getFinalCostEstimate();
170
171         // Modify this node's access paths.
172
ResultSetNode topNode = (ResultSetNode)modifyAccessPath(outerTables);
173
174         /* Now see if there are any left over predicates; if so, then we
175          * have to generate a ProjectRestrictNode. Note: we walk the
176          * entire chain of UnionNodes (if there is a chain) and see if
177          * any UnionNode at any level has un-pushed predicates; if so, then
178          * we use a PRN to enforce the predicate at this, the top-most
179          * UnionNode.
180          */

181         if (hasUnPushedPredicates())
182         {
183             // When we generate the project restrict node, we pass in the
184
// "pushedPredicates" list because that has the predicates in
185
// _unscoped_ form, which means they are intended for _this_
186
// node instead of this node's children. That's exactly what
187
// we want.
188
ResultSetNode prnRSN = (ResultSetNode) getNodeFactory().getNode(
189                 C_NodeTypes.PROJECT_RESTRICT_NODE,
190                 topNode, // Child ResultSet
191
topNode.getResultColumns(), // Projection
192
null, // Restriction
193
pushedPredicates, // Restriction as PredicateList
194
null, // Subquerys in Projection
195
null, // Subquerys in Restriction
196
null, // Table properties
197
getContextManager());
198             prnRSN.costEstimate = ce.cloneMe();
199             prnRSN.setReferencedTableMap(topNode.getReferencedTableMap());
200             topNode = prnRSN;
201         }
202
203         return (Optimizable)topNode;
204     }
205
206     /**
207      * @see org.apache.derby.iapi.sql.compile.Optimizable#pushOptPredicate
208      *
209      * Take a predicate and push it down to both the left AND right result
210      * sets. Return "true" if we successfully pushed it to both sides,
211      * and "false" otherwise. The assumption is that if we return "true",
212      * the caller will take the predicate and remove it from its own list
213      * of predicates to evaluate; if we return false, then the predicate
214      * will be evaluated at the level of the caller. So returning "false"
215      * means that the left and right result sets for this node will be fully
216      * returned, and then the predicate will be evaluated against the
217      * <set-operator> of those result sets (as of DERBY-805, the only set
218      * operator calling this method is UnionNode). If we can push the
219      * predicate down to both children, though, we can evaluate it closer
220      * to store, which means that each child result set returns only the
221      * correctly qualified rows, and thus the calling set operator will
222      * have a smaller result set on which to operate, which can boost
223      * performance.
224      *
225      * That said, if we can't push the predicate to _both_ sides, we don't
226      * push it at all. The reason is that if we push to one side but not
227      * to the other, we would have to ask the question of whether we should
228      * return "true" (meaning that the predicate would be removed from the
229      * caller's list and thus would _not_ be evaluated at the <set-operator>
230      * level) or "false" (meaning that the caller would keep the predicate
231      * and evaluate it at the <set-operator> level). Depending on the query
232      * in question, both answers could end up returning incorrect results.
233      *
234      * For example, if we push it to the right but not to the left, then
235      * leave it in the caller's list, the optimizer for the caller might
236      * decide to use the predicate to do a hash join with some outer result
237      * set (if the predicate is an equijoin predicate). That would result
238      * in materialization of the calling node and of its children--but since
239      * we pushed a predicate that depends on the outer table down into the
240      * right child, materialization of the right child will only return the
241      * rows that join with the _first_ row of the outer result set, which
242      * is wrong.
243      *
244      * If, on the other hand, we push the predicate to one side and then tell
245      * the caller to remove it from its list, the side to which we did _not_
246      * push the predicate could return rows that aren't qualified. Then,
247      * since the caller removed the predicate from its list, it (the caller)
248      * will not evaluate the predicate on its own result set--and thus we
249      * can end up returning rows that we weren't supposed to return.
250      *
251      * So all of that said, only push (and return "true") if we think we
252      * can push the predicate to both sides.
253      *
254      * @exception StandardException Thrown on error
255      */

256     public boolean pushOptPredicate(OptimizablePredicate optimizablePredicate)
257         throws StandardException
258     {
259         // This method was added to SetOperatorNode as part of DERBY-805,
260
// which was only targeted for UnionNodes. So for now, we don't
261
// do anything if "this" isn't a Union. This check can be removed
262
// when support for other SetOperators is added.
263
if (!(this instanceof UnionNode))
264             return false;
265
266         // We only handle certain types of predicates here; if the received
267
// predicate doesn't qualify, then don't push it.
268
Predicate pred = (Predicate)optimizablePredicate;
269         if (!pred.pushableToSubqueries())
270             return false;
271
272         // Check to see if the child nodes reference any base tables; if either
273
// child does not reference at least one base table, then we don't try
274
// to push the predicate.
275
boolean canPush = false;
276
277         JBitSet tableNums = new JBitSet(getReferencedTableMap().size());
278         BaseTableNumbersVisitor btnVis =
279             new BaseTableNumbersVisitor(tableNums);
280
281         // Check the left child.
282
leftResultSet.accept(btnVis);
283         canPush = (tableNums.getFirstSetBit() != -1);
284
285         /* If we can't push it to _both_ children, then we don't push at all.
286          * RESOLVE: We can add the ability to push a predicate to one side
287          * only by putting a ProjectRestrictNode between the union node and
288          * the child as a place to park the predicate. To make things simple,
289          * we might want to always put ProjectRestrictNodes under both sides
290          * of the union during preprocessing (i.e. after binding but before
291          * optimization). In some cases the extra nodes won't be needed, but
292          * PRNs (and the corresponding ProjectRestrictResultSets) are cheap.
293          * Also, we could eliminate unnecessary ProjectRestrictNodes at the
294          * end of optimization (possibly in modifyAccessPaths()). Until all
295          * of that is implemented, though, we only push if we can push to
296          * both sides...
297          */

298         if (!canPush)
299             return false;
300
301         // Check the right child.
302
tableNums.clearAll();
303         rightResultSet.accept(btnVis);
304         canPush = (tableNums.getFirstSetBit() != -1);
305         if (!canPush)
306             return false;
307
308         // Get a list of all of the underlying base tables that this node
309
// references. We pass this down when scoping so that we can tell
310
// if the operands are actually supposed to be scoped to _this_
311
// node's children. Note that in order for the predicate to
312
// have been pushed this far, at least one of its operands must
313
// apply to this node--we don't know which one it is, though,
314
// so we use this tableNums info to figure that out.
315
tableNums.clearAll();
316         this.accept(btnVis);
317
318         /* What we want to do here is push the predicate to the left/right
319          * child. That means that we need to find the equivalent column(s)
320          * in each child.
321          * Ex:
322          *
323          * select * from
324          * (select i,j from t1 union select i,j from t2) X1,
325          * (select a,b from t3 union select a,b from t4) X2
326          * where X1.j = X2.b;
327          *
328          * In this example, X1.j maps to "t1" for the left side of the
329          * union (X1) and "t2" for the right side of the union. So we have
330          * to get versions of the predicate that are appropriate to each
331          * side. That's what the call to getPredScopedForResultSet()
332          * in the following code does.
333          */

334
335         // For details on how this whichRC variable is used, see the
336
// comments in BinaryRelationalOperatorNode.getScopedOperand().
337
int [] whichRC = new int[] { -1 };
338
339         // See if we already have a scoped version of the predicate cached,
340
// and if so just use that.
341
Predicate scopedPred = null;
342         if (leftScopedPreds == null)
343             leftScopedPreds = new HashMap JavaDoc();
344         else
345             scopedPred = (Predicate)leftScopedPreds.get(pred);
346         if (scopedPred == null)
347         {
348             scopedPred = pred.getPredScopedForResultSet(
349                 tableNums, leftResultSet, whichRC);
350             leftScopedPreds.put(pred, scopedPred);
351         }
352
353         // Add the scoped predicate to our list for the left child.
354
getLeftOptPredicateList().addOptPredicate(scopedPred);
355
356         scopedPred = null;
357         if (rightScopedPreds == null)
358             rightScopedPreds = new HashMap JavaDoc();
359         else
360             scopedPred = (Predicate)rightScopedPreds.get(pred);
361         if (scopedPred == null)
362         {
363             scopedPred = pred.getPredScopedForResultSet(
364                 tableNums, rightResultSet, whichRC);
365             rightScopedPreds.put(pred, scopedPred);
366         }
367
368         // Add the scoped predicate to our list for the right child.
369
getRightOptPredicateList().addOptPredicate(scopedPred);
370
371         // Add the predicate (in its original form) to our list of predicates
372
// that we've pushed during this phase of optimization. We need to
373
// keep this list of pushed predicates around so that we can do
374
// a "pull" of them later, if needed. We also need this list for
375
// cases where predicates are not pushed all the way down; see
376
// modifyAccessPaths() in this class for more.
377
if (pushedPredicates == null)
378             pushedPredicates = new PredicateList();
379
380         pushedPredicates.addOptPredicate(pred);
381         return true;
382     }
383
384     /**
385      * @see Optimizable#pullOptPredicates
386      *
387      * @exception StandardException Thrown on error
388      */

389     public void pullOptPredicates(
390         OptimizablePredicateList optimizablePredicates)
391         throws StandardException
392     {
393         if (pushedPredicates == null)
394         // we didn't push anything, so nothing to pull.
395
return;
396
397         // It's possible that we tried to push a predicate down to this
398
// SetOperatorNode's children but weren't actually able to do so
399
// (see modifyAccessPaths() in this class for details on when that
400
// can happen). In that case the predicates will still be sitting
401
// in the left/right predicate list; we can ignore them here by
402
// just discarding them. When it comes time to modifyAccessPaths,
403
// though, we'll handle them correctly--i.e. we'll generate a
404
// ProjectRestrictNode over this node to ensure the predicates are
405
// enforced.
406

407         if (leftOptPredicates != null)
408             leftOptPredicates.removeAllElements();
409
410         if (rightOptPredicates != null)
411             rightOptPredicates.removeAllElements();
412
413         /* Note that predicates which have been explicitly scoped should
414          * not be pulled. The reason is that a scoped predicate can only
415          * be pushed to a specific, target result set. When it comes time
416          * to pull the predicate up, there's no need to pull the scoped
417          * predicate because it, by definition, was only intended for this
418          * specific result set and therefore cannot be pushed anywhere else.
419          * So at "pull" time, we can just discard the scoped predicates. We
420          * do, however, need to pull the original, unscoped predicate from
421          * which the scoped predicate was created because we can potentially
422          * push that predicate elsewhere
423          */

424         Predicate pred = null;
425         RemapCRsVisitor rcrv = new RemapCRsVisitor(false);
426         for (int i = 0; i < pushedPredicates.size(); i++)
427         {
428             pred = (Predicate)pushedPredicates.getOptPredicate(i);
429             if (pred.isScopedForPush())
430             {
431                 /* We don't need to pull the predicate if it's scoped, but
432                  * since scoped predicates are cached between rounds of
433                  * optimization, it's possible that we'll reuse the scoped
434                  * predicate again in a later round. So to make sure we
435                  * get a "fresh start" in later rounds, we un-remap the
436                  * predicate here.
437                  */

438                 pred.getAndNode().accept(rcrv);
439                 continue;
440             }
441             optimizablePredicates.addOptPredicate(pred);
442         }
443
444         // We're done with the pushedPredicates list, so clear it out
445
// in preparation for another phase of optimization.
446
pushedPredicates.removeAllElements();
447     }
448
449     /**
450      * It's possible that we tried to push predicates to this node's
451      * children but failed to do so. This can happen if this node's
452      * children both satisfy the criteria for pushing a predicate
453      * (namely, they reference base tables) but the children's
454      * children do not (see modifyAccessPaths() above for an example
455      * of how that can happen). So this method will walk the chain
456      * of nodes beneath this one and determine if any SetOperatorNode
457      * at any level has predicates that were not successfully pushed
458      * to both of its children (note: this currently only applies
459      * to UnionNodes).
460      *
461      * @return True if any UnionNode (or actually, any SetOperatorNode)
462      * in the chain of SetOperatorNodes (starting with this one) has
463      * unpushed predicates; false otherwise.
464      */

465     protected boolean hasUnPushedPredicates()
466     {
467         // Check this node.
468
if (((leftOptPredicates != null) && (leftOptPredicates.size() > 0)) ||
469             ((rightOptPredicates != null) && (rightOptPredicates.size() > 0)))
470         {
471             return true;
472         }
473
474         // Now check the children.
475
if ((leftResultSet instanceof SetOperatorNode) &&
476             ((SetOperatorNode)leftResultSet).hasUnPushedPredicates())
477         {
478             return true;
479         }
480
481         return ((rightResultSet instanceof SetOperatorNode) &&
482             ((SetOperatorNode)rightResultSet).hasUnPushedPredicates());
483     }
484
485     /**
486      * Convert this object to a String. See comments in QueryTreeNode.java
487      * for how this should be done for tree printing.
488      *
489      * @return This object as a String
490      */

491
492     public String JavaDoc toString()
493     {
494         if (SanityManager.DEBUG)
495         {
496             return "all: " + all + "\n" +
497                 "orderByList: " +
498                 (orderByList != null ? orderByList.toString() : "null") + "\n" +
499                 super.toString();
500         }
501         else
502         {
503             return "";
504         }
505     }
506
507     /**
508      * Bind the result columns of this ResultSetNode when there is no
509      * base table to bind them to. This is useful for SELECT statements,
510      * where the result columns get their types from the expressions that
511      * live under them.
512      *
513      * @param fromListParam FromList to use/append to.
514      *
515      * @exception StandardException Thrown on error
516      */

517     public void bindResultColumns(FromList fromListParam)
518                     throws StandardException
519     {
520         super.bindResultColumns(fromListParam);
521
522         /* Now we build our RCL */
523         buildRCL();
524     }
525
526     /**
527      * Bind the result columns for this ResultSetNode to a base table.
528      * This is useful for INSERT and UPDATE statements, where the
529      * result columns get their types from the table being updated or
530      * inserted into.
531      * If a result column list is specified, then the verification that the
532      * result column list does not contain any duplicates will be done when
533      * binding them by name.
534      *
535      * @param targetTableDescriptor The TableDescriptor for the table being
536      * updated or inserted into
537      * @param targetColumnList For INSERT statements, the user
538      * does not have to supply column
539      * names (for example, "insert into t
540      * values (1,2,3)". When this
541      * parameter is null, it means that
542      * the user did not supply column
543      * names, and so the binding should
544      * be done based on order. When it
545      * is not null, it means do the binding
546      * by name, not position.
547      * @param statement Calling DMLStatementNode (Insert or Update)
548      * @param fromListParam FromList to use/append to.
549      *
550      * @exception StandardException Thrown on error
551      */

552
553     public void bindResultColumns(TableDescriptor targetTableDescriptor,
554                     FromVTI targetVTI,
555                     ResultColumnList targetColumnList,
556                     DMLStatementNode statement,
557                     FromList fromListParam)
558                 throws StandardException
559     {
560         super.bindResultColumns(targetTableDescriptor,
561                                 targetVTI,
562                                 targetColumnList, statement,
563                                 fromListParam);
564
565         /* Now we build our RCL */
566         buildRCL();
567     }
568
569     /**
570      * Build the RCL for this node. We propagate the RCL up from the
571      * left child to form this node's RCL.
572      *
573      * @exception StandardException Thrown on error
574      */

575
576     private void buildRCL() throws StandardException
577     {
578         /* Verify that both sides of the union have the same # of columns in their
579          * RCL.
580          */

581         if (leftResultSet.getResultColumns().size() !=
582             rightResultSet.getResultColumns().size())
583         {
584             throw StandardException.newException(SQLState.LANG_UNION_UNMATCHED_COLUMNS,
585                                                  getOperatorName());
586         }
587
588         /* We need to recreate resultColumns for this node, since there
589          * may have been 1 or more *'s in the left's SELECT list.
590          */

591         resultColumns = leftResultSet.getResultColumns().copyListAndObjects();
592
593         /* Create new expressions with the dominant types after verifying
594          * union compatibility between left and right sides.
595          */

596         resultColumns.setUnionResultExpression(rightResultSet.getResultColumns(), tableNumber, level, getOperatorName());
597     }
598
599     /**
600      * Bind the result columns of a table constructor to the types in the
601      * given ResultColumnList. Use when inserting from a table constructor,
602      * and there are nulls in the values clauses.
603      *
604      * @param rcl The ResultColumnList with the types to bind to
605      *
606      * @exception StandardException Thrown on error.
607      */

608     public void bindUntypedNullsToResultColumns(ResultColumnList rcl)
609                 throws StandardException
610     {
611         /*
612         ** If the RCL from the parent is null, then
613         ** the types are coming from the union itself.
614         ** So we have to cross check the two child
615         ** rcls.
616         */

617         if (rcl == null)
618         {
619             ResultColumnList lrcl = rightResultSet.getResultColumns();
620             ResultColumnList rrcl = leftResultSet.getResultColumns();
621
622             leftResultSet.bindUntypedNullsToResultColumns(rrcl);
623             rightResultSet.bindUntypedNullsToResultColumns(lrcl);
624         }
625         else
626         {
627             leftResultSet.bindUntypedNullsToResultColumns(rcl);
628             rightResultSet.bindUntypedNullsToResultColumns(rcl);
629         }
630     }
631
632     /**
633      * Get the parameter types from the given RowResultSetNode into the
634      * given array of types. If an array position is already filled in,
635      * don't clobber it.
636      *
637      * @param types The array of types to fill in
638      * @param rrsn The RowResultSetNode from which to take the param types
639      *
640      * @return The number of new types found in the RowResultSetNode
641      */

642     int getParamColumnTypes(DataTypeDescriptor[] types, RowResultSetNode rrsn)
643      throws StandardException
644     {
645         int numTypes = 0;
646
647         /* Look for columns where we have not found a non-? yet. */
648         for (int i = 0; i < types.length; i++)
649         {
650             if (types[i] == null)
651             {
652                 ResultColumn rc =
653                     (ResultColumn) rrsn.getResultColumns().elementAt(i);
654                 if ( ! (rc.getExpression().requiresTypeFromContext()))
655                 {
656                     types[i] = rc.getExpressionType();
657                     numTypes++;
658                 }
659             }
660         }
661
662         return numTypes;
663     }
664
665     /**
666      * Set the type of each ? parameter in the given RowResultSetNode
667      * according to its ordinal position in the given array of types.
668      *
669      * @param types An array of types containing the proper type for each
670      * ? parameter, by ordinal position.
671      * @param rrsn A RowResultSetNode that could contain ? parameters whose
672      * types need to be set.
673      *
674      * @exception StandardException Thrown on error
675      */

676     void setParamColumnTypes(DataTypeDescriptor[] types, RowResultSetNode rrsn)
677                     throws StandardException
678     {
679         /*
680         ** Look for ? parameters in the result column list
681         ** of each RowResultSetNode
682         */

683         ResultColumnList rrcl = rrsn.getResultColumns();
684         int rrclSize = rrcl.size();
685         for (int index = 0; index < rrclSize; index++)
686         {
687             ResultColumn rc = (ResultColumn) rrcl.elementAt(index);
688
689             if (rc.getExpression().requiresTypeFromContext())
690             {
691                 /*
692                 ** We found a ? - set its type to the type from the
693                 ** type array.
694                 */

695                 rc.getExpression().setType(types[index]);
696             }
697         }
698     }
699
700     /**
701      * Bind the expressions in the target list. This means binding the
702      * sub-expressions, as well as figuring out what the return type is
703      * for each expression. This is useful for EXISTS subqueries, where we
704      * need to validate the target list before blowing it away and replacing
705      * it with a SELECT true.
706      *
707      * @exception StandardException Thrown on error
708      */

709
710     public void bindTargetExpressions(FromList fromListParam)
711                     throws StandardException
712     {
713         leftResultSet.bindTargetExpressions(fromListParam);
714         rightResultSet.bindTargetExpressions(fromListParam);
715     }
716
717     /**
718      * Push the order by list down from the cursor node
719      * into its child result set so that the optimizer
720      * has all of the information that it needs to
721      * consider sort avoidance.
722      *
723      * @param orderByList The order by list
724      */

725     void pushOrderByList(OrderByList orderByList)
726     {
727         this.orderByList = orderByList;
728     }
729
730     /**
731      * Put a ProjectRestrictNode on top of each FromTable in the FromList.
732      * ColumnReferences must continue to point to the same ResultColumn, so
733      * that ResultColumn must percolate up to the new PRN. However,
734      * that ResultColumn will point to a new expression, a VirtualColumnNode,
735      * which points to the FromTable and the ResultColumn that is the source for
736      * the ColumnReference.
737      * (The new PRN will have the original of the ResultColumnList and
738      * the ResultColumns from that list. The FromTable will get shallow copies
739      * of the ResultColumnList and its ResultColumns. ResultColumn.expression
740      * will remain at the FromTable, with the PRN getting a new
741      * VirtualColumnNode for each ResultColumn.expression.)
742      * We then project out the non-referenced columns. If there are no referenced
743      * columns, then the PRN's ResultColumnList will consist of a single ResultColumn
744      * whose expression is 1.
745      *
746      * @param numTables Number of tables in the DML Statement
747      * @param gbl The group by list, if any
748      * @param fromList The from list, if any
749      *
750      * @return The preprocessed ResultSetNode that can be optimized
751      *
752      * @exception StandardException Thrown on error
753      */

754
755     public ResultSetNode preprocess(int numTables,
756                                     GroupByList gbl,
757                                     FromList fromList)
758                                 throws StandardException
759     {
760         ResultSetNode newTop = this;
761
762         /* RESOLVE - what does numTables and referencedTableMap mean here? */
763         leftResultSet = leftResultSet.preprocess(numTables, gbl, fromList);
764         rightResultSet = rightResultSet.preprocess(numTables, gbl, fromList);
765
766         /* Build the referenced table map (left || right) */
767         referencedTableMap = (JBitSet) leftResultSet.getReferencedTableMap().clone();
768         referencedTableMap.or((JBitSet) rightResultSet.getReferencedTableMap());
769
770         /* If this is a UNION without an all and we have
771          * an order by then we can consider eliminating the sort for the
772          * order by. All of the columns in the order by list must
773          * be ascending in order to do this. There are 2 cases:
774          * o The order by list is an in order prefix of the columns
775          * in the select list. In this case the output of the
776          * sort from the distinct will be in the right order
777          * so we simply eliminate the order by list.
778          * o The order by list is a subset of the columns in the
779          * the select list. In this case we need to reorder the
780          * columns in the select list so that the ordering columns
781          * are an in order prefix of the select list and put a PRN
782          * above the select so that the shape of the result set
783          * is as expected.
784          */

785         if ((! all) && orderByList != null && orderByList.allAscending())
786         {
787             /* Order by list currently restricted to columns in select
788              * list, so we will always eliminate the order by here.
789              */

790             if (orderByList.isInOrderPrefix(resultColumns))
791             {
792                 orderByList = null;
793             }
794             /* RESOLVE - We currently only eliminate the order by if it is
795              * a prefix of the select list. We do not currently do the
796              * elimination if the order by is not a prefix because the code
797              * doesn't work. The problem has something to do with the
798              * fact that we generate additional nodes between the union
799              * and the PRN (for reordering that we would generate here)
800              * when modifying the access paths. VCNs under the PRN can be
801              * seen as correlated since their source resultset is the Union
802              * which is no longer the result set directly under them. This
803              * causes the wrong code to get generated. (jerry - 11/3/98)
804              * (bug 59)
805              */

806         }
807
808         return newTop;
809     }
810     
811     /**
812      * Ensure that the top of the RSN tree has a PredicateList.
813      *
814      * @param numTables The number of tables in the query.
815      * @return ResultSetNode A RSN tree with a node which has a PredicateList on top.
816      *
817      * @exception StandardException Thrown on error
818      */

819     public ResultSetNode ensurePredicateList(int numTables)
820         throws StandardException
821     {
822         return genProjectRestrict(numTables);
823     }
824
825     /**
826      * Verify that a SELECT * is valid for this type of subquery.
827      *
828      * @param outerFromList The FromList from the outer query block(s)
829      * @param subqueryType The subquery type
830      *
831      * @exception StandardException Thrown on error
832      */

833     public void verifySelectStarSubquery(FromList outerFromList, int subqueryType)
834                     throws StandardException
835     {
836         /* Check both sides - SELECT * is not valid on either side */
837         leftResultSet.verifySelectStarSubquery(outerFromList, subqueryType);
838         rightResultSet.verifySelectStarSubquery(outerFromList, subqueryType);
839     }
840
841     /**
842      * Determine whether or not the specified name is an exposed name in
843      * the current query block.
844      *
845      * @param name The specified name to search for as an exposed name.
846      * @param schemaName Schema name, if non-null.
847      * @param exactMatch Whether or not we need an exact match on specified schema and table
848      * names or match on table id.
849      *
850      * @return The FromTable, if any, with the exposed name.
851      *
852      * @exception StandardException Thrown on error
853      */

854     protected FromTable getFromTableByName(String JavaDoc name, String JavaDoc schemaName, boolean exactMatch)
855         throws StandardException
856     {
857         /* We search both sides for a TableOperatorNode (join nodes)
858          * but only the left side for a UnionNode.
859          */

860         return leftResultSet.getFromTableByName(name, schemaName, exactMatch);
861     }
862
863     /**
864      * Set the result column for the subquery to a boolean true,
865      * Useful for transformations such as
866      * changing:
867      * where exists (select ... from ...)
868      * to:
869      * where (select true from ...)
870      *
871      * NOTE: No transformation is performed if the ResultColumn.expression is
872      * already the correct boolean constant.
873      *
874      * @param onlyConvertAlls Boolean, whether or not to just convert *'s
875      *
876      * @exception StandardException Thrown on error
877      */

878     public void setResultToBooleanTrueNode(boolean onlyConvertAlls)
879                 throws StandardException
880     {
881         super.setResultToBooleanTrueNode(onlyConvertAlls);
882         leftResultSet.setResultToBooleanTrueNode(onlyConvertAlls);
883         rightResultSet.setResultToBooleanTrueNode(onlyConvertAlls);
884     }
885
886     /**
887      * This ResultSet is the source for an Insert. The target RCL
888      * is in a different order and/or a superset of this RCL. In most cases
889      * we will reorder and/or add defaults to the current RCL so that is
890      * matches the target RCL. Those RSNs whose generate() method does
891      * not handle projects will insert a PRN, with a new RCL which matches
892      * the target RCL, above the current RSN.
893      * NOTE - The new or enhanced RCL will be fully bound.
894      *
895      * @param numTargetColumns # of columns in target RCL
896      * @param colMap int array representation of correspondence between
897      * RCLs - colmap[i] = -1 -> missing in current RCL
898      * colmap[i] = j -> targetRCL(i) <-> thisRCL(j+1)
899      * @param dataDictionary DataDictionary to use
900      * @param targetTD TableDescriptor for target if the target is not a VTI, null if a VTI
901      * @param targetVTI Target description if it is a VTI, null if not a VTI
902      *
903      * @return ResultSetNode The new top of the tree
904      *
905      * @exception StandardException Thrown on error
906      */

907     public ResultSetNode enhanceRCLForInsert(int numTargetColumns, int[] colMap,
908                                              DataDictionary dataDictionary,
909                                              TableDescriptor targetTD,
910                                              FromVTI targetVTI)
911             throws StandardException
912     {
913         // our newResultCols are put into the bound form straight away.
914
ResultColumnList newResultCols =
915                                 (ResultColumnList) getNodeFactory().getNode(
916                                                 C_NodeTypes.RESULT_COLUMN_LIST,
917                                                 getContextManager());
918         int numResultSetColumns = resultColumns.size();
919
920         /* Create a massaged version of the source RCL.
921          * (Much simpler to build new list and then assign to source,
922          * rather than massage the source list in place.)
923          */

924         for (int index = 0; index < numTargetColumns; index++)
925         {
926             ResultColumn newResultColumn;
927             ResultColumn oldResultColumn;
928             ColumnReference newColumnReference;
929
930             if (colMap[index] != -1)
931             {
932                 // getResultColumn uses 1-based positioning, so offset the colMap entry appropriately
933
oldResultColumn = resultColumns.getResultColumn(colMap[index]+1);
934
935                 newColumnReference = (ColumnReference) getNodeFactory().getNode(
936                                                 C_NodeTypes.COLUMN_REFERENCE,
937                                                 oldResultColumn.getName(),
938                                                 null,
939                                                 getContextManager());
940                 /* The ColumnReference points to the source of the value */
941                 newColumnReference.setSource(oldResultColumn);
942                 // colMap entry is 0-based, columnId is 1-based.
943
newColumnReference.setType(oldResultColumn.getExpressionType());
944
945                 // Source of an insert, so nesting levels must be 0
946
newColumnReference.setNestingLevel(0);
947                 newColumnReference.setSourceLevel(0);
948
949                 // because the insert already copied the target table's
950
// column descriptors into the result, we grab it from there.
951
// alternatively, we could do what the else clause does,
952
// and look it up in the DD again.
953
newResultColumn = (ResultColumn) getNodeFactory().getNode(
954                         C_NodeTypes.RESULT_COLUMN,
955                         oldResultColumn.getType(),
956                         newColumnReference,
957                         getContextManager());
958             }
959             else
960             {
961                 newResultColumn = genNewRCForInsert(targetTD, targetVTI, index + 1, dataDictionary);
962             }
963
964             newResultCols.addResultColumn(newResultColumn);
965         }
966
967         /* The generated ProjectRestrictNode now has the ResultColumnList
968          * in the order that the InsertNode expects.
969          * NOTE: This code here is an exception to several "rules":
970          * o This is the only ProjectRestrictNode that is currently
971          * generated outside of preprocess().
972          * o The UnionNode is the only node which is not at the
973          * top of the query tree which has ColumnReferences under
974          * its ResultColumnList prior to expression push down.
975          */

976         return (ResultSetNode) getNodeFactory().getNode(
977                                     C_NodeTypes.PROJECT_RESTRICT_NODE,
978                                     this,
979                                     newResultCols,
980                                     null,
981                                     null,
982                                     null,
983                                     null,
984                                     tableProperties,
985                                     getContextManager());
986     }
987
988     /**
989      * Evaluate whether or not the subquery in a FromSubquery is flattenable.
990      * Currently, a FSqry is flattenable if all of the following are true:
991      * o Subquery is a SelectNode. (ie, not a RowResultSetNode or a UnionNode)
992      * o It contains no top level subqueries. (RESOLVE - we can relax this)
993      * o It does not contain a group by or having clause
994      * o It does not contain aggregates.
995      *
996      * @param fromList The outer from list
997      *
998      * @return boolean Whether or not the FromSubquery is flattenable.
999      */

1000    public boolean flattenableInFromSubquery(FromList fromList)
1001    {
1002        /* Unions in FromSubquerys are not flattenable. */
1003        return false;
1004    }
1005
1006    /**
1007     * Return whether or not to materialize this ResultSet tree.
1008     *
1009     * @return Whether or not to materialize this ResultSet tree.
1010     * would return valid results.
1011     *
1012     * @exception StandardException Thrown on error
1013     */

1014    public boolean performMaterialization(JBitSet outerTables)
1015        throws StandardException
1016    {
1017        // RESOLVE - just say no to materialization right now - should be a cost based decision
1018
return false;
1019
1020        /* Actual materialization, if appropriate, will be placed by our parent PRN.
1021         * This is because PRN might have a join condition to apply. (Materialization
1022         * can only occur before that.
1023         */

1024        //return true;
1025
}
1026
1027    /**
1028     * @return the operator name: "UNION", "INTERSECT", or "EXCEPT"
1029     */

1030    abstract String JavaDoc getOperatorName();
1031
1032    /**
1033     * Retrieve the list of optimizable predicates that are
1034     * targeted for the left child. Create a new (empty)
1035     * list if the list is null.
1036     */

1037    PredicateList getLeftOptPredicateList()
1038        throws StandardException
1039    {
1040        if (leftOptPredicates == null) {
1041            leftOptPredicates =
1042                (PredicateList) getNodeFactory().getNode(
1043                    C_NodeTypes.PREDICATE_LIST,
1044                    getContextManager());
1045        }
1046
1047        return leftOptPredicates;
1048    }
1049
1050    /**
1051     * Retrieve the list of optimizable predicates that are
1052     * targeted for the right child. Create a new (empty)
1053     * list if the list is null.
1054     */

1055    PredicateList getRightOptPredicateList()
1056        throws StandardException
1057    {
1058        if (rightOptPredicates == null) {
1059            rightOptPredicates =
1060                (PredicateList) getNodeFactory().getNode(
1061                    C_NodeTypes.PREDICATE_LIST,
1062                    getContextManager());
1063        }
1064
1065        return rightOptPredicates;
1066    }
1067
1068}
1069
Popular Tags