--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.sql.compile.Predicate\r
+\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to you under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+\r
+ */\r
+\r
+package org.apache.derby.impl.sql.compile;\r
+\r
+import org.apache.derby.impl.sql.compile.ExpressionClassBuilder;\r
+import org.apache.derby.impl.sql.compile.ActivationClassBuilder;\r
+\r
+import org.apache.derby.iapi.sql.compile.C_NodeTypes;\r
+import org.apache.derby.iapi.sql.compile.Visitable;\r
+import org.apache.derby.iapi.sql.compile.Visitor;\r
+import org.apache.derby.iapi.sql.compile.OptimizablePredicate;\r
+import org.apache.derby.iapi.sql.compile.Optimizable;\r
+\r
+import org.apache.derby.iapi.sql.dictionary.DataDictionary;\r
+\r
+import org.apache.derby.iapi.store.access.ScanController;\r
+\r
+import org.apache.derby.iapi.error.StandardException;\r
+\r
+import org.apache.derby.iapi.services.compiler.MethodBuilder;\r
+\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+\r
+import org.apache.derby.iapi.types.DataValueDescriptor;\r
+\r
+import org.apache.derby.iapi.util.JBitSet;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Hashtable;\r
+\r
+/**\r
+ * A Predicate represents a top level predicate.\r
+ *\r
+ */\r
+\r
+public final class Predicate extends QueryTreeNode implements OptimizablePredicate,\r
+ Comparable\r
+{\r
+ /* Top of the predicate */\r
+ AndNode andNode;\r
+ boolean pushable;\r
+ /* Bit map of referenced tables */\r
+ JBitSet referencedSet;\r
+ /* Join clauses are placed into equivalence classes when applying transitive\r
+ * closure for join clauses. This is useful for eliminating redundant predicates.\r
+ */\r
+ int equivalenceClass = -1;\r
+ int indexPosition;\r
+ protected boolean startKey;\r
+ protected boolean stopKey;\r
+ protected boolean isQualifier;\r
+\r
+ /* Hashtable used for tracking the search clause types that have been\r
+ * pushed through this predicate (if an equijoin) via transitive closure.\r
+ */\r
+ private Hashtable searchClauseHT;\r
+\r
+ // Whether or not this predicate has been scoped; see the\r
+ // getPredScopedForResultSet() method of this class for more.\r
+ private boolean scoped;\r
+\r
+ /**\r
+ * Initializer.\r
+ *\r
+ * @param andNode The top of the predicate \r
+ * @param referencedSet Bit map of referenced tables\r
+ */\r
+\r
+ public void init(Object andNode, Object referencedSet)\r
+ {\r
+ this.andNode = (AndNode) andNode;\r
+ pushable = false;\r
+ this.referencedSet = (JBitSet) referencedSet;\r
+ scoped = false;\r
+ }\r
+\r
+ /*\r
+ * Optimizable interface\r
+ */\r
+\r
+ /**\r
+ * @see org.apache.derby.iapi.sql.compile.OptimizablePredicate#getReferencedMap\r
+ */\r
+ public JBitSet getReferencedMap()\r
+ {\r
+ return referencedSet;\r
+ }\r
+\r
+ /**\r
+ * @see org.apache.derby.iapi.sql.compile.OptimizablePredicate#hasSubquery\r
+ */\r
+ public boolean hasSubquery()\r
+ {\r
+ /* RESOLVE - Currently, we record whether or not a predicate is pushable based\r
+ * on whether or not it contains a subquery or method call, but we do not\r
+ * record the underlying info.\r
+ */\r
+ return ! pushable;\r
+ }\r
+\r
+ /**\r
+ * @see org.apache.derby.iapi.sql.compile.OptimizablePredicate#hasMethodCall\r
+ */\r
+ public boolean hasMethodCall()\r
+ {\r
+ /* RESOLVE - Currently, we record whether or not a predicate is pushable based\r
+ * on whether or not it contains a subquery or method call, but we do not\r
+ * record the underlying info.\r
+ */\r
+ return ! pushable;\r
+ }\r
+\r
+ /** @see OptimizablePredicate#markStartKey */\r
+ public void markStartKey()\r
+ {\r
+ startKey = true;\r
+ }\r
+\r
+ /** @see OptimizablePredicate#isStartKey */\r
+ public boolean isStartKey()\r
+ {\r
+ return startKey;\r
+ }\r
+\r
+ /** @see OptimizablePredicate#markStopKey */\r
+ public void markStopKey()\r
+ {\r
+ stopKey = true;\r
+ }\r
+\r
+ /** @see OptimizablePredicate#isStopKey */\r
+ public boolean isStopKey()\r
+ {\r
+ return stopKey;\r
+ }\r
+\r
+ /** @see OptimizablePredicate#markQualifier */\r
+ public void markQualifier()\r
+ {\r
+ isQualifier = true;\r
+ }\r
+\r
+ /** @see OptimizablePredicate#isQualifier */\r
+ public boolean isQualifier()\r
+ {\r
+ return isQualifier;\r
+ }\r
+\r
+ /** @see OptimizablePredicate#compareWithKnownConstant */\r
+ public boolean compareWithKnownConstant(Optimizable optTable, boolean considerParameters)\r
+ {\r
+ boolean retval = false;\r
+ RelationalOperator relop = getRelop();\r
+\r
+ /* if this is for "in" operator node's dynamic start/stop key, relop is\r
+ * null, and it's not comparing with constant, beetle 3858\r
+ */\r
+ if (!isRelationalOpPredicate())\r
+ return false;\r
+\r
+ if (relop.compareWithKnownConstant(optTable, considerParameters))\r
+ retval = true;\r
+\r
+ return retval;\r
+ }\r
+\r
+ public int hasEqualOnColumnList(int[] baseColumnPositions,\r
+ Optimizable optTable)\r
+ throws StandardException\r
+ {\r
+ RelationalOperator relop = getRelop();\r
+\r
+ if (!isRelationalOpPredicate())\r
+ return -1;\r
+ \r
+ if (!(relop.getOperator() == RelationalOperator.EQUALS_RELOP))\r
+ return -1;\r
+ \r
+ for (int i = 0; i < baseColumnPositions.length; i++)\r
+ {\r
+ ColumnReference cr = relop.getColumnOperand(optTable, \r
+ baseColumnPositions[i]);\r
+ \r
+ if (cr == null)\r
+ continue;\r
+ \r
+ if (relop.selfComparison(cr))\r
+ continue;\r
+\r
+ // If I made it thus far in the loop, we've found\r
+ // something.\r
+ return i;\r
+ }\r
+ \r
+ return -1;\r
+ }\r
+\r
+ /**\r
+ * @see OptimizablePredicate#getCompareValue\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public DataValueDescriptor getCompareValue(Optimizable optTable)\r
+ throws StandardException\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(compareWithKnownConstant(optTable, true),\r
+ "Cannot get the compare value if not comparing with a known constant.");\r
+ }\r
+\r
+ RelationalOperator relop = getRelop();\r
+\r
+ return relop.getCompareValue(optTable);\r
+ }\r
+\r
+ /** @see OptimizablePredicate#equalsComparisonWithConstantExpression */\r
+ public boolean equalsComparisonWithConstantExpression(Optimizable optTable)\r
+ {\r
+ boolean retval = false;\r
+\r
+ if (isRelationalOpPredicate())\r
+ {\r
+ retval = getRelop().equalsComparisonWithConstantExpression(optTable);\r
+ }\r
+\r
+ return retval;\r
+ }\r
+\r
+ /** @see OptimizablePredicate#selectivity */\r
+ public double selectivity(Optimizable optTable)\r
+ throws StandardException\r
+ {\r
+ return andNode.getLeftOperand().selectivity(optTable);\r
+ }\r
+\r
+ /** @see OptimizablePredicate#getIndexPosition */\r
+ public int getIndexPosition()\r
+ {\r
+ return indexPosition;\r
+ }\r
+\r
+\r
+ /* Comparable interface */\r
+\r
+ public int compareTo(Object other)\r
+ {\r
+ Predicate otherPred = (Predicate) other;\r
+\r
+ /* Not all operators are "equal". If the predicates are on the\r
+ * same key column, then a "=" opertor takes precedence over all\r
+ * other operators. This ensures that the "=" will be both the start\r
+ * and stop predicates. Otherwise, we could end up with it being one\r
+ * but not the other and get incorrect results.\r
+ *\r
+ * Also, we want "<>" to come after all the other operators.\r
+ * Other parts of the optimizer use the first predicate on an index\r
+ * column to determine the cost of using the index, so we want the\r
+ * "<>" to come last because it's not useful for limiting the scan.\r
+ *\r
+ * In other words, P1 is before() P2 if:\r
+ * o The P1.indexPosition < P2.indexPosition\r
+ * or o P1.indexPosition == P2.indexPosition and\r
+ * P1's operator is ("=" or IS NULL) and\r
+ * P2's operator is not ("=" or IS NULL)\r
+ * or o P1.indexPosition == P2.indexPosition and\r
+ * P1's operator is not ("<>" or IS NOT NULL) and\r
+ * P2's operator is ("<>" or IS NOT NULL)\r
+ *\r
+ * (We have to impose an arbitrary, but reproducible ordering\r
+ * on the the "=" predicates on the same column, otherwise an\r
+ * ASSERTion, that after the predicates are order, Pn+1 is not\r
+ * before() Pn, will be violated.\r
+ */\r
+\r
+ int otherIndexPosition = otherPred.getIndexPosition();\r
+\r
+ if (indexPosition < otherIndexPosition)\r
+ return -1;\r
+\r
+ if (indexPosition > otherIndexPosition)\r
+ return 1;\r
+\r
+ // initialize these flags as if they are for "in" operator, then\r
+ // change them if they are not\r
+ //\r
+ boolean thisIsEquals = false, otherIsEquals = false;\r
+ boolean thisIsNotEquals = true, otherIsNotEquals = true;\r
+\r
+ /* The call to "isRelationalOpPredicate()" will return false\r
+ * for a "probe predicate" because a probe predicate is really\r
+ * a disguised IN list. But when it comes to sorting, the probe\r
+ * predicate (which is of the form "<col> = ?") should be treated\r
+ * as an equality--i.e. it should have precedence over any non-\r
+ * equals predicate, per the comment at the start of this\r
+ * method. So that's what we're checking here.\r
+ */\r
+ if (this.isRelationalOpPredicate() || // this is not "in" or\r
+ this.isInListProbePredicate()) // this is a probe predicate\r
+ {\r
+ int thisOperator = ((RelationalOperator)andNode.getLeftOperand()).getOperator();\r
+ thisIsEquals = (thisOperator == RelationalOperator.EQUALS_RELOP ||\r
+ thisOperator == RelationalOperator.IS_NULL_RELOP);\r
+ thisIsNotEquals = (thisOperator == RelationalOperator.NOT_EQUALS_RELOP ||\r
+ thisOperator == RelationalOperator.IS_NOT_NULL_RELOP);\r
+ }\r
+\r
+ if (otherPred.isRelationalOpPredicate() || // other is not "in" or\r
+ otherPred.isInListProbePredicate()) // other is a probe predicate\r
+ {\r
+ int otherOperator = ((RelationalOperator)(otherPred.getAndNode().getLeftOperand())).getOperator();\r
+ otherIsEquals = (otherOperator == RelationalOperator.EQUALS_RELOP ||\r
+ otherOperator == RelationalOperator.IS_NULL_RELOP);\r
+ otherIsNotEquals = (otherOperator == RelationalOperator.NOT_EQUALS_RELOP ||\r
+ otherOperator == RelationalOperator.IS_NOT_NULL_RELOP);\r
+ }\r
+\r
+ boolean thisIsBefore = (thisIsEquals && ! otherIsEquals) || ( ! thisIsNotEquals && otherIsNotEquals);\r
+ if (thisIsBefore)\r
+ return -1;\r
+\r
+ boolean otherIsBefore = (otherIsEquals && ! thisIsEquals) || ( ! otherIsNotEquals && thisIsNotEquals);\r
+ if (otherIsBefore)\r
+ return 1;\r
+ return 0;\r
+ }\r
+\r
+ /**\r
+ * Return the andNode.\r
+ *\r
+ * @return AndNode The andNode.\r
+ */\r
+ public AndNode getAndNode()\r
+ {\r
+ return andNode;\r
+ }\r
+\r
+ /**\r
+ * Set the andNode.\r
+ *\r
+ * @param andNode The new andNode.\r
+ */\r
+ public void setAndNode(AndNode andNode)\r
+ {\r
+ this.andNode = andNode;\r
+ }\r
+\r
+ /**\r
+ * Return the pushable.\r
+ *\r
+ * @return boolean Whether or not the predicate is pushable.\r
+ */\r
+ public boolean getPushable()\r
+ {\r
+ return pushable;\r
+ }\r
+\r
+ /**\r
+ * Set whether or not this predicate is pushable. This method\r
+ * is intended for use when creating a copy of the predicate, ex\r
+ * for predicate pushdown. We choose not to add this assignment\r
+ * to copyFields() because the comments for that method say that\r
+ * it should copy all fields _except_ the two specified at init\r
+ * time; "pushable" is one of the two specified at init time.\r
+ *\r
+ * @param pushable Whether or not the predicate is pushable.\r
+ */\r
+ public void setPushable(boolean pushable) {\r
+ this.pushable = pushable;\r
+ }\r
+\r
+ /**\r
+ * Return the referencedSet.\r
+ *\r
+ * @return JBitSet The referencedSet.\r
+ */\r
+ public JBitSet getReferencedSet()\r
+ {\r
+ return referencedSet;\r
+ }\r
+\r
+ /**\r
+ * Set the equivalence class, if any, for this predicate.\r
+ *\r
+ * @param equivalenceClass The equivalence class for this predicate.\r
+ */\r
+ void setEquivalenceClass(int equivalenceClass)\r
+ {\r
+ this.equivalenceClass = equivalenceClass;\r
+ }\r
+\r
+ /**\r
+ * Get the equivalenceClass for this predicate.\r
+ *\r
+ * @return The equivalenceClass for this predicate.\r
+ */\r
+ int getEquivalenceClass()\r
+ {\r
+ return equivalenceClass;\r
+ }\r
+\r
+ /**\r
+ * Categorize this predicate. Initially, this means\r
+ * building a bit map of the referenced tables for each predicate.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public void categorize() throws StandardException\r
+ {\r
+ pushable = andNode.categorize(referencedSet, false);\r
+ }\r
+\r
+ /**\r
+ * Get the RelationalOperator on the left side of the AND node, if\r
+ * there is one. If the left side is not a RelationalOperator, return\r
+ * null.\r
+ *\r
+ * @return The RelationalOperator on the left side of the AND node,\r
+ * if any.\r
+ */\r
+ public RelationalOperator getRelop()\r
+ {\r
+ \r
+ if (andNode.getLeftOperand() instanceof RelationalOperator)\r
+ {\r
+ return (RelationalOperator) andNode.getLeftOperand();\r
+ }\r
+ else\r
+ {\r
+ return null;\r
+ }\r
+ }\r
+\r
+ public final boolean isOrList()\r
+ {\r
+ return(andNode.getLeftOperand() instanceof OrNode);\r
+ }\r
+\r
+ /**\r
+ * Is this predicate a possible Qualifier for store?\r
+ * <p>\r
+ * Current 2 types of predicates can be pushed to store: \r
+ * 1) RelationalOperator - \r
+ * represented with by left operand as instance of RelationalOperator.\r
+ * 2) A single And'd term of a list of OR terms\r
+ * represented by left operand as instance of OrNode.\r
+ *\r
+ * More checking specific operator's terms to see if they are finally\r
+ * pushable to store. In the final push at execution each term of the AND \r
+ * or OR must be a Relational operator with a column reference on one side \r
+ * and a constant on the other.\r
+ *\r
+ *\r
+ * @return true if term is wither a AND of a RelationalOperator, or an\r
+ * OR of one or more Relational Operators.\r
+ *\r
+ *\r
+ * @exception StandardException Standard exception policy.\r
+ **/\r
+ public final boolean isStoreQualifier()\r
+ {\r
+ if ((andNode.getLeftOperand() instanceof RelationalOperator) ||\r
+ (andNode.getLeftOperand() instanceof OrNode))\r
+ {\r
+ return(true);\r
+ }\r
+ else\r
+ {\r
+ return(false);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Is this predicate an pushable OR list?\r
+ * <p>\r
+ * Does the predicate represent a AND'd list of OR term's, all of which\r
+ * are pushable. To be pushable each of OR terms must be a legal \r
+ * qualifier, which is a column reference on one side of a Relational\r
+ * operator and a constant on the other.\r
+ *\r
+ * @return true if the predicate is a pushable set of OR clauses.\r
+ *\r
+ *\r
+ * @exception StandardException Standard exception policy.\r
+ **/\r
+ public final boolean isPushableOrClause(Optimizable optTable)\r
+ throws StandardException\r
+ {\r
+ boolean ret_val = true;\r
+\r
+ if (andNode.getLeftOperand() instanceof OrNode)\r
+ {\r
+ QueryTreeNode node = andNode.getLeftOperand();\r
+\r
+ while (node instanceof OrNode)\r
+ {\r
+ OrNode or_node = (OrNode) node;\r
+\r
+ if (or_node.getLeftOperand() instanceof RelationalOperator)\r
+ {\r
+ // if any term of the OR clause is not a qualifier, then\r
+ // reject the entire OR clause.\r
+ if (!((RelationalOperator) or_node.getLeftOperand()).\r
+ isQualifier(optTable, true))\r
+ {\r
+ // one of the terms is not a pushable Qualifier.\r
+ return(false);\r
+ }\r
+\r
+ node = or_node.getRightOperand();\r
+ }\r
+ else\r
+ {\r
+ // one of the terms is not a RelationalOperator\r
+\r
+ return(false);\r
+ }\r
+ }\r
+\r
+ return(true);\r
+ }\r
+ else\r
+ {\r
+ // Not an OR list\r
+ return(false);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Return whether or not this predicate has been used\r
+ * to add a new search clause of the specified type via transitive closure.\r
+ * NOTE: This can only be true if this is an equijoin\r
+ * between 2 column references.\r
+ *\r
+ * @param ro The search clause that we are currently considering\r
+ * as the source for transitive closure\r
+ *\r
+ * @return Whether or not this predicate has been used\r
+ * to add a new search clause of the specified type via transitive \r
+ * closure.\r
+ */\r
+ boolean transitiveSearchClauseAdded(RelationalOperator ro)\r
+ {\r
+ if (searchClauseHT == null || \r
+ searchClauseHT.get(new Integer(ro.getOperator())) == null)\r
+ {\r
+ return false;\r
+ }\r
+ else\r
+ {\r
+ return true;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Mark this predicate as having been used to add a new predicate\r
+ * of the specified type via transitive closure on search clauses.\r
+ *\r
+ * @param ro The search clause that we are currently considering\r
+ * as the source for transitive closure\r
+ *\r
+ */\r
+ void setTransitiveSearchClauseAdded(RelationalOperator ro)\r
+ {\r
+ if (searchClauseHT == null)\r
+ {\r
+ searchClauseHT = new Hashtable();\r
+ }\r
+ /* I have to remember that this ro has been added to this predicate as a\r
+ * transitive search clause.\r
+ */\r
+ Integer i = new Integer(ro.getOperator());\r
+ searchClauseHT.put(i, i);\r
+ }\r
+\r
+ /**\r
+ * Get the start operator for this predicate for a scan.\r
+ *\r
+ * @param optTable The optimizable table, so we can tell which side of\r
+ * the operator the search column is on.\r
+ *\r
+ * @return The start operator for a start key on this column.\r
+ */\r
+ int getStartOperator(Optimizable optTable)\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(startKey, "Getting a start operator from a Predicate that's not a start key.");\r
+ }\r
+\r
+ /* if it's for "in" operator's dynamic start key, operator is GE,\r
+ * beetle 3858\r
+ */\r
+ if (andNode.getLeftOperand() instanceof InListOperatorNode)\r
+ return ScanController.GE;\r
+\r
+ return getRelop().getStartOperator(optTable);\r
+ }\r
+\r
+ int getStopOperator(Optimizable optTable)\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(stopKey, "Getting a stop operator from a Predicate that's not a stop key.");\r
+ }\r
+\r
+ /* if it's for "in" operator's dynamic stop key, operator is GT,\r
+ * beetle 3858\r
+ */\r
+ if (andNode.getLeftOperand() instanceof InListOperatorNode)\r
+ return ScanController.GT;\r
+\r
+ return getRelop().getStopOperator(optTable);\r
+ }\r
+\r
+ /**\r
+ * Set the position of the index column that this predicate restricts\r
+ *\r
+ * @param indexPosition The position of the index column that this\r
+ * predicate restricts.\r
+ */\r
+ void setIndexPosition(int indexPosition)\r
+ {\r
+ this.indexPosition = indexPosition;\r
+ }\r
+\r
+ /**\r
+ * Clear the start/stop position and qualifier flags\r
+ */\r
+ void clearScanFlags()\r
+ {\r
+ startKey = false;\r
+ stopKey = false;\r
+ isQualifier = false;\r
+ }\r
+\r
+ /**\r
+ * Clear the qualifier flag.\r
+ */\r
+ void clearQualifierFlag()\r
+ {\r
+ isQualifier = false;\r
+ }\r
+\r
+ void generateExpressionOperand(Optimizable optTable,\r
+ int columnPosition,\r
+ ExpressionClassBuilder acb,\r
+ MethodBuilder mb)\r
+ throws StandardException\r
+ {\r
+ getRelop().generateExpressionOperand(optTable,\r
+ columnPosition,\r
+ acb,\r
+ mb);\r
+ }\r
+\r
+ void generateAbsoluteColumnId(MethodBuilder mb,\r
+ Optimizable optTable)\r
+ {\r
+ getRelop().generateAbsoluteColumnId(mb, optTable);\r
+ }\r
+\r
+ void generateRelativeColumnId(MethodBuilder mb,\r
+ Optimizable optTable)\r
+ {\r
+ getRelop().generateRelativeColumnId(mb, optTable);\r
+ }\r
+\r
+ void generateOperator(MethodBuilder mb,\r
+ Optimizable optTable)\r
+ {\r
+ getRelop().generateOperator(mb, optTable);\r
+ }\r
+\r
+ void generateQualMethod(ExpressionClassBuilder acb,\r
+ MethodBuilder mb,\r
+ Optimizable optTable)\r
+ throws StandardException\r
+ {\r
+ getRelop().generateQualMethod(acb, mb, optTable);\r
+ }\r
+\r
+ void generateOrderedNulls(MethodBuilder mb)\r
+ {\r
+ getRelop().generateOrderedNulls(mb);\r
+ }\r
+\r
+ void generateNegate(MethodBuilder mb,\r
+ Optimizable optTable)\r
+ {\r
+ getRelop().generateNegate(mb, optTable);\r
+ }\r
+\r
+ void generateOrderableVariantType(MethodBuilder mb,\r
+ Optimizable optTable)\r
+ throws StandardException\r
+ {\r
+ int variantType = getRelop().getOrderableVariantType(optTable);\r
+ mb.push(variantType);\r
+\r
+ }\r
+ /**\r
+ * Convert this object to a String. See comments in QueryTreeNode.java\r
+ * for how this should be done for tree printing.\r
+ *\r
+ * @return This object as a String\r
+ */\r
+\r
+ public String toString()\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ return binaryRelOpColRefsToString() + "\nreferencedSet: " +\r
+ referencedSet + "\n" + "pushable: " + pushable + "\n" +\r
+ super.toString();\r
+ }\r
+ else\r
+ {\r
+ return "";\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Get a string version of the column references for this predicate\r
+ * IF it's a binary relational operator. We only print out the\r
+ * names of the operands if they are column references; otherwise\r
+ * we just print a dummy value. This is for debugging purposes\r
+ * only--it's a convenient way to see what columns the predicate\r
+ * is referencing, especially when tracing through code and printing\r
+ * assert failure.\r
+ */\r
+ public String binaryRelOpColRefsToString()\r
+ {\r
+ // We only consider binary relational operators here.\r
+ if (!(getAndNode().getLeftOperand()\r
+ instanceof BinaryRelationalOperatorNode))\r
+ {\r
+ return "";\r
+ }\r
+\r
+ final String DUMMY_VAL = "<expr>";\r
+ java.lang.StringBuffer sBuf = new java.lang.StringBuffer();\r
+ BinaryRelationalOperatorNode opNode =\r
+ (BinaryRelationalOperatorNode)getAndNode().getLeftOperand();\r
+\r
+ // Get left operand's name.\r
+ if (opNode.getLeftOperand() instanceof ColumnReference)\r
+ {\r
+ sBuf.append(\r
+ ((ColumnReference)opNode.getLeftOperand()).getTableName() +\r
+ "." +\r
+ ((ColumnReference)opNode.getLeftOperand()).getColumnName()\r
+ );\r
+ }\r
+ else\r
+ sBuf.append(DUMMY_VAL);\r
+\r
+ // Get the operator type.\r
+ sBuf.append(" " + opNode.operator + " ");\r
+\r
+ // Get right operand's name.\r
+ if (opNode.getRightOperand() instanceof ColumnReference) {\r
+ sBuf.append(\r
+ ((ColumnReference)opNode.getRightOperand()).getTableName() +\r
+ "." +\r
+ ((ColumnReference)opNode.getRightOperand()).getColumnName()\r
+ );\r
+ }\r
+ else\r
+ sBuf.append(DUMMY_VAL);\r
+\r
+ return sBuf.toString();\r
+ }\r
+\r
+ /**\r
+ * Prints the sub-nodes of this object. See QueryTreeNode.java for\r
+ * how tree printing is supposed to work.\r
+ *\r
+ * @param depth The depth of this node in the tree\r
+ */\r
+\r
+ public void printSubNodes(int depth)\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ printLabel(depth, "andNode: ");\r
+ andNode.treePrint(depth + 1);\r
+ super.printSubNodes(depth);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Accept a visitor, and call v.visit()\r
+ * on child nodes as necessary. \r
+ * \r
+ * @param v the visitor\r
+ *\r
+ * @exception StandardException on error\r
+ */\r
+ public Visitable accept(Visitor v) \r
+ throws StandardException\r
+ {\r
+ if (v.skipChildren(this))\r
+ {\r
+ return v.visit(this);\r
+ }\r
+\r
+ Visitable returnNode = super.accept(v);\r
+\r
+ if (andNode != null && !v.stopTraversal())\r
+ {\r
+ andNode = (AndNode)andNode.accept(v);\r
+ }\r
+\r
+ return returnNode;\r
+ }\r
+\r
+ /**\r
+ * Copy all fields of this Predicate (except the two that\r
+ * are set from 'init').\r
+ *\r
+ */\r
+\r
+ public void copyFields(Predicate otherPred) {\r
+\r
+ this.equivalenceClass = otherPred.getEquivalenceClass();\r
+ this.indexPosition = otherPred.getIndexPosition();\r
+ this.startKey = otherPred.isStartKey();\r
+ this.stopKey = otherPred.isStopKey();\r
+ this.isQualifier = otherPred.isQualifier();\r
+ this.searchClauseHT = otherPred.getSearchClauseHT();\r
+\r
+ }\r
+\r
+ /**\r
+ * Get the search clause Hash Table.\r
+ */\r
+\r
+ public Hashtable getSearchClauseHT() {\r
+ return searchClauseHT;\r
+ }\r
+\r
+ /**\r
+ * Determine whether or not this predicate is eligible for\r
+ * push-down into subqueries. Right now the only predicates\r
+ * we consider to be eligible are those which 1) are Binary\r
+ * Relational operator nodes and 2) have a column reference\r
+ * on BOTH sides, each of which has a reference to a base\r
+ * table somewhere beneath it.\r
+ *\r
+ * @return Whether or not this predicate is eligible to be\r
+ * pushed into subqueries.\r
+ */\r
+ protected boolean pushableToSubqueries()\r
+ throws StandardException\r
+ {\r
+ if (!isJoinPredicate())\r
+ return false;\r
+\r
+ // Make sure both column references ultimately point to base\r
+ // tables. If, for example, either column reference points to a\r
+ // a literal or an aggregate, then we do not push the predicate.\r
+ // This is because pushing involves remapping the references--\r
+ // but if the reference doesn't have a base table beneath it,\r
+ // the notion of "remapping" it doesn't (seem to) apply. RESOLVE:\r
+ // it might be okay to make the "remap" operation a no-op for\r
+ // such column references, but it's not clear whether that's\r
+ // always a safe option; further investigation required.\r
+\r
+ BinaryRelationalOperatorNode opNode =\r
+ (BinaryRelationalOperatorNode)getAndNode().getLeftOperand();\r
+\r
+ JBitSet tNums = new JBitSet(getReferencedSet().size());\r
+ BaseTableNumbersVisitor btnVis = new BaseTableNumbersVisitor(tNums);\r
+ opNode.getLeftOperand().accept(btnVis);\r
+ if (tNums.getFirstSetBit() == -1)\r
+ return false;\r
+\r
+ tNums.clearAll();\r
+ opNode.getRightOperand().accept(btnVis);\r
+ if (tNums.getFirstSetBit() == -1)\r
+ return false;\r
+\r
+ return true;\r
+ }\r
+\r
+ /**\r
+ * Is this predicate a join predicate? In order to be so,\r
+ * it must be a binary relational operator node that has\r
+ * a column reference on both sides.\r
+ *\r
+ * @return Whether or not this is a join predicate.\r
+ */\r
+ protected boolean isJoinPredicate()\r
+ {\r
+ // If the predicate isn't a binary relational operator,\r
+ // then it's not a join predicate.\r
+ if (!(getAndNode().getLeftOperand()\r
+ instanceof BinaryRelationalOperatorNode))\r
+ {\r
+ return false;\r
+ }\r
+\r
+ BinaryRelationalOperatorNode opNode =\r
+ (BinaryRelationalOperatorNode)getAndNode().getLeftOperand();\r
+\r
+ // If both sides are column references AND they point to different\r
+ // tables, then this is a join pred.\r
+ return ((opNode.getLeftOperand() instanceof ColumnReference) &&\r
+ (opNode.getRightOperand() instanceof ColumnReference) &&\r
+ (((ColumnReference)opNode.getLeftOperand()).getTableNumber() !=\r
+ ((ColumnReference)opNode.getRightOperand()).getTableNumber()));\r
+ }\r
+\r
+ /**\r
+ * If this predicate's operator is a BinaryRelationalOperatorNode,\r
+ * then look at the operands and return a new, equivalent predicate\r
+ * that is "scoped" to the received ResultSetNode. By "scoped" we\r
+ * mean that the operands, which shold be column references, have been\r
+ * mapped to the appropriate result columns in the received RSN.\r
+ * This is useful for pushing predicates from outer queries down\r
+ * into inner queries, in which case the column references need\r
+ * to be remapped.\r
+ *\r
+ * For example, let V1 represent\r
+ *\r
+ * select i,j from t1 UNION select i,j from t2\r
+ * \r
+ * and V2 represent\r
+ *\r
+ * select a,b from t3 UNION select a,b from t4\r
+ * \r
+ * Then assume we have the following query:\r
+ *\r
+ * select * from V1, V2 where V1.j = V2.b\r
+ *\r
+ * Let's further assume that this Predicate object represents the\r
+ * "V1.j = V2.b" operator and that the childRSN we received\r
+ * as a parameter represents one of the subqueries to which we\r
+ * want to push the predicate; let's say it's:\r
+ *\r
+ * select i,j from t1\r
+ *\r
+ * Then this method will return a new predicate whose binary\r
+ * operator represents the expression "T1.j = V2.b" (that is, V1.j\r
+ * will be mapped to the corresponding column in T1). For more on\r
+ * how that mapping is made, see the "getScopedOperand()" method\r
+ * in BinaryRelationalOperatorNode.java.\r
+ *\r
+ * ASSUMPTION: We should only get to this method if we know that\r
+ * at least one operand in this predicate can and should be mapped\r
+ * to the received childRSN. For an example of where that check is\r
+ * made, see the pushOptPredicate() method in SetOperatorNode.java.\r
+ *\r
+ * @param parentRSNsTables Set of all table numbers referenced by\r
+ * the ResultSetNode that is _parent_ to the received childRSN.\r
+ * We need this to make sure we don't scope the operands to a\r
+ * ResultSetNode to which they don't apply.\r
+ * @param childRSN The result set node for which we want to create\r
+ * a scoped predicate.\r
+ * @param whichRC If not -1 then this tells us which ResultColumn\r
+ * in the received childRSN we need to use for the scoped predicate;\r
+ * if -1 then the column position of the scoped column reference\r
+ * will be stored in this array and passed back to the caller.\r
+ * @return A new predicate whose operands have been scoped to the\r
+ * received childRSN.\r
+ */\r
+ protected Predicate getPredScopedForResultSet(\r
+ JBitSet parentRSNsTables, ResultSetNode childRSN,\r
+ int [] whichRC) throws StandardException\r
+ {\r
+ // We only deal with binary relational operators here.\r
+ if (!(getAndNode().getLeftOperand()\r
+ instanceof BinaryRelationalOperatorNode))\r
+ {\r
+ return this;\r
+ }\r
+\r
+ // The predicate must have an AndNode in CNF, so we\r
+ // need to create an AndNode representing:\r
+ // <scoped_bin_rel_op> AND TRUE\r
+ // First create the boolean constant for TRUE.\r
+ ValueNode trueNode = (ValueNode) getNodeFactory().getNode(\r
+ C_NodeTypes.BOOLEAN_CONSTANT_NODE,\r
+ Boolean.TRUE,\r
+ getContextManager());\r
+\r
+ BinaryRelationalOperatorNode opNode =\r
+ (BinaryRelationalOperatorNode)getAndNode().getLeftOperand();\r
+\r
+ // Create a new op node with left and right operands that point\r
+ // to the received result set's columns as appropriate.\r
+ BinaryRelationalOperatorNode newOpNode = \r
+ (BinaryRelationalOperatorNode) getNodeFactory().getNode(\r
+ opNode.getNodeType(),\r
+ opNode.getScopedOperand(\r
+ BinaryRelationalOperatorNode.LEFT,\r
+ parentRSNsTables,\r
+ childRSN,\r
+ whichRC),\r
+ opNode.getScopedOperand(\r
+ BinaryRelationalOperatorNode.RIGHT,\r
+ parentRSNsTables,\r
+ childRSN,\r
+ whichRC),\r
+ getContextManager());\r
+\r
+ // Bind the new op node.\r
+ newOpNode.bindComparisonOperator();\r
+\r
+ // Create and bind a new AND node in CNF form,\r
+ // i.e. "<newOpNode> AND TRUE".\r
+ AndNode newAnd = (AndNode) getNodeFactory().getNode(\r
+ C_NodeTypes.AND_NODE,\r
+ newOpNode,\r
+ trueNode,\r
+ getContextManager());\r
+ newAnd.postBindFixup();\r
+\r
+ // Categorize the new AND node; among other things, this\r
+ // call sets up the new operators's referenced table map,\r
+ // which is important for correct pushing of the new\r
+ // predicate.\r
+ JBitSet tableMap = new JBitSet(\r
+ childRSN.getReferencedTableMap().size());\r
+ newAnd.categorize(tableMap, false);\r
+\r
+ // Now put the pieces together to get a new predicate.\r
+ Predicate newPred = (Predicate) getNodeFactory().getNode(\r
+ C_NodeTypes.PREDICATE,\r
+ newAnd,\r
+ tableMap,\r
+ getContextManager());\r
+\r
+ // Copy all of this predicates other fields into the new predicate.\r
+ newPred.clearScanFlags();\r
+ newPred.copyFields(this);\r
+ newPred.setPushable(getPushable());\r
+\r
+ // Take note of the fact that the new predicate is scoped for\r
+ // the sake of pushing; we need this information during optimization\r
+ // to figure out what we should and should not "pull" back up.\r
+ newPred.markAsScopedForPush();\r
+ return newPred;\r
+ }\r
+\r
+ /**\r
+ * Indicate that this predicate is a scoped copy of some other\r
+ * predicate (i.e. it was created as the result of a call to\r
+ * getPredScopedForResultSet() on some other predicate).\r
+ */\r
+ protected void markAsScopedForPush() {\r
+ this.scoped = true;\r
+ }\r
+\r
+ /**\r
+ * Return whether or not this predicate is a scoped copy of\r
+ * another predicate.\r
+ */\r
+ protected boolean isScopedForPush() {\r
+ return scoped;\r
+ }\r
+\r
+ /**\r
+ * When remapping a "normal" (i.e. non-scoped) predicate both\r
+ * of the predicate's operands are remapped and that's it.\r
+ * But when remapping a scoped predicate, things are slightly\r
+ * different. This method handles remapping of scoped predicates.\r
+ *\r
+ * We know that, for a scoped predicate, exactly one operand has\r
+ * been scoped for a specific target result set; the other operand\r
+ * is pointing to some other instance of FromTable with which the\r
+ * target result set is to be joined (see getScopedOperand() in\r
+ * BinaryRelationalOperatorNode.java). For every level of the\r
+ * query through which the scoped predicate is pushed, we have\r
+ * to perform a remap operation of the scoped operand. We do\r
+ * *not*, however, remap the non-scoped operand. The reason\r
+ * is that the non-scoped operand is already pointing to the\r
+ * result set against which it must be evaluated. As the scoped\r
+ * predicate is pushed down the query tree, the non-scoped\r
+ * operand should not change where it's pointing and thus should\r
+ * not be remapped. For example, assume we have a query whose\r
+ * tree has the following form:\r
+ *\r
+ * SELECT[0] \r
+ * / \ \r
+ * PRN PRN \r
+ * | |\r
+ * SELECT[4] UNION\r
+ * | / \ \r
+ * PRN SELECT[1] SELECT[2] \r
+ * | | | \r
+ * <FBT:T1> PRN PRN \r
+ * | |\r
+ * SELECT[3] <FromBaseTable:T2> \r
+ * |\r
+ * PRN\r
+ * |\r
+ * <FromBaseTable:T3>\r
+ *\r
+ * Assume also that we have some predicate "SELECT[4].i = <UNION>.j".\r
+ * If the optimizer decides to push the predicate to the UNION\r
+ * node, it (the predicate) will be scoped to the UNION's children,\r
+ * yielding something like "SELECT[4].i = SELECT[1].j" for the\r
+ * left child and "SELECT[4].i = SELECT[2].j" for the right child.\r
+ * These scoped predicates will then be pushed to the PRNs above\r
+ * SELECT[3] and T2, respectively. As part of that pushing\r
+ * process a call to PRN.pushOptPredicate() will occur, which\r
+ * brings us to this method. So let's assume we're here for\r
+ * the scoped predicate "SELECT[4].i = SELECT[1].j". Then we want\r
+ * to remap the scoped operand, "SELECT[1].j", so that it will\r
+ * point to the correct column in "SELECT[3]". We do NOT, however,\r
+ * want to remap the non-scoped operand "SELECT[4].i" because that\r
+ * operand is already pointing to the correct result set--namely,\r
+ * to a column in SELECT[4]. That non-scoped operand should not\r
+ * change regardless of how far down the UNION subtree the scoped\r
+ * predicate is pushed.\r
+ * \r
+ * If we did try to remap the non-scoped operand, it would end up\r
+ * pointing to result sets too low in the tree, which could lead to\r
+ * execution-time errors. So when we remap a scoped predicate, we\r
+ * have to make sure we only remap the scoped operand. That's what\r
+ * this method does.\r
+ *\r
+ * @return True if this predicate is a scoped predicate, in which\r
+ * case we performed a one-sided remap. False if the predicate is\r
+ * not scoped; the caller can then make the calls to perform a\r
+ * "normal" remap on this predicate.\r
+ */\r
+ protected boolean remapScopedPred()\r
+ {\r
+ if (!scoped)\r
+ return false;\r
+\r
+ /* Note: right now the only predicates we scope are those\r
+ * which are join predicates and all scoped predicates will\r
+ * have the same relational operator as the predicates from\r
+ * which they were scoped. Thus if we get here, we know\r
+ * that andNode's leftOperand must be an instance of\r
+ * BinaryRelationalOperatorNode (and therefore the following\r
+ * cast is safe).\r
+ */\r
+ BinaryRelationalOperatorNode binRelOp =\r
+ (BinaryRelationalOperatorNode)andNode.getLeftOperand();\r
+\r
+ ValueNode operand = null;\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ /* If this predicate is scoped then one (and only one) of\r
+ * its operands should be scoped. Note that it's possible\r
+ * for an operand to be scoped to a non-ColumnReference\r
+ * value; if either operand is not a ColumnReference, then\r
+ * that operand must be the scoped operand.\r
+ */\r
+ operand = binRelOp.getLeftOperand();\r
+ boolean leftIsScoped =\r
+ !(operand instanceof ColumnReference) ||\r
+ ((ColumnReference)operand).isScoped();\r
+\r
+ operand = binRelOp.getRightOperand();\r
+ boolean rightIsScoped =\r
+ !(operand instanceof ColumnReference) ||\r
+ ((ColumnReference)operand).isScoped();\r
+\r
+ SanityManager.ASSERT(leftIsScoped ^ rightIsScoped,\r
+ "All scoped predicates should have exactly one scoped " +\r
+ "operand, but '" + binaryRelOpColRefsToString() +\r
+ "' has " + (leftIsScoped ? "TWO" : "NONE") + ".");\r
+ }\r
+\r
+ // Find the scoped operand and remap it.\r
+ operand = binRelOp.getLeftOperand();\r
+ if ((operand instanceof ColumnReference) &&\r
+ ((ColumnReference)operand).isScoped())\r
+ {\r
+ // Left operand is the scoped operand.\r
+ ((ColumnReference)operand).remapColumnReferences();\r
+ }\r
+ else\r
+ {\r
+ operand = binRelOp.getRightOperand();\r
+ if ((operand instanceof ColumnReference) &&\r
+ ((ColumnReference)operand).isScoped())\r
+ {\r
+ // Right operand is the scoped operand.\r
+ ((ColumnReference)operand).remapColumnReferences();\r
+ }\r
+\r
+ // Else scoped operand is not a ColumnReference, which\r
+ // means it can't (and doesn't need to) be remapped. So\r
+ // just fall through and return.\r
+ }\r
+\r
+ return true;\r
+ }\r
+\r
+ /**\r
+ * Return true if this predicate is scoped AND the scoped\r
+ * operand is a ColumnReference that points to a source result\r
+ * set. If the scoped operand is not a ColumnReference that\r
+ * points to a source result set then it must be pointing to\r
+ * some kind of expression, such as a literal (ex. 'strlit'),\r
+ * an aggregate value (ex. "count(*)"), or the result of a\r
+ * function (ex. "sin(i)") or operator (ex. "i+1").\r
+ *\r
+ * This method is used when pushing predicates to determine how\r
+ * far down the query tree a scoped predicate needs to be pushed\r
+ * to allow for successful evaluation of the scoped operand. If\r
+ * the scoped operand is not pointing to a source result set\r
+ * then it should not be pushed any further down tree. The reason\r
+ * is that evaluation of the expression to which the operand is\r
+ * pointing may depend on other values from the current level\r
+ * in the tree (ex. "sin(i)" depends on the value of "i", which\r
+ * could be a column at the predicate's current level). If we\r
+ * pushed the predicate further down, those values could become\r
+ * inaccessible, leading to execution-time errors.\r
+ *\r
+ * If, on the other hand, the scoped operand *is* pointing to\r
+ * a source result set, then we want to push it further down\r
+ * the tree until it reaches that result set, which allows\r
+ * evaluation of this predicate to occur as close to store as\r
+ * possible. This method doesn't actually do the push, it just\r
+ * returns "true" and then the caller can push as appropriate.\r
+ */\r
+ protected boolean isScopedToSourceResultSet()\r
+ throws StandardException\r
+ {\r
+ if (!scoped)\r
+ return false;\r
+\r
+ /* Note: right now the only predicates we scope are those\r
+ * which are join predicates and all scoped predicates will\r
+ * have the same relational operator as the predicates from\r
+ * which they were scoped. Thus if we get here, we know\r
+ * that andNode's leftOperand must be an instance of\r
+ * BinaryRelationalOperatorNode (and therefore the following\r
+ * cast is safe).\r
+ */\r
+ BinaryRelationalOperatorNode binRelOp =\r
+ (BinaryRelationalOperatorNode)andNode.getLeftOperand();\r
+\r
+ ValueNode operand = binRelOp.getLeftOperand();\r
+\r
+ /* If operand isn't a ColumnReference then is must be the\r
+ * scoped operand. This is because both operands have to\r
+ * be column references in order for scoping to occur (as\r
+ * per pushableToSubqueries()) and only the scoped operand\r
+ * can change (esp. can become a non-ColumnReference) as\r
+ * part of the scoping process. And since it's not a\r
+ * ColumnReference it can't be "a ColumnReference that\r
+ * points to a source result set", so return false.\r
+ */\r
+ if (!(operand instanceof ColumnReference))\r
+ return false;\r
+\r
+ /* If the operand is a ColumnReference and is scoped,\r
+ * then see if it is pointing to a ResultColumn whose\r
+ * expression is either another a CR or a Virtual\r
+ * ColumnNode. If it is then that operand applies\r
+ * to a source result set further down the tree and\r
+ * thus we return true.\r
+ */\r
+ ValueNode exp = null;\r
+ ColumnReference cRef = (ColumnReference)operand;\r
+ if (cRef.isScoped())\r
+ {\r
+ exp = cRef.getSource().getExpression();\r
+ return ((exp instanceof VirtualColumnNode) ||\r
+ (exp instanceof ColumnReference));\r
+ }\r
+\r
+ operand = binRelOp.getRightOperand();\r
+ if (!(operand instanceof ColumnReference))\r
+ return false;\r
+\r
+ cRef = (ColumnReference)operand;\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ // If we got here then the left operand was NOT the scoped\r
+ // operand; make sure the right one is scoped, then.\r
+ SanityManager.ASSERT(cRef.isScoped(),\r
+ "All scoped predicates should have exactly one scoped " +\r
+ "operand, but '" + binaryRelOpColRefsToString() +\r
+ "has NONE.");\r
+ }\r
+\r
+ exp = cRef.getSource().getExpression();\r
+ return ((exp instanceof VirtualColumnNode) ||\r
+ (exp instanceof ColumnReference));\r
+ }\r
+\r
+ /**\r
+ * Return whether or not this predicate corresponds to a legitimate\r
+ * relational operator.\r
+ *\r
+ * @return False if there is no relational operator for this predicate\r
+ * OR if this predicate is an internal "probe predicate" (in which\r
+ * case it "looks" like we have a relational operator but in truth\r
+ * it's a disguised IN-list operator). True otherwise.\r
+ */\r
+ protected boolean isRelationalOpPredicate()\r
+ {\r
+ /* The isRelationalOperator() method on the ValueNode\r
+ * interface tells us what we need to know, so all we have\r
+ * to do is call that method on the left child of our AND node.\r
+ * Note that BinaryRelationalOperatorNode.isRelationalOperator()\r
+ * includes logic to determine whether or not it (the BRON) is\r
+ * really a disguised IN-list operator--and if so, it will\r
+ * return false (which is what we want).\r
+ */\r
+ return andNode.getLeftOperand().isRelationalOperator();\r
+ }\r
+\r
+ /**\r
+ * Return whether or not this predicate is an IN-list probe\r
+ * predicate.\r
+ */\r
+ protected boolean isInListProbePredicate()\r
+ {\r
+ /* The isInListProbeNode() method on the ValueNode interface\r
+ * tells us what we need to know, so all we have to do is call\r
+ * that method on the left child of our AND node.\r
+ */\r
+ return andNode.getLeftOperand().isInListProbeNode();\r
+ }\r
+\r
+ /**\r
+ * If this predicate corresponds to an IN-list, return the underlying\r
+ * InListOperatorNode from which it was built. There are two forms\r
+ * to check for:\r
+ *\r
+ * 1. This predicate is an IN-list "probe predicate", in which case\r
+ * the underlying InListOpNode is stored within the binary relational\r
+ * operator that is the left operand of this predicate's AND node.\r
+ *\r
+ * 2. This predicate corresponds to an IN-list that could _not_ be\r
+ * transformed into a "probe predicate" (i.e. the IN-list contains\r
+ * one or more non-parameter, non-constant values). In that case\r
+ * the underlying InListOpNode is simply the left operand of\r
+ * this predicate's AND node.\r
+ *\r
+ * If this predicate does not correspond to an IN-list in any way,\r
+ * this method will return null.\r
+ */\r
+ protected InListOperatorNode getSourceInList()\r
+ {\r
+ return getSourceInList(false);\r
+ }\r
+\r
+ /**\r
+ * Does the work of getSourceInList() above, but can also be called\r
+ * directly with an argument to indicate whether or not we should\r
+ * limit ourselves to probe predicates.\r
+ *\r
+ * @param probePredOnly If true, only get the source IN list for this\r
+ * predicate *if* it is an IN-list probe predicate. If false,\r
+ * return the underlying InListOperatorNode (if it exists) regardless\r
+ * of whether this is a probe predicate or an un-transformed IN-list\r
+ * pred.\r
+ * \r
+ * @return Underlying InListOp for this predicate (depending on\r
+ * the value of probePredOnly), or null if this predicate does\r
+ * not correspond to an IN-list in any way.\r
+ */\r
+ protected InListOperatorNode getSourceInList(boolean probePredOnly)\r
+ {\r
+ ValueNode vn = andNode.getLeftOperand();\r
+ if (isInListProbePredicate())\r
+ return ((BinaryRelationalOperatorNode)vn).getInListOp();\r
+\r
+ if (probePredOnly)\r
+ return null;\r
+\r
+ if (vn instanceof InListOperatorNode)\r
+ return (InListOperatorNode)vn;\r
+\r
+ return null;\r
+ }\r
+}\r