use of org.apache.derby.iapi.util.JBitSet in project derby by apache.
the class BinaryRelationalOperatorNode method getScopedOperand.
/**
* Take a ResultSetNode and return a column reference that is scoped for
* for the received ResultSetNode, where "scoped" means that the column
* reference points to a specific column in the RSN. This is used for
* remapping predicates from an outer query down to a subquery.
*
* For example, assume we have the following query:
*
* select * from
* (select i,j from t1 union select i,j from t2) X1,
* (select a,b from t3 union select a,b from t4) X2
* where X1.j = X2.b;
*
* Then assume that this BinaryRelationalOperatorNode represents the
* "X1.j = X2.b" predicate and that the childRSN we received as a
* parameter represents one of the subqueries to which we want to push
* the predicate; let's say it's:
*
* select i,j from t1
*
* Then what we want to do in this method is map one of the operands
* X1.j or X2.b (depending on the 'whichSide' parameter) to the childRSN,
* if possible. Note that in our example, "X2.b" should _NOT_ be mapped
* because it doesn't apply to the childRSN for the subquery "select i,j
* from t1"; thus we should leave it as it is. "X1.j", however, _does_
* need to be scoped, and so this method will return a ColumnReference
* pointing to "T1.j" (or whatever the corresponding column in T1 is).
*
* ASSUMPTION: We should only get to this method if we know that
* exactly one operand in the predicate to which this operator belongs
* can and should be mapped to the received childRSN.
*
* @param whichSide The operand are we trying to scope (LEFT or RIGHT)
* @param parentRSNsTables Set of all table numbers referenced by
* the ResultSetNode that is _parent_ to the received childRSN.
* We need this to make sure we don't scope the operand to a
* ResultSetNode to which it doesn't apply.
* @param childRSN The result set node to which we want to create
* a scoped predicate.
* @param whichRC If not -1 then this tells us which ResultColumn
* in the received childRSN we need to use for the scoped predicate;
* if -1 then the column position of the scoped column reference
* will be stored in this array and passed back to the caller.
* @return A column reference scoped to the received childRSN, if possible.
* If the operand is a ColumnReference that is not supposed to be scoped,
* we return a _clone_ of the reference--this is necessary because the
* reference is going to be pushed to two places (left and right children
* of the parentRSN) and if both children are referencing the same
* instance of the column reference, they'll interfere with each other
* during optimization.
*/
ValueNode getScopedOperand(int whichSide, JBitSet parentRSNsTables, ResultSetNode childRSN, int[] whichRC) throws StandardException {
ResultColumn rc;
ColumnReference cr = whichSide == LEFT ? (ColumnReference) leftOperand : (ColumnReference) rightOperand;
/* When we scope a predicate we only scope one side of it--the
* side that is to be evaluated against childRSN. We figure out
* if "cr" is that side by using table numbers, as seen below.
* This means that for every scoped predicate there will be one
* operand that is scoped and one operand that is not scoped.
* When we get here for the operand that will not be scoped,
* we'll just return a clone of that operand. So in the example
* mentioned above, the scoped predicate for the left child of
* X1 would be
*
* T1.j <scoped> = X2.b <clone>
*
* That said, the first thing we need to do is see if this
* ColumnReference is supposed to be scoped for childRSN. We
* do that by figuring out what underlying base table the column
* reference is pointing to and then seeing if that base table
* is included in the list of table numbers from the parentRSN.
*/
JBitSet crTables = new JBitSet(parentRSNsTables.size());
BaseTableNumbersVisitor btnVisitor = new BaseTableNumbersVisitor(crTables);
cr.accept(btnVisitor);
/* If the column reference in question is not intended for
* the received result set node, just leave the operand as
* it is (i.e. return a clone). In the example mentioned at
* the start of this method, this will happen when the operand
* is X2.b and childRSN is either "select i,j from t1" or
* "select i,j from t2", in which case the operand does not
* apply to childRSN. When we get here and try to map the
* "X1.j" operand, though, the following "contains" check will
* return true and thus we can go ahead and return a scoped
* version of that operand.
*/
if (!parentRSNsTables.contains(crTables))
return (ColumnReference) cr.getClone();
/* Find the target ResultColumn in the received result set. At
* this point we know that we do in fact need to scope the column
* reference for childRSN, so go ahead and do it. The way in
* which we get the scope target column differs depending on
* if childRSN corresponds to the left or right child of the
* UNION node. Before explaining that, though, note that it's
* not good enough to just search for the target column by
* name. The reason is that it's possible the name provided
* for the column reference to be scoped doesn't match the
* name of the actual underlying column. Ex.
*
* select * from
* (select i,j from t1 union select i,j from t2) X1 (x,y),
* (select a,b from t3 union select a,b from t4) X2
* where X1.x = X2.b;
*
* If we were scoping "X1.x" and we searched for "x" in the
* childRSN "select i,j from t1" we wouldn't find it.
*
* It is similarly incorrect to search for the target column
* by position (DERBY-1633). This is a bit more subtle, but
* if the child to which we're scoping is a subquery whose RCL
* does not match the column ordering of the RCL for cr's source
* result set, then searching by column position can yield the
* wrong results, as well. For a detailed example of how this
* can happen, see the fix description attached to DERBY-1633.
*
* So how do we find the target column, then? As mentioned
* above, the way in which we get the scope target column
* differs depending on if childRSN corresponds to the left
* or right child of the parent UNION node. And that said,
* we can tell if we're scoping a left child by looking at
* "whichRC" argument: if it is -1 then we know we're scoping
* to the left child of a Union; otherwise we're scoping to
* the right child.
*/
if (whichRC[0] == -1) {
/*
* For the left side we start by figuring out what the source
* result set and column position for "cr" are. Then, since
* a) cr must be pointing to a result column in the parentRSN's
* ResultColumnList, b) we know that the parent RSN is a
* SetOperatorNode (at least for now, since we only get here
* for Union nodes), and c) SetOpNode's RCLs are built from the
* left child's RCL (see bindResultColumns() in SetOperatorNode),
* we know that if we search the child's RCL for a reference
* whose source result column is the same as cr's source result
* column, we'll find a match. Once found, the position of the
* matching column w.r.t childRSN's RCL will be stored in the
* whichRC parameter.
*/
// Find the source result set and source column position of cr.
int[] sourceColPos = new int[] { -1 };
ResultSetNode sourceRSN = cr.getSourceResultSet(sourceColPos);
if (SanityManager.DEBUG) {
/* We assumed that if we made it here "cr" was pointing
* to a base table somewhere down the tree. If that's
* true then sourceRSN won't be null. Make sure our
* assumption was correct.
*/
SanityManager.ASSERT(sourceRSN != null, "Failed to find source result set when trying to " + "scope column reference '" + cr.getTableName() + "." + cr.getColumnName());
}
// Now search for the corresponding ResultColumn in childRSN.
rc = childRSN.getResultColumns().getResultColumn(sourceColPos[0], sourceRSN, whichRC);
} else {
/*
* For the right side the story is slightly different. If we were
* to search the right child's RCL for a reference whose source
* result column was the same as cr's, we wouldn't find it. This
* is because cr's source result column comes from the left child's
* RCL and thus the right child doesn't know about it. That said,
* though, for set operations like UNION, the left and right RCL's
* are correlated by position--i.e. the operation occurs between
* the nth column in the left RCL and the nth column in the right
* RCL. So given that we will already have found the scope target
* in the left child's RCL at the position in whichRC, we know that
* that scope target for the right child's RCL is simply the
* whichRC'th column in that RCL.
*/
rc = childRSN.getResultColumns().getResultColumn(whichRC[0]);
}
// then we shouldn't have made it this far.
if (SanityManager.DEBUG) {
SanityManager.ASSERT(rc != null, "Failed to locate scope target result column when trying to " + "scope operand '" + cr.getTableName() + "." + cr.getColumnName() + "'.");
}
if (rc.getExpression() instanceof ColumnReference) {
/* We create a clone of the column reference and mark
* the clone as "scoped" so that we can do the right
* thing when it comes time to remap the predicate;
* see Predicate.remapScopedPred() for more.
*/
ColumnReference cRef = (ColumnReference) ((ColumnReference) rc.getExpression()).getClone();
cRef.markAsScoped();
return cRef;
}
/* Else just return rc's expression. This means the scoped
* predicate will have one operand that is _not_ a column
* reference--but that's okay, so long as we account for
* that when pushing/remapping the scoped predicate down
* the query tree (see esp. "isScopedToSourceResultSet()"
* in Predicate.java).
*/
return rc.getExpression();
}
use of org.apache.derby.iapi.util.JBitSet in project derby by apache.
the class BinaryRelationalOperatorNode method isQualifier.
/**
* @see RelationalOperator#isQualifier
*
* @exception StandardException Thrown on error
*/
public boolean isQualifier(Optimizable optTable, boolean forPush) throws StandardException {
/* If this rel op is for an IN-list probe predicate then we never
* treat it as a qualifer. The reason is that if we treat it as
* a qualifier then we could end up generating it as a qualifier,
* which would lead to the generation of an equality qualifier
* of the form "col = <val>" (where <val> is the first value in
* the IN-list). That would lead to wrong results (missing rows)
* because that restriction is incorrect.
*/
if (isInListProbeNode())
return false;
FromTable ft;
ValueNode otherSide = null;
JBitSet tablesReferenced;
ColumnReference cr;
boolean found = false;
boolean walkSubtree = true;
ft = (FromTable) optTable;
if (leftOperand instanceof ColumnReference) {
/*
** The left operand is a column reference.
** Is it the correct column?
*/
cr = (ColumnReference) leftOperand;
if (valNodeReferencesOptTable(cr, ft, forPush, walkSubtree)) {
otherSide = rightOperand;
found = true;
}
walkSubtree = false;
}
if ((!found) && (rightOperand instanceof ColumnReference)) {
/*
** The right operand is a column reference.
** Is it the correct column?
*/
cr = (ColumnReference) rightOperand;
if (valNodeReferencesOptTable(cr, ft, forPush, walkSubtree)) {
otherSide = leftOperand;
found = true;
}
}
/* Have we found a ColumnReference on either side? */
if (!found) {
/*
** Neither side is a ColumnReference to the table we're looking
** for, so it can't be a Qualifier
*/
return false;
}
/*
** One side is a ColumnReference to the correct table. It is a
** Qualifier if the other side does not refer to the table we are
** optimizing.
*/
return !valNodeReferencesOptTable(otherSide, ft, forPush, true);
}
use of org.apache.derby.iapi.util.JBitSet in project derby by apache.
the class SubqueryNode method pushNewPredicate.
/**
* Transform:
* expression QuantifiedOperator (select x from ...)
* into
* (select true from .. where expression <BinaryComparisonOperator> x ...)
* IS [NOT] NULL
*
* or, if we have an aggregate:
* (select true from
* (select AGG(x) from ...)
* where expression <BinaryComparisonOperator> x ...)
* IS [NOT] NULL
*
* For ANY and IN subqueries:
* o We generate an IS NULL above the SubqueryNode and return the top of
* the new tree to the caller.
* o The operator in the new predicate that is added to the subquery
* will correspond to the operator that modifies the ANY.
* (eg, = for = ANY, with = for IN.)
* For ALL and NOT IN subqueries:
* o We generate an IS NOT NULL above the SubqueryNode and return the top of
* the new tree to the caller.
* o The operator in the new predicate that is added to the subquery
* will be a BinaryAllOperatorNode whose bcoNodeType corresponds to
* the negation of the operator that modifies the ALL.
* (eg, <> for = ALL, with <> for NOT IN.)
*
* NOTE: This method is called after the underlying subquery has been
* preprocessed, so we build a new Predicate, not just a new expression.
*
* @param numTables Number of tables in DML Statement
*
* @return UnaryComparisonOperatorNode An IS [NOT] NULL above the
* transformed subquery.
*
* @exception StandardException Thrown on error
*/
private UnaryComparisonOperatorNode pushNewPredicate(int numTables) throws StandardException {
AndNode andNode;
JBitSet tableMap;
Predicate predicate;
ResultColumn firstRC;
ResultColumnList resultColumns;
UnaryComparisonOperatorNode ucoNode = null;
ValueNode rightOperand;
ContextManager cm = getContextManager();
/* We have to ensure that the resultSet immediately under us has
* a PredicateList, otherwise we can't push the predicate down.
*/
resultSet = resultSet.ensurePredicateList(numTables);
/* RESOLVE - once we understand how correlated columns will work,
* we probably want to mark leftOperand as a correlated column
*/
resultColumns = resultSet.getResultColumns();
/*
** Create a new PR node. Put it over the original subquery. resulSet
** is now the new PR. We give the chance that things under the PR node
** can be materialized. See beetle 4373.
*/
ResultColumnList newRCL = resultColumns.copyListAndObjects();
newRCL.genVirtualColumnNodes(resultSet, resultColumns);
resultSet = new ProjectRestrictNode(// child
resultSet, // result columns
newRCL, // restriction
null, // restriction list
null, // project subqueries
null, // restrict subqueries
null, null, cm);
resultColumns = newRCL;
firstRC = resultColumns.elementAt(0);
rightOperand = firstRC.getExpression();
BinaryComparisonOperatorNode bcoNode = getNewJoinCondition(leftOperand, rightOperand);
ValueNode andLeft = bcoNode;
/* For NOT IN or ALL, and if either side of the comparison is nullable, and the
* subquery can not be flattened (because of that), we need to add IS NULL node
* on top of the nullables, such that the behavior is (beetle 5173):
*
* (1) If we have nulls in right operand, no row is returned.
* (2) If subquery result is empty before applying join predicate, every
* left row (including NULLs) is returned.
* (3) Otherwise, return {all left row} - {NULLs}
*/
if (isNOT_IN() || isALL()) {
boolean leftNullable = leftOperand.getTypeServices().isNullable();
boolean rightNullable = rightOperand.getTypeServices().isNullable();
if (leftNullable || rightNullable) {
/* Create a normalized structure.
*/
BooleanConstantNode falseNode = new BooleanConstantNode(false, cm);
OrNode newOr = new OrNode(bcoNode, falseNode, cm);
newOr.postBindFixup();
andLeft = newOr;
if (leftNullable) {
UnaryComparisonOperatorNode leftIsNull = new IsNullNode(leftOperand, false, cm);
leftIsNull.bindComparisonOperator();
newOr = new OrNode(leftIsNull, andLeft, cm);
newOr.postBindFixup();
andLeft = newOr;
}
if (rightNullable) {
UnaryComparisonOperatorNode rightIsNull = new IsNullNode(rightOperand, false, cm);
rightIsNull.bindComparisonOperator();
newOr = new OrNode(rightIsNull, andLeft, cm);
newOr.postBindFixup();
andLeft = newOr;
}
}
}
/* Place an AndNode above the <BinaryComparisonOperator> */
andNode = new AndNode(andLeft, getTrueNode(), cm);
/* Build the referenced table map for the new predicate */
tableMap = new JBitSet(numTables);
andNode.postBindFixup();
/* Put the AndNode under a Predicate */
predicate = new Predicate(andNode, tableMap, cm);
predicate.categorize();
/* Push the new Predicate to the subquery's list */
resultSet = resultSet.addNewPredicate(predicate);
/* Clean up the leftOperand and subquery ResultColumn */
leftOperand = null;
firstRC.setType(getTypeServices());
firstRC.setExpression(getTrueNode());
/* Add the IS [NOT] NULL above the SubqueryNode */
switch(subqueryType) {
case IN_SUBQUERY:
case EQ_ANY_SUBQUERY:
case NE_ANY_SUBQUERY:
case LE_ANY_SUBQUERY:
case LT_ANY_SUBQUERY:
case GE_ANY_SUBQUERY:
case GT_ANY_SUBQUERY:
ucoNode = new IsNullNode(this, true, cm);
break;
case NOT_IN_SUBQUERY:
case EQ_ALL_SUBQUERY:
case NE_ALL_SUBQUERY:
case LE_ALL_SUBQUERY:
case LT_ALL_SUBQUERY:
case GE_ALL_SUBQUERY:
case GT_ALL_SUBQUERY:
ucoNode = new IsNullNode(this, false, cm);
break;
default:
if (SanityManager.DEBUG) {
SanityManager.NOTREACHED();
}
}
ucoNode.bindComparisonOperator();
return ucoNode;
}
use of org.apache.derby.iapi.util.JBitSet in project derby by apache.
the class UnaryComparisonOperatorNode method getOperand.
/**
* @see RelationalOperator#getOperand
*/
public ValueNode getOperand(ColumnReference cRef, int refSetSize, boolean otherSide) {
if (otherSide)
// there is no "other" side for Unary, so just return null.
return null;
ColumnReference cr;
if (operand instanceof ColumnReference) {
/*
** The operand is a column reference.
** Is it the correct column?
*/
JBitSet cRefTables = new JBitSet(refSetSize);
JBitSet crTables = new JBitSet(refSetSize);
BaseTableNumbersVisitor btnVis = new BaseTableNumbersVisitor(crTables);
cr = (ColumnReference) operand;
try {
cr.accept(btnVis);
btnVis.setTableMap(cRefTables);
cRef.accept(btnVis);
} catch (StandardException se) {
if (SanityManager.DEBUG) {
SanityManager.THROWASSERT("Failed when trying to " + "find base table number for column reference check:", se);
}
}
crTables.and(cRefTables);
if (crTables.getFirstSetBit() != -1) {
/*
** The table is correct, how about the column position?
*/
if (cr.getSource().getColumnPosition() == cRef.getColumnNumber()) {
/* We've found the correct column - return it. */
return operand;
}
}
}
/* Not the column we're looking for */
return null;
}
use of org.apache.derby.iapi.util.JBitSet in project derby by apache.
the class ValueNode method getTablesReferenced.
/**
* Get a bit map of table references in this expression
*
* @return A bit map of table numbers referred to in this expression
*
* @exception StandardException Thrown on error
*/
JBitSet getTablesReferenced() throws StandardException {
ReferencedTablesVisitor rtv = new ReferencedTablesVisitor(new JBitSet(0));
accept(rtv);
return rtv.getTableMap();
}
Aggregations