0001: /*
0002:
0003: Derby - Class org.apache.derby.impl.sql.compile.FromList
0004:
0005: Licensed to the Apache Software Foundation (ASF) under one or more
0006: contributor license agreements. See the NOTICE file distributed with
0007: this work for additional information regarding copyright ownership.
0008: The ASF licenses this file to you under the Apache License, Version 2.0
0009: (the "License"); you may not use this file except in compliance with
0010: the License. You may obtain a copy of the License at
0011:
0012: http://www.apache.org/licenses/LICENSE-2.0
0013:
0014: Unless required by applicable law or agreed to in writing, software
0015: distributed under the License is distributed on an "AS IS" BASIS,
0016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0017: See the License for the specific language governing permissions and
0018: limitations under the License.
0019:
0020: */
0021:
0022: package org.apache.derby.impl.sql.compile;
0023:
0024: import org.apache.derby.iapi.services.sanity.SanityManager;
0025:
0026: import org.apache.derby.iapi.sql.compile.CompilerContext;
0027: import org.apache.derby.iapi.sql.compile.Optimizable;
0028: import org.apache.derby.iapi.sql.compile.OptimizableList;
0029: import org.apache.derby.iapi.sql.compile.Optimizer;
0030: import org.apache.derby.iapi.sql.compile.Visitable;
0031: import org.apache.derby.iapi.sql.compile.Visitor;
0032: import org.apache.derby.iapi.sql.compile.C_NodeTypes;
0033:
0034: import org.apache.derby.iapi.sql.dictionary.DataDictionary;
0035:
0036: import org.apache.derby.iapi.error.StandardException;
0037:
0038: import org.apache.derby.iapi.reference.SQLState;
0039:
0040: import org.apache.derby.iapi.util.JBitSet;
0041: import org.apache.derby.iapi.util.StringUtil;
0042:
0043: import java.util.Properties;
0044: import java.util.Enumeration;
0045: import java.util.Vector;
0046:
0047: /**
0048: * A FromList represents the list of tables in a FROM clause in a DML
0049: * statement. It extends QueryTreeNodeVector.
0050: *
0051: * @author Jeff Lichtman
0052: */
0053:
0054: public class FromList extends QueryTreeNodeVector implements
0055: OptimizableList {
0056: Properties properties;
0057: // RESOLVE: The default should be false
0058: boolean fixedJoinOrder = true;
0059: // true by default.
0060: boolean useStatistics = true;
0061:
0062: // FromList could have a view in it's list. If the view is defined in SESSION
0063: // schema, then we do not want to cache the statement's plan. This boolean
0064: // will help keep track of such a condition.
0065: private boolean referencesSessionSchema;
0066:
0067: /** Initializer for a FromList */
0068:
0069: public void init(Object optimizeJoinOrder) {
0070: fixedJoinOrder = !(((Boolean) optimizeJoinOrder).booleanValue());
0071: }
0072:
0073: /**
0074: * Initializer for a FromList
0075: *
0076: * @exception StandardException Thrown on error
0077: */
0078: public void init(Object optimizeJoinOrder, Object fromTable)
0079: throws StandardException {
0080: init(optimizeJoinOrder);
0081:
0082: addFromTable((FromTable) fromTable);
0083: }
0084:
0085: /*
0086: * OptimizableList interface
0087: */
0088:
0089: /**
0090: * @see org.apache.derby.iapi.sql.compile.OptimizableList#getOptimizable
0091: */
0092: public Optimizable getOptimizable(int index) {
0093: return (Optimizable) elementAt(index);
0094: }
0095:
0096: /**
0097: * @see org.apache.derby.iapi.sql.compile.OptimizableList#setOptimizable
0098: */
0099: public void setOptimizable(int index, Optimizable optimizable) {
0100: setElementAt((FromTable) optimizable, index);
0101: }
0102:
0103: /**
0104: * @see OptimizableList#verifyProperties
0105: * @exception StandardException Thrown on error
0106: */
0107: public void verifyProperties(DataDictionary dDictionary)
0108: throws StandardException {
0109: int size = size();
0110: for (int index = 0; index < size; index++) {
0111: ((Optimizable) elementAt(index))
0112: .verifyProperties(dDictionary);
0113: }
0114: }
0115:
0116: /**
0117: * Add a table to the FROM list.
0118: *
0119: * @param fromTable A FromTable to add to the list
0120: *
0121: * @exception StandardException Thrown on error
0122: */
0123:
0124: public void addFromTable(FromTable fromTable)
0125: throws StandardException {
0126: /* Don't worry about checking TableOperatorNodes since
0127: * they don't have exposed names. This will potentially
0128: * allow duplicate exposed names in some degenerate cases,
0129: * but the binding of the ColumnReferences will catch those
0130: * cases with a different error. If the query does not have
0131: * any ColumnReferences from the duplicate exposed name, the
0132: * user is executing a really dumb query and we won't throw
0133: * and exception - consider it an ANSI extension.
0134: */
0135: TableName leftTable = null;
0136: TableName rightTable = null;
0137: if (!(fromTable instanceof TableOperatorNode)) {
0138: /* Check for duplicate table name in FROM list */
0139: int size = size();
0140: for (int index = 0; index < size; index++) {
0141: leftTable = fromTable.getTableName();
0142:
0143: if (((FromTable) elementAt(index)) instanceof TableOperatorNode) {
0144: continue;
0145: }
0146:
0147: else {
0148: rightTable = ((FromTable) elementAt(index))
0149: .getTableName();
0150: }
0151: if (leftTable.equals(rightTable)) {
0152: throw StandardException
0153: .newException(
0154: SQLState.LANG_FROM_LIST_DUPLICATE_TABLE_NAME,
0155: fromTable.getExposedName());
0156: }
0157: }
0158: }
0159:
0160: addElement(fromTable);
0161: }
0162:
0163: /**
0164: * Search to see if a query references the specifed table name.
0165: *
0166: * @param name Table name (String) to search for.
0167: * @param baseTable Whether or not name is for a base table
0168: *
0169: * @return true if found, else false
0170: *
0171: * @exception StandardException Thrown on error
0172: */
0173: public boolean referencesTarget(String name, boolean baseTable)
0174: throws StandardException {
0175: FromTable fromTable;
0176: boolean found = false;
0177:
0178: /* Check for table or VTI name in FROM list */
0179: int size = size();
0180: for (int index = 0; index < size; index++) {
0181: fromTable = (FromTable) elementAt(index);
0182:
0183: if (fromTable.referencesTarget(name, baseTable)) {
0184: found = true;
0185: break;
0186: }
0187: }
0188:
0189: return found;
0190: }
0191:
0192: /**
0193: * Return true if the node references SESSION schema tables (temporary or permanent)
0194: *
0195: * @return true if references SESSION schema tables, else false
0196: *
0197: * @exception StandardException Thrown on error
0198: */
0199: public boolean referencesSessionSchema() throws StandardException {
0200: FromTable fromTable;
0201: boolean found = false;
0202:
0203: // Following if will return true if this FromList object had any VIEWs
0204: // from SESSION schema as elements. This information is gathered during
0205: // the bindTables method. At the end of the bindTables, we loose
0206: // the information on VIEWs since they get replaced with their view
0207: // definition. Hence, we need to intercept in the middle on the bindTables
0208: // method and save that information in referencesSeesionSchema field.
0209: if (referencesSessionSchema)
0210: return true;
0211:
0212: /* Check for table or VTI name in FROM list */
0213: int size = size();
0214: for (int index = 0; index < size; index++) {
0215: fromTable = (FromTable) elementAt(index);
0216:
0217: if (fromTable.referencesSessionSchema()) {
0218: found = true;
0219: break;
0220: }
0221: }
0222:
0223: return found;
0224: }
0225:
0226: /**
0227: * Determine whether or not the specified name is an exposed name in
0228: * the current query block.
0229: *
0230: * @param name The specified name to search for as an exposed name.
0231: * @param schemaName Schema name, if non-null.
0232: * @param exactMatch Whether or not we need an exact match on specified schema and table
0233: * names or match on table id.
0234: *
0235: * @return The FromTable, if any, with the exposed name.
0236: *
0237: * @exception StandardException Thrown on error
0238: */
0239: protected FromTable getFromTableByName(String name,
0240: String schemaName, boolean exactMatch)
0241: throws StandardException {
0242: boolean found = false;
0243: FromTable fromTable;
0244: FromTable result = null;
0245:
0246: int size = size();
0247: for (int index = 0; index < size; index++) {
0248: fromTable = (FromTable) elementAt(index);
0249:
0250: result = fromTable.getFromTableByName(name, schemaName,
0251: exactMatch);
0252:
0253: if (result != null) {
0254: return result;
0255: }
0256: }
0257: return result;
0258: }
0259:
0260: /**
0261: * Bind the tables in this FromList. This includes looking them up in
0262: * the DataDictionary, getting their TableDescriptors and assigning the
0263: * table numbers.
0264: *
0265: * @param dataDictionary The DataDictionary to use for binding
0266: * @param fromListParam FromList to use/append to.
0267: *
0268: * @exception StandardException Thrown on error
0269: */
0270:
0271: public void bindTables(DataDictionary dataDictionary,
0272: FromList fromListParam) throws StandardException {
0273: FromTable fromTable;
0274:
0275: /* Now we bind the tables - this is a 2 step process.
0276: * We first bind all of the non-VTIs, then we bind the VTIs.
0277: * This enables us to handle the passing of correlation
0278: * columns in VTI parameters.
0279: * NOTE: We set the table numbers for all of the VTIs in the
0280: * first step, when we find them, in order to avoid an ordering
0281: * problem with join columns in parameters.
0282: */
0283: int size = size();
0284: for (int index = 0; index < size; index++) {
0285: fromTable = (FromTable) elementAt(index);
0286: ResultSetNode newNode = fromTable.bindNonVTITables(
0287: dataDictionary, fromListParam);
0288: // If the fromTable is a view in the SESSION schema, then we need to save that information
0289: // in referencesSessionSchema element. The reason for this is that the view will get
0290: // replaced by it's view definition and we will loose the information that the statement
0291: // was referencing a SESSION schema object.
0292: if (fromTable.referencesSessionSchema())
0293: referencesSessionSchema = true;
0294: setElementAt(newNode, index);
0295: }
0296: for (int index = 0; index < size; index++) {
0297: fromTable = (FromTable) elementAt(index);
0298: ResultSetNode newNode = fromTable
0299: .bindVTITables(fromListParam);
0300: if (fromTable.referencesSessionSchema())
0301: referencesSessionSchema = true;
0302: setElementAt(newNode, index);
0303: }
0304: }
0305:
0306: /**
0307: * Bind the expressions in this FromList. This means
0308: * binding the sub-expressions, as well as figuring out what the return
0309: * type is for each expression.
0310: *
0311: * @exception StandardException Thrown on error
0312: */
0313:
0314: public void bindExpressions(FromList fromListParam)
0315: throws StandardException {
0316: FromTable fromTable;
0317:
0318: int size = size();
0319: for (int index = 0; index < size; index++) {
0320: fromTable = (FromTable) elementAt(index);
0321: fromTable.bindExpressions(makeFromList(fromListParam,
0322: fromTable));
0323: }
0324: }
0325:
0326: /**
0327: * Construct an appropriate from list for binding an individual
0328: * table element. Normally, this is just this list. However,
0329: * for the special wrapper queries which the parser creates for
0330: * GROUP BY and HAVING clauses, the appropriate list is the
0331: * outer list passed into us--it will contain the appropriate
0332: * tables needed to resolve correlated columns.
0333: */
0334: private FromList makeFromList(FromList fromListParam,
0335: FromTable fromTable) {
0336: if (fromTable instanceof FromSubquery) {
0337: FromSubquery fromSubquery = (FromSubquery) fromTable;
0338:
0339: if (fromSubquery.generatedForGroupByClause
0340: || fromSubquery.generatedForHavingClause) {
0341: return fromListParam;
0342: }
0343: }
0344:
0345: return this ;
0346: }
0347:
0348: /**
0349: * Bind the result columns of the ResultSetNodes in this FromList when there is no
0350: * base table to bind them to. This is useful for SELECT statements,
0351: * where the result columns get their types from the expressions that
0352: * live under them.
0353: *
0354: * @param fromListParam FromList to use/append to.
0355: *
0356: * @exception StandardException Thrown on error
0357: */
0358:
0359: public void bindResultColumns(FromList fromListParam)
0360: throws StandardException {
0361: FromTable fromTable;
0362:
0363: int origList = fromListParam.size();
0364: int size = size();
0365: for (int index = 0; index < size; index++) {
0366: fromTable = (FromTable) elementAt(index);
0367: if (fromTable.needsSpecialRCLBinding())
0368: fromTable.bindResultColumns(fromListParam);
0369:
0370: fromListParam.insertElementAt(fromTable, 0);
0371: }
0372:
0373: /* Remove all references added here */
0374: while (fromListParam.size() > origList)
0375: fromListParam.removeElementAt(0);
0376: }
0377:
0378: /**
0379: * Returns true if any Outer joins present. Used to set Nullability
0380: *
0381: * @return True if has any outer joins. False otherwise.
0382: */
0383: public boolean hasOuterJoins() throws StandardException {
0384: FromTable fromTable;
0385:
0386: int size = size();
0387: for (int index = 0; index < size; index++) {
0388: fromTable = (FromTable) elementAt(index);
0389: if (fromTable instanceof HalfOuterJoinNode)
0390: return true;
0391: }
0392:
0393: return false;
0394: }
0395:
0396: /**
0397: * Expand a "*" into the appropriate ResultColumnList. If the "*"
0398: * is unqualified it will expand into a list of all columns in all
0399: * of the base tables in the from list, otherwise it will expand
0400: * into a list of all of the columns in the base table that matches
0401: * the qualification.
0402: *
0403: * @param allTableName The qualification on the "*" as a String.
0404: *
0405: * @return ResultColumnList representing expansion
0406: *
0407: * @exception StandardException Thrown on error
0408: */
0409: public ResultColumnList expandAll(TableName allTableName)
0410: throws StandardException {
0411: ResultColumnList resultColumnList = null;
0412: ResultColumnList tempRCList = null;
0413: boolean matchfound = false;
0414: FromTable fromTable;
0415:
0416: /* Expand the "*" for the table that matches, if it is qualified
0417: * (allTableName is not null) or for all tables in the list if the
0418: * "*" is not qualified (allTableName is null).
0419: */
0420: int size = size();
0421: for (int index = 0; index < size; index++) {
0422: fromTable = (FromTable) elementAt(index);
0423:
0424: /* We let the FromTable decide if there is a match on
0425: * the exposed name. (A JoinNode will not have an
0426: * exposed name, so it will need to pass the info to its
0427: * left and right children.)
0428: */
0429: tempRCList = fromTable.getAllResultColumns(allTableName);
0430:
0431: if (tempRCList == null) {
0432: continue;
0433: }
0434:
0435: /* Expand the column list and append to the list that
0436: * we will return.
0437: */
0438: if (resultColumnList == null) {
0439: resultColumnList = tempRCList;
0440: } else {
0441: resultColumnList.nondestructiveAppend(tempRCList);
0442: }
0443:
0444: /* If the "*" is qualified, then we can stop the
0445: * expansion as soon as we find the matching table.
0446: */
0447: if (allTableName != null) {
0448: matchfound = true;
0449: }
0450: }
0451:
0452: /* Give an error if the qualification name did not match
0453: * an exposed name
0454: */
0455: if (resultColumnList == null) {
0456: throw StandardException.newException(
0457: SQLState.LANG_EXPOSED_NAME_NOT_FOUND, allTableName);
0458: }
0459:
0460: return resultColumnList;
0461: }
0462:
0463: /**
0464: * Bind a column reference to one of the tables in this FromList. The column name
0465: * must be unique within the tables in the FromList. An exception is thrown
0466: * if a column name is not unique.
0467: *
0468: * NOTE: Callers are responsible for ordering the FromList by nesting level,
0469: * with tables at the deepest (current) nesting level first. We will try to
0470: * match against all FromTables at a given nesting level. If no match is
0471: * found at a nesting level, then we proceed to the next level. We stop
0472: * walking the list when the nesting level changes and we have found a match.
0473: *
0474: * NOTE: If the ColumnReference is qualified, then we will stop the search
0475: * at the first nesting level where there is a match on the exposed table name.
0476: * For example, s (a, b, c), t (d, e, f)
0477: * select * from s where exists (select * from t s where s.c = a)
0478: * will not find a match for s.c, which is the expected ANSI behavior.
0479: *
0480: * bindTables() must have already been called on this FromList before
0481: * calling this method.
0482: *
0483: * @param columnReference The ColumnReference describing the column to bind
0484: *
0485: * @return ResultColumn The matching ResultColumn
0486: *
0487: * @exception StandardException Thrown on error
0488: */
0489:
0490: public ResultColumn bindColumnReference(
0491: ColumnReference columnReference) throws StandardException {
0492: boolean columnNameMatch = false;
0493: boolean tableNameMatch = false;
0494: FromTable fromTable;
0495: int currentLevel = -1;
0496: int previousLevel = -1;
0497: ResultColumn matchingRC = null;
0498: ResultColumn resultColumn;
0499: String crTableName = columnReference.getTableName();
0500:
0501: /*
0502: ** Find the first table with matching column name. If there
0503: ** is more than one table with a matching column name at the same
0504: ** nesting level, give an error.
0505: */
0506: int size = size();
0507: for (int index = 0; index < size; index++) {
0508: fromTable = (FromTable) elementAt(index);
0509:
0510: /* We can stop if we've found a matching column or table name
0511: * at the previous nesting level.
0512: */
0513: currentLevel = fromTable.getLevel();
0514: if (previousLevel != currentLevel) {
0515: if (columnNameMatch) {
0516: break;
0517: }
0518:
0519: if (tableNameMatch) {
0520: break;
0521: }
0522: }
0523: /* Simpler to always set previousLevel then to test and set */
0524: previousLevel = currentLevel;
0525:
0526: resultColumn = fromTable.getMatchingColumn(columnReference);
0527:
0528: if (resultColumn != null) {
0529: if (!columnNameMatch) {
0530: /* TableNumbers are set in the CR in the underlying
0531: * FromTable. This ensures that they get the table
0532: * number from the underlying table, not the join node.
0533: * This is important for beging able to push predicates
0534: * down through join nodes.
0535: */
0536: matchingRC = resultColumn;
0537: columnReference.setSource(resultColumn);
0538: columnReference.setType(resultColumn
0539: .getTypeServices());
0540: /* Set the nesting level at which the CR appears and the nesting level
0541: * of its source RC.
0542: */
0543: columnReference
0544: .setNestingLevel(((FromTable) elementAt(0))
0545: .getLevel());
0546: columnReference.setSourceLevel(currentLevel);
0547: columnNameMatch = true;
0548:
0549: if (fromTable.isPrivilegeCollectionRequired())
0550: getCompilerContext()
0551: .addRequiredColumnPriv(
0552: resultColumn
0553: .getTableColumnDescriptor());
0554: } else {
0555: throw StandardException.newException(
0556: SQLState.LANG_AMBIGUOUS_COLUMN_NAME,
0557: columnReference.getSQLColumnName());
0558: }
0559: }
0560:
0561: /* Remember if we get a match on the exposed table name, so that
0562: * we can stop at the beginning of the next level.
0563: */
0564: tableNameMatch = tableNameMatch
0565: || (crTableName != null && crTableName
0566: .equals(fromTable.getExposedName()));
0567: }
0568:
0569: return matchingRC;
0570: }
0571:
0572: /**
0573: * Check for (and reject) all ? parameters directly under the ResultColumns.
0574: * This is done for SELECT statements.
0575: *
0576: * @exception StandardException Thrown if a ? parameter found
0577: * directly under a ResultColumn
0578: */
0579:
0580: public void rejectParameters() throws StandardException {
0581: FromTable fromTable;
0582:
0583: int size = size();
0584: for (int index = 0; index < size; index++) {
0585: fromTable = (FromTable) elementAt(index);
0586: fromTable.rejectParameters();
0587: }
0588: }
0589:
0590: // This method reorders LOJs in the FROM clause.
0591: // For now, we process only a LOJ. For example, "... from LOJ_1, LOJ2 ..."
0592: // will not be processed.
0593: public boolean LOJ_reorderable(int numTables)
0594: throws StandardException {
0595: boolean anyChange = false;
0596:
0597: if (size() > 1)
0598: return anyChange;
0599:
0600: FromTable ft = (FromTable) elementAt(0);
0601:
0602: anyChange = ft.LOJ_reorderable(numTables);
0603:
0604: return anyChange;
0605: }
0606:
0607: /**
0608: * Preprocess the query tree - this currently means:
0609: * o Generating a referenced table map for each ResultSetNode.
0610: * o Putting the WHERE and HAVING clauses in conjunctive normal form (CNF).
0611: * o Converting the WHERE and HAVING clauses into PredicateLists and
0612: * classifying them.
0613: * o Flatten those FromSubqueries which can be flattened.
0614: * o Ensuring that a ProjectRestrictNode is generated on top of every
0615: * FromBaseTable and generated in place of every FromSubquery which
0616: * could not be flattened.
0617: * o Pushing single table predicates down to the new ProjectRestrictNodes.
0618: *
0619: * @param numTables The number of tables in the DML Statement
0620: * @param gbl The group by list, if any
0621: *
0622: * @exception StandardException Thrown on error
0623: */
0624: public void preprocess(int numTables, GroupByList gbl,
0625: ValueNode predicateTree) throws StandardException {
0626: int size = size();
0627:
0628: /* Preprocess each FromTable in the list */
0629: for (int index = 0; index < size; index++) {
0630: FromTable ft = (FromTable) elementAt(index);
0631:
0632: /* Transform any outer joins to inner joins where appropriate */
0633: ft = ft.transformOuterJoins(predicateTree, numTables);
0634: /* Preprocess this FromTable */
0635: setElementAt(ft.preprocess(numTables, gbl, this ), index);
0636: }
0637: }
0638:
0639: /**
0640: * Flatten all the FromTables that are flattenable.
0641: * RESOLVE - right now we just flatten FromSubqueries. We
0642: * should also flatten flattenable JoinNodes here.
0643: *
0644: * @param rcl The RCL from the outer query
0645: * @param predicateList The PredicateList from the outer query
0646: * @param sql The SubqueryList from the outer query
0647: * @param gbl The group by list, if any
0648: *
0649: * @exception StandardException Thrown on error
0650: */
0651: public void flattenFromTables(ResultColumnList rcl,
0652: PredicateList predicateList, SubqueryList sql,
0653: GroupByList gbl) throws StandardException {
0654: boolean flattened = true;
0655: Vector flattenedTableNumbers = new Vector();
0656:
0657: if (SanityManager.DEBUG) {
0658: SanityManager.ASSERT(rcl != null,
0659: "rcl is expected to be non-null");
0660: SanityManager.ASSERT(predicateList != null,
0661: "predicateList is expected to be non-null");
0662: SanityManager.ASSERT(sql != null,
0663: "sql is expected to be non-null");
0664: }
0665:
0666: /* Loop until all flattenable entries are flattened.
0667: * We restart the inner loop after flattening an in place
0668: * to simplify the logic and so that we don't have to worry
0669: * about walking a list while we are modifying it.
0670: */
0671: while (flattened) {
0672: flattened = false;
0673:
0674: for (int index = 0; index < size() && !flattened; index++) {
0675: FromTable ft = (FromTable) elementAt(index);
0676:
0677: /* Flatten FromSubquerys and flattenable JoinNodes */
0678: if ((ft instanceof FromSubquery)
0679: || ft.isFlattenableJoinNode()) {
0680: //save the table number of the node to be flattened
0681: flattenedTableNumbers.addElement(new Integer(ft
0682: .getTableNumber()));
0683:
0684: /* Remove the node from the list and insert its
0685: * FromList here.
0686: */
0687: FromList flatteningFL = ft.flatten(rcl,
0688: predicateList, sql, gbl);
0689: if (SanityManager.DEBUG) {
0690: SanityManager
0691: .ASSERT(flatteningFL == null
0692: || flatteningFL.size() > 0,
0693: "flatteningFL expected to be null or size > 0");
0694: }
0695:
0696: if (flatteningFL != null) {
0697: setElementAt(flatteningFL.elementAt(0), index);
0698:
0699: int innerSize = flatteningFL.size();
0700: for (int inner = 1; inner < innerSize; inner++) {
0701: insertElementAt(flatteningFL
0702: .elementAt(inner), index + inner);
0703: }
0704: } else {
0705: /*
0706: ** If flatten returns null, that means it wants to
0707: ** be removed from the FromList.
0708: */
0709: removeElementAt(index);
0710: }
0711: flattened = true;
0712: }
0713: }
0714: }
0715:
0716: /* fix up dependency maps for exists base tables since they might have a
0717: * dependency on this join node
0718: */
0719: if (flattenedTableNumbers.size() > 0) {
0720: for (int i = 0; i < size(); i++) {
0721: FromTable ft = (FromTable) elementAt(i);
0722: if (ft instanceof ProjectRestrictNode) {
0723: ResultSetNode rst = ((ProjectRestrictNode) ft)
0724: .getChildResult();
0725: if (rst instanceof FromBaseTable) {
0726: ((FromBaseTable) rst)
0727: .clearDependency(flattenedTableNumbers);
0728: }
0729: }
0730: }
0731: }
0732: }
0733:
0734: /**
0735: * Categorize and push the predicates that are pushable.
0736: *
0737: * @param predicateList The query's PredicateList
0738: *
0739: * @exception StandardException Thrown on error
0740: */
0741: void pushPredicates(PredicateList predicateList)
0742: throws StandardException {
0743: if (SanityManager.DEBUG) {
0744: SanityManager.ASSERT(predicateList != null,
0745: "predicateList is expected to be non-null");
0746: }
0747:
0748: /* We can finally categorize each Predicate and try to push them down.
0749: * NOTE: The PredicateList may be empty, but that's okay, we still
0750: * call pushExpressions() for each entry in the FromList because that's
0751: * where any outer join conditions will get pushed down.
0752: */
0753: predicateList.categorize();
0754:
0755: int size = size();
0756: for (int index = 0; index < size; index++) {
0757: FromTable fromTable = (FromTable) elementAt(index);
0758: fromTable.pushExpressions(predicateList);
0759: }
0760: }
0761:
0762: /**
0763: * Prints the sub-nodes of this object. See QueryTreeNode.java for
0764: * how tree printing is supposed to work.
0765: *
0766: * @param depth The depth of this node in the tree
0767: */
0768:
0769: public void printSubNodes(int depth) {
0770: if (SanityManager.DEBUG) {
0771: FromTable fromTable;
0772:
0773: super .printSubNodes(depth);
0774:
0775: int size = size();
0776: for (int index = 0; index < size; index++) {
0777: fromTable = (FromTable) elementAt(index);
0778: fromTable.treePrint(depth + 1);
0779: }
0780: }
0781: }
0782:
0783: /**
0784: * Set the (query block) level (0-based) for the FromTables in this
0785: * FromList.
0786: *
0787: * @param level The query block level for this table.
0788: */
0789: public void setLevel(int level) {
0790: int size = size();
0791: for (int index = 0; index < size; index++) {
0792: FromTable fromTable = (FromTable) elementAt(index);
0793: fromTable.setLevel(level);
0794: }
0795: }
0796:
0797: /**
0798: * Get the FromTable from this list which has the specified ResultColumn in
0799: * its RCL.
0800: *
0801: * @param rc The ResultColumn match on.
0802: *
0803: * @return FromTable The matching FromTable.
0804: */
0805: public FromTable getFromTableByResultColumn(ResultColumn rc) {
0806: FromTable fromTable = null;
0807:
0808: int size = size();
0809: for (int index = 0; index < size; index++) {
0810: fromTable = (FromTable) elementAt(index);
0811:
0812: if (fromTable.getResultColumns().indexOf(rc) != -1) {
0813: break;
0814: }
0815: }
0816:
0817: if (SanityManager.DEBUG) {
0818: SanityManager.ASSERT(fromTable != null,
0819: "No matching FromTable found");
0820: }
0821: return fromTable;
0822: }
0823:
0824: /**
0825: * Set the Properties list for this FromList.
0826: *
0827: * @exception StandardException Thrown on error
0828: */
0829: public void setProperties(Properties props)
0830: throws StandardException {
0831: properties = props;
0832:
0833: /*
0834: ** Validate the properties list now. This is possible because
0835: ** there is nothing in this properties list that relies on binding
0836: ** or optimization to validate.
0837: */
0838: Enumeration e = properties.keys();
0839: while (e.hasMoreElements()) {
0840: String key = (String) e.nextElement();
0841: String value = (String) properties.get(key);
0842:
0843: if (key.equals("joinOrder")) {
0844: if (StringUtil.SQLEqualsIgnoreCase(value, "fixed")) {
0845: fixedJoinOrder = true;
0846: } else if (StringUtil.SQLEqualsIgnoreCase(value,
0847: "unfixed")) {
0848: fixedJoinOrder = false;
0849: } else {
0850: throw StandardException.newException(
0851: SQLState.LANG_INVALID_JOIN_ORDER_SPEC,
0852: value);
0853: }
0854: } else if (key.equals("useStatistics")) {
0855: if (StringUtil.SQLEqualsIgnoreCase(value, "true")) {
0856: useStatistics = true;
0857: } else if (StringUtil.SQLEqualsIgnoreCase(value,
0858: "false")) {
0859: useStatistics = false;
0860: } else {
0861: throw StandardException.newException(
0862: SQLState.LANG_INVALID_STATISTICS_SPEC,
0863: value);
0864: }
0865: } else {
0866: throw StandardException.newException(
0867: SQLState.LANG_INVALID_FROM_LIST_PROPERTY, key,
0868: value);
0869: }
0870: }
0871: }
0872:
0873: /** @see OptimizableList#reOrder */
0874: public void reOrder(int[] joinOrder) {
0875: int posn;
0876:
0877: if (SanityManager.DEBUG) {
0878: if (joinOrder.length != size()) {
0879: SanityManager
0880: .THROWASSERT("In reOrder(), size of FromList is "
0881: + size()
0882: + " while size of joinOrder array is "
0883: + joinOrder.length);
0884: }
0885:
0886: /*
0887: ** Determine that the values in the list are unique and in range.
0888: ** The easiest way to determine that they are unique is to add
0889: ** them all up and see whether the result is what's expected
0890: ** for that array size.
0891: */
0892: int sum = 0;
0893: for (int i = 0; i < joinOrder.length; i++) {
0894: if (joinOrder[i] < 0
0895: || joinOrder[i] > (joinOrder.length - 1)) {
0896: SanityManager
0897: .THROWASSERT("joinOrder["
0898: + i
0899: + "] == "
0900: + joinOrder[i]
0901: + " is out of range - must be between 0 and "
0902: + (joinOrder.length - 1)
0903: + " inclusive.");
0904: }
0905:
0906: sum += joinOrder[i];
0907: }
0908:
0909: /*
0910: ** The sum of all integers from 0 through n is (n * (n - 1)) / 2.
0911: */
0912: if (sum != ((joinOrder.length * (joinOrder.length - 1)) / 2)) {
0913: String arrayVals = "";
0914: for (int i = 0; i < joinOrder.length; i++)
0915: arrayVals = arrayVals + joinOrder[i] + " ";
0916: SanityManager
0917: .THROWASSERT("joinOrder array has some duplicate value: "
0918: + arrayVals);
0919: }
0920: }
0921:
0922: /* Form a list that's in the order we want */
0923: QueryTreeNode[] orderedFL = new FromTable[joinOrder.length];
0924: for (posn = 0; posn < joinOrder.length; posn++) {
0925: /*
0926: ** Get the element at the i'th join order position from the
0927: ** current list and make it the next element of orderedList.
0928: */
0929: orderedFL[posn] = elementAt(joinOrder[posn]);
0930: }
0931:
0932: /* Now orderedList has been built, so set this list to the same order */
0933: for (posn = 0; posn < joinOrder.length; posn++) {
0934: setElementAt(orderedFL[posn], posn);
0935: }
0936: }
0937:
0938: /** @see OptimizableList#useStatistics */
0939: public boolean useStatistics() {
0940: return useStatistics;
0941: }
0942:
0943: /** @see OptimizableList#optimizeJoinOrder */
0944: public boolean optimizeJoinOrder() {
0945: return !fixedJoinOrder;
0946: }
0947:
0948: /** @see OptimizableList#legalJoinOrder */
0949: public boolean legalJoinOrder(int numTablesInQuery) {
0950: JBitSet assignedTableMap = new JBitSet(numTablesInQuery);
0951:
0952: int size = size();
0953: for (int index = 0; index < size; index++) {
0954: FromTable ft = (FromTable) elementAt(index);
0955: assignedTableMap.or(ft.getReferencedTableMap());
0956: if (!ft.legalJoinOrder(assignedTableMap)) {
0957: return false;
0958: }
0959: }
0960: return true;
0961: }
0962:
0963: /** @see OptimizableList#initAccessPaths */
0964: public void initAccessPaths(Optimizer optimizer) {
0965: int size = size();
0966: for (int index = 0; index < size; index++) {
0967: FromTable ft = (FromTable) elementAt(index);
0968: ft.initAccessPaths(optimizer);
0969: }
0970: }
0971:
0972: /**
0973: * Bind any untyped null nodes to the types in the given ResultColumnList.
0974: *
0975: * @param bindingRCL The ResultColumnList with the types to bind to.
0976: *
0977: * @exception StandardException Thrown on error
0978: */
0979: public void bindUntypedNullsToResultColumns(
0980: ResultColumnList bindingRCL) throws StandardException {
0981: int size = size();
0982: for (int index = 0; index < size; index++) {
0983: FromTable fromTable = (FromTable) elementAt(index);
0984: fromTable.bindUntypedNullsToResultColumns(bindingRCL);
0985: }
0986: }
0987:
0988: /**
0989: * Decrement (query block) level (0-based) for
0990: * all of the tables in this from list.
0991: * This is useful when flattening a subquery.
0992: *
0993: * @param decrement The amount to decrement by.
0994: */
0995: void decrementLevel(int decrement) {
0996: int size = size();
0997: for (int index = 0; index < size; index++) {
0998: FromTable fromTable = (FromTable) elementAt(index);
0999: fromTable.decrementLevel(decrement);
1000:
1001: /* Decrement the level of any CRs in single table
1002: * predicates that are interesting to transitive
1003: * closure.
1004: */
1005: ProjectRestrictNode prn = (ProjectRestrictNode) fromTable;
1006: PredicateList pl = prn.getRestrictionList();
1007: if (pl != null) {
1008: pl.decrementLevel(this , decrement);
1009: }
1010: }
1011: }
1012:
1013: /**
1014: * This method is used for both subquery flattening and distinct
1015: * elimination based on a uniqueness condition. For subquery
1016: * flattening we want to make sure that the query block
1017: * will return at most 1 row. For distinct elimination we
1018: * want to make sure that the query block will not return
1019: * any duplicates.
1020: * This is true if every table in the from list is
1021: * (a base table and the set of columns from the table that
1022: * are in equality comparisons with expressions that do not include columns
1023: * from the same table is a superset of any unique index
1024: * on the table) or an EXISTS FBT. In addition, at least 1 of the tables
1025: * in the list has a set of columns in equality comparisons with expressions
1026: * that do not include column references from the same query block
1027: * is a superset of a unique index
1028: * on that table. (This ensures that the query block will onlyr
1029: * return a single row.)
1030: * This method is expected to be called after normalization and
1031: * after the from list has been preprocessed.
1032: * It can be called both before and after the predicates have
1033: * been pulled from the where clause.
1034: * The algorithm for this is as follows
1035: *
1036: * If any table in the query block is not a base table, give up.
1037: * For each table in the query
1038: * Ignore exists table since they can only produce one row
1039: *
1040: * create a matrix of tables and columns from the table (tableColMap)
1041: * (this is used to keep track of the join columns and constants
1042: * that can be used to figure out whether the rows from a join
1043: * or in a select list are distinct based on unique indexes)
1044: *
1045: * create an array of columns from the table(eqOuterCol)
1046: * (this is used to determine that only one row will be returned
1047: * from a join)
1048: *
1049: * if the current table is the table for the result columns
1050: * set the result columns in the eqOuterCol and tableColMap
1051: * (if these columns are a superset of a unique index and
1052: * all joining tables result in only one row, the
1053: * results will be distinct)
1054: * go through all the predicates and update tableColMap and
1055: * eqOuterCol with join columns and correlation variables,
1056: * parameters and constants
1057: * since setting constants, correlation variables and parameters,
1058: * reduces the number of columns required for uniqueness in a
1059: * multi-column index, they are set for all the tables (if the
1060: * table is not the result table, in this case only the column of the
1061: * result table is set)
1062: * join columns are just updated for the column in the row of the
1063: * joining table.
1064: *
1065: * check if the marked columns in tableColMap are a superset of a unique
1066: * index
1067: * (This means that the join will only produce 1 row when joined
1068: * with 1 row of another table)
1069: * check that there is a least one table for which the columns in
1070: * eqOuterCol(i.e. constant values) are a superset of a unique index
1071: * (This quarantees that there will be only one row selected
1072: * from this table).
1073: *
1074: * Once all tables have been evaluated, check that all the tables can be
1075: * joined by unique index or will have only one row
1076: *
1077: *
1078: *
1079: * @param rcl If non-null, the RCL from the query block.
1080: * If non-null for subqueries, then entry can
1081: * be considered as part of an = comparison.
1082: * @param whereClause The WHERE clause to consider.
1083: * @param wherePredicates The predicates that have already been
1084: * pulled from the WHERE clause.
1085: * @param dd The DataDictionary to use.
1086: *
1087: * @return Whether or not query block will return
1088: * at most 1 row for a subquery, no duplicates
1089: * for a distinct.
1090: *
1091: * @exception StandardException Thrown on error
1092: */
1093: boolean returnsAtMostSingleRow(ResultColumnList rcl,
1094: ValueNode whereClause, PredicateList wherePredicates,
1095: DataDictionary dd) throws StandardException {
1096: boolean satisfiesOuter = false;
1097: int[] tableNumbers;
1098: ColumnReference additionalCR = null;
1099:
1100: PredicateList predicatesTemp;
1101: predicatesTemp = (PredicateList) getNodeFactory().getNode(
1102: C_NodeTypes.PREDICATE_LIST, getContextManager());
1103: int wherePredicatesSize = wherePredicates.size();
1104: for (int index = 0; index < wherePredicatesSize; index++)
1105: predicatesTemp.addPredicate((Predicate) wherePredicates
1106: .elementAt(index));
1107:
1108: /* When considering subquery flattening, we are interested
1109: * in the 1st (and only) entry in the RCL. (The RCL will be
1110: * null if result column is not of interest for subquery flattening.)
1111: * We are interested in all entries in the RCL for distinct
1112: * elimination.
1113: */
1114: if (rcl != null) {
1115: ResultColumn rc = (ResultColumn) rcl.elementAt(0);
1116: if (rc.getExpression() instanceof ColumnReference) {
1117: additionalCR = (ColumnReference) rc.getExpression();
1118: }
1119: }
1120:
1121: /* First see if all entries are FromBaseTables. No point
1122: * in continuing if not.
1123: */
1124: int size = size();
1125: for (int index = 0; index < size; index++) {
1126: FromTable fromTable = (FromTable) elementAt(index);
1127: if (!(fromTable instanceof ProjectRestrictNode)) {
1128: return false;
1129: }
1130:
1131: ProjectRestrictNode prn = (ProjectRestrictNode) fromTable;
1132:
1133: if (!(prn.getChildResult() instanceof FromBaseTable)) {
1134: return false;
1135: }
1136: FromBaseTable fbt = (FromBaseTable) prn.getChildResult();
1137: //Following for loop code is to take care of Derby-251 (DISTINCT returns
1138: //duplicate rows).
1139: //Derby-251 returned duplicate rows because we were looking at predicates
1140: //that belong to existsTable to determine DISTINCT elimination
1141: //
1142: //(Check method level comments to understand DISTINCT elimination rules.)
1143: //
1144: //For one specific example, consider the query below
1145: //select distinct q1."NO1" from IDEPT q1, IDEPT q2
1146: //where ( q2."DISCRIM_DEPT" = 'HardwareDept')
1147: //and ( q1."DISCRIM_DEPT" = 'SoftwareDept') and ( q1."NO1" <> ALL
1148: //(select q3."NO1" from IDEPT q3 where (q3."REPORTTO_NO" = q2."NO1")))
1149: //(select q3."NO1" from IDEPT q3 where ( ABS(q3."REPORTTO_NO") = q2."NO1")))
1150: //
1151: //Table IDEPT in the query above has a primary key defined on column "NO1"
1152: //This query gets converted to following during optimization
1153: //
1154: //select distinct q1."NO1" from IDEPT q1, IDEPT q2
1155: //where ( q2."DISCRIM_DEPT" = 'HardwareDept')
1156: //and ( q1."DISCRIM_DEPT" = 'SoftwareDept') and not exists (
1157: //(select q3."NO1" from IDEPT q3 where
1158: //( ( ABS(q3."REPORTTO_NO") = q2."NO1") and q3."NO1" = q1."NO1") ) ) ;
1159: //
1160: //For the optimized query above, Derby generates following predicates.
1161: //ABS(q3.reportto_no) = q2.no1
1162: //q2.discrim_dept = 'HardwareDept'
1163: //q1.descrim_dept = 'SoftwareDept'
1164: //q1.no1 = q3.no1
1165: //The predicate ABS(q3."NO1") = q1."NO1" should not be considered when trying
1166: //to determine if q1 in the outer query has equality comparisons.
1167: //Similarly, the predicate q3.reportto_no = q2.no1 should not be
1168: //considered when trying to determine if q2 in the outer query has
1169: //equality comparisons. To achieve this, predicates based on exists base
1170: //table q3 (the first and the last predicate) should be removed while
1171: //evaluating outer query for uniqueness.
1172: //
1173: if (fbt.getExistsBaseTable()) {
1174: int existsTableNumber = fbt.getTableNumber();
1175: int predicatesTempSize = predicatesTemp.size();
1176: for (int predicatesTempIndex = predicatesTempSize - 1; predicatesTempIndex >= 0; predicatesTempIndex--) {
1177: AndNode topAndNode = (AndNode) ((Predicate) predicatesTemp
1178: .elementAt(predicatesTempIndex))
1179: .getAndNode();
1180:
1181: for (ValueNode whereWalker = topAndNode; whereWalker instanceof AndNode; whereWalker = ((AndNode) whereWalker)
1182: .getRightOperand()) {
1183: // See if this is a candidate =
1184: AndNode and = (AndNode) whereWalker;
1185:
1186: //we only need to worry about equality predicates because only those
1187: //predicates are considered during DISTINCT elimination.
1188: if (!and.getLeftOperand()
1189: .isRelationalOperator()
1190: || !(((RelationalOperator) (and
1191: .getLeftOperand()))
1192: .getOperator() == RelationalOperator.EQUALS_RELOP)) {
1193: continue;
1194: }
1195:
1196: JBitSet referencedTables = and.getLeftOperand()
1197: .getTablesReferenced();
1198: if (referencedTables.get(existsTableNumber)) {
1199: predicatesTemp
1200: .removeElementAt(predicatesTempIndex);
1201: break;
1202: }
1203: }
1204: }
1205: }
1206: }
1207:
1208: /* Build an array of tableNumbers from this query block.
1209: * We will use that array to find out if we have at least
1210: * one table with a uniqueness condition based only on
1211: * constants, parameters and correlation columns.
1212: */
1213: tableNumbers = getTableNumbers();
1214: JBitSet[][] tableColMap = new JBitSet[size][size];
1215: boolean[] oneRow = new boolean[size];
1216: boolean oneRowResult = false;
1217:
1218: /* See if each table has a uniqueness condition */
1219: for (int index = 0; index < size; index++) {
1220: ProjectRestrictNode prn = (ProjectRestrictNode) elementAt(index);
1221: FromBaseTable fbt = (FromBaseTable) prn.getChildResult();
1222:
1223: // Skip over EXISTS FBT since they cannot introduce duplicates
1224: if (fbt.getExistsBaseTable()) {
1225: oneRow[index] = true;
1226: continue;
1227: }
1228:
1229: int numColumns = fbt.getTableDescriptor()
1230: .getNumberOfColumns();
1231: boolean[] eqOuterCols = new boolean[numColumns + 1];
1232: int tableNumber = fbt.getTableNumber();
1233: boolean resultColTable = false;
1234: for (int i = 0; i < size; i++)
1235: tableColMap[index][i] = new JBitSet(numColumns + 1);
1236:
1237: if (additionalCR != null
1238: && additionalCR.getTableNumber() == tableNumber) {
1239: rcl.recordColumnReferences(eqOuterCols,
1240: tableColMap[index], index);
1241: resultColTable = true;
1242: }
1243:
1244: /* Now see if there are any equality conditions
1245: * of interest in the where clause.
1246: */
1247: if (whereClause != null) {
1248: whereClause.checkTopPredicatesForEqualsConditions(
1249: tableNumber, eqOuterCols, tableNumbers,
1250: tableColMap[index], resultColTable);
1251: }
1252:
1253: /* Now see if there are any equality conditions
1254: * of interest in the where predicates.
1255: */
1256: predicatesTemp.checkTopPredicatesForEqualsConditions(
1257: tableNumber, eqOuterCols, tableNumbers,
1258: tableColMap[index], resultColTable);
1259:
1260: /* Now see if there are any equality conditions
1261: * of interest that were already pushed down to the
1262: * PRN above the FBT. (Single table predicates.)
1263: */
1264: if (prn.getRestrictionList() != null) {
1265: prn.getRestrictionList()
1266: .checkTopPredicatesForEqualsConditions(
1267: tableNumber, eqOuterCols, tableNumbers,
1268: tableColMap[index], resultColTable);
1269: }
1270:
1271: /* We can finally check to see if the marked columns
1272: * are a superset of any unique index.
1273: */
1274: if (!fbt.super setOfUniqueIndex(tableColMap[index])) {
1275: return false;
1276: }
1277:
1278: /* Do we have at least 1 table whose equality condition
1279: * is based solely on constants, parameters and correlation columns.
1280: */
1281: oneRowResult = fbt.super setOfUniqueIndex(eqOuterCols);
1282: if (oneRowResult) {
1283: oneRow[index] = true;
1284: satisfiesOuter = true;
1285: }
1286: }
1287:
1288: /* Have we met all of the criteria */
1289: if (satisfiesOuter) {
1290: /* check that all the tables are joined by unique indexes
1291: * or only produce 1 row
1292: */
1293: boolean foundOneRow = true;
1294: while (foundOneRow) {
1295: foundOneRow = false;
1296: for (int index = 0; index < size; index++) {
1297: if (oneRow[index]) {
1298: for (int i = 0; i < size; i++) {
1299: /* unique key join - exists tables already marked as
1300: * 1 row - so don't need to look at them
1301: */
1302: if (!oneRow[i]
1303: && tableColMap[i][index].get(0)) {
1304: oneRow[i] = true;
1305: foundOneRow = true;
1306: }
1307: }
1308: }
1309: }
1310: }
1311: /* does any table produce more than one row */
1312: for (int index = 0; index < size; index++) {
1313: if (!oneRow[index]) {
1314: satisfiesOuter = false;
1315: break;
1316: }
1317: }
1318: }
1319: return satisfiesOuter;
1320: }
1321:
1322: int[] getTableNumbers() {
1323: int size = size();
1324: int[] tableNumbers = new int[size];
1325: for (int index = 0; index < size; index++) {
1326: ProjectRestrictNode prn = (ProjectRestrictNode) elementAt(index);
1327: if (!(prn.getChildResult() instanceof FromTable)) {
1328: continue;
1329: }
1330: FromTable ft = (FromTable) prn.getChildResult();
1331: tableNumbers[index] = ft.getTableNumber();
1332: }
1333:
1334: return tableNumbers;
1335: }
1336:
1337: /**
1338: * Mark all of the FromBaseTables in the list as EXISTS FBTs.
1339: * Each EBT has the same dependency list - those tables that are referenced
1340: * minus the tables in the from list.
1341: *
1342: * @param referencedTableMap The referenced table map.
1343: * @param outerFromList FromList from outer query block
1344: * @param isNotExists Whether or not for NOT EXISTS
1345: *
1346: * @exception StandardException Thrown on error
1347: */
1348: void genExistsBaseTables(JBitSet referencedTableMap,
1349: FromList outerFromList, boolean isNotExists)
1350: throws StandardException {
1351: JBitSet dependencyMap = (JBitSet) referencedTableMap.clone();
1352:
1353: // We currently only flatten single table from lists
1354: if (SanityManager.DEBUG) {
1355: if (size() != 1) {
1356: SanityManager
1357: .THROWASSERT("size() expected to be 1, not "
1358: + size());
1359: }
1360: }
1361:
1362: /* Create the dependency map */
1363: int size = size();
1364: for (int index = 0; index < size; index++) {
1365: ResultSetNode ft = ((ProjectRestrictNode) elementAt(index))
1366: .getChildResult();
1367: if (ft instanceof FromTable) {
1368: dependencyMap.clear(((FromTable) ft).getTableNumber());
1369: }
1370: }
1371:
1372: /* Degenerate case - If flattening a non-correlated EXISTS subquery
1373: * then we need to make the table that is getting flattened dependendent on
1374: * all of the tables in the outer query block. Gross but true. Otherwise
1375: * that table can get chosen as an outer table and introduce duplicates.
1376: * The reason that duplicates can be introduced is that we do special processing
1377: * in the join to make sure only one qualified row from the right side is
1378: * returned. If the exists table is on the left, we can return all the
1379: * qualified rows.
1380: */
1381: if (dependencyMap.getFirstSetBit() == -1) {
1382: int outerSize = outerFromList.size();
1383: for (int outer = 0; outer < outerSize; outer++)
1384: dependencyMap.or(((FromTable) outerFromList
1385: .elementAt(outer)).getReferencedTableMap());
1386: }
1387:
1388: /* Do the marking */
1389: for (int index = 0; index < size; index++) {
1390: FromTable fromTable = (FromTable) elementAt(index);
1391: if (fromTable instanceof ProjectRestrictNode) {
1392: ProjectRestrictNode prn = (ProjectRestrictNode) fromTable;
1393: if (prn.getChildResult() instanceof FromBaseTable) {
1394: FromBaseTable fbt = (FromBaseTable) prn
1395: .getChildResult();
1396: fbt.setExistsBaseTable(true,
1397: (JBitSet) dependencyMap.clone(),
1398: isNotExists);
1399: }
1400: }
1401: }
1402: }
1403:
1404: /**
1405: * Get the lock mode for the target of an update statement
1406: * (a delete or update). The update mode will always be row for
1407: * CurrentOfNodes. It will be table if there is no where clause.
1408: *
1409: * @return The lock mode
1410: */
1411: public int updateTargetLockMode() {
1412: if (SanityManager.DEBUG) {
1413: if (size() != 1) {
1414: SanityManager.THROWASSERT("size() expected to be 1");
1415: }
1416: }
1417: return ((ResultSetNode) elementAt(0)).updateTargetLockMode();
1418: }
1419:
1420: /**
1421: * Return whether or not the user specified a hash join for any of the
1422: * tables in this list.
1423: *
1424: * @return Whether or not the user specified a hash join for any of the
1425: * tables in this list.
1426: */
1427: boolean hashJoinSpecified() {
1428: int size = size();
1429: for (int index = 0; index < size; index++) {
1430: FromTable ft = (FromTable) elementAt(index);
1431: String joinStrategy = ft.getUserSpecifiedJoinStrategy();
1432:
1433: if (joinStrategy != null
1434: && StringUtil.SQLToUpperCase(joinStrategy).equals(
1435: "HASH")) {
1436: return true;
1437: }
1438: }
1439:
1440: return false;
1441: }
1442:
1443: /**
1444: * Accept a visitor, and call v.visit()
1445: * on child nodes as necessary.
1446: *
1447: * @param v the visitor
1448: *
1449: * @exception StandardException on error
1450: */
1451: public Visitable accept(Visitor v) throws StandardException {
1452: int size = size();
1453: for (int index = 0; index < size; index++) {
1454: FromTable fromTable = (FromTable) elementAt(index);
1455: setElementAt((QueryTreeNode) fromTable.accept(v), index);
1456: }
1457:
1458: return this;
1459: }
1460: }
|