KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2
3    Derby - Class org.apache.derby.impl.sql.compile.FromList
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.sql.compile.CompilerContext;
27 import org.apache.derby.iapi.sql.compile.Optimizable;
28 import org.apache.derby.iapi.sql.compile.OptimizableList;
29 import org.apache.derby.iapi.sql.compile.Optimizer;
30 import org.apache.derby.iapi.sql.compile.Visitable;
31 import org.apache.derby.iapi.sql.compile.Visitor;
32 import org.apache.derby.iapi.sql.compile.C_NodeTypes;
33
34 import org.apache.derby.iapi.sql.dictionary.DataDictionary;
35
36 import org.apache.derby.iapi.error.StandardException;
37
38 import org.apache.derby.iapi.reference.SQLState;
39
40 import org.apache.derby.iapi.util.JBitSet;
41 import org.apache.derby.iapi.util.StringUtil;
42
43 import java.util.Properties JavaDoc;
44 import java.util.Enumeration JavaDoc;
45 import java.util.Vector JavaDoc;
46
47 /**
48  * A FromList represents the list of tables in a FROM clause in a DML
49  * statement. It extends QueryTreeNodeVector.
50  *
51  * @author Jeff Lichtman
52  */

53
54 public class FromList extends QueryTreeNodeVector implements OptimizableList
55 {
56     Properties JavaDoc properties;
57     // RESOLVE: The default should be false
58
boolean fixedJoinOrder = true;
59     // true by default.
60
boolean useStatistics = true;
61
62     // FromList could have a view in it's list. If the view is defined in SESSION
63
// schema, then we do not want to cache the statement's plan. This boolean
64
// will help keep track of such a condition.
65
private boolean referencesSessionSchema;
66
67
68     /** Initializer for a FromList */
69
70     public void init(Object JavaDoc optimizeJoinOrder)
71     {
72         fixedJoinOrder = ! (((Boolean JavaDoc) optimizeJoinOrder).booleanValue());
73     }
74
75     /**
76      * Initializer for a FromList
77      *
78      * @exception StandardException Thrown on error
79      */

80     public void init(Object JavaDoc optimizeJoinOrder, Object JavaDoc fromTable)
81                 throws StandardException
82     {
83         init(optimizeJoinOrder);
84
85         addFromTable((FromTable) fromTable);
86     }
87
88     /*
89      * OptimizableList interface
90      */

91
92     /**
93      * @see org.apache.derby.iapi.sql.compile.OptimizableList#getOptimizable
94      */

95     public Optimizable getOptimizable(int index)
96     {
97         return (Optimizable) elementAt(index);
98     }
99
100     /**
101      * @see org.apache.derby.iapi.sql.compile.OptimizableList#setOptimizable
102      */

103     public void setOptimizable(int index, Optimizable optimizable)
104     {
105         setElementAt((FromTable) optimizable, index);
106     }
107
108     /**
109      * @see OptimizableList#verifyProperties
110      * @exception StandardException Thrown on error
111      */

112     public void verifyProperties(DataDictionary dDictionary) throws StandardException
113     {
114         int size = size();
115         for (int index = 0; index < size; index++)
116         {
117             ((Optimizable) elementAt(index)).verifyProperties(dDictionary);
118         }
119     }
120
121
122     /**
123      * Add a table to the FROM list.
124      *
125      * @param fromTable A FromTable to add to the list
126      *
127      * @exception StandardException Thrown on error
128      */

129
130     public void addFromTable(FromTable fromTable) throws StandardException
131     {
132         /* Don't worry about checking TableOperatorNodes since
133          * they don't have exposed names. This will potentially
134          * allow duplicate exposed names in some degenerate cases,
135          * but the binding of the ColumnReferences will catch those
136          * cases with a different error. If the query does not have
137          * any ColumnReferences from the duplicate exposed name, the
138          * user is executing a really dumb query and we won't throw
139          * and exception - consider it an ANSI extension.
140          */

141         TableName leftTable = null;
142         TableName rightTable = null;
143         if (! (fromTable instanceof TableOperatorNode))
144         {
145             /* Check for duplicate table name in FROM list */
146             int size = size();
147             for (int index = 0; index < size; index++)
148             {
149                 leftTable = fromTable.getTableName();
150
151                 if(((FromTable) elementAt(index)) instanceof TableOperatorNode) {
152                     continue;
153                 }
154
155                 else {
156                     rightTable = ((FromTable) elementAt(index)).getTableName();
157                 }
158                 if(leftTable.equals(rightTable))
159                 {
160                     throw StandardException.newException(SQLState.LANG_FROM_LIST_DUPLICATE_TABLE_NAME, fromTable.getExposedName());
161                 }
162             }
163         }
164
165         addElement(fromTable);
166     }
167
168     /**
169      * Search to see if a query references the specifed table name.
170      *
171      * @param name Table name (String) to search for.
172      * @param baseTable Whether or not name is for a base table
173      *
174      * @return true if found, else false
175      *
176      * @exception StandardException Thrown on error
177      */

178     public boolean referencesTarget(String JavaDoc name, boolean baseTable)
179         throws StandardException
180     {
181         FromTable fromTable;
182         boolean found = false;
183
184         /* Check for table or VTI name in FROM list */
185         int size = size();
186         for (int index = 0; index < size; index++)
187         {
188             fromTable = (FromTable) elementAt(index);
189
190             if (fromTable.referencesTarget(name, baseTable))
191             {
192                 found = true;
193                 break;
194             }
195         }
196
197         return found;
198     }
199
200     /**
201      * Return true if the node references SESSION schema tables (temporary or permanent)
202      *
203      * @return true if references SESSION schema tables, else false
204      *
205      * @exception StandardException Thrown on error
206      */

207     public boolean referencesSessionSchema()
208         throws StandardException
209     {
210         FromTable fromTable;
211         boolean found = false;
212
213         // Following if will return true if this FromList object had any VIEWs
214
// from SESSION schema as elements. This information is gathered during
215
// the bindTables method. At the end of the bindTables, we loose
216
// the information on VIEWs since they get replaced with their view
217
// definition. Hence, we need to intercept in the middle on the bindTables
218
// method and save that information in referencesSeesionSchema field.
219
if (referencesSessionSchema) return true;
220
221         /* Check for table or VTI name in FROM list */
222         int size = size();
223         for (int index = 0; index < size; index++)
224         {
225             fromTable = (FromTable) elementAt(index);
226
227             if (fromTable.referencesSessionSchema())
228             {
229                 found = true;
230                 break;
231             }
232         }
233
234         return found;
235     }
236
237     /**
238      * Determine whether or not the specified name is an exposed name in
239      * the current query block.
240      *
241      * @param name The specified name to search for as an exposed name.
242      * @param schemaName Schema name, if non-null.
243      * @param exactMatch Whether or not we need an exact match on specified schema and table
244      * names or match on table id.
245      *
246      * @return The FromTable, if any, with the exposed name.
247      *
248      * @exception StandardException Thrown on error
249      */

250     protected FromTable getFromTableByName(String JavaDoc name, String JavaDoc schemaName, boolean exactMatch)
251         throws StandardException
252     {
253         boolean found = false;
254         FromTable fromTable;
255         FromTable result = null;
256
257         int size = size();
258         for (int index = 0; index < size; index++)
259         {
260             fromTable = (FromTable) elementAt(index);
261
262             result = fromTable.getFromTableByName(name, schemaName, exactMatch);
263
264             if (result != null)
265             {
266                 return result;
267             }
268         }
269         return result;
270     }
271
272     /**
273      * Bind the tables in this FromList. This includes looking them up in
274      * the DataDictionary, getting their TableDescriptors and assigning the
275      * table numbers.
276      *
277      * @param dataDictionary The DataDictionary to use for binding
278      * @param fromListParam FromList to use/append to.
279      *
280      * @exception StandardException Thrown on error
281      */

282
283     public void bindTables(DataDictionary dataDictionary,
284                             FromList fromListParam)
285             throws StandardException
286     {
287         FromTable fromTable;
288
289         /* Now we bind the tables - this is a 2 step process.
290          * We first bind all of the non-VTIs, then we bind the VTIs.
291          * This enables us to handle the passing of correlation
292          * columns in VTI parameters.
293          * NOTE: We set the table numbers for all of the VTIs in the
294          * first step, when we find them, in order to avoid an ordering
295          * problem with join columns in parameters.
296          */

297         int size = size();
298         for (int index = 0; index < size; index++)
299         {
300             fromTable = (FromTable) elementAt(index);
301             ResultSetNode newNode = fromTable.bindNonVTITables(dataDictionary, fromListParam);
302             // If the fromTable is a view in the SESSION schema, then we need to save that information
303
// in referencesSessionSchema element. The reason for this is that the view will get
304
// replaced by it's view definition and we will loose the information that the statement
305
// was referencing a SESSION schema object.
306
if (fromTable.referencesSessionSchema())
307                 referencesSessionSchema = true;
308             setElementAt(newNode, index);
309         }
310         for (int index = 0; index < size; index++)
311         {
312             fromTable = (FromTable) elementAt(index);
313             ResultSetNode newNode = fromTable.bindVTITables(fromListParam);
314             if (fromTable.referencesSessionSchema())
315                 referencesSessionSchema = true;
316             setElementAt(newNode, index);
317         }
318     }
319
320     /**
321      * Bind the expressions in this FromList. This means
322      * binding the sub-expressions, as well as figuring out what the return
323      * type is for each expression.
324      *
325      * @exception StandardException Thrown on error
326      */

327
328     public void bindExpressions( FromList fromListParam )
329                     throws StandardException
330     {
331         FromTable fromTable;
332
333         int size = size();
334         for (int index = 0; index < size; index++)
335         {
336             fromTable = (FromTable) elementAt(index);
337             fromTable.bindExpressions( makeFromList( fromListParam, fromTable ) );
338         }
339     }
340
341     /**
342      * Construct an appropriate from list for binding an individual
343      * table element. Normally, this is just this list. However,
344      * for the special wrapper queries which the parser creates for
345      * GROUP BY and HAVING clauses, the appropriate list is the
346      * outer list passed into us--it will contain the appropriate
347      * tables needed to resolve correlated columns.
348      */

349     private FromList makeFromList( FromList fromListParam, FromTable fromTable )
350     {
351         if ( fromTable instanceof FromSubquery )
352         {
353             FromSubquery fromSubquery = (FromSubquery) fromTable;
354
355             if ( fromSubquery.generatedForGroupByClause || fromSubquery.generatedForHavingClause )
356             { return fromListParam; }
357         }
358
359         return this;
360     }
361     
362     /**
363      * Bind the result columns of the ResultSetNodes in this FromList when there is no
364      * base table to bind them to. This is useful for SELECT statements,
365      * where the result columns get their types from the expressions that
366      * live under them.
367      *
368      * @param fromListParam FromList to use/append to.
369      *
370      * @exception StandardException Thrown on error
371      */

372
373     public void bindResultColumns(FromList fromListParam)
374                 throws StandardException
375     {
376         FromTable fromTable;
377
378         int origList = fromListParam.size();
379         int size = size();
380         for (int index = 0; index < size; index++)
381         {
382             fromTable = (FromTable) elementAt(index);
383             if (fromTable.needsSpecialRCLBinding())
384                 fromTable.bindResultColumns(fromListParam);
385
386             fromListParam.insertElementAt(fromTable, 0);
387         }
388
389         /* Remove all references added here */
390         while (fromListParam.size() > origList)
391             fromListParam.removeElementAt(0);
392     }
393
394     /**
395      * Returns true if any Outer joins present. Used to set Nullability
396      *
397      * @return True if has any outer joins. False otherwise.
398      */

399     public boolean hasOuterJoins()
400                 throws StandardException
401     {
402         FromTable fromTable;
403
404         int size = size();
405         for (int index = 0; index < size; index++)
406         {
407             fromTable = (FromTable) elementAt(index);
408             if (fromTable instanceof HalfOuterJoinNode)
409                 return true;
410         }
411
412         return false;
413     }
414
415     /**
416      * Expand a "*" into the appropriate ResultColumnList. If the "*"
417      * is unqualified it will expand into a list of all columns in all
418      * of the base tables in the from list, otherwise it will expand
419      * into a list of all of the columns in the base table that matches
420      * the qualification.
421      *
422      * @param allTableName The qualification on the "*" as a String.
423      *
424      * @return ResultColumnList representing expansion
425      *
426      * @exception StandardException Thrown on error
427      */

428     public ResultColumnList expandAll(TableName allTableName)
429             throws StandardException
430     {
431         ResultColumnList resultColumnList = null;
432         ResultColumnList tempRCList = null;
433         boolean matchfound = false;
434         FromTable fromTable;
435  
436         /* Expand the "*" for the table that matches, if it is qualified
437          * (allTableName is not null) or for all tables in the list if the
438          * "*" is not qualified (allTableName is null).
439          */

440         int size = size();
441         for (int index = 0; index < size; index++)
442         {
443             fromTable = (FromTable) elementAt(index);
444
445             /* We let the FromTable decide if there is a match on
446              * the exposed name. (A JoinNode will not have an
447              * exposed name, so it will need to pass the info to its
448              * left and right children.)
449              */

450             tempRCList = fromTable.getAllResultColumns(allTableName);
451
452             if (tempRCList == null)
453             {
454                 continue;
455             }
456
457             /* Expand the column list and append to the list that
458              * we will return.
459              */

460             if (resultColumnList == null)
461             {
462                 resultColumnList = tempRCList;
463             }
464             else
465             {
466                 resultColumnList.nondestructiveAppend(tempRCList);
467             }
468
469             /* If the "*" is qualified, then we can stop the
470              * expansion as soon as we find the matching table.
471              */

472             if (allTableName != null)
473             {
474                 matchfound = true;
475             }
476         }
477
478         /* Give an error if the qualification name did not match
479          * an exposed name
480          */

481         if (resultColumnList == null)
482         {
483             throw StandardException.newException(SQLState.LANG_EXPOSED_NAME_NOT_FOUND, allTableName);
484         }
485
486         return resultColumnList;
487     }
488
489     /**
490      * Bind a column reference to one of the tables in this FromList. The column name
491      * must be unique within the tables in the FromList. An exception is thrown
492      * if a column name is not unique.
493      *
494      * NOTE: Callers are responsible for ordering the FromList by nesting level,
495      * with tables at the deepest (current) nesting level first. We will try to
496      * match against all FromTables at a given nesting level. If no match is
497      * found at a nesting level, then we proceed to the next level. We stop
498      * walking the list when the nesting level changes and we have found a match.
499      *
500      * NOTE: If the ColumnReference is qualified, then we will stop the search
501      * at the first nesting level where there is a match on the exposed table name.
502      * For example, s (a, b, c), t (d, e, f)
503      * select * from s where exists (select * from t s where s.c = a)
504      * will not find a match for s.c, which is the expected ANSI behavior.
505      *
506      * bindTables() must have already been called on this FromList before
507      * calling this method.
508      *
509      * @param columnReference The ColumnReference describing the column to bind
510      *
511      * @return ResultColumn The matching ResultColumn
512      *
513      * @exception StandardException Thrown on error
514      */

515
516     public ResultColumn bindColumnReference(ColumnReference columnReference)
517                 throws StandardException
518     {
519         boolean columnNameMatch = false;
520         boolean tableNameMatch = false;
521         FromTable fromTable;
522         int currentLevel = -1;
523         int previousLevel = -1;
524         ResultColumn matchingRC = null;
525         ResultColumn resultColumn;
526         String JavaDoc crTableName = columnReference.getTableName();
527
528         /*
529         ** Find the first table with matching column name. If there
530         ** is more than one table with a matching column name at the same
531         ** nesting level, give an error.
532         */

533         int size = size();
534         for (int index = 0; index < size; index++)
535         {
536             fromTable = (FromTable) elementAt(index);
537
538             /* We can stop if we've found a matching column or table name
539              * at the previous nesting level.
540              */

541             currentLevel = fromTable.getLevel();
542             if (previousLevel != currentLevel)
543             {
544                 if (columnNameMatch)
545                 {
546                     break;
547                 }
548
549                 if (tableNameMatch)
550                 {
551                     break;
552                 }
553             }
554             /* Simpler to always set previousLevel then to test and set */
555             previousLevel = currentLevel;
556
557             resultColumn = fromTable.getMatchingColumn(columnReference);
558
559             if (resultColumn != null)
560             {
561                 if (! columnNameMatch)
562                 {
563                     /* TableNumbers are set in the CR in the underlying
564                      * FromTable. This ensures that they get the table
565                      * number from the underlying table, not the join node.
566                      * This is important for beging able to push predicates
567                      * down through join nodes.
568                      */

569                     matchingRC = resultColumn;
570                     columnReference.setSource(resultColumn);
571                     columnReference.setType(resultColumn.getTypeServices());
572                     /* Set the nesting level at which the CR appears and the nesting level
573                      * of its source RC.
574                      */

575                     columnReference.setNestingLevel(((FromTable) elementAt(0)).getLevel());
576                     columnReference.setSourceLevel(currentLevel);
577                     columnNameMatch = true;
578
579                     if (fromTable.isPrivilegeCollectionRequired())
580                         getCompilerContext().addRequiredColumnPriv( resultColumn.getTableColumnDescriptor());
581                 }
582                 else
583                 {
584                     throw StandardException.newException(SQLState.LANG_AMBIGUOUS_COLUMN_NAME,
585                              columnReference.getSQLColumnName());
586                 }
587             }
588
589             /* Remember if we get a match on the exposed table name, so that
590              * we can stop at the beginning of the next level.
591              */

592             tableNameMatch = tableNameMatch ||
593                         (crTableName != null &&
594                          crTableName.equals(fromTable.getExposedName()) );
595         }
596
597         return matchingRC;
598     }
599
600     /**
601      * Check for (and reject) all ? parameters directly under the ResultColumns.
602      * This is done for SELECT statements.
603      *
604      * @exception StandardException Thrown if a ? parameter found
605      * directly under a ResultColumn
606      */

607
608     public void rejectParameters() throws StandardException
609     {
610         FromTable fromTable;
611
612         int size = size();
613         for (int index = 0; index < size; index++)
614         {
615             fromTable = (FromTable) elementAt(index);
616             fromTable.rejectParameters();
617         }
618     }
619
620     // This method reorders LOJs in the FROM clause.
621
// For now, we process only a LOJ. For example, "... from LOJ_1, LOJ2 ..."
622
// will not be processed.
623
public boolean LOJ_reorderable(int numTables) throws StandardException
624     {
625         boolean anyChange = false;
626
627         if (size() > 1) return anyChange;
628
629         FromTable ft = (FromTable) elementAt(0);
630
631         anyChange = ft.LOJ_reorderable(numTables);
632
633         return anyChange;
634     }
635
636     /**
637      * Preprocess the query tree - this currently means:
638      * o Generating a referenced table map for each ResultSetNode.
639      * o Putting the WHERE and HAVING clauses in conjunctive normal form (CNF).
640      * o Converting the WHERE and HAVING clauses into PredicateLists and
641      * classifying them.
642      * o Flatten those FromSubqueries which can be flattened.
643      * o Ensuring that a ProjectRestrictNode is generated on top of every
644      * FromBaseTable and generated in place of every FromSubquery which
645      * could not be flattened.
646      * o Pushing single table predicates down to the new ProjectRestrictNodes.
647      *
648      * @param numTables The number of tables in the DML Statement
649      * @param gbl The group by list, if any
650      *
651      * @exception StandardException Thrown on error
652      */

653     public void preprocess(int numTables,
654                            GroupByList gbl,
655                            ValueNode predicateTree)
656                                 throws StandardException
657     {
658         int size = size();
659
660         /* Preprocess each FromTable in the list */
661         for (int index = 0; index < size; index++)
662         {
663             FromTable ft = (FromTable) elementAt(index);
664
665             /* Transform any outer joins to inner joins where appropriate */
666             ft = ft.transformOuterJoins(predicateTree, numTables);
667             /* Preprocess this FromTable */
668             setElementAt(ft.preprocess(numTables, gbl, this), index);
669         }
670     }
671
672     /**
673      * Flatten all the FromTables that are flattenable.
674      * RESOLVE - right now we just flatten FromSubqueries. We
675      * should also flatten flattenable JoinNodes here.
676      *
677      * @param rcl The RCL from the outer query
678      * @param predicateList The PredicateList from the outer query
679      * @param sql The SubqueryList from the outer query
680      * @param gbl The group by list, if any
681      *
682      * @exception StandardException Thrown on error
683      */

684     public void flattenFromTables(ResultColumnList rcl,
685                                   PredicateList predicateList,
686                                   SubqueryList sql,
687                                   GroupByList gbl)
688                                     throws StandardException
689     {
690         boolean flattened = true;
691         Vector JavaDoc flattenedTableNumbers = new Vector JavaDoc();
692
693         if (SanityManager.DEBUG)
694         {
695             SanityManager.ASSERT(rcl != null,
696                              "rcl is expected to be non-null");
697             SanityManager.ASSERT(predicateList != null,
698                              "predicateList is expected to be non-null");
699             SanityManager.ASSERT(sql != null,
700                              "sql is expected to be non-null");
701         }
702
703         /* Loop until all flattenable entries are flattened.
704          * We restart the inner loop after flattening an in place
705          * to simplify the logic and so that we don't have to worry
706          * about walking a list while we are modifying it.
707          */

708         while (flattened)
709         {
710             flattened = false;
711
712             for (int index = 0; index < size() && ! flattened; index++)
713             {
714                 FromTable ft = (FromTable) elementAt(index);
715
716                 /* Flatten FromSubquerys and flattenable JoinNodes */
717                 if ((ft instanceof FromSubquery) ||
718                     ft.isFlattenableJoinNode())
719                 {
720                     //save the table number of the node to be flattened
721
flattenedTableNumbers.addElement(new Integer JavaDoc(ft.getTableNumber()));
722
723                     /* Remove the node from the list and insert its
724                      * FromList here.
725                      */

726                     FromList flatteningFL = ft.flatten(
727                                                         rcl,
728                                                         predicateList,
729                                                         sql,
730                                                         gbl);
731                     if (SanityManager.DEBUG)
732                     {
733                         SanityManager.ASSERT(flatteningFL == null ||
734                                              flatteningFL.size() > 0,
735                             "flatteningFL expected to be null or size > 0");
736                     }
737
738                     if (flatteningFL != null)
739                     {
740                         setElementAt(flatteningFL.elementAt(0), index);
741
742                         int innerSize = flatteningFL.size();
743                         for (int inner = 1; inner < innerSize; inner++)
744                         {
745                             insertElementAt(flatteningFL.elementAt(inner), index + inner);
746                         }
747                     }
748                     else
749                     {
750                         /*
751                         ** If flatten returns null, that means it wants to
752                         ** be removed from the FromList.
753                         */

754                         removeElementAt(index);
755                     }
756                     flattened = true;
757                 }
758             }
759         }
760         
761         /* fix up dependency maps for exists base tables since they might have a
762          * dependency on this join node
763          */

764         if (flattenedTableNumbers.size() > 0)
765         {
766             for (int i = 0; i < size(); i++)
767             {
768                 FromTable ft = (FromTable) elementAt(i);
769                 if (ft instanceof ProjectRestrictNode)
770                 {
771                     ResultSetNode rst = ((ProjectRestrictNode)ft).getChildResult();
772                     if (rst instanceof FromBaseTable)
773                     {
774                         ((FromBaseTable)rst).clearDependency(flattenedTableNumbers);
775                     }
776                 }
777             }
778         }
779     }
780
781     /**
782      * Categorize and push the predicates that are pushable.
783      *
784      * @param predicateList The query's PredicateList
785      *
786      * @exception StandardException Thrown on error
787      */

788     void pushPredicates(PredicateList predicateList)
789             throws StandardException
790     {
791         if (SanityManager.DEBUG)
792         {
793             SanityManager.ASSERT(predicateList != null,
794                              "predicateList is expected to be non-null");
795         }
796
797         /* We can finally categorize each Predicate and try to push them down.
798          * NOTE: The PredicateList may be empty, but that's okay, we still
799          * call pushExpressions() for each entry in the FromList because that's
800          * where any outer join conditions will get pushed down.
801          */

802         predicateList.categorize();
803
804         int size = size();
805         for (int index = 0; index < size; index++)
806         {
807             FromTable fromTable = (FromTable) elementAt(index);
808             fromTable.pushExpressions(predicateList);
809         }
810     }
811
812     /**
813      * Prints the sub-nodes of this object. See QueryTreeNode.java for
814      * how tree printing is supposed to work.
815      *
816      * @param depth The depth of this node in the tree
817      */

818
819     public void printSubNodes(int depth)
820     {
821         if (SanityManager.DEBUG)
822         {
823             FromTable fromTable;
824
825             super.printSubNodes(depth);
826
827             int size = size();
828             for (int index = 0; index < size; index++)
829             {
830                 fromTable = (FromTable) elementAt(index);
831                 fromTable.treePrint(depth + 1);
832             }
833         }
834     }
835
836     /**
837      * Set the (query block) level (0-based) for the FromTables in this
838      * FromList.
839      *
840      * @param level The query block level for this table.
841      */

842     public void setLevel(int level)
843     {
844         int size = size();
845         for (int index = 0; index < size; index++)
846         {
847             FromTable fromTable = (FromTable) elementAt(index);
848             fromTable.setLevel(level);
849         }
850     }
851
852     /**
853      * Get the FromTable from this list which has the specified ResultColumn in
854      * its RCL.
855      *
856      * @param rc The ResultColumn match on.
857      *
858      * @return FromTable The matching FromTable.
859      */

860     public FromTable getFromTableByResultColumn(ResultColumn rc)
861     {
862         FromTable fromTable = null;
863
864         int size = size();
865         for (int index = 0; index < size; index++)
866         {
867             fromTable = (FromTable) elementAt(index);
868
869             if (fromTable.getResultColumns().indexOf(rc) != -1)
870             {
871                 break;
872             }
873         }
874
875         if (SanityManager.DEBUG)
876         {
877             SanityManager.ASSERT(fromTable != null,
878                 "No matching FromTable found");
879         }
880         return fromTable;
881     }
882
883     /**
884      * Set the Properties list for this FromList.
885      *
886      * @exception StandardException Thrown on error
887      */

888     public void setProperties(Properties JavaDoc props) throws StandardException
889     {
890         properties = props;
891
892         /*
893         ** Validate the properties list now. This is possible because
894         ** there is nothing in this properties list that relies on binding
895         ** or optimization to validate.
896         */

897         Enumeration JavaDoc e = properties.keys();
898         while (e.hasMoreElements())
899         {
900             String JavaDoc key = (String JavaDoc) e.nextElement();
901             String JavaDoc value = (String JavaDoc) properties.get(key);
902
903             if (key.equals("joinOrder"))
904             {
905                 if (StringUtil.SQLEqualsIgnoreCase(value,"fixed"))
906                 {
907                     fixedJoinOrder = true;
908                 }
909                 else if (StringUtil.SQLEqualsIgnoreCase(value,"unfixed"))
910                 {
911                     fixedJoinOrder = false;
912                 }
913                 else
914                 {
915                     throw StandardException.newException(SQLState.LANG_INVALID_JOIN_ORDER_SPEC, value);
916                 }
917             }
918             else if (key.equals("useStatistics"))
919             {
920                 if (StringUtil.SQLEqualsIgnoreCase(value,"true"))
921                 {
922                     useStatistics = true;
923                 }
924                 else if (StringUtil.SQLEqualsIgnoreCase(value,"false"))
925                 {
926                     useStatistics = false;
927                 }
928                 else
929                 {
930                     throw StandardException.newException(SQLState.LANG_INVALID_STATISTICS_SPEC, value);
931                 }
932             }
933             else
934             {
935                 throw StandardException.newException(SQLState.LANG_INVALID_FROM_LIST_PROPERTY, key, value);
936             }
937         }
938     }
939
940     /** @see OptimizableList#reOrder */
941     public void reOrder(int[] joinOrder)
942     {
943         int posn;
944
945         if (SanityManager.DEBUG)
946         {
947             if (joinOrder.length != size())
948             {
949                 SanityManager.THROWASSERT("In reOrder(), size of FromList is " + size() + " while size of joinOrder array is " + joinOrder.length);
950             }
951
952             /*
953             ** Determine that the values in the list are unique and in range.
954             ** The easiest way to determine that they are unique is to add
955             ** them all up and see whether the result is what's expected
956             ** for that array size.
957             */

958             int sum = 0;
959             for (int i = 0; i < joinOrder.length; i++)
960             {
961                 if (joinOrder[i] < 0 || joinOrder[i] > (joinOrder.length - 1))
962                 {
963                     SanityManager.THROWASSERT("joinOrder[" + i + "] == " +
964                                             joinOrder[i] +
965                                             " is out of range - must be between 0 and " +
966                                             (joinOrder.length - 1) +
967                                             " inclusive.");
968                 }
969
970                 sum += joinOrder[i];
971             }
972
973             /*
974             ** The sum of all integers from 0 through n is (n * (n - 1)) / 2.
975             */

976             if (sum != ( ( joinOrder.length * (joinOrder.length - 1) ) / 2) )
977             {
978                 String JavaDoc arrayVals = "";
979                 for (int i = 0; i < joinOrder.length; i++)
980                     arrayVals = arrayVals + joinOrder[i] + " ";
981                 SanityManager.THROWASSERT("joinOrder array has some duplicate value: " + arrayVals);
982             }
983         }
984
985         /* Form a list that's in the order we want */
986         QueryTreeNode[] orderedFL = new FromTable[joinOrder.length];
987         for (posn = 0; posn < joinOrder.length; posn++)
988         {
989             /*
990             ** Get the element at the i'th join order position from the
991             ** current list and make it the next element of orderedList.
992             */

993             orderedFL[posn] = elementAt(joinOrder[posn]);
994         }
995
996         /* Now orderedList has been built, so set this list to the same order */
997         for (posn = 0; posn < joinOrder.length; posn++)
998         {
999             setElementAt(orderedFL[posn], posn);
1000        }
1001    }
1002
1003    /** @see OptimizableList#useStatistics */
1004    public boolean useStatistics()
1005    {
1006        return useStatistics;
1007    }
1008
1009    /** @see OptimizableList#optimizeJoinOrder */
1010    public boolean optimizeJoinOrder()
1011    {
1012        return ! fixedJoinOrder;
1013    }
1014
1015    /** @see OptimizableList#legalJoinOrder */
1016    public boolean legalJoinOrder(int numTablesInQuery)
1017    {
1018        JBitSet assignedTableMap = new JBitSet(numTablesInQuery);
1019
1020        int size = size();
1021        for (int index = 0; index < size; index++)
1022        {
1023            FromTable ft = (FromTable) elementAt(index);
1024            assignedTableMap.or(ft.getReferencedTableMap());
1025            if ( ! ft.legalJoinOrder(assignedTableMap))
1026            {
1027                return false;
1028            }
1029        }
1030        return true;
1031    }
1032
1033    /** @see OptimizableList#initAccessPaths */
1034    public void initAccessPaths(Optimizer optimizer)
1035    {
1036        int size = size();
1037        for (int index = 0; index < size; index++)
1038        {
1039            FromTable ft = (FromTable) elementAt(index);
1040            ft.initAccessPaths(optimizer);
1041        }
1042    }
1043
1044    /**
1045     * Bind any untyped null nodes to the types in the given ResultColumnList.
1046     *
1047     * @param bindingRCL The ResultColumnList with the types to bind to.
1048     *
1049     * @exception StandardException Thrown on error
1050     */

1051    public void bindUntypedNullsToResultColumns(ResultColumnList bindingRCL)
1052                throws StandardException
1053    {
1054        int size = size();
1055        for (int index = 0; index < size; index++)
1056        {
1057            FromTable fromTable = (FromTable) elementAt(index);
1058            fromTable.bindUntypedNullsToResultColumns(bindingRCL);
1059        }
1060    }
1061
1062    /**
1063     * Decrement (query block) level (0-based) for
1064     * all of the tables in this from list.
1065     * This is useful when flattening a subquery.
1066     *
1067     * @param decrement The amount to decrement by.
1068     */

1069    void decrementLevel(int decrement)
1070    {
1071        int size = size();
1072        for (int index = 0; index < size; index++)
1073        {
1074            FromTable fromTable = (FromTable) elementAt(index);
1075            fromTable.decrementLevel(decrement);
1076
1077            /* Decrement the level of any CRs in single table
1078             * predicates that are interesting to transitive
1079             * closure.
1080             */

1081            ProjectRestrictNode prn = (ProjectRestrictNode) fromTable;
1082            PredicateList pl = prn.getRestrictionList();
1083            if (pl != null)
1084            {
1085                pl.decrementLevel(this, decrement);
1086            }
1087        }
1088    }
1089
1090
1091    /**
1092     * This method is used for both subquery flattening and distinct
1093     * elimination based on a uniqueness condition. For subquery
1094     * flattening we want to make sure that the query block
1095     * will return at most 1 row. For distinct elimination we
1096     * want to make sure that the query block will not return
1097     * any duplicates.
1098     * This is true if every table in the from list is
1099     * (a base table and the set of columns from the table that
1100     * are in equality comparisons with expressions that do not include columns
1101     * from the same table is a superset of any unique index
1102     * on the table) or an EXISTS FBT. In addition, at least 1 of the tables
1103     * in the list has a set of columns in equality comparisons with expressions
1104     * that do not include column references from the same query block
1105     * is a superset of a unique index
1106     * on that table. (This ensures that the query block will onlyr
1107     * return a single row.)
1108     * This method is expected to be called after normalization and
1109     * after the from list has been preprocessed.
1110     * It can be called both before and after the predicates have
1111     * been pulled from the where clause.
1112     * The algorithm for this is as follows
1113     *
1114     * If any table in the query block is not a base table, give up.
1115     * For each table in the query
1116     * Ignore exists table since they can only produce one row
1117     *
1118     * create a matrix of tables and columns from the table (tableColMap)
1119     * (this is used to keep track of the join columns and constants
1120     * that can be used to figure out whether the rows from a join
1121     * or in a select list are distinct based on unique indexes)
1122     *
1123     * create an array of columns from the table(eqOuterCol)
1124     * (this is used to determine that only one row will be returned
1125     * from a join)
1126     *
1127     * if the current table is the table for the result columns
1128     * set the result columns in the eqOuterCol and tableColMap
1129     * (if these columns are a superset of a unique index and
1130     * all joining tables result in only one row, the
1131     * results will be distinct)
1132     * go through all the predicates and update tableColMap and
1133     * eqOuterCol with join columns and correlation variables,
1134     * parameters and constants
1135     * since setting constants, correlation variables and parameters,
1136     * reduces the number of columns required for uniqueness in a
1137     * multi-column index, they are set for all the tables (if the
1138     * table is not the result table, in this case only the column of the
1139     * result table is set)
1140     * join columns are just updated for the column in the row of the
1141     * joining table.
1142     *
1143     * check if the marked columns in tableColMap are a superset of a unique
1144     * index
1145     * (This means that the join will only produce 1 row when joined
1146     * with 1 row of another table)
1147     * check that there is a least one table for which the columns in
1148     * eqOuterCol(i.e. constant values) are a superset of a unique index
1149     * (This quarantees that there will be only one row selected
1150     * from this table).
1151     *
1152     * Once all tables have been evaluated, check that all the tables can be
1153     * joined by unique index or will have only one row
1154     *
1155     *
1156     *
1157     * @param rcl If non-null, the RCL from the query block.
1158     * If non-null for subqueries, then entry can
1159     * be considered as part of an = comparison.
1160     * @param whereClause The WHERE clause to consider.
1161     * @param wherePredicates The predicates that have already been
1162     * pulled from the WHERE clause.
1163     * @param dd The DataDictionary to use.
1164     *
1165     * @return Whether or not query block will return
1166     * at most 1 row for a subquery, no duplicates
1167     * for a distinct.
1168     *
1169     * @exception StandardException Thrown on error
1170     */

1171    boolean returnsAtMostSingleRow(ResultColumnList rcl,
1172                                   ValueNode whereClause,
1173                                   PredicateList wherePredicates,
1174                                   DataDictionary dd)
1175        throws StandardException
1176    {
1177        boolean satisfiesOuter = false;
1178        int[] tableNumbers;
1179        ColumnReference additionalCR = null;
1180
1181        PredicateList predicatesTemp;
1182        predicatesTemp = (PredicateList) getNodeFactory().getNode(
1183            C_NodeTypes.PREDICATE_LIST, getContextManager());
1184        int wherePredicatesSize = wherePredicates.size();
1185        for (int index = 0; index < wherePredicatesSize; index++)
1186            predicatesTemp.addPredicate((Predicate)wherePredicates.elementAt(index));
1187
1188        /* When considering subquery flattening, we are interested
1189         * in the 1st (and only) entry in the RCL. (The RCL will be
1190         * null if result column is not of interest for subquery flattening.)
1191         * We are interested in all entries in the RCL for distinct
1192         * elimination.
1193         */

1194        if (rcl != null)
1195        {
1196            ResultColumn rc = (ResultColumn) rcl.elementAt(0);
1197            if (rc.getExpression() instanceof ColumnReference)
1198            {
1199                additionalCR = (ColumnReference) rc.getExpression();
1200            }
1201        }
1202
1203        /* First see if all entries are FromBaseTables. No point
1204         * in continuing if not.
1205         */

1206        int size = size();
1207        for (int index = 0; index < size; index++)
1208        {
1209            FromTable fromTable = (FromTable) elementAt(index);
1210            if (! (fromTable instanceof ProjectRestrictNode))
1211            {
1212                return false;
1213            }
1214
1215            ProjectRestrictNode prn = (ProjectRestrictNode) fromTable;
1216
1217            if (! (prn.getChildResult() instanceof FromBaseTable))
1218            {
1219                return false;
1220            }
1221            FromBaseTable fbt = (FromBaseTable) prn.getChildResult();
1222            //Following for loop code is to take care of Derby-251 (DISTINCT returns
1223
//duplicate rows).
1224
//Derby-251 returned duplicate rows because we were looking at predicates
1225
//that belong to existsTable to determine DISTINCT elimination
1226
//
1227
//(Check method level comments to understand DISTINCT elimination rules.)
1228
//
1229
//For one specific example, consider the query below
1230
//select distinct q1."NO1" from IDEPT q1, IDEPT q2
1231
//where ( q2."DISCRIM_DEPT" = 'HardwareDept')
1232
//and ( q1."DISCRIM_DEPT" = 'SoftwareDept') and ( q1."NO1" <> ALL
1233
//(select q3."NO1" from IDEPT q3 where (q3."REPORTTO_NO" = q2."NO1")))
1234
//(select q3."NO1" from IDEPT q3 where ( ABS(q3."REPORTTO_NO") = q2."NO1")))
1235
//
1236
//Table IDEPT in the query above has a primary key defined on column "NO1"
1237
//This query gets converted to following during optimization
1238
//
1239
//select distinct q1."NO1" from IDEPT q1, IDEPT q2
1240
//where ( q2."DISCRIM_DEPT" = 'HardwareDept')
1241
//and ( q1."DISCRIM_DEPT" = 'SoftwareDept') and not exists (
1242
//(select q3."NO1" from IDEPT q3 where
1243
//( ( ABS(q3."REPORTTO_NO") = q2."NO1") and q3."NO1" = q1."NO1") ) ) ;
1244
//
1245
//For the optimized query above, Derby generates following predicates.
1246
//ABS(q3.reportto_no) = q2.no1
1247
//q2.discrim_dept = 'HardwareDept'
1248
//q1.descrim_dept = 'SoftwareDept'
1249
//q1.no1 = q3.no1
1250
//The predicate ABS(q3."NO1") = q1."NO1" should not be considered when trying
1251
//to determine if q1 in the outer query has equality comparisons.
1252
//Similarly, the predicate q3.reportto_no = q2.no1 should not be
1253
//considered when trying to determine if q2 in the outer query has
1254
//equality comparisons. To achieve this, predicates based on exists base
1255
//table q3 (the first and the last predicate) should be removed while
1256
//evaluating outer query for uniqueness.
1257
//
1258
if (fbt.getExistsBaseTable())
1259            {
1260                int existsTableNumber = fbt.getTableNumber();
1261                int predicatesTempSize = predicatesTemp.size();
1262                for (int predicatesTempIndex = predicatesTempSize-1;
1263                    predicatesTempIndex >= 0; predicatesTempIndex--)
1264                {
1265                    AndNode topAndNode = (AndNode)
1266                        ((Predicate) predicatesTemp.elementAt(predicatesTempIndex)).getAndNode();
1267
1268                    for (ValueNode whereWalker = topAndNode; whereWalker instanceof AndNode;
1269                        whereWalker = ((AndNode) whereWalker).getRightOperand())
1270                    {
1271                        // See if this is a candidate =
1272
AndNode and = (AndNode) whereWalker;
1273
1274                        //we only need to worry about equality predicates because only those
1275
//predicates are considered during DISTINCT elimination.
1276
if (!and.getLeftOperand().isRelationalOperator() ||
1277                            !(((RelationalOperator)(and.getLeftOperand())).getOperator() ==
1278                            RelationalOperator.EQUALS_RELOP))
1279                        {
1280                            continue;
1281                        }
1282
1283                        JBitSet referencedTables = and.getLeftOperand().getTablesReferenced();
1284                        if (referencedTables.get(existsTableNumber))
1285                        {
1286                            predicatesTemp.removeElementAt(predicatesTempIndex);
1287                            break;
1288                        }
1289                    }
1290                }
1291            }
1292        }
1293
1294        /* Build an array of tableNumbers from this query block.
1295         * We will use that array to find out if we have at least
1296         * one table with a uniqueness condition based only on
1297         * constants, parameters and correlation columns.
1298         */

1299        tableNumbers = getTableNumbers();
1300        JBitSet[][] tableColMap = new JBitSet[size][size];
1301        boolean[] oneRow = new boolean[size];
1302        boolean oneRowResult = false;
1303
1304        /* See if each table has a uniqueness condition */
1305        for (int index = 0; index < size; index++)
1306        {
1307            ProjectRestrictNode prn = (ProjectRestrictNode) elementAt(index);
1308            FromBaseTable fbt = (FromBaseTable) prn.getChildResult();
1309
1310            // Skip over EXISTS FBT since they cannot introduce duplicates
1311
if (fbt.getExistsBaseTable())
1312            {
1313                oneRow[index] = true;
1314                continue;
1315            }
1316
1317            int numColumns = fbt.getTableDescriptor().getNumberOfColumns();
1318            boolean[] eqOuterCols = new boolean[numColumns + 1];
1319            int tableNumber = fbt.getTableNumber();
1320            boolean resultColTable = false;
1321            for (int i = 0; i < size; i++)
1322                tableColMap[index][i] = new JBitSet(numColumns + 1);
1323
1324            if (additionalCR != null &&
1325                additionalCR.getTableNumber() == tableNumber)
1326            {
1327                rcl.recordColumnReferences(eqOuterCols, tableColMap[index], index);
1328                resultColTable = true;
1329            }
1330
1331            /* Now see if there are any equality conditions
1332             * of interest in the where clause.
1333             */

1334            if (whereClause != null)
1335            {
1336                whereClause.checkTopPredicatesForEqualsConditions(
1337                                tableNumber, eqOuterCols, tableNumbers,
1338                                tableColMap[index], resultColTable);
1339            }
1340
1341            /* Now see if there are any equality conditions
1342             * of interest in the where predicates.
1343             */

1344            predicatesTemp.checkTopPredicatesForEqualsConditions(
1345                                tableNumber, eqOuterCols, tableNumbers,
1346                                tableColMap[index], resultColTable);
1347
1348            /* Now see if there are any equality conditions
1349             * of interest that were already pushed down to the
1350             * PRN above the FBT. (Single table predicates.)
1351             */

1352            if (prn.getRestrictionList() != null)
1353            {
1354                prn.getRestrictionList().checkTopPredicatesForEqualsConditions(
1355                                tableNumber, eqOuterCols, tableNumbers,
1356                                tableColMap[index], resultColTable);
1357            }
1358
1359            /* We can finally check to see if the marked columns
1360             * are a superset of any unique index.
1361             */

1362            if (! fbt.supersetOfUniqueIndex(tableColMap[index]))
1363            {
1364                return false;
1365            }
1366            
1367            /* Do we have at least 1 table whose equality condition
1368             * is based solely on constants, parameters and correlation columns.
1369             */

1370            oneRowResult = fbt.supersetOfUniqueIndex(eqOuterCols);
1371            if (oneRowResult)
1372            {
1373                oneRow[index] = true;
1374                satisfiesOuter = true;
1375            }
1376        }
1377
1378        /* Have we met all of the criteria */
1379        if (satisfiesOuter)
1380        {
1381            /* check that all the tables are joined by unique indexes
1382             * or only produce 1 row
1383             */

1384            boolean foundOneRow = true;
1385            while (foundOneRow)
1386            {
1387                foundOneRow = false;
1388                for (int index = 0; index < size; index++)
1389                {
1390                    if (oneRow[index])
1391                    {
1392                        for (int i = 0; i < size; i++)
1393                        {
1394                            /* unique key join - exists tables already marked as
1395                             * 1 row - so don't need to look at them
1396                             */

1397                            if (!oneRow[i] && tableColMap[i][index].get(0))
1398                            {
1399                                oneRow[i] = true;
1400                                foundOneRow = true;
1401                            }
1402                        }
1403                    }
1404                }
1405            }
1406            /* does any table produce more than one row */
1407            for (int index = 0; index < size; index++)
1408            {
1409                if (!oneRow[index])
1410                {
1411                    satisfiesOuter = false;
1412                    break;
1413                }
1414            }
1415        }
1416        return satisfiesOuter;
1417    }
1418
1419    int[] getTableNumbers()
1420    {
1421        int size = size();
1422        int[] tableNumbers = new int[size];
1423        for (int index = 0; index < size; index++)
1424        {
1425            ProjectRestrictNode prn = (ProjectRestrictNode) elementAt(index);
1426            if (! (prn.getChildResult() instanceof FromTable))
1427            {
1428                continue;
1429            }
1430            FromTable ft = (FromTable) prn.getChildResult();
1431            tableNumbers[index] = ft.getTableNumber();
1432        }
1433
1434        return tableNumbers;
1435    }
1436
1437    /**
1438     * Mark all of the FromBaseTables in the list as EXISTS FBTs.
1439     * Each EBT has the same dependency list - those tables that are referenced
1440     * minus the tables in the from list.
1441     *
1442     * @param referencedTableMap The referenced table map.
1443     * @param outerFromList FromList from outer query block
1444     * @param isNotExists Whether or not for NOT EXISTS
1445     *
1446     * @exception StandardException Thrown on error
1447     */

1448    void genExistsBaseTables(JBitSet referencedTableMap, FromList outerFromList,
1449                             boolean isNotExists)
1450        throws StandardException
1451    {
1452        JBitSet dependencyMap = (JBitSet) referencedTableMap.clone();
1453
1454        // We currently only flatten single table from lists
1455
if (SanityManager.DEBUG)
1456        {
1457            if (size() != 1)
1458            {
1459                SanityManager.THROWASSERT(
1460                    "size() expected to be 1, not " + size());
1461            }
1462        }
1463
1464        /* Create the dependency map */
1465        int size = size();
1466        for (int index = 0; index < size; index++)
1467        {
1468            ResultSetNode ft = ((ProjectRestrictNode) elementAt(index)).getChildResult();
1469            if (ft instanceof FromTable)
1470            {
1471                dependencyMap.clear(((FromTable) ft).getTableNumber());
1472            }
1473        }
1474
1475        /* Degenerate case - If flattening a non-correlated EXISTS subquery
1476         * then we need to make the table that is getting flattened dependendent on
1477         * all of the tables in the outer query block. Gross but true. Otherwise
1478         * that table can get chosen as an outer table and introduce duplicates.
1479         * The reason that duplicates can be introduced is that we do special processing
1480         * in the join to make sure only one qualified row from the right side is
1481         * returned. If the exists table is on the left, we can return all the
1482         * qualified rows.
1483         */

1484        if (dependencyMap.getFirstSetBit() == -1)
1485        {
1486            int outerSize = outerFromList.size();
1487            for (int outer = 0; outer < outerSize; outer++)
1488                dependencyMap.or(((FromTable) outerFromList.elementAt(outer)).getReferencedTableMap());
1489        }
1490
1491        /* Do the marking */
1492        for (int index = 0; index < size; index++)
1493        {
1494            FromTable fromTable = (FromTable) elementAt(index);
1495            if (fromTable instanceof ProjectRestrictNode)
1496            {
1497                ProjectRestrictNode prn = (ProjectRestrictNode) fromTable;
1498                if (prn.getChildResult() instanceof FromBaseTable)
1499                {
1500                    FromBaseTable fbt = (FromBaseTable) prn.getChildResult();
1501                    fbt.setExistsBaseTable(true, (JBitSet) dependencyMap.clone(), isNotExists);
1502                }
1503            }
1504        }
1505    }
1506
1507    /**
1508     * Get the lock mode for the target of an update statement
1509     * (a delete or update). The update mode will always be row for
1510     * CurrentOfNodes. It will be table if there is no where clause.
1511     *
1512     * @return The lock mode
1513     */

1514    public int updateTargetLockMode()
1515    {
1516        if (SanityManager.DEBUG)
1517        {
1518            if (size() != 1)
1519            {
1520                SanityManager.THROWASSERT(
1521                    "size() expected to be 1");
1522            }
1523        }
1524        return ((ResultSetNode) elementAt(0)).updateTargetLockMode();
1525    }
1526
1527    /**
1528     * Return whether or not the user specified a hash join for any of the
1529     * tables in this list.
1530     *
1531     * @return Whether or not the user specified a hash join for any of the
1532     * tables in this list.
1533     */

1534    boolean hashJoinSpecified()
1535    {
1536        int size = size();
1537        for (int index = 0; index < size; index++)
1538        {
1539            FromTable ft = (FromTable) elementAt(index);
1540            String JavaDoc joinStrategy = ft.getUserSpecifiedJoinStrategy();
1541
1542            if (joinStrategy != null && StringUtil.SQLToUpperCase(joinStrategy).equals("HASH"))
1543            {
1544                return true;
1545            }
1546        }
1547
1548        return false;
1549    }
1550
1551    /**
1552     * Accept a visitor, and call v.visit()
1553     * on child nodes as necessary.
1554     *
1555     * @param v the visitor
1556     *
1557     * @exception StandardException on error
1558     */

1559    public Visitable accept(Visitor v)
1560        throws StandardException
1561    {
1562        int size = size();
1563        for (int index = 0; index < size; index++)
1564        {
1565            FromTable fromTable = (FromTable) elementAt(index);
1566            setElementAt((QueryTreeNode) fromTable.accept(v), index);
1567        }
1568
1569        return this;
1570    }
1571}
1572
Popular Tags