0001: /*
0002:
0003: Derby - Class org.apache.derby.impl.sql.compile.SelectNode
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.sql.compile.CostEstimate;
0025: import org.apache.derby.iapi.sql.compile.Optimizer;
0026: import org.apache.derby.iapi.sql.compile.Visitable;
0027: import org.apache.derby.iapi.sql.compile.Visitor;
0028: import org.apache.derby.iapi.sql.compile.C_NodeTypes;
0029:
0030: import org.apache.derby.iapi.sql.conn.Authorizer;
0031:
0032: import org.apache.derby.iapi.sql.dictionary.DataDictionary;
0033: import org.apache.derby.iapi.sql.dictionary.TableDescriptor;
0034:
0035: import org.apache.derby.iapi.types.TypeId;
0036: import org.apache.derby.iapi.types.DataTypeDescriptor;
0037:
0038: import org.apache.derby.iapi.reference.Limits;
0039: import org.apache.derby.iapi.reference.SQLState;
0040: import org.apache.derby.iapi.error.StandardException;
0041:
0042: import org.apache.derby.iapi.store.access.TransactionController;
0043:
0044: import org.apache.derby.iapi.services.sanity.SanityManager;
0045:
0046: import org.apache.derby.iapi.util.JBitSet;
0047:
0048: import java.util.Vector;
0049: import java.util.HashSet;
0050:
0051: /**
0052: * A SelectNode represents the result set for any of the basic DML
0053: * operations: SELECT, INSERT, UPDATE, and DELETE. (A RowResultSetNode
0054: * will be used for an INSERT with a VALUES clause.) For INSERT - SELECT,
0055: * any of the fields in a SelectNode can be used (the SelectNode represents
0056: * the SELECT statement in the INSERT - SELECT). For UPDATE and
0057: * DELETE, there will be one table in the fromList, and the groupByList
0058: * fields will be null. For both INSERT and UPDATE,
0059: * the resultColumns in the selectList will contain the names of the columns
0060: * being inserted into or updated.
0061: *
0062: * @author Jeff Lichtman
0063: */
0064:
0065: public class SelectNode extends ResultSetNode {
0066: /**
0067: * List of tables in the FROM clause of this SELECT
0068: */
0069: FromList fromList;
0070: FromTable targetTable;
0071:
0072: /* Aggregate Vectors for select and where clauses */
0073: Vector selectAggregates;
0074: Vector whereAggregates;
0075:
0076: /**
0077: * The ValueNode for the WHERE clause must represent a boolean
0078: * expression. The binding phase will enforce this - the parser
0079: * does not have enough information to enforce it in all cases
0080: * (for example, user methods that return boolean).
0081: */
0082: ValueNode whereClause;
0083: ValueNode originalWhereClause;
0084:
0085: /**
0086: * List of result columns in GROUP BY clause
0087: */
0088: GroupByList groupByList;
0089:
0090: /* List of columns in ORDER BY list */
0091: OrderByList orderByList;
0092: boolean orderByQuery;
0093:
0094: /* PredicateLists for where clause */
0095: PredicateList wherePredicates;
0096:
0097: /* SubqueryLists for select and where clauses */
0098: SubqueryList selectSubquerys;
0099: SubqueryList whereSubquerys;
0100:
0101: /* Whether or not we are only binding the target list */
0102: private boolean bindTargetListOnly;
0103:
0104: private boolean isDistinct;
0105:
0106: private boolean orderByAndDistinctMerged;
0107:
0108: private boolean generatedForGroupByClause;
0109: private boolean generatedForHavingClause;
0110:
0111: /* Copy of fromList prior to generating join tree */
0112: private FromList preJoinFL;
0113:
0114: /**
0115: * Initializer for a SelectNode.
0116: *
0117: * @param selectList The result column list for the SELECT statement
0118: * @param aggregateVector The aggregate vector for this SELECT
0119: * @param fromList The FROM list for the SELECT statement
0120: * @param whereClause An expression representing the WHERE clause.
0121: * It must be a boolean expression, but this is
0122: * not checked until binding.
0123: * @param groupByList The GROUP BY list, if any.
0124: * @exception StandardException Thrown on error
0125: */
0126:
0127: public void init(Object selectList, Object aggregateVector,
0128: Object fromList, Object whereClause, Object groupByList)
0129: throws StandardException {
0130: /* RESOLVE - remove aggregateList from constructor.
0131: * Consider adding selectAggregates and whereAggregates
0132: */
0133: resultColumns = (ResultColumnList) selectList;
0134: if (resultColumns != null)
0135: resultColumns.markInitialSize();
0136: this .fromList = (FromList) fromList;
0137: this .whereClause = (ValueNode) whereClause;
0138: this .originalWhereClause = (ValueNode) whereClause;
0139: this .groupByList = (GroupByList) groupByList;
0140: bindTargetListOnly = false;
0141: }
0142:
0143: /**
0144: * Convert this object to a String. See comments in QueryTreeNode.java
0145: * for how this should be done for tree printing.
0146: *
0147: * @return This object as a String
0148: */
0149:
0150: public String toString() {
0151: if (SanityManager.DEBUG) {
0152: return "isDistinct: "
0153: + isDistinct
0154: + "\n"
0155: + "groupByList: "
0156: + (groupByList != null ? groupByList.toString()
0157: : "null")
0158: + "\n"
0159: + "orderByList: "
0160: + (orderByList != null ? orderByList.toString()
0161: : "null") + "\n"
0162: + "generatedForGroupByClause: "
0163: + generatedForGroupByClause + "\n"
0164: + "generatedForHavingClause: "
0165: + generatedForHavingClause + "\n"
0166: + super .toString();
0167: } else {
0168: return "";
0169: }
0170: }
0171:
0172: public String statementToString() {
0173: return "SELECT";
0174: }
0175:
0176: public void makeDistinct() {
0177: isDistinct = true;
0178: }
0179:
0180: public void clearDistinct() {
0181: isDistinct = false;
0182: }
0183:
0184: boolean hasDistinct() {
0185: return isDistinct;
0186: }
0187:
0188: /**
0189: * Mark this SelectNode as being generated for a GROUP BY clause.
0190: */
0191: public void markAsForGroupByClause() {
0192: generatedForGroupByClause = true;
0193: }
0194:
0195: /**
0196: * Return whether or not this SelectNode was generated for a GROUP BY clause.
0197: *
0198: * @return boolean Whether or not this SelectNode was generated for a GROUP BY clause.
0199: */
0200: public boolean getGeneratedForGroupbyClause() {
0201: return generatedForGroupByClause;
0202: }
0203:
0204: /**
0205: * Mark this SelectNode as being generated for a HAVING clause.
0206: */
0207: public void markAsForHavingClause() {
0208: generatedForHavingClause = true;
0209: }
0210:
0211: /**
0212: * Prints the sub-nodes of this object. See QueryTreeNode.java for
0213: * how tree printing is supposed to work.
0214: *
0215: * @param depth The depth of this node in the tree
0216: */
0217:
0218: public void printSubNodes(int depth) {
0219: if (SanityManager.DEBUG) {
0220: super .printSubNodes(depth);
0221:
0222: if (selectSubquerys != null) {
0223: printLabel(depth, "selectSubquerys: ");
0224: selectSubquerys.treePrint(depth + 1);
0225: }
0226:
0227: printLabel(depth, "fromList: ");
0228:
0229: if (fromList != null) {
0230: fromList.treePrint(depth + 1);
0231: }
0232:
0233: if (whereClause != null) {
0234: printLabel(depth, "whereClause: ");
0235: whereClause.treePrint(depth + 1);
0236: }
0237:
0238: if ((wherePredicates != null) && wherePredicates.size() > 0) {
0239: printLabel(depth, "wherePredicates: ");
0240: wherePredicates.treePrint(depth + 1);
0241: }
0242:
0243: if (whereSubquerys != null) {
0244: printLabel(depth, "whereSubquerys: ");
0245: whereSubquerys.treePrint(depth + 1);
0246: }
0247:
0248: printLabel(depth, "preJoinFL: ");
0249:
0250: if (preJoinFL != null) {
0251: preJoinFL.treePrint(depth + 1);
0252: }
0253: }
0254: }
0255:
0256: /**
0257: * Return the fromList for this SelectNode.
0258: *
0259: * @return FromList The fromList for this SelectNode.
0260: */
0261: public FromList getFromList() {
0262: return fromList;
0263: }
0264:
0265: /**
0266: * Find colName in the result columns and return underlying columnReference.
0267: * Note that this function returns null if there are more than one FromTable
0268: * for this SelectNode and the columnReference needs to be directly under
0269: * the resultColumn. So having an expression under the resultSet would cause
0270: * returning null.
0271: *
0272: * @param colName Name of the column
0273: *
0274: * @return ColumnReference ColumnReference to the column, if found
0275: */
0276: public ColumnReference findColumnReferenceInResult(String colName)
0277: throws StandardException {
0278: if (fromList.size() != 1)
0279: return null;
0280:
0281: // This logic is similar to SubQueryNode.singleFromBaseTable(). Refactor
0282: FromTable ft = (FromTable) fromList.elementAt(0);
0283: if (!((ft instanceof ProjectRestrictNode) && ((ProjectRestrictNode) ft)
0284: .getChildResult() instanceof FromBaseTable)
0285: && !(ft instanceof FromBaseTable))
0286: return null;
0287:
0288: // Loop through the result columns looking for a match
0289: int rclSize = resultColumns.size();
0290: for (int index = 0; index < rclSize; index++) {
0291: ResultColumn rc = (ResultColumn) resultColumns
0292: .elementAt(index);
0293: if (!(rc.getExpression() instanceof ColumnReference))
0294: return null;
0295:
0296: ColumnReference crNode = (ColumnReference) rc
0297: .getExpression();
0298:
0299: if (crNode.columnName.equals(colName))
0300: return (ColumnReference) crNode.getClone();
0301: }
0302:
0303: return null;
0304: }
0305:
0306: /**
0307: * Return the whereClause for this SelectNode.
0308: *
0309: * @return ValueNode The whereClause for this SelectNode.
0310: */
0311: public ValueNode getWhereClause() {
0312: return whereClause;
0313: }
0314:
0315: /**
0316: * Return the wherePredicates for this SelectNode.
0317: *
0318: * @return PredicateList The wherePredicates for this SelectNode.
0319: */
0320: public PredicateList getWherePredicates() {
0321: return wherePredicates;
0322: }
0323:
0324: /**
0325: * Return the selectSubquerys for this SelectNode.
0326: *
0327: * @return SubqueryList The selectSubquerys for this SelectNode.
0328: */
0329: public SubqueryList getSelectSubquerys() {
0330: return selectSubquerys;
0331: }
0332:
0333: /**
0334: * Return the specified aggregate vector for this SelectNode.
0335: *
0336: * @param clause Which clause to get the aggregate list for
0337: *
0338: * @return aggregateVector The specified aggregate vector for this SelectNode.
0339: */
0340: public Vector getAggregateVector(int clause) {
0341: switch (clause) {
0342: case ValueNode.IN_SELECT_LIST:
0343: return selectAggregates;
0344:
0345: case ValueNode.IN_WHERE_CLAUSE:
0346: if (generatedForHavingClause) {
0347: return null;
0348: } else {
0349: return whereAggregates;
0350: }
0351:
0352: case ValueNode.IN_HAVING_CLAUSE:
0353: if (generatedForHavingClause) {
0354: return whereAggregates;
0355: } else {
0356: return null;
0357: }
0358:
0359: default:
0360: if (SanityManager.DEBUG) {
0361: SanityManager.ASSERT(false,
0362: "Unexpected value for clause");
0363: }
0364: return null;
0365: }
0366: }
0367:
0368: /**
0369: * Return the whereSubquerys for this SelectNode.
0370: *
0371: * @return SubqueryList The whereSubquerys for this SelectNode.
0372: */
0373: public SubqueryList getWhereSubquerys() {
0374: return whereSubquerys;
0375: }
0376:
0377: /**
0378: * Bind the tables in this SelectNode. This includes getting their
0379: * TableDescriptors from the DataDictionary and numbering the FromTables.
0380: * NOTE: Because this node represents the top of a new query block, we bind
0381: * both the non VTI and VTI tables under this node in this method call.
0382: *
0383: * @param dataDictionary The DataDictionary to use for binding
0384: * @param fromListParam FromList to use/append to.
0385: *
0386: * @return ResultSetNode
0387: *
0388: * @exception StandardException Thrown on error
0389: */
0390:
0391: public ResultSetNode bindNonVTITables(
0392: DataDictionary dataDictionary, FromList fromListParam)
0393: throws StandardException {
0394: int fromListParamSize = fromListParam.size();
0395: int fromListSize = fromList.size();
0396: int nestingLevel;
0397: FromList fromListClone = (FromList) getNodeFactory().getNode(
0398: C_NodeTypes.FROM_LIST,
0399: getNodeFactory().doJoinOrderOptimization(),
0400: getContextManager());
0401:
0402: wherePredicates = (PredicateList) getNodeFactory().getNode(
0403: C_NodeTypes.PREDICATE_LIST, getContextManager());
0404: preJoinFL = (FromList) getNodeFactory().getNode(
0405: C_NodeTypes.FROM_LIST,
0406: getNodeFactory().doJoinOrderOptimization(),
0407: getContextManager());
0408:
0409: /* Set the nesting level in the fromList */
0410: if (fromListParam.size() == 0) {
0411: nestingLevel = 0;
0412: } else {
0413: nestingLevel = ((FromTable) fromListParam.elementAt(0))
0414: .getLevel() + 1;
0415: }
0416: fromList.setLevel(nestingLevel);
0417:
0418: /* Splice a clone of our FromList on to the beginning of fromListParam, before binding
0419: * the tables, for correlated column resolution in VTIs.
0420: */
0421: for (int index = 0; index < fromListSize; index++) {
0422: fromListParam.insertElementAt(fromList.elementAt(index), 0);
0423: }
0424:
0425: // Now bind our from list
0426: fromList.bindTables(dataDictionary, fromListParam);
0427:
0428: /* Restore fromListParam */
0429: for (int index = 0; index < fromListSize; index++) {
0430: fromListParam.removeElementAt(0);
0431: }
0432: return this ;
0433: }
0434:
0435: /**
0436: * Bind the expressions in this SelectNode. This means binding the
0437: * sub-expressions, as well as figuring out what the return type is
0438: * for each expression.
0439: *
0440: * @param fromListParam FromList to use/append to.
0441: *
0442: * @exception StandardException Thrown on error
0443: */
0444: public void bindExpressions(FromList fromListParam)
0445: throws StandardException {
0446: int fromListParamSize = fromListParam.size();
0447: int fromListSize = fromList.size();
0448: int numDistinctAggs;
0449:
0450: if (SanityManager.DEBUG)
0451: SanityManager
0452: .ASSERT(fromList != null && resultColumns != null,
0453: "Both fromList and resultColumns are expected to be non-null");
0454:
0455: /* NOTE - a lot of this code would be common to bindTargetExpression(),
0456: * so we use a private boolean to share the code instead of duplicating
0457: * it. bindTargetExpression() is responsible for toggling the boolean.
0458: */
0459: if (!bindTargetListOnly) {
0460: /* Bind the expressions in FromSubquerys, JoinNodes, etc. */
0461: fromList.bindExpressions(fromListParam);
0462: }
0463:
0464: selectSubquerys = (SubqueryList) getNodeFactory().getNode(
0465: C_NodeTypes.SUBQUERY_LIST, getContextManager());
0466: selectAggregates = new Vector();
0467:
0468: /* Splice our FromList on to the beginning of fromListParam, before binding
0469: * the expressions, for correlated column resolution.
0470: */
0471: for (int index = 0; index < fromListSize; index++) {
0472: fromListParam.insertElementAt(fromList.elementAt(index),
0473: index);
0474: }
0475:
0476: resultColumns.setClause(ValueNode.IN_SELECT_LIST);
0477: resultColumns.bindExpressions(fromListParam, selectSubquerys,
0478: selectAggregates);
0479:
0480: /* We're done if we're only binding the target list.
0481: * (After we restore the fromList, of course.)
0482: */
0483: if (bindTargetListOnly) {
0484: for (int index = 0; index < fromListSize; index++) {
0485: fromListParam.removeElementAt(0);
0486: }
0487: return;
0488: }
0489:
0490: whereAggregates = new Vector();
0491: whereSubquerys = (SubqueryList) getNodeFactory().getNode(
0492: C_NodeTypes.SUBQUERY_LIST, getContextManager());
0493: if (whereClause != null) {
0494: getCompilerContext().pushCurrentPrivType(
0495: Authorizer.SELECT_PRIV);
0496: whereClause = whereClause.bindExpression(fromListParam,
0497: whereSubquerys, whereAggregates);
0498:
0499: /* RESOLVE - Temporarily disable aggregates in the HAVING clause.
0500: ** (We may remove them in the parser anyway.)
0501: ** RESOLVE - Disable aggregates in the WHERE clause. Someday
0502: ** Aggregates will be allowed iff they are in a subquery
0503: ** of the having clause and they correlate to an outer
0504: ** query block. For now, aggregates are not supported
0505: ** in the WHERE clause at all.
0506: ** Note: a similar check is made in JoinNode.
0507: */
0508: if ((whereAggregates.size() > 0)
0509: && !generatedForHavingClause) {
0510: throw StandardException
0511: .newException(SQLState.LANG_NO_AGGREGATES_IN_WHERE_CLAUSE);
0512: }
0513:
0514: /* If whereClause is a parameter, (where ?/where -?/where +?), then we should catch it and throw exception
0515: */
0516: if (whereClause.isParameterNode())
0517: throw StandardException.newException(
0518: SQLState.LANG_NON_BOOLEAN_WHERE_CLAUSE,
0519: "PARAMETER");
0520: if ((whereClause instanceof UnaryOperatorNode)
0521: && ((UnaryOperatorNode) whereClause)
0522: .isUnaryMinusOrPlusWithParameter())
0523: throw StandardException.newException(
0524: SQLState.LANG_NON_BOOLEAN_WHERE_CLAUSE,
0525: "PARAMETER");
0526:
0527: whereClause = whereClause.checkIsBoolean();
0528: getCompilerContext().popCurrentPrivType();
0529: }
0530:
0531: /* Restore fromList */
0532: for (int index = 0; index < fromListSize; index++) {
0533: fromListParam.removeElementAt(0);
0534: }
0535:
0536: if (SanityManager.DEBUG) {
0537: SanityManager.ASSERT(
0538: fromListParam.size() == fromListParamSize,
0539: "fromListParam.size() = " + fromListParam.size()
0540: + ", expected to be restored to "
0541: + fromListParamSize);
0542: SanityManager.ASSERT(fromList.size() == fromListSize,
0543: "fromList.size() = " + fromList.size()
0544: + ", expected to be restored to "
0545: + fromListSize);
0546: }
0547:
0548: /* If query is grouped, bind the group by list. */
0549: if (groupByList != null) {
0550: Vector gbAggregateVector = new Vector();
0551:
0552: groupByList.bindGroupByColumns(this , gbAggregateVector);
0553:
0554: /*
0555: ** There should be no aggregates in the Group By list.
0556: ** We don't expect any, but just to be on the safe side
0557: ** we will check under sanity.
0558: */
0559: if (SanityManager.DEBUG) {
0560: SanityManager
0561: .ASSERT(gbAggregateVector.size() == 0,
0562: "Unexpected Aggregate vector generated by Group By clause");
0563: }
0564: }
0565: /* If ungrouped query with aggregates in SELECT list, verify
0566: * that all result columns are valid aggregate expressions -
0567: * no column references outside of an aggregate.
0568: * If grouped query with aggregates in SELECT list, verify that all
0569: * result columns are either grouping expressions or valid
0570: * grouped aggregate expressions - the only column references
0571: * allowed outside of an aggregate are columns in expressions in
0572: * the group by list.
0573: */
0574: if (groupByList != null || selectAggregates.size() > 0) {
0575:
0576: VerifyAggregateExpressionsVisitor visitor = new VerifyAggregateExpressionsVisitor(
0577: groupByList);
0578: resultColumns.accept(visitor);
0579: }
0580:
0581: /*
0582: ** RESOLVE: for now, only one distinct aggregate is supported
0583: ** in the select list.
0584: */
0585: numDistinctAggs = numDistinctAggregates(selectAggregates);
0586: if (numDistinctAggs > 1) {
0587: throw StandardException
0588: .newException(SQLState.LANG_USER_AGGREGATE_MULTIPLE_DISTINCTS);
0589: }
0590: }
0591:
0592: /**
0593: * Bind the expressions in this ResultSetNode if it has tables. This means binding the
0594: * sub-expressions, as well as figuring out what the return type is for
0595: * each expression.
0596: *
0597: * @param fromListParam FromList to use/append to.
0598: *
0599: * @exception StandardException Thrown on error
0600: */
0601: public void bindExpressionsWithTables(FromList fromListParam)
0602: throws StandardException {
0603: /* We have tables, so simply call bindExpressions() */
0604: bindExpressions(fromListParam);
0605: }
0606:
0607: /**
0608: * Bind the expressions in the target list. This means binding the
0609: * sub-expressions, as well as figuring out what the return type is
0610: * for each expression. This is useful for EXISTS subqueries, where we
0611: * need to validate the target list before blowing it away and replacing
0612: * it with a SELECT true.
0613: *
0614: * @exception StandardException Thrown on error
0615: */
0616:
0617: public void bindTargetExpressions(FromList fromListParam)
0618: throws StandardException {
0619: bindTargetListOnly = true;
0620: bindExpressions(fromListParam);
0621: bindTargetListOnly = false;
0622: }
0623:
0624: /**
0625: * Bind the result columns of this ResultSetNode when there is no
0626: * base table to bind them to. This is useful for SELECT statements,
0627: * where the result columns get their types from the expressions that
0628: * live under them.
0629: *
0630: * @param fromListParam FromList to use/append to.
0631: *
0632: * @exception StandardException Thrown on error
0633: */
0634:
0635: public void bindResultColumns(FromList fromListParam)
0636: throws StandardException {
0637: /* We first bind the resultColumns for any FromTable which
0638: * needs its own binding, such as JoinNodes.
0639: * We pass through the fromListParam without adding our fromList
0640: * to it, since the elements in our fromList can only be correlated
0641: * with outer query blocks.
0642: */
0643: fromList.bindResultColumns(fromListParam);
0644: super .bindResultColumns(fromListParam);
0645:
0646: /* Only 1012 elements allowed in select list */
0647: if (resultColumns.size() > Limits.DB2_MAX_ELEMENTS_IN_SELECT_LIST) {
0648: throw StandardException
0649: .newException(SQLState.LANG_TOO_MANY_ELEMENTS);
0650: }
0651:
0652: /* Fix nullability in case of any outer joins in the fromList */
0653: if (fromList.hasOuterJoins())
0654: resultColumns.setNullability(true);
0655: }
0656:
0657: /**
0658: * Bind the result columns for this ResultSetNode to a base table.
0659: * This is useful for INSERT and UPDATE statements, where the
0660: * result columns get their types from the table being updated or
0661: * inserted into.
0662: * If a result column list is specified, then the verification that the
0663: * result column list does not contain any duplicates will be done when
0664: * binding them by name.
0665: *
0666: * @param targetTableDescriptor The TableDescriptor for the table being
0667: * updated or inserted into
0668: * @param targetColumnList For INSERT statements, the user
0669: * does not have to supply column
0670: * names (for example, "insert into t
0671: * values (1,2,3)". When this
0672: * parameter is null, it means that
0673: * the user did not supply column
0674: * names, and so the binding should
0675: * be done based on order. When it
0676: * is not null, it means do the binding
0677: * by name, not position.
0678: * @param statement Calling DMLStatementNode (Insert or Update)
0679: * @param fromListParam FromList to use/append to.
0680: *
0681: * @exception StandardException Thrown on error
0682: */
0683:
0684: public void bindResultColumns(
0685: TableDescriptor targetTableDescriptor, FromVTI targetVTI,
0686: ResultColumnList targetColumnList,
0687: DMLStatementNode statement, FromList fromListParam)
0688: throws StandardException {
0689: /* We first bind the resultColumns for any FromTable which
0690: * needs its own binding, such as JoinNodes.
0691: * We pass through the fromListParam without adding our fromList
0692: * to it, since the elements in our fromList can only be correlated
0693: * with outer query blocks.
0694: */
0695: fromList.bindResultColumns(fromListParam);
0696: super .bindResultColumns(targetTableDescriptor, targetVTI,
0697: targetColumnList, statement, fromListParam);
0698: }
0699:
0700: /**
0701: * Push an expression into this SELECT (and possibly down into
0702: * one of the tables in the FROM list). This is useful when
0703: * trying to push predicates into unflattened views or
0704: * derived tables.
0705: *
0706: * @param predicate The predicate that we attempt to push
0707: *
0708: * @exception StandardException Thrown on error
0709: */
0710: void pushExpressionsIntoSelect(Predicate predicate)
0711: throws StandardException {
0712: wherePredicates.pullExpressions(referencedTableMap.size(),
0713: predicate.getAndNode());
0714: fromList.pushPredicates(wherePredicates);
0715: }
0716:
0717: /**
0718: * Verify that a SELECT * is valid for this type of subquery.
0719: *
0720: * @param outerFromList The FromList from the outer query block(s)
0721: * @param subqueryType The subquery type
0722: *
0723: * @exception StandardException Thrown on error
0724: */
0725: public void verifySelectStarSubquery(FromList outerFromList,
0726: int subqueryType) throws StandardException {
0727: if (!((ResultColumn) resultColumns.elementAt(0) instanceof AllResultColumn)) {
0728: return;
0729: }
0730:
0731: /* Select * always okay when SelectNode generated to wrap
0732: * GROUP BY or HAVING.
0733: */
0734: if (generatedForGroupByClause || generatedForHavingClause) {
0735: return;
0736: }
0737:
0738: /* Select * currently only valid for EXISTS/NOT EXISTS.
0739: * NOT EXISTS does not appear prior to preprocessing.
0740: */
0741: if (subqueryType != SubqueryNode.EXISTS_SUBQUERY) {
0742: throw StandardException
0743: .newException(SQLState.LANG_CANT_SELECT_STAR_SUBQUERY);
0744: }
0745:
0746: /* If the AllResultColumn is qualified, then we have to verify
0747: * that the qualification is a valid exposed name.
0748: * NOTE: The exposed name can come from an outer query block.
0749: */
0750: String fullTableName;
0751:
0752: fullTableName = ((AllResultColumn) resultColumns.elementAt(0))
0753: .getFullTableName();
0754:
0755: if (fullTableName != null) {
0756: if (fromList.getFromTableByName(fullTableName, null, true) == null
0757: && outerFromList.getFromTableByName(fullTableName,
0758: null, true) == null) {
0759: if (fromList.getFromTableByName(fullTableName, null,
0760: false) == null
0761: && outerFromList.getFromTableByName(
0762: fullTableName, null, false) == null) {
0763: throw StandardException.newException(
0764: SQLState.LANG_EXPOSED_NAME_NOT_FOUND,
0765: fullTableName);
0766: }
0767: }
0768: }
0769: }
0770:
0771: /**
0772: * Determine whether or not the specified name is an exposed name in
0773: * the current query block.
0774: *
0775: * @param name The specified name to search for as an exposed name.
0776: * @param schemaName Schema name, if non-null.
0777: * @param exactMatch Whether or not we need an exact match on specified schema and table
0778: * names or match on table id.
0779: *
0780: * @return The FromTable, if any, with the exposed name.
0781: *
0782: * @exception StandardException Thrown on error
0783: */
0784: protected FromTable getFromTableByName(String name,
0785: String schemaName, boolean exactMatch)
0786: throws StandardException {
0787: return fromList
0788: .getFromTableByName(name, schemaName, exactMatch);
0789: }
0790:
0791: /**
0792: * Check for (and reject) ? parameters directly under the ResultColumns.
0793: * This is done for SELECT statements.
0794: *
0795: * @exception StandardException Thrown if a ? parameter found
0796: * directly under a ResultColumn
0797: */
0798:
0799: public void rejectParameters() throws StandardException {
0800: super .rejectParameters();
0801: fromList.rejectParameters();
0802: }
0803:
0804: /**
0805: * Push the order by list down from the cursor node
0806: * into its child result set so that the optimizer
0807: * has all of the information that it needs to
0808: * consider sort avoidance.
0809: *
0810: * @param orderByList The order by list
0811: */
0812: void pushOrderByList(OrderByList orderByList) {
0813: this .orderByList = orderByList;
0814: // remember that there was an order by list
0815: orderByQuery = true;
0816: }
0817:
0818: /**
0819: * Put a ProjectRestrictNode on top of each FromTable in the FromList.
0820: * ColumnReferences must continue to point to the same ResultColumn, so
0821: * that ResultColumn must percolate up to the new PRN. However,
0822: * that ResultColumn will point to a new expression, a VirtualColumnNode,
0823: * which points to the FromTable and the ResultColumn that is the source for
0824: * the ColumnReference.
0825: * (The new PRN will have the original of the ResultColumnList and
0826: * the ResultColumns from that list. The FromTable will get shallow copies
0827: * of the ResultColumnList and its ResultColumns. ResultColumn.expression
0828: * will remain at the FromTable, with the PRN getting a new
0829: * VirtualColumnNode for each ResultColumn.expression.)
0830: * We then project out the non-referenced columns. If there are no referenced
0831: * columns, then the PRN's ResultColumnList will consist of a single ResultColumn
0832: * whose expression is 1.
0833: *
0834: * @param numTables The number of tables in the DML Statement
0835: * @param gbl The outer group by list, if any
0836: * @param fl The from list, if any
0837: *
0838: * @return The generated ProjectRestrictNode atop the original FromTable.
0839: *
0840: * @exception StandardException Thrown on error
0841: */
0842:
0843: public ResultSetNode preprocess(int numTables, GroupByList gbl,
0844: FromList fl) throws StandardException {
0845: ResultSetNode newTop = this ;
0846:
0847: /* Put the expression trees in conjunctive normal form.
0848: * NOTE - This needs to occur before we preprocess the subqueries
0849: * because the subquery transformations assume that any subquery operator
0850: * negation has already occurred.
0851: */
0852: normExpressions();
0853:
0854: /**
0855: * This method determines if (1) the query is a LOJ, and (2) if the LOJ is a candidate for
0856: * reordering (i.e., linearization). The condition for LOJ linearization is:
0857: * 1. either LOJ or ROJ in the fromList, i.e., no INNER, NO FULL JOINs
0858: * 2. ON clause must be equality join between left and right operands and in CNF (i.e., AND is allowed)
0859: */
0860: boolean anyChange = fromList.LOJ_reorderable(numTables);
0861: if (anyChange) {
0862: FromList afromList = (FromList) getNodeFactory().getNode(
0863: C_NodeTypes.FROM_LIST,
0864: getNodeFactory().doJoinOrderOptimization(),
0865: getContextManager());
0866: bindExpressions(afromList);
0867: }
0868:
0869: /* Preprocess the fromList. For each FromTable, if it is a FromSubquery
0870: * then we will preprocess it, replacing the FromSubquery with a
0871: * ProjectRestrictNode. If it is a FromBaseTable, then we will generate
0872: * the ProjectRestrictNode above it.
0873: */
0874: fromList.preprocess(numTables, groupByList, whereClause);
0875:
0876: /* selectSubquerys is always allocated at bind() time */
0877: if (SanityManager.DEBUG) {
0878: SanityManager.ASSERT(selectSubquerys != null,
0879: "selectSubquerys is expected to be non-null");
0880: }
0881:
0882: /* Preprocess the RCL after the from list so that
0883: * we can flatten/optimize any subqueries in the
0884: * select list.
0885: */
0886: resultColumns.preprocess(numTables, fromList, whereSubquerys,
0887: wherePredicates);
0888:
0889: /* Preprocess the expressions. (This is necessary for subqueries.
0890: * This is also where we do tranformations such as for LIKE.)
0891: *
0892: * NOTE: We do this after preprocessing the fromList so that, for
0893: * quantified subqueries, the join expression with the outer
0894: * expression will be pushable (to be pushable, the ColumnReference
0895: * has to point to a VirtualColumnNode, and not to a BaseColumnNode).
0896: */
0897: if (whereClause != null) {
0898: whereClause.preprocess(numTables, fromList, whereSubquerys,
0899: wherePredicates);
0900: }
0901:
0902: /* Preprocess the group by list too. We need to compare
0903: * expressions in the group by list with the select list and we
0904: * can't rewrite one and not the other.
0905: */
0906: if (groupByList != null) {
0907: groupByList.preprocess(numTables, fromList, whereSubquerys,
0908: wherePredicates);
0909: }
0910:
0911: /* Pull apart the expression trees */
0912: if (whereClause != null) {
0913: wherePredicates.pullExpressions(numTables, whereClause);
0914: whereClause = null;
0915: }
0916:
0917: /* RESOLVE - Where should we worry about expression pull up for
0918: * expensive predicates?
0919: */
0920:
0921: // Flatten any flattenable FromSubquerys or JoinNodes
0922: fromList.flattenFromTables(resultColumns, wherePredicates,
0923: whereSubquerys, groupByList);
0924:
0925: if (wherePredicates != null && wherePredicates.size() > 0
0926: && fromList.size() > 0) {
0927: // Perform various forms of transitive closure on wherePredicates
0928: if (fromList.size() > 1) {
0929: performTransitiveClosure(numTables);
0930: }
0931:
0932: if (orderByList != null) {
0933: // Remove constant columns from order by list. Constant
0934: // columns are ones that have equality comparisons with
0935: // constant expressions (e.g. x = 3)
0936: orderByList.removeConstantColumns(wherePredicates);
0937:
0938: /*
0939: ** It's possible for the order by list to shrink to nothing
0940: ** as a result of removing constant columns. If this happens,
0941: ** get rid of the list entirely.
0942: */
0943: if (orderByList.size() == 0) {
0944: orderByList = null;
0945: }
0946: }
0947: }
0948:
0949: /* A valid group by without any aggregates is equivalent to
0950: * a distinct without the group by. We do the transformation
0951: * in order to simplify the group by code.
0952: */
0953: if (groupByList != null && selectAggregates.size() == 0
0954: && whereAggregates.size() == 0) {
0955: isDistinct = true;
0956: groupByList = null;
0957: }
0958:
0959: /* Consider distinct elimination based on a uniqueness condition.
0960: * In order to do this:
0961: * o All top level ColumnReferences in the select list are
0962: * from the same base table. (select t1.c1, t2.c2 + t3.c3 is
0963: * okay - t1 is the table of interest.)
0964: * o If the from list is a single table then the columns in the
0965: * select list plus the columns in the where clause that are
0966: * in = comparisons with constants or parameters must be a
0967: * superset of any unique index.
0968: * o If the from list has multiple tables then at least 1 table
0969: * meet the following - the set of columns in = comparisons
0970: * with constants or parameters is a superset of any unique
0971: * index on the table. All of the other tables must meet
0972: * the following - the set of columns in = comparisons with
0973: * constants, parameters or join columns is a superset of
0974: * any unique index on the table. If the table from which
0975: * the columns in the select list are coming from is in this
0976: * later group then the rule for it is to also include
0977: * the columns in the select list in the set of columns that
0978: * needs to be a superset of the unique index. Whew!
0979: */
0980: if (isDistinct && groupByList == null) {
0981: int distinctTable = resultColumns.allTopCRsFromSameTable();
0982:
0983: if (distinctTable != -1) {
0984: if (fromList.returnsAtMostSingleRow(resultColumns,
0985: whereClause, wherePredicates,
0986: getDataDictionary())) {
0987: isDistinct = false;
0988: }
0989: }
0990:
0991: /* If we were unable to eliminate the distinct and we have
0992: * an order by then we can consider eliminating the sort for the
0993: * order by. All of the columns in the order by list must
0994: * be ascending in order to do this. There are 2 cases:
0995: * o The order by list is an in order prefix of the columns
0996: * in the select list. In this case the output of the
0997: * sort from the distinct will be in the right order
0998: * so we simply eliminate the order by list.
0999: * o The order by list is a subset of the columns in the
1000: * the select list. In this case we need to reorder the
1001: * columns in the select list so that the ordering columns
1002: * are an in order prefix of the select list and put a PRN
1003: * above the select so that the shape of the result set
1004: * is as expected.
1005: */
1006: if (isDistinct && orderByList != null
1007: && orderByList.allAscending()) {
1008: /* Order by list currently restricted to columns in select
1009: * list, so we will always eliminate the order by here.
1010: */
1011: if (orderByList.isInOrderPrefix(resultColumns)) {
1012: orderByList = null;
1013: } else {
1014: /* Order by list is not an in order prefix of the select list
1015: * so we must reorder the columns in the the select list to
1016: * match the order by list and generate the PRN above us to
1017: * preserve the expected order.
1018: */
1019: newTop = genProjectRestrictForReordering();
1020: orderByList.resetToSourceRCs();
1021: resultColumns = orderByList
1022: .reorderRCL(resultColumns);
1023: orderByList = null;
1024: }
1025: orderByAndDistinctMerged = true;
1026: }
1027: }
1028:
1029: /*
1030: * Push predicates that are pushable.
1031: *
1032: * NOTE: We pass the wherePredicates down to the new PRNs here,
1033: * so they can pull any clauses and possibily push them down further.
1034: * NOTE: We wait until all of the FromTables have been preprocessed
1035: * until we attempt to push down predicates, because we cannot push down
1036: * a predicate if the immediate source of any of its column references
1037: * is not a ColumnReference or a VirtualColumnNode.
1038: */
1039: fromList.pushPredicates(wherePredicates);
1040:
1041: /* Set up the referenced table map */
1042: referencedTableMap = new JBitSet(numTables);
1043: int flSize = fromList.size();
1044: for (int index = 0; index < flSize; index++) {
1045: referencedTableMap.or(((FromTable) fromList
1046: .elementAt(index)).getReferencedTableMap());
1047: }
1048:
1049: /* Copy the referenced table map to the new tree top, if necessary */
1050: if (newTop != this ) {
1051: newTop.setReferencedTableMap((JBitSet) referencedTableMap
1052: .clone());
1053: }
1054: return newTop;
1055: }
1056:
1057: /**
1058: * Peform the various types of transitive closure on the where clause.
1059: * The 2 types are transitive closure on join clauses and on search clauses.
1060: * Join clauses will be processed first to maximize benefit for search clauses.
1061: *
1062: * @param numTables The number of tables in the query
1063: *
1064: * @exception StandardException Thrown on error
1065: */
1066: private void performTransitiveClosure(int numTables)
1067: throws StandardException {
1068: // Join clauses
1069: wherePredicates.joinClauseTransitiveClosure(numTables,
1070: fromList, getCompilerContext());
1071:
1072: // Search clauses
1073: wherePredicates.searchClauseTransitiveClosure(numTables,
1074: fromList.hashJoinSpecified());
1075: }
1076:
1077: /** Put the expression trees in conjunctive normal form
1078: *
1079: * @exception StandardException Thrown on error
1080: */
1081: private void normExpressions() throws StandardException {
1082: /* For each expression tree:
1083: * o Eliminate NOTs (eliminateNots())
1084: * o Ensure that there is an AndNode on top of every
1085: * top level expression. (putAndsOnTop())
1086: * o Finish the job (changeToCNF())
1087: */
1088: if (whereClause != null) {
1089: whereClause = whereClause.eliminateNots(false);
1090: if (SanityManager.DEBUG) {
1091: if (!(whereClause.verifyEliminateNots())) {
1092: whereClause.treePrint();
1093: SanityManager
1094: .THROWASSERT("whereClause in invalid form: "
1095: + whereClause);
1096: }
1097: }
1098: whereClause = whereClause.putAndsOnTop();
1099: if (SanityManager.DEBUG) {
1100: if (!((whereClause instanceof AndNode) && (whereClause
1101: .verifyPutAndsOnTop()))) {
1102: whereClause.treePrint();
1103: SanityManager
1104: .THROWASSERT("whereClause in invalid form: "
1105: + whereClause);
1106: }
1107: }
1108: whereClause = whereClause.changeToCNF(true);
1109: if (SanityManager.DEBUG) {
1110: if (!((whereClause instanceof AndNode) && (whereClause
1111: .verifyChangeToCNF()))) {
1112: whereClause.treePrint();
1113: SanityManager
1114: .THROWASSERT("whereClause in invalid form: "
1115: + whereClause);
1116: }
1117: }
1118: }
1119: }
1120:
1121: /**
1122: * Add a new predicate to the list. This is useful when doing subquery
1123: * transformations, when we build a new predicate with the left side of
1124: * the subquery operator and the subquery's result column.
1125: *
1126: * @param predicate The predicate to add
1127: *
1128: * @return ResultSetNode The new top of the tree.
1129: *
1130: * @exception StandardException Thrown on error
1131: */
1132: public ResultSetNode addNewPredicate(Predicate predicate)
1133: throws StandardException {
1134: wherePredicates.addPredicate(predicate);
1135: return this ;
1136: }
1137:
1138: /**
1139: * Evaluate whether or not the subquery in a FromSubquery is flattenable.
1140: * Currently, a FSqry is flattenable if all of the following are true:
1141: * o Subquery is a SelectNode. (ie, not a RowResultSetNode or a UnionNode)
1142: * o It contains a single table in its FROM list.
1143: * o It contains no subqueries in the SELECT list.
1144: * o It does not contain a group by or having clause
1145: * o It does not contain aggregates.
1146: * o It is not a DISTINCT.
1147: *
1148: * @param fromList The outer from list
1149: *
1150: * @return boolean Whether or not the FromSubquery is flattenable.
1151: */
1152: public boolean flattenableInFromSubquery(FromList fromList) {
1153: if (isDistinct) {
1154: return false;
1155: }
1156: if (this .fromList.size() > 1) {
1157: return false;
1158: }
1159:
1160: /* Don't flatten (at least for now) if selectNode's SELECT list contains a subquery */
1161: if ((selectSubquerys != null) && (selectSubquerys.size() > 0)) {
1162: return false;
1163: }
1164:
1165: /* Don't flatten if selectNode contains a group by or having clause */
1166: if ((groupByList != null) || generatedForHavingClause) {
1167: return false;
1168: }
1169:
1170: /* Don't flatten if select list contains something that isn't cloneable.
1171: */
1172: if (!resultColumns.isCloneable()) {
1173: return false;
1174: }
1175:
1176: /* Don't flatten if selectNode contains an aggregate */
1177: if ((selectAggregates != null) && (selectAggregates.size() > 0)) {
1178: return false;
1179: }
1180:
1181: return true;
1182: }
1183:
1184: /**
1185: * Replace this SelectNode with a ProjectRestrictNode,
1186: * since it has served its purpose.
1187: *
1188: * @param origFromListSize The size of the original FROM list, before
1189: * generation of join tree.
1190: * @return ResultSetNode new ResultSetNode atop the query tree.
1191: *
1192: * @exception StandardException Thrown on error
1193: */
1194:
1195: public ResultSetNode genProjectRestrict(int origFromListSize)
1196: throws StandardException {
1197: boolean orderingDependent = false;
1198: PredicateList restrictionList;
1199: ResultColumnList prRCList;
1200: ResultSetNode prnRSN;
1201:
1202: prnRSN = (ResultSetNode) getNodeFactory().getNode(
1203: C_NodeTypes.PROJECT_RESTRICT_NODE,
1204: fromList.elementAt(0), /* Child ResultSet */
1205: resultColumns, /* Projection */
1206: whereClause, /* Restriction */
1207: wherePredicates,/* Restriction as PredicateList */
1208: selectSubquerys,/* Subquerys in Projection */
1209: whereSubquerys, /* Subquerys in Restriction */
1210: null, getContextManager());
1211:
1212: /*
1213: ** If we have aggregates OR a select list we want
1214: ** to generate a GroupByNode. In the case of a
1215: ** scalar aggregate we have no grouping columns.
1216: **
1217: ** JRESOLVE: what about correlated aggregates from another
1218: ** block.
1219: */
1220: if (((selectAggregates != null) && (selectAggregates.size() > 0))
1221: || (groupByList != null)) {
1222: GroupByNode gbn = (GroupByNode) getNodeFactory().getNode(
1223: C_NodeTypes.GROUP_BY_NODE, prnRSN, groupByList,
1224: selectAggregates, null, getContextManager());
1225: gbn
1226: .considerPostOptimizeOptimizations(originalWhereClause != null);
1227: gbn.assignCostEstimate(optimizer.getOptimizedCost());
1228:
1229: groupByList = null;
1230: prnRSN = gbn.getParent();
1231:
1232: // Remember if the result is dependent on the ordering
1233: orderingDependent = orderingDependent
1234: || gbn.getIsInSortedOrder();
1235: }
1236:
1237: // if it is distinct, that must also be taken care of.
1238: if (isDistinct) {
1239: // We first verify that a distinct is valid on the
1240: // RCL.
1241: resultColumns.verifyAllOrderable();
1242:
1243: /* See if we can push duplicate elimination into the store
1244: * via a hash scan. This is possible iff:
1245: * o A single table query
1246: * o We haven't merged the order by and distinct sorts.
1247: * (Results do not have to be in a particular order.)
1248: * o All entries in the select's RCL are ColumnReferences.
1249: * o No predicates (This is because we currently do not
1250: * differentiate between columns referenced in the select
1251: * list and columns referenced in other clauses. In other
1252: * words, the store will do duplicate elimination based on
1253: * all referenced columns.)
1254: * RESOLVE - We can change this to be all referenced columns
1255: * have to be in the select list. In that case, we need to
1256: * refine which predicates are allowed. Basically, all predicates
1257: * must have been pushed down to the index/table scan.(If we make
1258: * this change, then we need to verify that non of the columns in
1259: * the predicates are correlated columns.)
1260: * o NOTE: The implementation of isPossibleDistinctScan() will return
1261: * false if there is an IndexRowToBaseRow above the
1262: * FromBaseTable. This is because all of a table's columns must come
1263: * from the same conglomerate in order to get consistent data.
1264: */
1265: boolean distinctScanPossible = false;
1266: if (origFromListSize == 1
1267: && (!orderByAndDistinctMerged)
1268: && resultColumns
1269: .countNumberOfSimpleColumnReferences() == resultColumns
1270: .size()) {
1271: boolean simpleColumns = true;
1272: HashSet distinctColumns = new HashSet();
1273: int size = resultColumns.size();
1274: for (int i = 1; i <= size; i++) {
1275: BaseColumnNode bc = resultColumns
1276: .getResultColumn(i).getBaseColumnNode();
1277: if (bc == null) {
1278: simpleColumns = false;
1279: break;
1280: }
1281: distinctColumns.add(bc);
1282: }
1283: if (simpleColumns
1284: && prnRSN
1285: .isPossibleDistinctScan(distinctColumns)) {
1286: prnRSN.markForDistinctScan();
1287: distinctScanPossible = true;
1288: }
1289: }
1290:
1291: if (!distinctScanPossible) {
1292: /* We can't do a distinct scan. Determine if we can filter out
1293: * duplicates without a sorter.
1294: */
1295: boolean inSortedOrder = isOrderedResult(resultColumns,
1296: prnRSN, !(orderByAndDistinctMerged));
1297: prnRSN = (ResultSetNode) getNodeFactory().getNode(
1298: C_NodeTypes.DISTINCT_NODE, prnRSN,
1299: new Boolean(inSortedOrder), null,
1300: getContextManager());
1301: prnRSN.costEstimate = costEstimate.cloneMe();
1302:
1303: // Remember if the result is dependent on the ordering
1304: orderingDependent = orderingDependent || inSortedOrder;
1305: }
1306: }
1307:
1308: /* Generate the OrderByNode if a sort is still required for
1309: * the order by.
1310: */
1311: if (orderByList != null) {
1312: if (orderByList.getSortNeeded()) {
1313: prnRSN = (ResultSetNode) getNodeFactory().getNode(
1314: C_NodeTypes.ORDER_BY_NODE, prnRSN, orderByList,
1315: null, getContextManager());
1316: prnRSN.costEstimate = costEstimate.cloneMe();
1317: }
1318:
1319: int orderBySelect = this .getResultColumns()
1320: .getOrderBySelect();
1321: if (orderBySelect > 0) {
1322: ResultColumnList selectRCs = prnRSN.getResultColumns()
1323: .copyListAndObjects();
1324: int wholeSize = selectRCs.size();
1325: for (int i = wholeSize - 1; orderBySelect > 0; i--, orderBySelect--)
1326: selectRCs.removeElementAt(i);
1327: selectRCs.genVirtualColumnNodes(prnRSN, prnRSN
1328: .getResultColumns());
1329: prnRSN = (ResultSetNode) getNodeFactory().getNode(
1330: C_NodeTypes.PROJECT_RESTRICT_NODE, prnRSN,
1331: selectRCs, null, null, null, null, null,
1332: getContextManager());
1333: }
1334: }
1335:
1336: if (!(orderByList != null && orderByList.getSortNeeded())
1337: && orderByQuery) {
1338: // Remember if the result is dependent on the ordering
1339: orderingDependent = true;
1340: }
1341:
1342: /* If the result is ordering dependent, then we must
1343: * tell the underlying tree. At minimum, this means no
1344: * group fetch on an index under an IndexRowToBaseRow
1345: * since that could lead to incorrect results. (Bug 2347.)
1346: */
1347: if (orderingDependent) {
1348: prnRSN.markOrderingDependent();
1349: }
1350:
1351: /* Set the cost of this node in the generated node */
1352: prnRSN.costEstimate = costEstimate.cloneMe();
1353:
1354: return prnRSN;
1355: }
1356:
1357: /**
1358: * Is the result of this node an ordered result set. An ordered result set
1359: * means that the results from this node will come in a known sorted order.
1360: * This means that the data is ordered according to the order of the elements in the RCL.
1361: * Today, the data is considered ordered if:
1362: * o The RCL is composed entirely of CRs or ConstantNodes
1363: * o The underlying tree is ordered on the CRs in the order in which
1364: * they appear in the RCL, taking equality predicates into account.
1365: * Future Enhancements:
1366: * o The prefix will not be required to be in order. (We will need to
1367: * reorder the RCL and generate a PRN with an RCL in the expected order.)
1368: *
1369: * @return boolean Whether or not this node returns an ordered result set.
1370: *
1371: * @exception StandardException Thrown on error
1372: */
1373: private boolean isOrderedResult(ResultColumnList resultColumns,
1374: ResultSetNode newTopRSN, boolean permuteOrdering)
1375: throws StandardException {
1376: int rclSize = resultColumns.size();
1377:
1378: /* Not ordered if RCL contains anything other than a ColumnReference
1379: * or a ConstantNode.
1380: */
1381: int numCRs = 0;
1382: for (int index = 0; index < rclSize; index++) {
1383: ResultColumn rc = (ResultColumn) resultColumns
1384: .elementAt(index);
1385: if (rc.getExpression() instanceof ColumnReference) {
1386: numCRs++;
1387: } else if (!(rc.getExpression() instanceof ConstantNode)) {
1388: return false;
1389: }
1390: }
1391:
1392: // Corner case, all constants
1393: if (numCRs == 0) {
1394: return true;
1395: }
1396:
1397: ColumnReference[] crs = new ColumnReference[numCRs];
1398:
1399: // Now populate the CR array and see if ordered
1400: int crsIndex = 0;
1401: for (int index = 0; index < rclSize; index++) {
1402: ResultColumn rc = (ResultColumn) resultColumns
1403: .elementAt(index);
1404: if (rc.getExpression() instanceof ColumnReference) {
1405: crs[crsIndex++] = (ColumnReference) rc.getExpression();
1406: }
1407: }
1408:
1409: return newTopRSN.isOrderedOn(crs, permuteOrdering,
1410: (Vector) null);
1411: }
1412:
1413: /**
1414: * Ensure that the top of the RSN tree has a PredicateList.
1415: *
1416: * @param numTables The number of tables in the query.
1417: * @return ResultSetNode A RSN tree with a node which has a PredicateList on top.
1418: *
1419: * @exception StandardException Thrown on error
1420: */
1421: public ResultSetNode ensurePredicateList(int numTables)
1422: throws StandardException {
1423: return this ;
1424: }
1425:
1426: /**
1427: * Optimize this SelectNode. This means choosing the best access path
1428: * for each table, among other things.
1429: *
1430: * @param dataDictionary The DataDictionary to use for optimization
1431: * @param predicateList The predicate list to optimize against
1432: * @param outerRows The number of outer joining rows
1433: *
1434: * @return ResultSetNode The top of the optimized tree
1435: *
1436: * @exception StandardException Thrown on error
1437: */
1438:
1439: public ResultSetNode optimize(DataDictionary dataDictionary,
1440: PredicateList predicateList, double outerRows)
1441: throws StandardException {
1442: Optimizer optimizer;
1443:
1444: /* Optimize any subquerys before optimizing the underlying result set */
1445:
1446: /* selectSubquerys is always allocated at bind() time */
1447: if (SanityManager.DEBUG)
1448: SanityManager.ASSERT(selectSubquerys != null,
1449: "selectSubquerys is expected to be non-null");
1450:
1451: /* If this select node is the child of an outer node that is
1452: * being optimized, we can get here multiple times (once for
1453: * every permutation that is done for the outer node). With
1454: * DERBY-805, we can add optimizable predicates to the WHERE
1455: * list as part of this method; thus, before proceeding we
1456: * need go through and remove any opt predicates that we added
1457: * to our WHERE list the last time we were here; if we don't
1458: * do that, we'll end up with the same predicates in our
1459: * WHERE list multiple times, which can lead to incorrect
1460: * optimization.
1461: */
1462:
1463: if (wherePredicates != null) {
1464: // Iterate backwards because we might be deleting entries.
1465: for (int i = wherePredicates.size() - 1; i >= 0; i--) {
1466: if (((Predicate) wherePredicates.elementAt(i))
1467: .isScopedForPush())
1468: wherePredicates.removeOptPredicate(i);
1469: }
1470: }
1471:
1472: /* Get a new optimizer */
1473:
1474: /* With DERBY-805 we take any optimizable predicates that
1475: * were pushed into this node and we add them to the list of
1476: * predicates that we pass to the optimizer, thus allowing
1477: * the optimizer to use them when choosing an access path
1478: * for this SELECT node. We do that by adding the predicates
1479: * to our WHERE list, since the WHERE predicate list is what
1480: * we pass to the optimizer for this select node (see below).
1481: * We have to pass the WHERE list directly (as opposed to
1482: * passing a copy) because the optimizer is only created one
1483: * time; it then uses the list we pass it for the rest of the
1484: * optimization phase and finally for "modifyAccessPaths()".
1485: * Since the optimizer can update/modify the list based on the
1486: * WHERE predicates (such as by adding internal predicates or
1487: * by modifying the actual predicates themselves), we need
1488: * those changes to be applied to the WHERE list directly for
1489: * subsequent processing (esp. for modification of the access
1490: * path). Note that by adding outer opt predicates directly
1491: * to the WHERE list, we're changing the semantics of this
1492: * SELECT node. This is only temporary, though--once the
1493: * optimizer is done with all of its work, any predicates
1494: * that were pushed here will have been pushed even further
1495: * down and thus will have been removed from the WHERE list
1496: * (if it's not possible to push them further down, then they
1497: * shouldn't have made it this far to begin with).
1498: */
1499: if (predicateList != null) {
1500: if (wherePredicates == null) {
1501: wherePredicates = (PredicateList) getNodeFactory()
1502: .getNode(C_NodeTypes.PREDICATE_LIST,
1503: getContextManager());
1504: }
1505:
1506: Predicate pred = null;
1507: int sz = predicateList.size();
1508: for (int i = sz - 1; i >= 0; i--) {
1509: // We can tell if a predicate was pushed into this select
1510: // node because it will have been "scoped" for this node
1511: // or for some result set below this one.
1512: pred = (Predicate) predicateList.getOptPredicate(i);
1513: if (pred.isScopedToSourceResultSet()) {
1514: // If we're pushing the predicate down here, we have to
1515: // remove it from the predicate list of the node above
1516: // this select, in order to keep in line with established
1517: // push 'protocol'.
1518: wherePredicates.addOptPredicate(pred);
1519: predicateList.removeOptPredicate(pred);
1520: }
1521: }
1522: }
1523:
1524: optimizer = getOptimizer(fromList, wherePredicates,
1525: dataDictionary, orderByList);
1526: optimizer.setOuterRows(outerRows);
1527:
1528: /* Optimize this SelectNode */
1529: while (optimizer.getNextPermutation()) {
1530: while (optimizer.getNextDecoratedPermutation()) {
1531: optimizer.costPermutation();
1532: }
1533: }
1534:
1535: /* When we're done optimizing, any scoped predicates that
1536: * we pushed down the tree should now be sitting again
1537: * in our wherePredicates list. Put those back in the
1538: * the list from which we received them, to allow them
1539: * to be "pulled" back up to where they came from.
1540: */
1541: if (wherePredicates != null) {
1542: Predicate pred = null;
1543: for (int i = wherePredicates.size() - 1; i >= 0; i--) {
1544: pred = (Predicate) wherePredicates.getOptPredicate(i);
1545: if (pred.isScopedForPush()) {
1546: predicateList.addOptPredicate(pred);
1547: wherePredicates.removeOptPredicate(pred);
1548: }
1549: }
1550: }
1551:
1552: /* Get the cost */
1553: costEstimate = optimizer.getOptimizedCost();
1554:
1555: /* Update row counts if this is a scalar aggregate */
1556: if ((selectAggregates != null) && (selectAggregates.size() > 0)) {
1557: costEstimate.setEstimatedRowCount((long) outerRows);
1558: costEstimate.setSingleScanRowCount(1);
1559: }
1560:
1561: selectSubquerys.optimize(dataDictionary, costEstimate
1562: .rowCount());
1563:
1564: if (whereSubquerys != null && whereSubquerys.size() > 0) {
1565: whereSubquerys.optimize(dataDictionary, costEstimate
1566: .rowCount());
1567: }
1568:
1569: return this ;
1570: }
1571:
1572: /**
1573: * Modify the access paths according to the decisions the optimizer
1574: * made. This can include adding project/restrict nodes,
1575: * index-to-base-row nodes, etc.
1576: *
1577: * @param predList A list of optimizable predicates that should
1578: * be pushed to this ResultSetNode, as determined by optimizer.
1579: * @return The modified query tree
1580: * @exception StandardException Thrown on error
1581: */
1582: public ResultSetNode modifyAccessPaths(PredicateList predList)
1583: throws StandardException {
1584: // Take the received list of predicates and propagate them to the
1585: // predicate list for this node's optimizer. Then, when we call
1586: // optimizer.modifyAccessPaths(), the optimizer will have the
1587: // predicates and can push them down as necessary, according
1588: // the join order that it has chosen.
1589:
1590: if (SanityManager.DEBUG) {
1591: SanityManager.ASSERT(optimizer != null,
1592: "SelectNode's optimizer not expected to be null when "
1593: + "modifying access paths.");
1594: }
1595:
1596: ((OptimizerImpl) optimizer).addScopedPredicatesToList(predList);
1597: return modifyAccessPaths();
1598: }
1599:
1600: /**
1601: * Modify the access paths according to the choices the optimizer made.
1602: *
1603: * @return A QueryTree with the necessary modifications made
1604: *
1605: * @exception StandardException Thrown on error
1606: */
1607: public ResultSetNode modifyAccessPaths() throws StandardException {
1608: int origFromListSize = fromList.size();
1609: ResultColumnList leftRCList;
1610: ResultColumnList rightRCList;
1611: ResultSetNode leftResultSet;
1612: ResultSetNode rightResultSet;
1613:
1614: /*
1615: ** Modify the access path for each Optimizable, as necessary
1616: **
1617: ** This should be the same optimizer we got above.
1618: */
1619: optimizer.modifyAccessPaths();
1620:
1621: // Load the costEstimate for the final "best" join order.
1622: costEstimate = optimizer.getFinalCost();
1623:
1624: if (SanityManager.DEBUG) {
1625: // When we optimized this select node, we may have added pushable
1626: // outer predicates to the wherePredicates list for this node
1627: // (see the optimize() method above). When we did so, we said
1628: // that all such predicates should have been removed from the
1629: // where list by the time optimization was completed. So we
1630: // check that here, just to be safe. NOTE: We do this _after_
1631: // calling optimizer.modifyAccessPaths(), because it's only in
1632: // that call that the scoped predicates are officially pushed
1633: // and thus removed from the list.
1634: if (wherePredicates != null) {
1635: Predicate pred = null;
1636: for (int i = wherePredicates.size() - 1; i >= 0; i--) {
1637: pred = (Predicate) wherePredicates
1638: .getOptPredicate(i);
1639: if (pred.isScopedForPush()) {
1640: SanityManager
1641: .THROWASSERT("Found scoped predicate "
1642: + pred
1643: .binaryRelOpColRefsToString()
1644: + " in WHERE list when no scoped predicates were"
1645: + " expected.");
1646: }
1647: }
1648: }
1649: }
1650:
1651: selectSubquerys.modifyAccessPaths();
1652:
1653: if (whereSubquerys != null && whereSubquerys.size() > 0) {
1654: whereSubquerys.modifyAccessPaths();
1655: }
1656:
1657: /* Build a temp copy of the current FromList for sort elimination, etc. */
1658: preJoinFL.removeAllElements();
1659: preJoinFL.nondestructiveAppend(fromList);
1660:
1661: /* Now we build a JoinNode tree from the bottom up until there is only
1662: * a single entry in the fromList and that entry points to the top of
1663: * the JoinNode tree.
1664: *
1665: * While there is still more than 1 entry in the list, create a JoinNode
1666: * which points to the 1st 2 entries in the list. This JoinNode becomes
1667: * the new 1st entry in the list and the 2nd entry is deleted. The
1668: * old 1st and 2nd entries will get shallow copies of their
1669: * ResultColumnLists. The JoinNode's ResultColumnList will be the
1670: * concatenation of the originals from the old 1st and 2nd entries.
1671: * The virtualColumnIds will be updated to reflect there new positions
1672: * and each ResultColumn.expression will be replaced with a new
1673: * VirtualColumnNode.
1674: */
1675: while (fromList.size() > 1) {
1676: /* Get left's ResultColumnList, assign shallow copy back to it
1677: * and create new VirtualColumnNodes for the original's
1678: * ResultColumn.expressions.
1679: */
1680: leftResultSet = (ResultSetNode) fromList.elementAt(0);
1681: leftRCList = leftResultSet.getResultColumns();
1682: leftResultSet.setResultColumns(leftRCList
1683: .copyListAndObjects());
1684: leftRCList.genVirtualColumnNodes(leftResultSet,
1685: leftResultSet.resultColumns);
1686:
1687: /* Get right's ResultColumnList, assign shallow copy back to it,
1688: * create new VirtualColumnNodes for the original's
1689: * ResultColumn.expressions and increment the virtualColumnIds.
1690: * (Right gets appended to left, so only right's ids need updating.)
1691: */
1692: rightResultSet = (ResultSetNode) fromList.elementAt(1);
1693: rightRCList = rightResultSet.getResultColumns();
1694: rightResultSet.setResultColumns(rightRCList
1695: .copyListAndObjects());
1696: rightRCList.genVirtualColumnNodes(rightResultSet,
1697: rightResultSet.resultColumns);
1698: rightRCList.adjustVirtualColumnIds(leftRCList.size());
1699:
1700: /* Concatenate the 2 ResultColumnLists */
1701: leftRCList.nondestructiveAppend(rightRCList);
1702:
1703: /* Now we're finally ready to generate the JoinNode and have it
1704: * replace the 1st 2 entries in the FromList.
1705: */
1706: fromList.setElementAt((JoinNode) getNodeFactory().getNode(
1707: C_NodeTypes.JOIN_NODE, leftResultSet,
1708: rightResultSet, null, null, leftRCList, null,
1709: //user supplied optimizer overrides
1710: fromList.properties, getContextManager()), 0);
1711:
1712: fromList.removeElementAt(1);
1713: }
1714:
1715: return genProjectRestrict(origFromListSize);
1716: }
1717:
1718: /**
1719: * Get the final CostEstimate for this SelectNode.
1720: *
1721: * @return The final CostEstimate for this SelectNode, which is
1722: * the final cost estimate for the best join order of
1723: * this SelectNode's optimizer.
1724: */
1725: public CostEstimate getFinalCostEstimate() throws StandardException {
1726: return optimizer.getFinalCost();
1727: }
1728:
1729: /**
1730: Determine if this select is updatable or not, for a cursor.
1731: */
1732: boolean isUpdatableCursor(DataDictionary dd)
1733: throws StandardException {
1734: TableDescriptor targetTableDescriptor;
1735:
1736: if (isDistinct) {
1737: if (SanityManager.DEBUG)
1738: SanityManager.DEBUG("DumpUpdateCheck",
1739: "cursor select has distinct");
1740: return false;
1741: }
1742:
1743: if ((selectAggregates == null) || (selectAggregates.size() > 0)) {
1744: return false;
1745: }
1746:
1747: if (groupByList != null || generatedForHavingClause) {
1748: return false;
1749: }
1750:
1751: if (SanityManager.DEBUG)
1752: SanityManager.ASSERT(fromList != null,
1753: "select must have from tables");
1754: if (fromList.size() != 1) {
1755: if (SanityManager.DEBUG)
1756: SanityManager.DEBUG("DumpUpdateCheck",
1757: "cursor select has more than one from table");
1758: return false;
1759: }
1760:
1761: targetTable = (FromTable) (fromList.elementAt(0));
1762:
1763: if (targetTable instanceof FromVTI) {
1764:
1765: return ((FromVTI) targetTable).isUpdatableCursor();
1766: }
1767:
1768: if (!(targetTable instanceof FromBaseTable)) {
1769: if (SanityManager.DEBUG)
1770: SanityManager
1771: .DEBUG("DumpUpdateCheck",
1772: "cursor select has non base table as target table");
1773: return false;
1774: }
1775:
1776: /* Get the TableDescriptor and verify that it is not for a
1777: * view or a system table.
1778: * NOTE: We need to use the base table name for the table.
1779: * Simplest way to get it is from a FromBaseTable. We
1780: * know that targetTable is a FromBaseTable because of check
1781: * just above us.
1782: * NOTE: We also need to use the base table's schema name; otherwise
1783: * we will think it is the default schema Beetle 4417
1784: */
1785: targetTableDescriptor = getTableDescriptor(
1786: ((FromBaseTable) targetTable).getBaseTableName(),
1787: getSchemaDescriptor(((FromBaseTable) targetTable)
1788: .getTableNameField().getSchemaName()));
1789: if (targetTableDescriptor.getTableType() == TableDescriptor.SYSTEM_TABLE_TYPE) {
1790: if (SanityManager.DEBUG)
1791: SanityManager.DEBUG("DumpUpdateCheck",
1792: "cursor select is on system table");
1793: return false;
1794: }
1795: if (targetTableDescriptor.getTableType() == TableDescriptor.VIEW_TYPE) {
1796: if (SanityManager.DEBUG)
1797: SanityManager.DEBUG("DumpUpdateCheck",
1798: "cursor select is on view");
1799: return false;
1800: }
1801: if ((getSelectSubquerys() != null)
1802: && (getSelectSubquerys().size() != 0)) {
1803: if (SanityManager.DEBUG)
1804: SanityManager.DEBUG("DumpUpdateCheck",
1805: "cursor select has subquery in SELECT list");
1806: return false;
1807: }
1808:
1809: if ((getWhereSubquerys() != null)
1810: && (getWhereSubquerys().size() != 0)) {
1811: if (SanityManager.DEBUG)
1812: SanityManager.DEBUG("DumpUpdateCheck",
1813: "cursor select has subquery in WHERE clause");
1814: return false;
1815: }
1816:
1817: return true;
1818: }
1819:
1820: /**
1821: Assumes that isCursorUpdatable has been called, and that it
1822: is only called for updatable cursors.
1823: */
1824: FromTable getCursorTargetTable() {
1825: if (SanityManager.DEBUG)
1826: SanityManager
1827: .ASSERT(targetTable != null,
1828: "must call isUpdatableCursor() first, and must be updatable");
1829: return targetTable;
1830: }
1831:
1832: /**
1833: * Search to see if a query references the specifed table name.
1834: *
1835: * @param name Table name (String) to search for.
1836: * @param baseTable Whether or not name is for a base table
1837: *
1838: * @return true if found, else false
1839: *
1840: * @exception StandardException Thrown on error
1841: */
1842: public boolean referencesTarget(String name, boolean baseTable)
1843: throws StandardException {
1844: if (fromList.referencesTarget(name, baseTable)
1845: || (selectSubquerys != null && selectSubquerys
1846: .referencesTarget(name, baseTable))
1847: || (whereSubquerys != null && whereSubquerys
1848: .referencesTarget(name, baseTable))) {
1849: return true;
1850: }
1851: return false;
1852: }
1853:
1854: /**
1855: * @see QueryTreeNode#disablePrivilegeCollection
1856: */
1857: public void disablePrivilegeCollection() {
1858: super .disablePrivilegeCollection();
1859: int fromListSize = fromList.size();
1860: for (int i = 0; i < fromListSize; i++)
1861: ((FromTable) fromList.elementAt(i))
1862: .disablePrivilegeCollection();
1863: }
1864:
1865: /**
1866: * Return whether or not this ResultSetNode contains a subquery with a
1867: * reference to the specified target table.
1868: *
1869: * @param name The table name.
1870: * @param baseTable Whether or not table is a base table.
1871: *
1872: * @return boolean Whether or not a reference to the table was found.
1873: *
1874: * @exception StandardException Thrown on error
1875: */
1876: boolean subqueryReferencesTarget(String name, boolean baseTable)
1877: throws StandardException {
1878: if ((selectSubquerys != null && selectSubquerys
1879: .referencesTarget(name, baseTable))
1880: || (whereSubquerys != null && whereSubquerys
1881: .referencesTarget(name, baseTable))) {
1882: return true;
1883: }
1884: return false;
1885: }
1886:
1887: /**
1888: * Bind any untyped null nodes to the types in the given ResultColumnList.
1889: *
1890: * @param bindingRCL The ResultColumnList with the types to bind to.
1891: *
1892: * @exception StandardException Thrown on error
1893: */
1894: public void bindUntypedNullsToResultColumns(
1895: ResultColumnList bindingRCL) throws StandardException {
1896: fromList.bindUntypedNullsToResultColumns(bindingRCL);
1897: }
1898:
1899: /**
1900: * Decrement (query block) level (0-based) for
1901: * all of the tables in this ResultSet tree.
1902: * This is useful when flattening a subquery.
1903: *
1904: * @param decrement The amount to decrement by.
1905: */
1906: void decrementLevel(int decrement) {
1907: /* Decrement the level in the tables */
1908: fromList.decrementLevel(decrement);
1909: selectSubquerys.decrementLevel(decrement);
1910: whereSubquerys.decrementLevel(decrement);
1911: /* Decrement the level in any CRs in predicates
1912: * that are interesting to transitive closure.
1913: */
1914: wherePredicates.decrementLevel(fromList, decrement);
1915: }
1916:
1917: /**
1918: * Determine whether or not this subquery,
1919: * the SelectNode is in a subquery, can be flattened
1920: * into the outer query block based on a uniqueness condition.
1921: * A uniqueness condition exists when we can guarantee
1922: * that at most 1 row will qualify in each table in the
1923: * subquery. This is true if every table in the from list is
1924: * (a base table and the set of columns from the table that
1925: * are in equality comparisons with expressions that do not
1926: * include a column from the same table is a superset of any unique index
1927: * on the table) or an ExistsBaseTable.
1928: *
1929: * @param additionalEQ Whether or not the column returned
1930: * by this select, if it is a ColumnReference,
1931: * is in an equality comparison.
1932: *
1933: * @return Whether or not this subquery can be flattened based
1934: * on a uniqueness condition.
1935: *
1936: * @exception StandardException Thrown on error
1937: */
1938: boolean uniqueSubquery(boolean additionalEQ)
1939: throws StandardException {
1940: ColumnReference additionalCR = null;
1941: ResultColumn rc = (ResultColumn) getResultColumns()
1942: .elementAt(0);
1943:
1944: /* Figure out if we have an additional ColumnReference
1945: * in an equality comparison.
1946: */
1947: if (additionalEQ
1948: && rc.getExpression() instanceof ColumnReference) {
1949: additionalCR = (ColumnReference) rc.getExpression();
1950:
1951: /* ColumnReference only interesting if it is
1952: * not correlated.
1953: */
1954: if (additionalCR.getCorrelated()) {
1955: additionalCR = null;
1956: }
1957: }
1958:
1959: return fromList.returnsAtMostSingleRow(
1960: (additionalCR == null) ? null : getResultColumns(),
1961: whereClause, wherePredicates, getDataDictionary());
1962: }
1963:
1964: /**
1965: * Get the lock mode for the target of an update statement
1966: * (a delete or update). The update mode will always be row for
1967: * CurrentOfNodes. It will be table if there is no where clause.
1968: *
1969: * @see TransactionController
1970: *
1971: * @return The lock mode
1972: */
1973: public int updateTargetLockMode() {
1974: /* Do row locking if there is a restriction */
1975: return fromList.updateTargetLockMode();
1976: }
1977:
1978: /**
1979: * Return whether or not this ResultSet tree is guaranteed to return
1980: * at most 1 row based on heuristics. (A RowResultSetNode and a
1981: * SELECT with a non-grouped aggregate will return at most 1 row.)
1982: *
1983: * @return Whether or not this ResultSet tree is guaranteed to return
1984: * at most 1 row based on heuristics.
1985: */
1986: boolean returnsAtMostOneRow() {
1987: return (groupByList == null && selectAggregates != null && selectAggregates
1988: .size() != 0);
1989: }
1990:
1991: /**
1992: * Return true if the node references SESSION schema tables (temporary or permanent)
1993: *
1994: * @return true if references SESSION schema tables, else false
1995: *
1996: * @exception StandardException Thrown on error
1997: */
1998: public boolean referencesSessionSchema() throws StandardException {
1999: if (fromList.referencesSessionSchema()
2000: || (selectSubquerys != null && selectSubquerys
2001: .referencesSessionSchema())
2002: || (whereSubquerys != null && whereSubquerys
2003: .referencesSessionSchema()))
2004: return true;
2005:
2006: return false;
2007: }
2008:
2009: /**
2010: * Accept a visitor, and call v.visit()
2011: * on child nodes as necessary.
2012: *
2013: * @param v the visitor
2014: *
2015: * @exception StandardException on error
2016: */
2017: public Visitable accept(Visitor v) throws StandardException {
2018: Visitable returnNode = v.visit(this );
2019:
2020: if (v.skipChildren(this )) {
2021: return returnNode;
2022: }
2023:
2024: if (!v.stopTraversal()) {
2025: super .accept(v);
2026: }
2027:
2028: if (fromList != null && !v.stopTraversal()) {
2029: fromList = (FromList) fromList.accept(v);
2030: }
2031:
2032: if (whereClause != null && !v.stopTraversal()) {
2033: whereClause = (ValueNode) whereClause.accept(v);
2034: }
2035:
2036: if (wherePredicates != null && !v.stopTraversal()) {
2037: wherePredicates = (PredicateList) wherePredicates.accept(v);
2038: }
2039:
2040: return returnNode;
2041: }
2042: }
|