0001: /*
0002:
0003: Derby - Class org.apache.derby.impl.sql.compile.SubqueryNode
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.context.ContextManager;
0025:
0026: import org.apache.derby.iapi.error.StandardException;
0027:
0028: import org.apache.derby.iapi.sql.compile.CompilerContext;
0029: import org.apache.derby.iapi.sql.compile.CostEstimate;
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: import org.apache.derby.iapi.reference.SQLState;
0036: import org.apache.derby.iapi.reference.ClassName;
0037:
0038: import org.apache.derby.iapi.types.DataTypeDescriptor;
0039:
0040: import org.apache.derby.iapi.sql.execute.ExecRow;
0041:
0042: import org.apache.derby.iapi.sql.Activation;
0043: import org.apache.derby.iapi.types.DataValueDescriptor;
0044: import org.apache.derby.iapi.sql.Row;
0045: import org.apache.derby.iapi.types.DataTypeDescriptor;
0046: import org.apache.derby.iapi.sql.ResultSet;
0047: import org.apache.derby.iapi.types.TypeId;
0048:
0049: import org.apache.derby.iapi.services.loader.GeneratedMethod;
0050:
0051: import org.apache.derby.iapi.services.compiler.MethodBuilder;
0052: import org.apache.derby.iapi.services.compiler.LocalField;
0053:
0054: import org.apache.derby.iapi.services.sanity.SanityManager;
0055:
0056: import org.apache.derby.iapi.store.access.Qualifier;
0057:
0058: import java.lang.reflect.Modifier;
0059:
0060: import org.apache.derby.impl.sql.compile.ExpressionClassBuilder;
0061: import org.apache.derby.impl.sql.compile.ActivationClassBuilder;
0062: import org.apache.derby.impl.sql.execute.OnceResultSet;
0063:
0064: import org.apache.derby.iapi.util.JBitSet;
0065: import org.apache.derby.iapi.util.ReuseFactory;
0066: import org.apache.derby.iapi.services.classfile.VMOpcode;
0067:
0068: import java.util.Properties;
0069: import java.util.Vector;
0070:
0071: /**
0072: * A SubqueryNode represents a subquery. Subqueries return values to their
0073: * outer queries. An quantified subquery is one that appears under a quantified
0074: * operator (like IN or EXISTS) - quantified subqueries can return more than
0075: * one value per invocation. An expression subquery is one that is not directly
0076: * under a quantified operator - expression subqueries are allowed to return
0077: * at most one value per invocation (returning no value is considered to be
0078: * equivalent to returning NULL).
0079: *
0080: * There are a large number of subquery types. Because of the large number of
0081: * types, and the large amount of shared code, we have decided to have 1 SubqueryNode
0082: * without any subclasses. The subquery type (and operator) is encoded in the
0083: * subqueryType field.
0084: *
0085: * The query optimizer is responsible for optimizing subqueries, and also for
0086: * transforming them so that code can be generated for them. The optimizer may
0087: * eliminate some subqueries by transforming them into joins, or it may
0088: * change the internal form of a subquery (for example, transforming
0089: * 'where x in (select y from z where ...)' into
0090: * 'where (select true from z where x = y and ...)').
0091: *
0092: * Note that aggregates present some additional issues. A transformation
0093: * such as:
0094: * <UL> where x in (SELECT <I>expression</I> FROM z) </UL>
0095: * has to be treated specially if <I>expression</I> has an aggregate.
0096: * We change it to:
0097: * <UL> where x = (SELECT true FROM (SELECT MAX(x) FROM z) WHERE SQLCOL1 = y) </UL>
0098: *
0099: * @author Jeff Lichtman
0100: */
0101:
0102: public class SubqueryNode extends ValueNode {
0103: /*
0104: ** This must be a single-column result set. If the subquery is
0105: ** not quantified, it must also be a single-row result set - that is,
0106: ** expression subqueries are allowed to return only a single value
0107: ** per invocation.
0108: ** NOTE: SubqueryNodes are used as an intermediate step within the parser
0109: ** for building a derived table. Derived tables can be multi-column and
0110: ** multi-table.
0111: */
0112: ResultSetNode resultSet;
0113:
0114: /* Type of this subquery */
0115: int subqueryType;
0116:
0117: /* Whether or not this subquery is immediately under a top level AndNode.
0118: * (Important for subquery flattening.)
0119: */
0120: boolean underTopAndNode;
0121:
0122: /* Whether or not we've been preprocessed. (Only do the work once.) */
0123: boolean preprocessed;
0124:
0125: /* Whether or not this subquery began life as a distinct expression subquery */
0126: boolean distinctExpression;
0127:
0128: /* Since we do not have separate subquery operator nodes, the
0129: * type of the subquery is stored in the subqueryType field. Most subquery
0130: * types take a left operand (except for expression and exists). We could
0131: * either add a leftOperand field here or subclass SubqueryNode for those
0132: * types that take a left operand. We have decided to add the left operand
0133: * here for now.
0134: */
0135: ValueNode leftOperand;
0136: boolean pushedNewPredicate;
0137:
0138: /* Expression subqueries on the right side of a BinaryComparisonOperatorNode
0139: * will get passed a pointer to that node prior to preprocess(). This
0140: * allows us to replace the entire comparison, if we want to, when
0141: * flattening.
0142: */
0143: BinaryComparisonOperatorNode parentComparisonOperator;
0144:
0145: /* Private fields (all references via private methods) -
0146: * We reuse true BooleanConstantNodes within
0147: * this class, creating them on the first reference.
0148: */
0149: private BooleanConstantNode trueNode;
0150: /* Reuse generated code where possible */
0151: //private Expression genResult;
0152: /* Subquery # for this subquery */
0153: private int subqueryNumber = -1;
0154:
0155: /* ResultSet # for the point of attachment for this subquery */
0156: private int pointOfAttachment = -1;
0157:
0158: /*
0159: ** Indicate whether we found a correlation or not.
0160: ** And track whether we have checked yet.
0161: */
0162: private boolean foundCorrelation;
0163: private boolean doneCorrelationCheck;
0164:
0165: /*
0166: ** Indicate whether we found an invariant node
0167: ** below us or not. And track whether we have
0168: ** checked yet.
0169: */
0170: private boolean foundVariant;
0171: private boolean doneInvariantCheck;
0172:
0173: /* Subquery types.
0174: * NOTE: FROM_SUBQUERY only exists for a brief second in the parser. It
0175: * should never appear in a query tree.
0176: * NOTE: NOT EXISTS and NOT IN subquery types do not exist prior to NOT
0177: * elimination during preprocessing. Prior to that, there is a separate
0178: * NotNode above the SubqueryNode in the tree.
0179: *
0180: */
0181: public final static int NOTIMPLEMENTED_SUBQUERY = -1;
0182: public final static int FROM_SUBQUERY = 0;
0183: public final static int IN_SUBQUERY = 1;
0184: public final static int NOT_IN_SUBQUERY = 2;
0185: public final static int EQ_ANY_SUBQUERY = 3;
0186: public final static int EQ_ALL_SUBQUERY = 4;
0187: public final static int NE_ANY_SUBQUERY = 5;
0188: public final static int NE_ALL_SUBQUERY = 6;
0189: public final static int GT_ANY_SUBQUERY = 7;
0190: public final static int GT_ALL_SUBQUERY = 8;
0191: public final static int GE_ANY_SUBQUERY = 9;
0192: public final static int GE_ALL_SUBQUERY = 10;
0193: public final static int LT_ANY_SUBQUERY = 11;
0194: public final static int LT_ALL_SUBQUERY = 12;
0195: public final static int LE_ANY_SUBQUERY = 13;
0196: public final static int LE_ALL_SUBQUERY = 14;
0197: public final static int EXISTS_SUBQUERY = 15;
0198: public final static int NOT_EXISTS_SUBQUERY = 16;
0199: public final static int EXPRESSION_SUBQUERY = 17;
0200:
0201: /**
0202: * Initializer.
0203: *
0204: * @param resultSet The ResultSetNode for the subquery
0205: * @param subqueryType The type of the subquery
0206: * @param leftOperand The left operand, if any, of the subquery
0207: */
0208:
0209: public void init(Object resultSet, Object subqueryType,
0210: Object leftOperand) {
0211: this .resultSet = (ResultSetNode) resultSet;
0212: this .subqueryType = ((Integer) subqueryType).intValue();
0213:
0214: /* Subqueries are presumed not to be under a top level AndNode by
0215: * default. This is because expression normalization only recurses
0216: * under Ands and Ors, not under comparison operators, method calls,
0217: * built-in functions, etc.
0218: */
0219: underTopAndNode = false;
0220: this .leftOperand = (ValueNode) leftOperand;
0221: }
0222:
0223: /**
0224: * Convert this object to a String. See comments in QueryTreeNode.java
0225: * for how this should be done for tree printing.
0226: *
0227: * @return This object as a String
0228: */
0229:
0230: public String toString() {
0231: if (SanityManager.DEBUG) {
0232: return "subqueryType: " + subqueryType + "\n"
0233: + "underTopAndNode: " + underTopAndNode + "\n"
0234: + "subqueryNumber: " + subqueryNumber + "\n"
0235: + "pointOfAttachment: " + pointOfAttachment + "\n"
0236: + "preprocessed: " + preprocessed + "\n"
0237: + "distinctExpression: " + distinctExpression
0238: + "\n" + super .toString();
0239: } else {
0240: return "";
0241: }
0242: }
0243:
0244: /**
0245: * Prints the sub-nodes of this object. See QueryTreeNode.java for
0246: * how tree printing is supposed to work.
0247: *
0248: * @param depth The depth of this node in the tree
0249: */
0250:
0251: public void printSubNodes(int depth) {
0252: if (SanityManager.DEBUG) {
0253: super .printSubNodes(depth);
0254:
0255: if (resultSet != null) {
0256: printLabel(depth, "resultSet: ");
0257: resultSet.treePrint(depth + 1);
0258: }
0259:
0260: if (leftOperand != null) {
0261: printLabel(depth, "leftOperand: ");
0262: leftOperand.treePrint(depth + 1);
0263: }
0264: }
0265: }
0266:
0267: /**
0268: * Return the resultSet for this SubqueryNode.
0269: *
0270: * @return ResultSetNode underlying this SubqueryNode.
0271: */
0272: public ResultSetNode getResultSet() {
0273: return resultSet;
0274: }
0275:
0276: /**
0277: * Return the type of this subquery.
0278: *
0279: * @return int Type of this subquery.
0280: */
0281: public int getSubqueryType() {
0282: return subqueryType;
0283: }
0284:
0285: /**
0286: * Set the type of this subquery.
0287: *
0288: * @param subqueryType of this subquery.
0289: */
0290: public void setSubqueryType(int subqueryType) {
0291: this .subqueryType = subqueryType;
0292: }
0293:
0294: /**
0295: * Set the point of attachment of this subquery.
0296: *
0297: * @param pointOfAttachment The point of attachment of this subquery.
0298: *
0299: * @exception StandardException Thrown on error
0300: */
0301: public void setPointOfAttachment(int pointOfAttachment)
0302: throws StandardException {
0303: /* Materialized subqueries always keep their point of
0304: * attachment as -1.
0305: */
0306: if (!isMaterializable()) {
0307: this .pointOfAttachment = pointOfAttachment;
0308: }
0309: }
0310:
0311: /**
0312: * Return whether or not this subquery is immediately under a top level
0313: * AndNode.
0314: *
0315: * @return boolean Whether or not this subquery is immediately under a
0316: * top level AndNode.
0317: */
0318: public boolean getUnderTopAndNode() {
0319: return underTopAndNode;
0320: }
0321:
0322: /**
0323: * Get the ResultSet # for the point of attachment for this SubqueryNode.
0324: *
0325: * @return int The ResultSet # for the point of attachment
0326: */
0327: public int getPointOfAttachment() {
0328: if (SanityManager.DEBUG) {
0329: SanityManager.ASSERT(pointOfAttachment >= 0,
0330: "pointOfAttachment expected to be >= 0");
0331: }
0332: return pointOfAttachment;
0333: }
0334:
0335: /**
0336: * Get whether or not this SubqueryNode has already been
0337: * preprocessed.
0338: *
0339: * @return Whether or not this SubqueryNode has already been
0340: * preprocessed.
0341: */
0342: boolean getPreprocessed() {
0343: return preprocessed;
0344: }
0345:
0346: /**
0347: * Set the parent BCON. Useful when considering flattening
0348: * expression subqueries.
0349: *
0350: * @param parent The parent BCON.
0351: */
0352: void setParentComparisonOperator(BinaryComparisonOperatorNode parent) {
0353: parentComparisonOperator = parent;
0354: }
0355:
0356: /**
0357: * Remap all ColumnReferences in this tree to be clones of the
0358: * underlying expression.
0359: *
0360: * @return ValueNode The remapped expression tree.
0361: *
0362: * @exception StandardException Thrown on error
0363: */
0364: public ValueNode remapColumnReferencesToExpressions()
0365: throws StandardException {
0366: /* We need to remap both the SELECT and Predicate lists
0367: * since there may be correlated columns in either of them.
0368: */
0369: if (resultSet instanceof SelectNode) {
0370: ResultColumnList selectRCL = resultSet.getResultColumns();
0371: SelectNode select = (SelectNode) resultSet;
0372: PredicateList selectPL = select.getWherePredicates();
0373:
0374: if (SanityManager.DEBUG) {
0375: SanityManager.ASSERT(selectPL != null,
0376: "selectPL expected to be non-null");
0377: }
0378: selectRCL.remapColumnReferencesToExpressions();
0379: selectPL.remapColumnReferencesToExpressions();
0380: }
0381: return this ;
0382: }
0383:
0384: /**
0385: * Bind this expression. This means binding the sub-expressions,
0386: * as well as figuring out what the return type is for this expression.
0387: *
0388: * @param fromList The FROM list for the query this
0389: * expression is in, for binding columns.
0390: * NOTE: fromList will be null if the subquery appears
0391: * in a VALUES clause.
0392: * @param subqueryList The subquery list being built as we find SubqueryNodes
0393: * @param aggregateVector The aggregate vector being built as we find AggregateNodes
0394: *
0395: * @return The new top of the expression tree.
0396: *
0397: * @exception StandardException Thrown on error
0398: */
0399: public ValueNode bindExpression(FromList fromList,
0400: SubqueryList subqueryList, Vector aggregateVector)
0401: throws StandardException {
0402: ResultColumnList resultColumns;
0403:
0404: //check if subquery is allowed in expression tree
0405: checkReliability(CompilerContext.SUBQUERY_ILLEGAL,
0406: SQLState.LANG_SUBQUERY);
0407:
0408: resultColumns = resultSet.getResultColumns();
0409:
0410: /* The parser does not enforce the fact that a subquery can only return
0411: * a single column, so we must check here.
0412: */
0413: if (resultColumns.size() != 1) {
0414: throw StandardException
0415: .newException(SQLState.LANG_NON_SINGLE_COLUMN_SUBQUERY);
0416: }
0417:
0418: /* Verify the usage of "*" in the select list:
0419: * o Only valid in EXISTS subqueries
0420: * o If the AllResultColumn is qualified, then we have to verify
0421: * that the qualification is a valid exposed name.
0422: * NOTE: The exposed name can come from an outer query block.
0423: */
0424: resultSet.verifySelectStarSubquery(fromList, subqueryType);
0425:
0426: /* For an EXISTS subquery:
0427: * o If the SELECT list is a "*", then we convert it to a true.
0428: * (We need to do the conversion since we don't want the "*" to
0429: * get expanded.)
0430: * o We then must bind the expression under the SELECT list to
0431: * verify that it is a valid expression. (We must do this as a
0432: * separate step because we need to validate the expression and
0433: * we need to handle EXISTS (select * ... union all select 1 ...)
0434: * without getting a type compatability error.)
0435: * o Finally, we convert the expression to a SELECT true.
0436: */
0437: if (subqueryType == EXISTS_SUBQUERY) {
0438: /* Transform the * into true (EXISTS). */
0439: resultSet.setResultToBooleanTrueNode(true);
0440: }
0441:
0442: /* We need to bind the tables before we can bind the target list
0443: * (for exists subqueries). However, we need to wait until after
0444: * any *'s have been replaced, so that they don't get expanded.
0445: */
0446: CompilerContext cc = getCompilerContext();
0447:
0448: resultSet = resultSet.bindNonVTITables(getDataDictionary(),
0449: fromList);
0450: resultSet = resultSet.bindVTITables(fromList);
0451:
0452: /* Set the subquery # for this SubqueryNode */
0453: if (subqueryNumber == -1)
0454: subqueryNumber = cc.getNextSubqueryNumber();
0455:
0456: /* reject ? parameters in the select list of subqueries */
0457: resultSet.rejectParameters();
0458:
0459: if (subqueryType == EXISTS_SUBQUERY) {
0460: /* Bind the expression in the SELECT list */
0461: resultSet.bindTargetExpressions(fromList);
0462:
0463: /* Transform the ResultColumn into true.
0464: * NOTE: This may be a 2nd instance of the same transformation for
0465: * an EXISTS (select * ...), since we had to transform the
0466: * AllResultColumn above, but we have to also handle
0467: * EXISTS (select r from s ...)
0468: */
0469: resultSet.setResultToBooleanTrueNode(false);
0470: }
0471:
0472: /* bind the left operand, if there is one */
0473: if (leftOperand != null) {
0474: leftOperand = leftOperand.bindExpression(fromList,
0475: subqueryList, aggregateVector);
0476: }
0477:
0478: /* bind the expressions in the underlying subquery */
0479: resultSet.bindExpressions(fromList);
0480:
0481: resultSet.bindResultColumns(fromList);
0482:
0483: /* We need to reset resultColumns since the underlying resultSet may
0484: * be a UNION (and UnionNode.bindResultColumns() regens a new RCL).
0485: */
0486: resultColumns = resultSet.getResultColumns();
0487:
0488: /*
0489: * A ? parameter to the left of this subquery gets type of the
0490: * subquery's sole column.
0491: */
0492: if (leftOperand != null
0493: && leftOperand.requiresTypeFromContext()) {
0494: leftOperand.setType(((ResultColumn) resultColumns
0495: .elementAt(0)).getTypeServices());
0496: }
0497:
0498: // Set the DataTypeServices
0499: setDataTypeServices(resultColumns);
0500:
0501: /* Add this subquery to the subquery list */
0502: subqueryList.addSubqueryNode(this );
0503:
0504: return this ;
0505: }
0506:
0507: /**
0508: * Preprocess an expression tree. We do a number of transformations
0509: * here (including subqueries, IN lists, LIKE and BETWEEN) plus
0510: * subquery flattening.
0511: * NOTE: This is done before the outer ResultSetNode is preprocessed.
0512: *
0513: * @param numTables Number of tables in the DML Statement
0514: * @param outerFromList FromList from outer query block
0515: * @param outerSubqueryList SubqueryList from outer query block
0516: * @param outerPredicateList PredicateList from outer query block
0517: *
0518: * @return The modified expression
0519: *
0520: * @exception StandardException Thrown on error
0521: */
0522: public ValueNode preprocess(int numTables, FromList outerFromList,
0523: SubqueryList outerSubqueryList,
0524: PredicateList outerPredicateList) throws StandardException {
0525: /* Only preprocess this node once. We may get called multiple times
0526: * due to tree transformations.
0527: */
0528: if (preprocessed) {
0529: return this ;
0530: }
0531: preprocessed = true;
0532:
0533: boolean flattenable;
0534: ValueNode topNode = this ;
0535:
0536: resultSet = resultSet.preprocess(numTables, null,
0537: (FromList) null);
0538:
0539: // Eliminate any unnecessary DISTINCTs
0540: if (resultSet instanceof SelectNode) {
0541: if (((SelectNode) resultSet).hasDistinct()) {
0542: ((SelectNode) resultSet).clearDistinct();
0543: /* We need to remember to check for single unique value
0544: * at execution time for expression subqueries.
0545: */
0546: if (subqueryType == EXPRESSION_SUBQUERY) {
0547: distinctExpression = true;
0548: }
0549: }
0550: }
0551:
0552: /* Lame transformation - For IN/ANY subqueries, if
0553: * result set is guaranteed to return at most 1 row
0554: * and it is not correlated
0555: * then convert the subquery into the matching expression
0556: * subquery type. For example:
0557: * c1 in (select min(c1) from t2)
0558: * becomes:
0559: * c1 = (select min(c1) from t2)
0560: * (This actually showed up in an app that a potential customer
0561: * was porting from SQL Server.)
0562: * The transformed query can then be flattened if appropriate.
0563: */
0564: if ((isIN() || isANY()) && resultSet.returnsAtMostOneRow()) {
0565: if (!hasCorrelatedCRs()) {
0566: changeToCorrespondingExpressionType();
0567: }
0568: }
0569:
0570: /* NOTE: Flattening occurs before the pushing of
0571: * the predicate, since the pushing will add a node
0572: * above the SubqueryNode.
0573: */
0574:
0575: /* Values subquery is flattenable if:
0576: * o It is not under an OR.
0577: * o It is an expression subquery on the right side
0578: * of a BinaryComparisonOperatorNode.
0579: */
0580: flattenable = (resultSet instanceof RowResultSetNode)
0581: && underTopAndNode
0582: && parentComparisonOperator instanceof BinaryComparisonOperatorNode;
0583: if (flattenable) {
0584: /* If we got this far and we are an expression subquery
0585: * then we want to set leftOperand to be the left side
0586: * of the comparison in case we pull the comparison into
0587: * the flattened subquery.
0588: */
0589: leftOperand = parentComparisonOperator.getLeftOperand();
0590: // Flatten the subquery
0591: RowResultSetNode rrsn = (RowResultSetNode) resultSet;
0592: FromList fl = new FromList();
0593:
0594: // Remove ourselves from the outer subquery list
0595: outerSubqueryList.removeElement(this );
0596:
0597: /* We only need to add the table from the subquery into
0598: * the outer from list if the subquery itself contains
0599: * another subquery. Otherwise, it just becomes a constant.
0600: */
0601: if (rrsn.subquerys.size() != 0) {
0602: fl.addElement(rrsn);
0603: outerFromList.destructiveAppend(fl);
0604: }
0605:
0606: /* Append the subquery's subquery list to the
0607: * outer subquery list.
0608: */
0609: outerSubqueryList.destructiveAppend(rrsn.subquerys);
0610:
0611: /* return the new join condition
0612: * If we are flattening an EXISTS then there is no new join
0613: * condition since there is no leftOperand. Simply return
0614: * TRUE.
0615: *
0616: * NOTE: The outer where clause, etc. has already been normalized,
0617: * so we simply return the BinaryComparisonOperatorNode above
0618: * the new join condition.
0619: */
0620: ValueNode rightOperand;
0621: rightOperand = ((ResultColumn) rrsn.getResultColumns()
0622: .elementAt(0)).getExpression();
0623: return getNewJoinCondition(leftOperand, rightOperand);
0624: }
0625:
0626: /* Select subquery is flattenable if:
0627: * o It is not under an OR.
0628: * o The subquery type is IN, ANY or EXISTS or
0629: * an expression subquery on the right side
0630: * of a BinaryComparisonOperatorNode.
0631: * o There are no aggregates in the select list
0632: * o There is no group by clause
0633: * o There is a uniqueness condition that ensures
0634: * that the flattening of the subquery will not
0635: * introduce duplicates into the result set.
0636: *
0637: * OR,
0638: * o The subquery is NOT EXISTS, NOT IN, ALL (beetle 5173).
0639: */
0640: boolean flattenableNotExists = (isNOT_EXISTS() || canAllBeFlattened());
0641:
0642: flattenable = (resultSet instanceof SelectNode)
0643: && underTopAndNode
0644: && (isIN() || isANY() || isEXISTS()
0645: || flattenableNotExists || parentComparisonOperator != null);
0646:
0647: if (flattenable) {
0648: SelectNode select = (SelectNode) resultSet;
0649: if ((select.getAggregateVector(IN_SELECT_LIST).size() == 0)
0650: && (!select.getGeneratedForGroupbyClause())) {
0651: ValueNode origLeftOperand = leftOperand;
0652:
0653: /* Check for uniqueness condition. */
0654: /* Is the column being returned by the subquery
0655: * a candidate for an = condition?
0656: */
0657: boolean additionalEQ = (subqueryType == IN_SUBQUERY)
0658: || (subqueryType == EQ_ANY_SUBQUERY);
0659:
0660: additionalEQ = additionalEQ
0661: && ((leftOperand instanceof ConstantNode)
0662: || (leftOperand instanceof ColumnReference) || (leftOperand
0663: .requiresTypeFromContext()));
0664: /* If we got this far and we are an expression subquery
0665: * then we want to set leftOperand to be the left side
0666: * of the comparison in case we pull the comparison into
0667: * the flattened subquery.
0668: */
0669: if (parentComparisonOperator instanceof BinaryComparisonOperatorNode) {
0670: leftOperand = parentComparisonOperator
0671: .getLeftOperand();
0672: }
0673: /* Never flatten to normal join for NOT EXISTS.
0674: */
0675: if ((!flattenableNotExists)
0676: && select.uniqueSubquery(additionalEQ)) {
0677: // Flatten the subquery
0678: return flattenToNormalJoin(numTables,
0679: outerFromList, outerSubqueryList,
0680: outerPredicateList);
0681: }
0682: /* We can flatten into an EXISTS join if all of the above
0683: * conditions except for a uniqueness condition are true
0684: * and:
0685: * o Subquery only has a single entry in its from list
0686: * and that entry is a FromBaseTable
0687: * o All predicates in the subquery's where clause are
0688: * pushable.
0689: * o The leftOperand, if non-null, is pushable.
0690: * If the subquery meets these conditions then we will flatten
0691: * the FBT into an EXISTS FBT, pushd the subquery's
0692: * predicates down to the PRN above the EBT and
0693: * mark the predicates to say that they cannot be pulled
0694: * above the PRN. (The only way that we can guarantee correctness
0695: * is if the predicates do not get pulled up. If they get pulled
0696: * up then the single next logic for an EXISTS join does not work
0697: * because that row may get disqualified at a higher level.)
0698: */
0699: else if ((isIN() || isANY() || isEXISTS() || flattenableNotExists)
0700: && ((leftOperand == null) ? true : leftOperand
0701: .categorize(new JBitSet(numTables),
0702: false))
0703: && select.getWherePredicates().allPushable()
0704: && singleFromBaseTable(select.getFromList())) {
0705: return flattenToExistsJoin(numTables,
0706: outerFromList, outerSubqueryList,
0707: outerPredicateList, flattenableNotExists);
0708: }
0709:
0710: // restore leftOperand to its original value
0711: leftOperand = origLeftOperand;
0712: }
0713: }
0714:
0715: /* We transform the leftOperand and the select list for quantified
0716: * predicates that have a leftOperand into a new predicate and push it
0717: * down to the subquery after we preprocess the subquery's resultSet.
0718: * We must do this after preprocessing the underlying subquery so that
0719: * we know where to attach the new predicate.
0720: * NOTE - If we pushed the predicate before preprocessing the underlying
0721: * subquery, then the point of attachment would depend on the form of
0722: * that subquery. (Where clause? Having clause?)
0723: */
0724: if (leftOperand != null) {
0725: topNode = pushNewPredicate(numTables);
0726: pushedNewPredicate = true;
0727: }
0728: /* Since NOT EXISTS subquery is not flattened, now is good time to create
0729: * an IS NULL node on top. Other cases are taken care of in pushNewPredicate.
0730: */
0731: else if (subqueryType == NOT_EXISTS_SUBQUERY) {
0732: topNode = genIsNullTree();
0733: subqueryType = EXISTS_SUBQUERY;
0734: }
0735:
0736: /*
0737: ** Do inVariant and correlated checks now. We
0738: ** aren't going to use the results here, but they
0739: ** have been stashed away by isInvariant() and hasCorrelatedCRs()
0740: */
0741: isInvariant();
0742: hasCorrelatedCRs();
0743:
0744: /* If parentComparisonOperator is non-null then we are an
0745: * expression subquery that was considered to be a candidate
0746: * for flattening, but we didn't get flattened. In that case
0747: * we are the rightOperand of the parent. We need to update
0748: * the parent's rightOperand with the new topNode and return
0749: * the parent because the parent is letting us decide whether
0750: * or not to replace the entire comparison, which we can do
0751: * if we flatten. Otherwise we simply return the new top node.
0752: */
0753: if (parentComparisonOperator != null) {
0754: parentComparisonOperator.setRightOperand(topNode);
0755: return parentComparisonOperator;
0756: }
0757:
0758: return topNode;
0759: }
0760:
0761: /**
0762: * Does the from list from the subquery contain a
0763: * single entry which is a FBT or a PRN/FBT.
0764: *
0765: * @param fromList The from list from the subquery
0766: *
0767: * @return Whether or not the from list from the subquery contains a
0768: * single entry which is a FBT or a PRN/FBT.
0769: */
0770: private boolean singleFromBaseTable(FromList fromList) {
0771: boolean retCode = (fromList.size() == 1);
0772:
0773: if (retCode) {
0774: FromTable ft = (FromTable) fromList.elementAt(0);
0775:
0776: if (((ft instanceof ProjectRestrictNode) && ((ProjectRestrictNode) ft)
0777: .getChildResult() instanceof FromBaseTable)
0778: || ft instanceof FromBaseTable) {
0779: } else {
0780: retCode = false;
0781: }
0782: }
0783:
0784: return retCode;
0785: }
0786:
0787: /**
0788: * Can NOT IN, ALL be falttened to NOT EXISTS join? We can't or the flattening doesn't
0789: * easily make sense if either side of the comparison is nullable. (beetle 5173)
0790: *
0791: * @return Whether or not the NOT IN or ALL subquery can be flattened.
0792: */
0793: private boolean canAllBeFlattened() throws StandardException {
0794: boolean result = false;
0795: if (isNOT_IN() || isALL()) {
0796: ValueNode rightOperand = ((ResultColumn) resultSet
0797: .getResultColumns().elementAt(0)).getExpression();
0798: result = (!leftOperand.getTypeServices().isNullable() && !rightOperand
0799: .getTypeServices().isNullable());
0800: }
0801: return result;
0802: }
0803:
0804: /**
0805: * Flatten this subquery into the outer query block.
0806: * At this point we are only flattening based on a uniqueness
0807: * condition and only flattening non-aggregate subqueries.
0808: * So, we promote the subquery's from list, as is, into
0809: * the outer from list. For EXISTS subquerys, we return a
0810: * TRUE. Otherwise we return a new comparison between
0811: * the leftOperand and the expression in the subquery's
0812: * SELECT list.
0813: * RESOLVE - we will need to modify this logic to account
0814: * for exists joins and aggregates as we support flattening
0815: * for them.
0816: *
0817: * Anyway, here's what we do:
0818: * o We remove ourself from the outer subquery list.
0819: * o We decrement the nesting level for all tables
0820: * in the subquery tree.
0821: * o We append the subquery's from list to the outer
0822: * from list.
0823: * o We add the subquery's predicate list to the outer
0824: * predicate list. (The subquery has already been
0825: * preprocessed.)
0826: * o We add the subquery's subquery list to the outer
0827: * subquery list.
0828: * o For EXISTS, we return a true.
0829: * o Otherwise, we return a new comparison between the
0830: * leftOperand and the expression in the inner select's
0831: * RCL.
0832: *
0833: * @param numTables Number of tables in the DML Statement
0834: * @param outerFromList FromList from outer query block
0835: * @param outerSubqueryList SubqueryList from outer query block
0836: * @param outerPredicateList PredicateList from outer query block
0837: *
0838: * @return The modified expression
0839: *
0840: * @exception StandardException Thrown on error
0841: */
0842: private ValueNode flattenToNormalJoin(int numTables,
0843: FromList outerFromList, SubqueryList outerSubqueryList,
0844: PredicateList outerPredicateList) throws StandardException {
0845: SelectNode select = (SelectNode) resultSet;
0846: FromList fl = select.getFromList();
0847: int[] tableNumbers = fl.getTableNumbers();
0848:
0849: // Remove ourselves from the outer subquery list
0850: outerSubqueryList.removeElement(this );
0851:
0852: /* Decrease the nesting level for all
0853: * tables in the subquey tree.
0854: */
0855: select.decrementLevel(1);
0856:
0857: /* Add the table(s) from the subquery into the outer from list */
0858: outerFromList.destructiveAppend(fl);
0859:
0860: /* Append the subquery's predicate list to the
0861: * outer predicate list.
0862: */
0863: outerPredicateList.destructiveAppend(select
0864: .getWherePredicates());
0865:
0866: /* Append the subquery's subquery list to the
0867: * outer subquery list.
0868: * NOTE: We must propagate any subqueries from both the
0869: * SELECT list and WHERE clause of the subquery that's
0870: * getting flattened.
0871: */
0872: outerSubqueryList.destructiveAppend(select.getWhereSubquerys());
0873: outerSubqueryList
0874: .destructiveAppend(select.getSelectSubquerys());
0875:
0876: /* return the new join condition
0877: * If we are flattening an EXISTS then there is no new join
0878: * condition since there is no leftOperand. Simply return
0879: * TRUE.
0880: *
0881: * NOTE: The outer where clause, etc. has already been normalized,
0882: * so we simply return the BinaryComparisonOperatorNode above
0883: * the new join condition.
0884: */
0885: if (leftOperand == null) {
0886: return (ValueNode) getNodeFactory().getNode(
0887: C_NodeTypes.BOOLEAN_CONSTANT_NODE, Boolean.TRUE,
0888: getContextManager());
0889: } else {
0890: ValueNode rightOperand;
0891: rightOperand = ((ResultColumn) select.getResultColumns()
0892: .elementAt(0)).getExpression();
0893: /* If the right operand is a CR, then we need to decrement
0894: * its source level as part of flattening so that
0895: * transitive closure will work correctly.
0896: */
0897: if (rightOperand instanceof ColumnReference) {
0898: ColumnReference cr = (ColumnReference) rightOperand;
0899: int tableNumber = cr.getTableNumber();
0900: for (int index = 0; index < tableNumbers.length; index++) {
0901: if (tableNumber == tableNumbers[index]) {
0902: cr.setSourceLevel(cr.getSourceLevel() - 1);
0903: break;
0904: }
0905: }
0906: }
0907: return getNewJoinCondition(leftOperand, rightOperand);
0908: }
0909: }
0910:
0911: /**
0912: * Flatten this subquery into the outer query block
0913: * as an exists join.
0914: * At this point we are only flattening non-aggregate subqueries
0915: * with a single FBT in the from list.
0916: * So, we transform all FBTs in the from list into ExistBaseTables,
0917: * update the dependency lists for each of the tables and then
0918: * flatten the subquery.
0919: * RESOLVE - we will need to modify this logic to account
0920: * for aggregates as we support flattening
0921: * for them.
0922: *
0923: * @param numTables Number of tables in the DML Statement
0924: * @param outerFromList FromList from outer query block
0925: * @param outerSubqueryList SubqueryList from outer query block
0926: * @param outerPredicateList PredicateList from outer query block
0927: * @param flattenableNotExists Is it a flattening into a NOT EXISTS join
0928: *
0929: * @return The modified expression
0930: *
0931: * @exception StandardException Thrown on error
0932: */
0933: private ValueNode flattenToExistsJoin(int numTables,
0934: FromList outerFromList, SubqueryList outerSubqueryList,
0935: PredicateList outerPredicateList,
0936: boolean flattenableNotExists) throws StandardException {
0937: SelectNode select = (SelectNode) resultSet;
0938:
0939: // Replace the FromBaseTables in the from list with ExistBaseTables
0940: select.getFromList().genExistsBaseTables(
0941: resultSet.getReferencedTableMap(), outerFromList,
0942: flattenableNotExists);
0943:
0944: /* NOTE: Because we are currently only flattening single table subqueries
0945: * whose predicates are all pushable, we simply follow the rest of the
0946: * flattening algorithm for unique subqueries. Should we decide to
0947: * loosen these restrictions then we need to do more work such as:
0948: *
0949: * Mark all of the predicates from the subquery as non-pullable. They must
0950: * not be pulled so that we can guarantee correctness. Otherwise, we could
0951: * add or subtract rows from the result set.
0952: *
0953: * Remap all of the non-correlated CRs in the predicate list so that they
0954: * point to the correct source. (We've chopped a level out of the RCL/VCN
0955: * chain.) We then transfer those predicates to the PRN in the subquery's
0956: * from list.
0957: */
0958:
0959: return flattenToNormalJoin(numTables, outerFromList,
0960: outerSubqueryList, outerPredicateList);
0961: }
0962:
0963: /**
0964: * Check to see if we have a Variant value below us.
0965: * If so, return true. Caches the result so multiple
0966: * calls are ok.
0967: *
0968: * @return boolean whether we have
0969: *
0970: * @exception StandardException Thrown on error
0971: */
0972: private boolean isInvariant() throws StandardException {
0973: if (doneInvariantCheck) {
0974: return !foundVariant;
0975: }
0976:
0977: doneInvariantCheck = true;
0978: HasVariantValueNodeVisitor visitor = new HasVariantValueNodeVisitor();
0979: resultSet.accept(visitor);
0980: foundVariant = visitor.hasVariant();
0981: return !foundVariant;
0982: }
0983:
0984: /**
0985: * Check to see if this subquery has correlated
0986: * column references. Only useful results if
0987: * called AFTER binding (after CRs have been bound).
0988: *
0989: * @return whether the subquery has correlated column
0990: * references.
0991: * @exception StandardException Thrown on error
0992: */
0993: public boolean hasCorrelatedCRs() throws StandardException {
0994: if (doneCorrelationCheck) {
0995: return foundCorrelation;
0996: }
0997: doneCorrelationCheck = true;
0998:
0999: ResultSetNode realSubquery = resultSet;
1000: ResultColumnList oldRCL = null;
1001:
1002: /* If we have pushed the new join predicate on top, we want to disregard it
1003: * to see if anything under the predicate is correlated. If nothing correlated
1004: * under the new join predicate, we could then materialize the subquery.
1005: * See beetle 4373.
1006: */
1007: if (pushedNewPredicate) {
1008: if (SanityManager.DEBUG) {
1009: SanityManager
1010: .ASSERT(
1011: resultSet instanceof ProjectRestrictNode,
1012: "resultSet expected to be a ProjectRestrictNode!");
1013: }
1014:
1015: realSubquery = ((ProjectRestrictNode) resultSet)
1016: .getChildResult();
1017: oldRCL = realSubquery.getResultColumns();
1018:
1019: /* Only first column matters.
1020: */
1021: if (oldRCL.size() > 1) {
1022: ResultColumnList newRCL = new ResultColumnList();
1023: newRCL.addResultColumn(oldRCL.getResultColumn(1));
1024: realSubquery.setResultColumns(newRCL);
1025: }
1026: }
1027:
1028: HasCorrelatedCRsVisitor visitor = new HasCorrelatedCRsVisitor();
1029: realSubquery.accept(visitor);
1030: foundCorrelation = visitor.hasCorrelatedCRs();
1031:
1032: if (pushedNewPredicate && (oldRCL.size() > 1)) {
1033: realSubquery.setResultColumns(oldRCL);
1034: }
1035:
1036: return foundCorrelation;
1037: }
1038:
1039: /**
1040: * Transform:
1041: * expresion QuantifiedOperator (select x from ...)
1042: * into
1043: * (select true from .. where expression <BinaryComparisonOperator> x ...)
1044: * IS [NOT] NULL
1045: *
1046: * or, if we have an aggregate:
1047: * (select true from
1048: * (select AGG(x) from ...)
1049: * where expression <BinaryComparisonOperator> x ...)
1050: * IS [NOT] NULL
1051: *
1052: *
1053: * For ANY and IN subqueries:
1054: * o We generate an IS NULL above the SubqueryNode and return the top of
1055: * the new tree to the caller.
1056: * o The operator in the new predicate that is added to the subquery
1057: * will correspond to the operator that modifies the ANY.
1058: * (eg, = for = ANY, with = for IN.)
1059: * For ALL and NOT IN subqueries:
1060: * o We generate an IS NOT NULL above the SubqueryNode and return the top of
1061: * the new tree to the caller.
1062: * o The operator in the new predicate that is added to the subquery
1063: * will be a BinaryAllOperatorNode whose bcoNodeType corresponds to
1064: * the negation of the operator that modifies the ALL.
1065: * (eg, <> for = ALL, with <> for NOT IN.)
1066: *
1067: * NOTE: This method is called after the underlying subquery has been
1068: * preprocessed, so we build a new Predicate, not just a new expression.
1069: *
1070: * @param numTables Number of tables in DML Statement
1071: *
1072: * @return UnaryComparisonOperatorNode An IS [NOT] NULL above the
1073: * transformed subquery.
1074: *
1075: * @exception StandardException Thrown on error
1076: */
1077: private UnaryComparisonOperatorNode pushNewPredicate(int numTables)
1078: throws StandardException {
1079: AndNode andNode;
1080: BinaryComparisonOperatorNode bcoNode = null;
1081: JBitSet tableMap;
1082: Predicate predicate;
1083: ResultColumn firstRC;
1084: ResultColumnList resultColumns;
1085: UnaryComparisonOperatorNode ucoNode = null;
1086: ValueNode oldWhereClause;
1087: ValueNode rightOperand;
1088:
1089: /* We have to ensure that the resultSet immediately under us has
1090: * a PredicateList, otherwise we can't push the predicate down.
1091: */
1092: resultSet = resultSet.ensurePredicateList(numTables);
1093:
1094: /* RESOLVE - once we understand how correlated columns will work,
1095: * we probably want to mark leftOperand as a correlated column
1096: */
1097: resultColumns = resultSet.getResultColumns();
1098:
1099: /*
1100: ** Create a new PR node. Put it over the original subquery. resulSet
1101: ** is now the new PR. We give the chance that things under the PR node
1102: ** can be materialized. See beetle 4373.
1103: */
1104: ResultColumnList newRCL = resultColumns.copyListAndObjects();
1105: newRCL.genVirtualColumnNodes(resultSet, resultColumns);
1106: resultSet = (ResultSetNode) getNodeFactory().getNode(
1107: C_NodeTypes.PROJECT_RESTRICT_NODE, resultSet, // child
1108: newRCL, // result columns
1109: null, // restriction
1110: null, // restriction list
1111: null, // project subqueries
1112: null, // restrict subqueries
1113: null, getContextManager());
1114: resultColumns = newRCL;
1115:
1116: firstRC = (ResultColumn) resultColumns.elementAt(0);
1117: rightOperand = firstRC.getExpression();
1118:
1119: bcoNode = getNewJoinCondition(leftOperand, rightOperand);
1120:
1121: ValueNode andLeft = bcoNode;
1122:
1123: /* For NOT IN or ALL, and if either side of the comparison is nullable, and the
1124: * subquery can not be flattened (because of that), we need to add IS NULL node
1125: * on top of the nullables, such that the behavior is (beetle 5173):
1126: *
1127: * (1) If we have nulls in right operand, no row is returned.
1128: * (2) If subquery result is empty before applying join predicate, every
1129: * left row (including NULLs) is returned.
1130: * (3) Otherwise, return {all left row} - {NULLs}
1131: */
1132: if (isNOT_IN() || isALL()) {
1133: boolean leftNullable = leftOperand.getTypeServices()
1134: .isNullable();
1135: boolean rightNullable = rightOperand.getTypeServices()
1136: .isNullable();
1137: if (leftNullable || rightNullable) {
1138: /* Create a normalized structure.
1139: */
1140: BooleanConstantNode falseNode = (BooleanConstantNode) getNodeFactory()
1141: .getNode(C_NodeTypes.BOOLEAN_CONSTANT_NODE,
1142: Boolean.FALSE, getContextManager());
1143: OrNode newOr = (OrNode) getNodeFactory().getNode(
1144: C_NodeTypes.OR_NODE, bcoNode, falseNode,
1145: getContextManager());
1146: newOr.postBindFixup();
1147: andLeft = newOr;
1148:
1149: if (leftNullable) {
1150: UnaryComparisonOperatorNode leftIsNull = (UnaryComparisonOperatorNode) getNodeFactory()
1151: .getNode(C_NodeTypes.IS_NULL_NODE,
1152: leftOperand, getContextManager());
1153: leftIsNull.bindComparisonOperator();
1154: newOr = (OrNode) getNodeFactory().getNode(
1155: C_NodeTypes.OR_NODE, leftIsNull, andLeft,
1156: getContextManager());
1157: newOr.postBindFixup();
1158: andLeft = newOr;
1159: }
1160: if (rightNullable) {
1161: UnaryComparisonOperatorNode rightIsNull = (UnaryComparisonOperatorNode) getNodeFactory()
1162: .getNode(C_NodeTypes.IS_NULL_NODE,
1163: rightOperand, getContextManager());
1164: rightIsNull.bindComparisonOperator();
1165: newOr = (OrNode) getNodeFactory().getNode(
1166: C_NodeTypes.OR_NODE, rightIsNull, andLeft,
1167: getContextManager());
1168: newOr.postBindFixup();
1169: andLeft = newOr;
1170: }
1171: }
1172: }
1173:
1174: /* Place an AndNode above the <BinaryComparisonOperator> */
1175: andNode = (AndNode) getNodeFactory().getNode(
1176: C_NodeTypes.AND_NODE, andLeft, getTrueNode(),
1177: getContextManager());
1178:
1179: /* Build the referenced table map for the new predicate */
1180: tableMap = new JBitSet(numTables);
1181: andNode.postBindFixup();
1182:
1183: /* Put the AndNode under a Predicate */
1184: predicate = (Predicate) getNodeFactory().getNode(
1185: C_NodeTypes.PREDICATE, andNode, tableMap,
1186: getContextManager());
1187: predicate.categorize();
1188:
1189: /* Push the new Predicate to the subquery's list */
1190: resultSet = resultSet.addNewPredicate(predicate);
1191:
1192: /* Clean up the leftOperand and subquery ResultColumn */
1193: leftOperand = null;
1194: firstRC.setType(getTypeServices());
1195: firstRC.setExpression(getTrueNode());
1196:
1197: /* Add the IS [NOT] NULL above the SubqueryNode */
1198: switch (subqueryType) {
1199: case IN_SUBQUERY:
1200: case EQ_ANY_SUBQUERY:
1201: case NE_ANY_SUBQUERY:
1202: case LE_ANY_SUBQUERY:
1203: case LT_ANY_SUBQUERY:
1204: case GE_ANY_SUBQUERY:
1205: case GT_ANY_SUBQUERY:
1206: ucoNode = (UnaryComparisonOperatorNode) getNodeFactory()
1207: .getNode(C_NodeTypes.IS_NOT_NULL_NODE, this ,
1208: getContextManager());
1209: break;
1210:
1211: case NOT_IN_SUBQUERY:
1212: case EQ_ALL_SUBQUERY:
1213: case NE_ALL_SUBQUERY:
1214: case LE_ALL_SUBQUERY:
1215: case LT_ALL_SUBQUERY:
1216: case GE_ALL_SUBQUERY:
1217: case GT_ALL_SUBQUERY:
1218: ucoNode = (UnaryComparisonOperatorNode) getNodeFactory()
1219: .getNode(C_NodeTypes.IS_NULL_NODE, this ,
1220: getContextManager());
1221: break;
1222: }
1223: ucoNode.bindComparisonOperator();
1224: return ucoNode;
1225: }
1226:
1227: /**
1228: * Build a new join condition between the leftOperand
1229: * and the rightOperand. The comparison operator
1230: * is dependent on the subquery type.
1231: *
1232: * @param leftOperand The left operand for the new condition.
1233: * @param rightOperand The right operand for the new condition.
1234: *
1235: * @exception StandardException Thrown on error
1236: */
1237: private BinaryComparisonOperatorNode getNewJoinCondition(
1238: ValueNode leftOperand, ValueNode rightOperand)
1239: throws StandardException {
1240: BinaryComparisonOperatorNode bcoNode = null;
1241:
1242: /* NOTE: If we are an expression subquery that's getting
1243: * flattened then our subqueryType is EXPRESSION_SUBQUERY.
1244: * However, we can get the comparison type from the
1245: * parentComparisonOperator. In that case we dovetail on
1246: * the ANY subquery types.
1247: */
1248: int operatorType = subqueryType;
1249: if (subqueryType == EXPRESSION_SUBQUERY) {
1250: if (SanityManager.DEBUG) {
1251: SanityManager
1252: .ASSERT(parentComparisonOperator != null,
1253: "parentComparisonOperator expected to be non-null");
1254: }
1255:
1256: int parentOperator = -1;
1257:
1258: if (parentComparisonOperator.isRelationalOperator()) {
1259: RelationalOperator ro = (RelationalOperator) parentComparisonOperator;
1260: parentOperator = ro.getOperator();
1261: }
1262:
1263: if (parentOperator == RelationalOperator.EQUALS_RELOP) {
1264: operatorType = EQ_ANY_SUBQUERY;
1265: } else if (parentOperator == RelationalOperator.NOT_EQUALS_RELOP) {
1266: operatorType = NE_ANY_SUBQUERY;
1267: } else if (parentOperator == RelationalOperator.LESS_EQUALS_RELOP) {
1268: operatorType = LE_ANY_SUBQUERY;
1269: } else if (parentOperator == RelationalOperator.LESS_THAN_RELOP) {
1270: operatorType = LT_ANY_SUBQUERY;
1271: } else if (parentOperator == RelationalOperator.GREATER_EQUALS_RELOP) {
1272: operatorType = GE_ANY_SUBQUERY;
1273: } else if (parentOperator == RelationalOperator.GREATER_THAN_RELOP) {
1274: operatorType = GT_ANY_SUBQUERY;
1275: }
1276: }
1277:
1278: int bcoType = 0;
1279: int nodeType = 0;
1280:
1281: /* Build the <BinaryComparisonOperator> */
1282: switch (operatorType) {
1283: case IN_SUBQUERY:
1284: case EQ_ANY_SUBQUERY:
1285: case NOT_IN_SUBQUERY:
1286: case NE_ALL_SUBQUERY:
1287: nodeType = C_NodeTypes.BINARY_EQUALS_OPERATOR_NODE;
1288: break;
1289:
1290: case NE_ANY_SUBQUERY:
1291: case EQ_ALL_SUBQUERY:
1292: nodeType = C_NodeTypes.BINARY_NOT_EQUALS_OPERATOR_NODE;
1293: break;
1294:
1295: case LE_ANY_SUBQUERY:
1296: case GT_ALL_SUBQUERY:
1297: nodeType = C_NodeTypes.BINARY_LESS_EQUALS_OPERATOR_NODE;
1298: break;
1299:
1300: case LT_ANY_SUBQUERY:
1301: case GE_ALL_SUBQUERY:
1302: nodeType = C_NodeTypes.BINARY_LESS_THAN_OPERATOR_NODE;
1303: break;
1304:
1305: case GE_ANY_SUBQUERY:
1306: case LT_ALL_SUBQUERY:
1307: nodeType = C_NodeTypes.BINARY_GREATER_EQUALS_OPERATOR_NODE;
1308: break;
1309:
1310: case GT_ANY_SUBQUERY:
1311: case LE_ALL_SUBQUERY:
1312: nodeType = C_NodeTypes.BINARY_GREATER_THAN_OPERATOR_NODE;
1313: break;
1314:
1315: default:
1316: if (SanityManager.DEBUG)
1317: SanityManager.ASSERT(false, "subqueryType ("
1318: + subqueryType + ") is an unexpected type");
1319: }
1320:
1321: bcoNode = (BinaryComparisonOperatorNode) getNodeFactory()
1322: .getNode(nodeType, leftOperand, rightOperand,
1323: getContextManager());
1324:
1325: bcoNode.bindComparisonOperator();
1326: return bcoNode;
1327: }
1328:
1329: /**
1330: * Eliminate NotNodes in the current query block. We traverse the tree,
1331: * inverting ANDs and ORs and eliminating NOTs as we go. We stop at
1332: * ComparisonOperators and boolean expressions. We invert
1333: * ComparisonOperators and replace boolean expressions with
1334: * boolean expression = false.
1335: * NOTE: Since we do not recurse under ComparisonOperators, there
1336: * still could be NotNodes left in the tree.
1337: *
1338: * @param underNotNode Whether or not we are under a NotNode.
1339: *
1340: *
1341: * @return The modified expression
1342: *
1343: * @exception StandardException Thrown on error
1344: */
1345: ValueNode eliminateNots(boolean underNotNode)
1346: throws StandardException {
1347: ValueNode result = this ;
1348:
1349: if (underNotNode) {
1350: /* Negate the subqueryType. For expression subqueries
1351: * we simply return subquery = false
1352: */
1353: /* RESOLVE - This code needs to get cleaned up once there are
1354: * more subquery types. (Consider using arrays.)
1355: */
1356: switch (subqueryType) {
1357: case EXPRESSION_SUBQUERY:
1358: result = genEqualsFalseTree();
1359: break;
1360:
1361: case EXISTS_SUBQUERY:
1362: subqueryType = NOT_EXISTS_SUBQUERY;
1363: break;
1364:
1365: /* ANY subqueries */
1366: case IN_SUBQUERY:
1367: case EQ_ANY_SUBQUERY:
1368: subqueryType = NOT_IN_SUBQUERY;
1369: break;
1370:
1371: case NE_ANY_SUBQUERY:
1372: subqueryType = EQ_ALL_SUBQUERY;
1373: break;
1374:
1375: case GE_ANY_SUBQUERY:
1376: subqueryType = LT_ALL_SUBQUERY;
1377: break;
1378:
1379: case GT_ANY_SUBQUERY:
1380: subqueryType = LE_ALL_SUBQUERY;
1381: break;
1382:
1383: case LE_ANY_SUBQUERY:
1384: subqueryType = GT_ALL_SUBQUERY;
1385: break;
1386:
1387: case LT_ANY_SUBQUERY:
1388: subqueryType = GE_ALL_SUBQUERY;
1389: break;
1390:
1391: /* ALL subqueries - no need for NOT NOT_IN_SUBQUERY, since
1392: * NOT IN only comes into existence here.
1393: */
1394: case EQ_ALL_SUBQUERY:
1395: subqueryType = NE_ANY_SUBQUERY;
1396: break;
1397:
1398: case NE_ALL_SUBQUERY:
1399: subqueryType = EQ_ANY_SUBQUERY;
1400: break;
1401:
1402: case GE_ALL_SUBQUERY:
1403: subqueryType = LT_ANY_SUBQUERY;
1404: break;
1405:
1406: case GT_ALL_SUBQUERY:
1407: subqueryType = LE_ANY_SUBQUERY;
1408: break;
1409:
1410: case LE_ALL_SUBQUERY:
1411: subqueryType = GT_ANY_SUBQUERY;
1412: break;
1413:
1414: case LT_ALL_SUBQUERY:
1415: subqueryType = GE_ANY_SUBQUERY;
1416: break;
1417:
1418: default:
1419: if (SanityManager.DEBUG)
1420: SanityManager
1421: .ASSERT(false,
1422: "NOT is not supported for this time of subquery");
1423: }
1424: }
1425:
1426: /* Halt recursion here, as each query block is preprocessed separately */
1427: return result;
1428: }
1429:
1430: /**
1431: * Finish putting an expression into conjunctive normal
1432: * form. An expression tree in conjunctive normal form meets
1433: * the following criteria:
1434: * o If the expression tree is not null,
1435: * the top level will be a chain of AndNodes terminating
1436: * in a true BooleanConstantNode.
1437: * o The left child of an AndNode will never be an AndNode.
1438: * o Any right-linked chain that includes an AndNode will
1439: * be entirely composed of AndNodes terminated by a true BooleanConstantNode.
1440: * o The left child of an OrNode will never be an OrNode.
1441: * o Any right-linked chain that includes an OrNode will
1442: * be entirely composed of OrNodes terminated by a false BooleanConstantNode.
1443: * o ValueNodes other than AndNodes and OrNodes are considered
1444: * leaf nodes for purposes of expression normalization.
1445: * In other words, we won't do any normalization under
1446: * those nodes.
1447: *
1448: * In addition, we track whether or not we are under a top level AndNode.
1449: * SubqueryNodes need to know this for subquery flattening.
1450: *
1451: * @param underTopAndNode Whether or not we are under a top level AndNode.
1452: *
1453: *
1454: * @return The modified expression
1455: *
1456: * @exception StandardException Thrown on error
1457: */
1458: public ValueNode changeToCNF(boolean underTopAndNode)
1459: throws StandardException {
1460: /* Remember whether or not we are immediately under a top leve
1461: * AndNode. This is important for subquery flattening.
1462: * (We can only flatten subqueries under a top level AndNode.)
1463: */
1464: this .underTopAndNode = underTopAndNode;
1465:
1466: /* Halt recursion here, as each query block is preprocessed separately */
1467: return this ;
1468: }
1469:
1470: /**
1471: * Categorize this predicate. Initially, this means
1472: * building a bit map of the referenced tables for each predicate.
1473: * If the source of this ColumnReference (at the next underlying level)
1474: * is not a ColumnReference or a VirtualColumnNode then this predicate
1475: * will not be pushed down.
1476: *
1477: * For example, in:
1478: * select * from (select 1 from s) a (x) where x = 1
1479: * we will not push down x = 1.
1480: * NOTE: It would be easy to handle the case of a constant, but if the
1481: * inner SELECT returns an arbitrary expression, then we would have to copy
1482: * that tree into the pushed predicate, and that tree could contain
1483: * subqueries and method calls.
1484: * RESOLVE - revisit this issue once we have views.
1485: *
1486: * @param referencedTabs JBitSet with bit map of referenced FromTables
1487: * @return boolean Whether or not source.expression is a ColumnReference
1488: * or a VirtualColumnNode.
1489: *
1490: * @exception StandardException Thrown on error
1491: */
1492: public boolean categorize(JBitSet referencedTabs,
1493: boolean simplePredsOnly) throws StandardException {
1494: /* We stop here when only considering simple predicates
1495: * as we don't consider method calls when looking
1496: * for null invariant predicates.
1497: */
1498: if (simplePredsOnly) {
1499: return false;
1500: }
1501:
1502: /* RESOLVE - We need to or in a bit map when there are correlation columns */
1503:
1504: /* We categorize a query block at a time, so stop the recursion here */
1505:
1506: /* Predicates with subqueries are not pushable for now */
1507:
1508: /*
1509: ** If we can materialize the subquery, then it is
1510: ** both invariant and non-correlated. And so it
1511: ** is pushable.
1512: */
1513: return isMaterializable();
1514:
1515: }
1516:
1517: /*
1518: ** Subquery is materializable if
1519: ** it is an expression subquery that
1520: ** has no correlations and is invariant.
1521: */
1522: boolean isMaterializable() throws StandardException {
1523: boolean retval = (subqueryType == EXPRESSION_SUBQUERY)
1524: && !hasCorrelatedCRs() && isInvariant();
1525: /* If we can materialize the subquery, then we set
1526: * the level of all of the tables to 0 so that we can
1527: * consider bulk fetch for them.
1528: */
1529: if (retval) {
1530: if (resultSet instanceof SelectNode) {
1531: SelectNode select = (SelectNode) resultSet;
1532: FromList fromList = select.getFromList();
1533: fromList.setLevel(0);
1534: }
1535: }
1536:
1537: return retval;
1538: }
1539:
1540: /**
1541: * Optimize this SubqueryNode.
1542: *
1543: * @param dataDictionary The DataDictionary to use for optimization
1544: * @param outerRows The optimizer's estimate of the number of
1545: * times this subquery will be executed.
1546: *
1547: * @exception StandardException Thrown on error
1548: */
1549:
1550: public void optimize(DataDictionary dataDictionary, double outerRows)
1551: throws StandardException {
1552: /* RESOLVE - is there anything else that we need to do for this
1553: * node.
1554: */
1555:
1556: /* Optimize the underlying result set */
1557: resultSet = resultSet.optimize(dataDictionary, null, outerRows);
1558: }
1559:
1560: /**
1561: * Make any changes to the access paths, as decided by the optimizer.
1562: *
1563: * @exception StandardException Thrown on error
1564: */
1565: public void modifyAccessPaths() throws StandardException {
1566: resultSet = resultSet.modifyAccessPaths();
1567: }
1568:
1569: /**
1570: * Return the variant type for the underlying expression.
1571: * The variant type can be:
1572: * VARIANT - variant within a scan
1573: * (method calls and non-static field access)
1574: * SCAN_INVARIANT - invariant within a scan
1575: * (column references from outer tables)
1576: * QUERY_INVARIANT - invariant within the life of a query
1577: * (constant expressions)
1578: *
1579: * @return The variant type for the underlying expression.
1580: *
1581: * @exception StandardException Thrown on error
1582: */
1583: protected int getOrderableVariantType() throws StandardException {
1584: /*
1585: * If the subquery is variant, than return
1586: * VARIANT. Otherwise, if we have an expression
1587: * subquery and no correlated CRs we are going
1588: * to materialize it, so it is QUERY_INVARIANT.
1589: * Otherwise, SCAN_INVARIANT.
1590: */
1591: if (isInvariant()) {
1592: if (!hasCorrelatedCRs()
1593: && (subqueryType == EXPRESSION_SUBQUERY)) {
1594: return Qualifier.QUERY_INVARIANT;
1595: } else {
1596: return Qualifier.SCAN_INVARIANT;
1597: }
1598: } else {
1599: return Qualifier.VARIANT;
1600: }
1601: }
1602:
1603: /**
1604: * Do code generation for this subquery.
1605: *
1606: * @param expressionBuilder The ExpressionClassBuilder for the class being built
1607: * @param mbex The method the expression will go into
1608: *
1609: *
1610: * @exception StandardException Thrown on error
1611: */
1612:
1613: public void generateExpression(
1614: ExpressionClassBuilder expressionBuilder, MethodBuilder mbex)
1615: throws StandardException {
1616: CompilerContext cc = getCompilerContext();
1617: String resultSetString;
1618:
1619: ///////////////////////////////////////////////////////////////////////////
1620: //
1621: // Subqueries should not appear in Filter expressions. We should get here
1622: // only if we're compiling a query. That means that our class builder
1623: // is an activation builder. If we ever allow subqueries in filters, we'll
1624: // have to revisit this code.
1625: //
1626: ///////////////////////////////////////////////////////////////////////////
1627:
1628: if (SanityManager.DEBUG) {
1629: SanityManager
1630: .ASSERT(
1631: expressionBuilder instanceof ActivationClassBuilder,
1632: "Expecting an ActivationClassBuilder");
1633: }
1634:
1635: ActivationClassBuilder acb = (ActivationClassBuilder) expressionBuilder;
1636: /* Reuse generated code, where possible */
1637:
1638: /* Generate the appropriate (Any or Once) ResultSet */
1639: if (subqueryType == EXPRESSION_SUBQUERY) {
1640: resultSetString = "getOnceResultSet";
1641: } else {
1642: resultSetString = "getAnyResultSet";
1643: }
1644:
1645: // Get cost estimate for underlying subquery
1646: CostEstimate costEstimate = resultSet.getFinalCostEstimate();
1647:
1648: /* Generate a new method. It's only used within the other
1649: * exprFuns, so it could be private. but since we don't
1650: * generate the right bytecodes to invoke private methods,
1651: * we just make it protected. This generated class won't
1652: * have any subclasses, certainly! (nat 12/97)
1653: */
1654: String subqueryTypeString = getTypeCompiler().interfaceName();
1655: MethodBuilder mb = acb.newGeneratedFun(subqueryTypeString,
1656: Modifier.PROTECTED);
1657:
1658: /* Declare the field to hold the suquery's ResultSet tree */
1659: LocalField rsFieldLF = acb.newFieldDeclaration(
1660: Modifier.PRIVATE, ClassName.NoPutResultSet);
1661:
1662: ResultSetNode subNode = null;
1663:
1664: if (!isMaterializable()) {
1665: MethodBuilder executeMB = acb.getExecuteMethod();
1666: if (pushedNewPredicate && (!hasCorrelatedCRs())) {
1667: /* We try to materialize the subquery if it can fit in the memory. We
1668: * evaluate the subquery first. If the result set fits in the memory,
1669: * we replace the resultset with in-memory unions of row result sets.
1670: * We do this trick by replacing the child result with a new node --
1671: * MaterializeSubqueryNode, which essentially generates the suitable
1672: * code to materialize the subquery if possible. This may have big
1673: * performance improvement. See beetle 4373.
1674: */
1675: if (SanityManager.DEBUG) {
1676: SanityManager
1677: .ASSERT(
1678: resultSet instanceof ProjectRestrictNode,
1679: "resultSet expected to be a ProjectRestrictNode!");
1680: }
1681: subNode = ((ProjectRestrictNode) resultSet)
1682: .getChildResult();
1683: LocalField subRS = acb.newFieldDeclaration(
1684: Modifier.PRIVATE, ClassName.NoPutResultSet);
1685: mb.getField(subRS);
1686: mb.conditionalIfNull();
1687:
1688: ResultSetNode materialSubNode = new MaterializeSubqueryNode(
1689: subRS);
1690:
1691: // Propagate the resultSet's cost estimate to the new node.
1692: materialSubNode.costEstimate = resultSet
1693: .getFinalCostEstimate();
1694:
1695: ((ProjectRestrictNode) resultSet)
1696: .setChildResult(materialSubNode);
1697:
1698: /* Evaluate subquery resultset here first. Next time when we come to
1699: * this subquery it may be replaced by a bunch of unions of rows.
1700: */
1701: subNode.generate(acb, mb);
1702: mb.startElseCode();
1703: mb.getField(subRS);
1704: mb.completeConditional();
1705:
1706: mb.setField(subRS);
1707:
1708: executeMB.pushNull(ClassName.NoPutResultSet);
1709: executeMB.setField(subRS);
1710: }
1711:
1712: executeMB.pushNull(ClassName.NoPutResultSet);
1713: executeMB.setField(rsFieldLF);
1714:
1715: // now we fill in the body of the conditional
1716: mb.getField(rsFieldLF);
1717: mb.conditionalIfNull();
1718: }
1719:
1720: acb.pushGetResultSetFactoryExpression(mb);
1721:
1722: // start of args
1723: int nargs;
1724:
1725: /* Inside here is where subquery could already have been materialized. 4373
1726: */
1727: resultSet.generate(acb, mb);
1728:
1729: /* Get the next ResultSet #, so that we can number the subquery's
1730: * empty row ResultColumnList and Once/Any ResultSet.
1731: */
1732: int subqResultSetNumber = cc.getNextResultSetNumber();
1733:
1734: /* We will be reusing the RCL from the subquery's ResultSet for the
1735: * empty row function. We need to reset the resultSetNumber in the
1736: * RCL, before we generate that function. Now that we've called
1737: * generate() on the subquery's ResultSet, we can reset that
1738: * resultSetNumber.
1739: */
1740: resultSet.getResultColumns().setResultSetNumber(
1741: subqResultSetNumber);
1742:
1743: /* Generate code for empty row */
1744: resultSet.getResultColumns().generateNulls(acb, mb);
1745:
1746: /*
1747: * arg1: suqueryExpress - Expression for subquery's
1748: * ResultSet
1749: * arg2: Activation
1750: * arg3: Method to generate Row with null(s) if subquery
1751: * Result Set is empty
1752: */
1753: if (subqueryType == EXPRESSION_SUBQUERY) {
1754: int cardinalityCheck;
1755:
1756: /* No need to do sort if subquery began life as a distinct expression subquery.
1757: * (We simply check for a single unique value at execution time.)
1758: * No need for cardinality check if we know that underlying
1759: * ResultSet can contain at most 1 row.
1760: * RESOLVE - Not necessary if we know we
1761: * are getting a single row because of a unique index.
1762: */
1763: if (distinctExpression) {
1764: cardinalityCheck = OnceResultSet.UNIQUE_CARDINALITY_CHECK;
1765: } else if (resultSet.returnsAtMostOneRow()) {
1766: cardinalityCheck = OnceResultSet.NO_CARDINALITY_CHECK;
1767: } else {
1768: cardinalityCheck = OnceResultSet.DO_CARDINALITY_CHECK;
1769: }
1770:
1771: /* arg4: int - whether or not cardinality check is required
1772: * DO_CARDINALITY_CHECK - required
1773: * NO_CARDINALITY_CHECK - not required
1774: * UNIQUE_CARDINALITY_CHECK - verify single
1775: * unique value
1776: */
1777: mb.push(cardinalityCheck);
1778: nargs = 8;
1779:
1780: } else {
1781: nargs = 7;
1782: }
1783:
1784: mb.push(subqResultSetNumber);
1785: mb.push(subqueryNumber);
1786: mb.push(pointOfAttachment);
1787: mb.push(costEstimate.rowCount());
1788: mb.push(costEstimate.getEstimatedCost());
1789:
1790: mb.callMethod(VMOpcode.INVOKEINTERFACE, (String) null,
1791: resultSetString, ClassName.NoPutResultSet, nargs);
1792:
1793: /* Fill in the body of the method
1794: * generates the following.
1795: * (NOTE: the close() method only generated for
1796: * materialized subqueries. All other subqueries
1797: * closed by top result set in the query.):
1798: *
1799: * NoPutResultSet rsFieldX;
1800: * {
1801: * <Datatype interface> col;
1802: * ExecRow r;
1803: * rsFieldX = (rsFieldX == null) ? outerRSCall() : rsFieldX; // <== NONmaterialized specific
1804: * rsFieldX.openCore();
1805: * r = rsFieldX.getNextRowCore();
1806: * col = (<Datatype interface>) r.getColumn(1);
1807: * return col;
1808: * }
1809: *
1810: * MATERIALIZED:
1811: * NoPutResultSet rsFieldX;
1812: * {
1813: * <Datatype interface> col;
1814: * ExecRow r;
1815: * rsFieldX = outerRSCall();
1816: * rsFieldX.openCore();
1817: * r = rsFieldX.getNextRowCore();
1818: * col = (<Datatype interface>) r.getColumn(1);
1819: * rsFieldX.close(); // <== materialized specific
1820: * return col;
1821: * }
1822: * and adds it to exprFun
1823: */
1824:
1825: /* Generate the declarations */// PUSHCOMPILE
1826: //VariableDeclaration colVar = mb.addVariableDeclaration(subqueryTypeString);
1827: //VariableDeclaration rVar = mb.addVariableDeclaration(ClassName.ExecRow);
1828: if (!isMaterializable()) {
1829: /* put it back
1830: */
1831: if (pushedNewPredicate && (!hasCorrelatedCRs()))
1832: ((ProjectRestrictNode) resultSet)
1833: .setChildResult(subNode);
1834:
1835: // now we fill in the body of the conditional
1836: mb.startElseCode();
1837: mb.getField(rsFieldLF);
1838: mb.completeConditional();
1839: }
1840:
1841: mb.setField(rsFieldLF);
1842:
1843: /* rs.openCore() */
1844: mb.getField(rsFieldLF);
1845: mb.callMethod(VMOpcode.INVOKEINTERFACE, (String) null,
1846: "openCore", "void", 0);
1847:
1848: /* r = rs.next() */
1849: mb.getField(rsFieldLF);
1850: mb.callMethod(VMOpcode.INVOKEINTERFACE, (String) null,
1851: "getNextRowCore", ClassName.ExecRow, 0);
1852: //mb.putVariable(rVar);
1853: //mb.endStatement();
1854:
1855: /* col = (<Datatype interface>) r.getColumn(1) */
1856: //mb.getVariable(rVar);
1857: mb.push(1); // both the Row interface and columnId are 1-based
1858: mb.callMethod(VMOpcode.INVOKEINTERFACE, ClassName.Row,
1859: "getColumn", ClassName.DataValueDescriptor, 1);
1860: mb.cast(subqueryTypeString);
1861: //mb.putVariable(colVar);
1862: //mb.endStatement();
1863:
1864: /* Only generate the close() method for materialized
1865: * subqueries. All others will be closed when the
1866: * close() method is called on the top ResultSet.
1867: */
1868: if (isMaterializable()) {
1869: /* rs.close() */
1870: mb.getField(rsFieldLF);
1871: mb.callMethod(VMOpcode.INVOKEINTERFACE,
1872: ClassName.ResultSet, "close", "void", 0);
1873: }
1874:
1875: /* return col */
1876: //mb.getVariable(colVar);
1877: mb.methodReturn();
1878: mb.complete();
1879:
1880: /*
1881: ** If we have an expression subquery, then we
1882: ** can materialize it if it has no correlated
1883: ** column references and is invariant.
1884: */
1885: if (isMaterializable()) {
1886: LocalField lf = generateMaterialization(acb, mb,
1887: subqueryTypeString);
1888: mbex.getField(lf);
1889:
1890: } else {
1891: /* Generate the call to the new method */
1892: mbex.pushThis();
1893: mbex.callMethod(VMOpcode.INVOKEVIRTUAL, (String) null, mb
1894: .getName(), subqueryTypeString, 0);
1895: }
1896: }
1897:
1898: /*
1899: ** Materialize the subquery in question. Given the expression
1900: ** that represents the subquery, this returns fieldX where
1901: ** fieldX is set up as follows:
1902: **
1903: ** private ... fieldX
1904: **
1905: ** execute()
1906: ** {
1907: ** fieldX = <subqueryExpression>
1908: ** ...
1909: ** }
1910: **
1911: ** So we wind up evaluating the subquery when we start
1912: ** execution. Obviously, it is absolutely necessary that
1913: ** the subquery is invariant and has no correlations
1914: ** for this to work.
1915: **
1916: ** Ideally we wouldn't evaluate the expression subquery
1917: ** until we know we need to, but because we are marking
1918: ** this expression subquery as pushable, we must evaluate
1919: ** it up front because it might wind up as a qualification,
1920: ** and we cannot execute a subquery in the store as a
1921: ** qualification because the store executes qualifications
1922: ** while holding a latch.
1923: **
1924: ** @param acb
1925: ** @param type
1926: ** @param subqueryExpression
1927: */
1928: private LocalField generateMaterialization(
1929: ActivationClassBuilder acb, MethodBuilder mbsq, String type) {
1930: MethodBuilder mb = acb.executeMethod;
1931:
1932: // declare field
1933: LocalField field = acb.newFieldDeclaration(Modifier.PRIVATE,
1934: type);
1935:
1936: /* Generate the call to the new method */
1937: mb.pushThis();
1938: mb.callMethod(VMOpcode.INVOKEVIRTUAL, (String) null, mbsq
1939: .getName(), type, 0);
1940:
1941: // generate: field = value (value is on stack)
1942: mb.setField(field);
1943:
1944: return field;
1945: }
1946:
1947: /* Private methods on private variables */
1948: private BooleanConstantNode getTrueNode() throws StandardException {
1949: if (trueNode == null) {
1950: trueNode = (BooleanConstantNode) getNodeFactory().getNode(
1951: C_NodeTypes.BOOLEAN_CONSTANT_NODE, Boolean.TRUE,
1952: getContextManager());
1953: }
1954: return trueNode;
1955: }
1956:
1957: /**
1958: * Accept a visitor, and call v.visit()
1959: * on child nodes as necessary.
1960: *
1961: * @param v the visitor
1962: *
1963: * @exception StandardException on error
1964: */
1965: public Visitable accept(Visitor v) throws StandardException {
1966: Visitable returnNode = v.visit(this );
1967:
1968: /* shortcut if we've already done it
1969: */
1970: if ((v instanceof HasCorrelatedCRsVisitor)
1971: && doneCorrelationCheck) {
1972: ((HasCorrelatedCRsVisitor) v)
1973: .setHasCorrelatedCRs(foundCorrelation);
1974: return returnNode;
1975: }
1976:
1977: if (v.skipChildren(this )) {
1978: return returnNode;
1979: }
1980:
1981: if (resultSet != null && !v.stopTraversal()) {
1982: resultSet = (ResultSetNode) resultSet.accept(v);
1983: }
1984:
1985: if (leftOperand != null && !v.stopTraversal()) {
1986: leftOperand = (ValueNode) leftOperand.accept(v);
1987: }
1988: return returnNode;
1989: }
1990:
1991: private boolean isIN() {
1992: return subqueryType == IN_SUBQUERY;
1993: }
1994:
1995: private boolean isNOT_IN() {
1996: return subqueryType == NOT_IN_SUBQUERY;
1997: }
1998:
1999: private boolean isANY() {
2000: switch (subqueryType) {
2001: case EQ_ANY_SUBQUERY:
2002: case NE_ANY_SUBQUERY:
2003: case LE_ANY_SUBQUERY:
2004: case LT_ANY_SUBQUERY:
2005: case GE_ANY_SUBQUERY:
2006: case GT_ANY_SUBQUERY:
2007: return true;
2008:
2009: default:
2010: return false;
2011: }
2012: }
2013:
2014: private boolean isALL() {
2015: switch (subqueryType) {
2016: case EQ_ALL_SUBQUERY:
2017: case NE_ALL_SUBQUERY:
2018: case LE_ALL_SUBQUERY:
2019: case LT_ALL_SUBQUERY:
2020: case GE_ALL_SUBQUERY:
2021: case GT_ALL_SUBQUERY:
2022: return true;
2023:
2024: default:
2025: return false;
2026: }
2027: }
2028:
2029: private boolean isEXISTS() {
2030: return subqueryType == EXISTS_SUBQUERY;
2031: }
2032:
2033: private boolean isNOT_EXISTS() {
2034: return subqueryType == NOT_EXISTS_SUBQUERY;
2035: }
2036:
2037: /**
2038: * Convert this IN/ANY subquery, which is known to return at most 1 row,
2039: * to an equivalent expression subquery.
2040: *
2041: * @exception StandardException Thrown on error
2042: */
2043: private void changeToCorrespondingExpressionType()
2044: throws StandardException {
2045: BinaryOperatorNode bcon = null;
2046:
2047: switch (subqueryType) {
2048: case EQ_ANY_SUBQUERY:
2049: case IN_SUBQUERY:
2050: bcon = (BinaryOperatorNode) getNodeFactory().getNode(
2051: C_NodeTypes.BINARY_EQUALS_OPERATOR_NODE,
2052: leftOperand, this , getContextManager());
2053: break;
2054:
2055: case NE_ANY_SUBQUERY:
2056: bcon = (BinaryOperatorNode) getNodeFactory().getNode(
2057: C_NodeTypes.BINARY_NOT_EQUALS_OPERATOR_NODE,
2058: leftOperand, this , getContextManager());
2059: break;
2060:
2061: case LE_ANY_SUBQUERY:
2062: bcon = (BinaryOperatorNode) getNodeFactory().getNode(
2063: C_NodeTypes.BINARY_LESS_EQUALS_OPERATOR_NODE,
2064: leftOperand, this , getContextManager());
2065: break;
2066:
2067: case LT_ANY_SUBQUERY:
2068: bcon = (BinaryOperatorNode) getNodeFactory().getNode(
2069: C_NodeTypes.BINARY_LESS_THAN_OPERATOR_NODE,
2070: leftOperand, this , getContextManager());
2071: break;
2072:
2073: case GE_ANY_SUBQUERY:
2074: bcon = (BinaryOperatorNode) getNodeFactory().getNode(
2075: C_NodeTypes.BINARY_GREATER_EQUALS_OPERATOR_NODE,
2076: leftOperand, this , getContextManager());
2077: break;
2078:
2079: case GT_ANY_SUBQUERY:
2080: bcon = (BinaryOperatorNode) getNodeFactory().getNode(
2081: C_NodeTypes.BINARY_GREATER_THAN_OPERATOR_NODE,
2082: leftOperand, this , getContextManager());
2083: break;
2084: }
2085:
2086: // clean up the state of the tree to reflect a bound expression subquery
2087: subqueryType = EXPRESSION_SUBQUERY;
2088: setDataTypeServices(resultSet.getResultColumns());
2089:
2090: parentComparisonOperator = (BinaryComparisonOperatorNode) bcon;
2091: /* Set type info for the operator node */
2092: parentComparisonOperator.bindComparisonOperator();
2093: leftOperand = null;
2094: }
2095:
2096: private void setDataTypeServices(ResultColumnList resultColumns)
2097: throws StandardException {
2098: DataTypeDescriptor dts;
2099:
2100: /* Set the result type for this subquery (must be nullable).
2101: * Quantified predicates will return boolean.
2102: * However, the return type of the subquery's result list will
2103: * probably not be boolean at this point, because we do not
2104: * transform the subquery (other than EXISTS) into
2105: * (select true/false ...) until preprocess(). So, we force
2106: * the return type to boolean.
2107: */
2108: if (subqueryType == EXPRESSION_SUBQUERY) {
2109: dts = ((ResultColumn) resultColumns.elementAt(0))
2110: .getTypeServices();
2111: } else {
2112: dts = getTrueNode().getTypeServices();
2113: }
2114:
2115: /* If datatype of underlying resultSet is nullable, reuse it, otherwise
2116: * we need to generate a new one.
2117: */
2118: if (!dts.isNullable()) {
2119: dts = new DataTypeDescriptor(dts, true);
2120: }
2121: setType(dts);
2122: }
2123:
2124: /**
2125: * {@inheritDoc}
2126: */
2127: protected boolean isEquivalent(ValueNode o) {
2128: return false;
2129: }
2130: }
|