use of org.apache.derby.iapi.util.JBitSet in project derby by apache.
the class JoinNode method grabJoinPredicates.
private void grabJoinPredicates(PredicateList outerPredicateList) throws StandardException {
FromTable leftFromTable = (FromTable) leftResultSet;
FromTable rightFromTable = (FromTable) rightResultSet;
JBitSet leftReferencedTableMap = leftFromTable.getReferencedTableMap();
JBitSet rightReferencedTableMap = rightFromTable.getReferencedTableMap();
// Walk outerPredicateList backwards due to possible deletes
for (int index = outerPredicateList.size() - 1; index >= 0; index--) {
Predicate predicate = outerPredicateList.elementAt(index);
if (!predicate.getPushable()) {
continue;
}
JBitSet curBitSet = predicate.getReferencedSet();
/* Do we have a match? */
JBitSet innerBitSet = (JBitSet) rightReferencedTableMap.clone();
innerBitSet.or(leftReferencedTableMap);
if (innerBitSet.contains(curBitSet)) {
/* Add the matching predicate to the push list */
joinPredicates.addPredicate(predicate);
/* Remap all of the ColumnReferences to point to the
* source of the values.
* The tree is something like:
* PRN1
* |
* JN (this)
* / \
* PRN2 PRN3
* | |
* FBT1 FBT2
*
* The ColumnReferences start off pointing to the RCL off of
* PRN1. For optimization, we want them to point to the
* RCL off of PRN2 or PRN3. In order to do that, we remap them
* twice here. If optimization pushes them down to the
* base table, it will remap them again.
*/
RemapCRsVisitor rcrv = new RemapCRsVisitor(true);
predicate.getAndNode().accept(rcrv);
predicate.getAndNode().accept(rcrv);
/* Remove the matching predicate from the outer list */
outerPredicateList.removeElementAt(index);
}
}
}
use of org.apache.derby.iapi.util.JBitSet in project derby by apache.
the class JoinNode method pushExpressionsToLeft.
protected void pushExpressionsToLeft(PredicateList outerPredicateList) throws StandardException {
FromTable leftFromTable = (FromTable) leftResultSet;
JBitSet leftReferencedTableMap = leftFromTable.getReferencedTableMap();
// Walk outerPredicateList backwards due to possible deletes
for (int index = outerPredicateList.size() - 1; index >= 0; index--) {
Predicate predicate = outerPredicateList.elementAt(index);
if (!predicate.getPushable()) {
continue;
}
JBitSet curBitSet = predicate.getReferencedSet();
/* Do we have a match? */
if (leftReferencedTableMap.contains(curBitSet)) {
/* Add the matching predicate to the push list */
getLeftPredicateList().addPredicate(predicate);
/* Remap all of the ColumnReferences to point to the
* source of the values.
* The tree is something like:
* PRN1
* |
* JN (this)
* / \
* PRN2 PRN3
* | |
* FBT1 FBT2
*
* The ColumnReferences start off pointing to the RCL off of
* PRN1. For optimization, we want them to point to the
* RCL off of PRN2. In order to do that, we remap them
* twice here. If optimization pushes them down to the
* base table, it will remap them again.
*/
RemapCRsVisitor rcrv = new RemapCRsVisitor(true);
predicate.getAndNode().accept(rcrv);
predicate.getAndNode().accept(rcrv);
/* Remove the matching predicate from the outer list */
outerPredicateList.removeElementAt(index);
}
}
}
use of org.apache.derby.iapi.util.JBitSet in project derby by apache.
the class SelectNode method preprocess.
/**
* Put a ProjectRestrictNode on top of each FromTable in the FromList.
* ColumnReferences must continue to point to the same ResultColumn, so
* that ResultColumn must percolate up to the new PRN. However,
* that ResultColumn will point to a new expression, a VirtualColumnNode,
* which points to the FromTable and the ResultColumn that is the source for
* the ColumnReference.
* (The new PRN will have the original of the ResultColumnList and
* the ResultColumns from that list. The FromTable will get shallow copies
* of the ResultColumnList and its ResultColumns. ResultColumn.expression
* will remain at the FromTable, with the PRN getting a new
* VirtualColumnNode for each ResultColumn.expression.)
* We then project out the non-referenced columns. If there are no referenced
* columns, then the PRN's ResultColumnList will consist of a single ResultColumn
* whose expression is 1.
*
* @param numTables The number of tables in the DML Statement
* @param gbl The outer group by list, if any
* @param fl The from list, if any
*
* @return The generated ProjectRestrictNode atop the original FromTable.
*
* @exception StandardException Thrown on error
*/
@Override
ResultSetNode preprocess(int numTables, GroupByList gbl, FromList fl) throws StandardException {
ResultSetNode newTop = this;
/* Put the expression trees in conjunctive normal form.
* NOTE - This needs to occur before we preprocess the subqueries
* because the subquery transformations assume that any subquery operator
* negation has already occurred.
*/
whereClause = normExpressions(whereClause);
// DERBY-3257. We need to normalize the having clause as well, because
// preProcess expects CNF.
havingClause = normExpressions(havingClause);
/**
* This method determines if (1) the query is a LOJ, and (2) if the LOJ is a candidate for
* reordering (i.e., linearization). The condition for LOJ linearization is:
* 1. either LOJ or ROJ in the fromList, i.e., no INNER, NO FULL JOINs
* 2. ON clause must be equality join between left and right operands and in CNF (i.e., AND is allowed)
*/
boolean anyChange = fromList.LOJ_reorderable(numTables);
if (anyChange) {
FromList afromList = new FromList(getOptimizerFactory().doJoinOrderOptimization(), getContextManager());
bindExpressions(afromList);
fromList.bindResultColumns(afromList);
}
/* Preprocess the fromList. For each FromTable, if it is a FromSubquery
* then we will preprocess it, replacing the FromSubquery with a
* ProjectRestrictNode. If it is a FromBaseTable, then we will generate
* the ProjectRestrictNode above it.
*/
fromList.preprocess(numTables, groupByList, whereClause);
/* selectSubquerys is always allocated at bind() time */
if (SanityManager.DEBUG) {
SanityManager.ASSERT(selectSubquerys != null, "selectSubquerys is expected to be non-null");
}
/* Preprocess the RCL after the from list so that
* we can flatten/optimize any subqueries in the
* select list.
*/
getResultColumns().preprocess(numTables, fromList, whereSubquerys, wherePredicates);
/* Preprocess the expressions. (This is necessary for subqueries.
* This is also where we do tranformations such as for LIKE.)
*
* NOTE: We do this after preprocessing the fromList so that, for
* quantified subqueries, the join expression with the outer
* expression will be pushable (to be pushable, the ColumnReference
* has to point to a VirtualColumnNode, and not to a BaseColumnNode).
*/
if (whereClause != null) {
// WHERE EXISTS subqueries.
if (whereSubquerys != null) {
whereSubquerys.markWhereSubqueries();
}
whereClause = whereClause.preprocess(numTables, fromList, whereSubquerys, wherePredicates);
}
/* Preprocess the group by list too. We need to compare
* expressions in the group by list with the select list and we
* can't rewrite one and not the other.
*/
if (groupByList != null) {
groupByList.preprocess(numTables, fromList, whereSubquerys, wherePredicates);
}
if (havingClause != null) {
// DERBY-3257
// Mark subqueries that are part of the having clause as
// such so we can avoid flattenning later. Having subqueries
// cannot be flattened because we cannot currently handle
// column references at the same source level.
// DERBY-3257 required we normalize the having clause which
// triggered flattening because SubqueryNode.underTopAndNode
// became true after normalization. We needed another way to
// turn flattening off. Perhaps the long term solution is
// to avoid this restriction all together but that was beyond
// the scope of this bugfix.
havingSubquerys.markHavingSubqueries();
havingClause = havingClause.preprocess(numTables, fromList, havingSubquerys, wherePredicates);
}
/* Pull apart the expression trees */
if (whereClause != null) {
wherePredicates.pullExpressions(numTables, whereClause);
whereClause = null;
}
/* RESOLVE - Where should we worry about expression pull up for
* expensive predicates?
*/
// Flatten any flattenable FromSubquerys or JoinNodes
fromList.flattenFromTables(getResultColumns(), wherePredicates, whereSubquerys, groupByList, havingClause);
if (wherePredicates != null && wherePredicates.size() > 0 && fromList.size() > 0) {
// Perform various forms of transitive closure on wherePredicates
if (fromList.size() > 1) {
performTransitiveClosure(numTables);
}
for (int i = 0; i < qec.size(); i++) {
final OrderByList obl = qec.getOrderByList(i);
if (obl != null) {
// Remove constant columns from order by list. Constant
// columns are ones that have equality comparisons with
// constant expressions (e.g. x = 3)
obl.removeConstantColumns(wherePredicates);
/*
** It's possible for the order by list to shrink to
** nothing as a result of removing constant columns. If
** this happens, get rid of the list entirely.
*/
if (obl.size() == 0) {
qec.setOrderByList(i, null);
getResultColumns().removeOrderByColumns();
}
}
}
}
/* A valid group by without any aggregates or a having clause
* is equivalent to a distinct without the group by. We do the transformation
* in order to simplify the group by code.
*/
if (groupByList != null && havingClause == null && selectAggregates.isEmpty() && whereAggregates.isEmpty()) {
isDistinct = true;
groupByList = null;
wasGroupBy = true;
}
/* Consider distinct elimination based on a uniqueness condition.
* In order to do this:
* o All top level ColumnReferences in the select list are
* from the same base table. (select t1.c1, t2.c2 + t3.c3 is
* okay - t1 is the table of interest.)
* o If the from list is a single table then the columns in the
* select list plus the columns in the where clause that are
* in = comparisons with constants or parameters must be a
* superset of any unique index.
* o If the from list has multiple tables then at least 1 table
* meet the following - the set of columns in = comparisons
* with constants or parameters is a superset of any unique
* index on the table. All of the other tables must meet
* the following - the set of columns in = comparisons with
* constants, parameters or join columns is a superset of
* any unique index on the table. If the table from which
* the columns in the select list are coming from is in this
* later group then the rule for it is to also include
* the columns in the select list in the set of columns that
* needs to be a superset of the unique index. Whew!
*/
if (isDistinct && groupByList == null) {
int distinctTable = getResultColumns().allTopCRsFromSameTable();
if (distinctTable != -1) {
if (fromList.returnsAtMostSingleRow(getResultColumns(), whereClause, wherePredicates, getDataDictionary())) {
isDistinct = false;
}
}
for (int i = 0; i < qec.size(); i++) {
/* If we were unable to eliminate the distinct and we have
* an order by then we can consider eliminating the sort for
* the order by. All of the columns in the order by list must
* be ascending in order to do this. There are 2 cases:
* o The order by list is an in order prefix of the columns
* in the select list. In this case the output of the
* sort from the distinct will be in the right order
* so we simply eliminate the order by list.
* o The order by list is a subset of the columns in the
* the select list. In this case we need to reorder the
* columns in the select list so that the ordering columns
* are an in order prefix of the select list and put a PRN
* above the select so that the shape of the result set
* is as expected.
*/
final OrderByList obl = qec.getOrderByList(i);
if (isDistinct && obl != null && obl.allAscending()) {
/* Order by list currently restricted to columns in select
* list, so we will always eliminate the order by here.
*/
if (obl.isInOrderPrefix(getResultColumns())) {
qec.setOrderByList(i, null);
} else {
/* Order by list is not an in order prefix of the
* select list so we must reorder the columns in the
* the select list to match the order by list and
* generate the PRN above us to preserve the expected
* order.
*/
newTop = genProjectRestrictForReordering();
obl.resetToSourceRCs();
setResultColumns(obl.reorderRCL(getResultColumns()));
newTop.getResultColumns().removeOrderByColumns();
qec.setOrderByList(i, null);
}
orderByAndDistinctMerged = true;
}
}
}
/*
* Push predicates that are pushable.
*
* NOTE: We pass the wherePredicates down to the new PRNs here,
* so they can pull any clauses and possibily push them down further.
* NOTE: We wait until all of the FromTables have been preprocessed
* until we attempt to push down predicates, because we cannot push down
* a predicate if the immediate source of any of its column references
* is not a ColumnReference or a VirtualColumnNode.
*/
fromList.pushPredicates(wherePredicates);
/* Set up the referenced table map */
setReferencedTableMap(new JBitSet(numTables));
int flSize = fromList.size();
for (int index = 0; index < flSize; index++) {
getReferencedTableMap().or(((FromTable) fromList.elementAt(index)).getReferencedTableMap());
}
/* Copy the referenced table map to the new tree top, if necessary */
if (newTop != this) {
newTop.setReferencedTableMap((JBitSet) getReferencedTableMap().clone());
}
if (qec.getOrderByList(0) != null) {
// only relevant for first one
// Collect window function calls and in-lined window definitions
// contained in them from the orderByList.
CollectNodesVisitor<WindowFunctionNode> cnvw = new CollectNodesVisitor<WindowFunctionNode>(WindowFunctionNode.class);
qec.getOrderByList(0).accept(cnvw);
for (WindowFunctionNode wfn : cnvw.getList()) {
windowFuncCalls.add(wfn);
if (wfn.getWindow() instanceof WindowDefinitionNode) {
// Window function call contains an inline definition, add
// it to our list of windows.
windows = addInlinedWindowDefinition(windows, wfn);
} else {
if (SanityManager.DEBUG) {
SanityManager.ASSERT(false, "a window reference, should be bound already");
}
}
}
}
return newTop;
}
use of org.apache.derby.iapi.util.JBitSet in project derby by apache.
the class FromBaseTable method uniqueJoin.
/**
* @see org.apache.derby.iapi.sql.compile.Optimizable#uniqueJoin
*/
@Override
public double uniqueJoin(OptimizablePredicateList predList) throws StandardException {
double retval = -1.0;
PredicateList pl = (PredicateList) predList;
int numColumns = getTableDescriptor().getNumberOfColumns();
int tableNo = getTableNumber();
// This is supposed to be an array of table numbers for the current
// query block. It is used to determine whether a join is with a
// correlation column, to fill in eqOuterCols properly. We don't care
// about eqOuterCols, so just create a zero-length array, pretending
// that all columns are correlation columns.
int[] tableNumbers = new int[0];
JBitSet[] tableColMap = new JBitSet[1];
tableColMap[0] = new JBitSet(numColumns + 1);
pl.checkTopPredicatesForEqualsConditions(tableNo, null, tableNumbers, tableColMap, false);
if (supersetOfUniqueIndex(tableColMap)) {
retval = getBestAccessPath().getCostEstimate().singleScanRowCount();
}
return retval;
}
use of org.apache.derby.iapi.util.JBitSet in project derby by apache.
the class FromList method returnsAtMostSingleRow.
/**
* This method is used for both subquery flattening and distinct
* elimination based on a uniqueness condition. For subquery
* flattening we want to make sure that the query block
* will return at most 1 row. For distinct elimination we
* want to make sure that the query block will not return
* any duplicates.
* This is true if every table in the from list is
* (a base table and the set of columns from the table that
* are in equality comparisons with expressions that do not include columns
* from the same table is a superset of any unique index
* on the table) or an EXISTS FBT. In addition, at least 1 of the tables
* in the list has a set of columns in equality comparisons with expressions
* that do not include column references from the same query block
* is a superset of a unique index
* on that table. (This ensures that the query block will onlyr
* return a single row.)
* This method is expected to be called after normalization and
* after the from list has been preprocessed.
* It can be called both before and after the predicates have
* been pulled from the where clause.
* The algorithm for this is as follows
*
* If any table in the query block is not a base table, give up.
* For each table in the query
* Ignore exists table since they can only produce one row
*
* create a matrix of tables and columns from the table (tableColMap)
* (this is used to keep track of the join columns and constants
* that can be used to figure out whether the rows from a join
* or in a select list are distinct based on unique indexes)
*
* create an array of columns from the table(eqOuterCol)
* (this is used to determine that only one row will be returned
* from a join)
*
* if the current table is the table for the result columns
* set the result columns in the eqOuterCol and tableColMap
* (if these columns are a superset of a unique index and
* all joining tables result in only one row, the
* results will be distinct)
* go through all the predicates and update tableColMap and
* eqOuterCol with join columns and correlation variables,
* parameters and constants
* since setting constants, correlation variables and parameters,
* reduces the number of columns required for uniqueness in a
* multi-column index, they are set for all the tables (if the
* table is not the result table, in this case only the column of the
* result table is set)
* join columns are just updated for the column in the row of the
* joining table.
*
* check if the marked columns in tableColMap are a superset of a unique
* index
* (This means that the join will only produce 1 row when joined
* with 1 row of another table)
* check that there is a least one table for which the columns in
* eqOuterCol(i.e. constant values) are a superset of a unique index
* (This quarantees that there will be only one row selected
* from this table).
*
* Once all tables have been evaluated, check that all the tables can be
* joined by unique index or will have only one row
*
* @param rcl If non-null, the RCL from the query block.
* If non-null for subqueries, then entry can
* be considered as part of an = comparison.
* @param whereClause The WHERE clause to consider.
* @param wherePredicates The predicates that have already been
* pulled from the WHERE clause.
* @param dd The DataDictionary to use.
*
* @return Whether or not query block will return
* at most 1 row for a subquery, no duplicates
* for a distinct.
*
* @exception StandardException Thrown on error
*/
boolean returnsAtMostSingleRow(ResultColumnList rcl, ValueNode whereClause, PredicateList wherePredicates, DataDictionary dd) throws StandardException {
boolean satisfiesOuter = false;
int[] tableNumbers;
ColumnReference additionalCR = null;
PredicateList predicatesTemp;
predicatesTemp = new PredicateList(getContextManager());
for (Predicate p : wherePredicates) {
predicatesTemp.addPredicate(p);
}
/* When considering subquery flattening, we are interested
* in the 1st (and only) entry in the RCL. (The RCL will be
* null if result column is not of interest for subquery flattening.)
* We are interested in all entries in the RCL for distinct
* elimination.
*/
if (rcl != null) {
ResultColumn rc = rcl.elementAt(0);
if (rc.getExpression() instanceof ColumnReference) {
additionalCR = (ColumnReference) rc.getExpression();
}
}
/* First see if all entries are FromBaseTables. No point
* in continuing if not.
*/
int size = size();
for (int index = 0; index < size; index++) {
FromTable fromTable = (FromTable) elementAt(index);
if (!(fromTable instanceof ProjectRestrictNode)) {
return false;
}
ProjectRestrictNode prn = (ProjectRestrictNode) fromTable;
if (!(prn.getChildResult() instanceof FromBaseTable)) {
return false;
}
FromBaseTable fbt = (FromBaseTable) prn.getChildResult();
//
if (fbt.getExistsBaseTable()) {
int existsTableNumber = fbt.getTableNumber();
for (int i = predicatesTemp.size() - 1; i >= 0; i--) {
AndNode topAndNode = predicatesTemp.elementAt(i).getAndNode();
for (ValueNode whereWalker = topAndNode; whereWalker instanceof AndNode; whereWalker = ((AndNode) whereWalker).getRightOperand()) {
// See if this is a candidate =
AndNode and = (AndNode) whereWalker;
// predicates are considered during DISTINCT elimination.
if (!and.getLeftOperand().isRelationalOperator() || !(((RelationalOperator) (and.getLeftOperand())).getOperator() == RelationalOperator.EQUALS_RELOP)) {
continue;
}
JBitSet referencedTables = and.getLeftOperand().getTablesReferenced();
if (referencedTables.get(existsTableNumber)) {
predicatesTemp.removeElementAt(i);
break;
}
}
}
}
}
/* Build an array of tableNumbers from this query block.
* We will use that array to find out if we have at least
* one table with a uniqueness condition based only on
* constants, parameters and correlation columns.
*/
tableNumbers = getTableNumbers();
JBitSet[][] tableColMap = new JBitSet[size][size];
boolean[] oneRow = new boolean[size];
boolean oneRowResult;
/* See if each table has a uniqueness condition */
for (int index = 0; index < size; index++) {
ProjectRestrictNode prn = (ProjectRestrictNode) elementAt(index);
FromBaseTable fbt = (FromBaseTable) prn.getChildResult();
// Skip over EXISTS FBT since they cannot introduce duplicates
if (fbt.getExistsBaseTable()) {
oneRow[index] = true;
continue;
}
int numColumns = fbt.getTableDescriptor().getNumberOfColumns();
boolean[] eqOuterCols = new boolean[numColumns + 1];
int tableNumber = fbt.getTableNumber();
boolean resultColTable = false;
for (int i = 0; i < size; i++) tableColMap[index][i] = new JBitSet(numColumns + 1);
if (additionalCR != null && additionalCR.getTableNumber() == tableNumber) {
rcl.recordColumnReferences(eqOuterCols, tableColMap[index], index);
resultColTable = true;
}
/* Now see if there are any equality conditions
* of interest in the where clause.
*/
if (whereClause != null) {
whereClause.checkTopPredicatesForEqualsConditions(tableNumber, eqOuterCols, tableNumbers, tableColMap[index], resultColTable);
}
/* Now see if there are any equality conditions
* of interest in the where predicates.
*/
predicatesTemp.checkTopPredicatesForEqualsConditions(tableNumber, eqOuterCols, tableNumbers, tableColMap[index], resultColTable);
/* Now see if there are any equality conditions
* of interest that were already pushed down to the
* PRN above the FBT. (Single table predicates.)
*/
if (prn.getRestrictionList() != null) {
prn.getRestrictionList().checkTopPredicatesForEqualsConditions(tableNumber, eqOuterCols, tableNumbers, tableColMap[index], resultColTable);
}
/* We can finally check to see if the marked columns
* are a superset of any unique index.
*/
if (!fbt.supersetOfUniqueIndex(tableColMap[index])) {
return false;
}
/* Do we have at least 1 table whose equality condition
* is based solely on constants, parameters and correlation columns.
*/
oneRowResult = fbt.supersetOfUniqueIndex(eqOuterCols);
if (oneRowResult) {
oneRow[index] = true;
satisfiesOuter = true;
}
}
/* Have we met all of the criteria */
if (satisfiesOuter) {
/* check that all the tables are joined by unique indexes
* or only produce 1 row
*/
boolean foundOneRow = true;
while (foundOneRow) {
foundOneRow = false;
for (int index = 0; index < size; index++) {
if (oneRow[index]) {
for (int i = 0; i < size; i++) {
/* unique key join - exists tables already marked as
* 1 row - so don't need to look at them
*/
if (!oneRow[i] && tableColMap[i][index].get(0)) {
oneRow[i] = true;
foundOneRow = true;
}
}
}
}
}
/* does any table produce more than one row */
for (int index = 0; index < size; index++) {
if (!oneRow[index]) {
satisfiesOuter = false;
break;
}
}
}
return satisfiesOuter;
}
Aggregations