mirror of
https://github.com/postgres/postgres.git
synced 2025-11-07 19:06:32 +03:00
First cut at planner support for bitmap index scans. Lots to do yet,
but the code is basically working. Along the way, rewrite the entire approach to processing OR index conditions, and make it work in join cases for the first time ever. orindxpath.c is now basically obsolete, but I left it in for the time being to allow easy comparison testing against the old implementation.
This commit is contained in:
@@ -49,7 +49,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.144 2005/04/21 19:18:12 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.145 2005/04/22 21:58:31 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -104,7 +104,6 @@ bool enable_hashjoin = true;
|
||||
|
||||
|
||||
static bool cost_qual_eval_walker(Node *node, QualCost *total);
|
||||
static void cost_bitmap_tree_node(Path *path, Cost *cost, Selectivity *selec);
|
||||
static Selectivity approx_selectivity(Query *root, List *quals,
|
||||
JoinType jointype);
|
||||
static Selectivity join_in_selectivity(JoinPath *path, Query *root);
|
||||
@@ -474,8 +473,11 @@ cost_bitmap_heap_scan(Path *path, Query *root, RelOptInfo *baserel,
|
||||
* For lack of a better idea, interpolate like this to determine the
|
||||
* cost per page.
|
||||
*/
|
||||
cost_per_page = random_page_cost -
|
||||
(random_page_cost - 1.0) * sqrt(pages_fetched / T);
|
||||
if (pages_fetched >= 2.0)
|
||||
cost_per_page = random_page_cost -
|
||||
(random_page_cost - 1.0) * sqrt(pages_fetched / T);
|
||||
else
|
||||
cost_per_page = random_page_cost;
|
||||
|
||||
run_cost += pages_fetched * cost_per_page;
|
||||
|
||||
@@ -500,7 +502,7 @@ cost_bitmap_heap_scan(Path *path, Query *root, RelOptInfo *baserel,
|
||||
* cost_bitmap_tree_node
|
||||
* Extract cost and selectivity from a bitmap tree node (index/and/or)
|
||||
*/
|
||||
static void
|
||||
void
|
||||
cost_bitmap_tree_node(Path *path, Cost *cost, Selectivity *selec)
|
||||
{
|
||||
if (IsA(path, IndexPath))
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,7 +10,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.182 2005/04/21 19:18:12 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.183 2005/04/22 21:58:31 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -870,6 +870,9 @@ create_bitmap_scan_plan(Query *root,
|
||||
/* Also extract the true index conditions */
|
||||
indexquals = create_bitmap_indxqual(best_path->bitmapqual);
|
||||
|
||||
/* Reduce RestrictInfo list to bare expressions */
|
||||
scan_clauses = get_actual_clauses(scan_clauses);
|
||||
|
||||
/*
|
||||
* If this is a innerjoin scan, the indexclauses will contain join
|
||||
* clauses that are not present in scan_clauses (since the passed-in
|
||||
@@ -881,16 +884,9 @@ create_bitmap_scan_plan(Query *root,
|
||||
*/
|
||||
if (best_path->isjoininner)
|
||||
{
|
||||
/*
|
||||
* Pointer comparison should be enough to determine RestrictInfo
|
||||
* matches.
|
||||
*/
|
||||
scan_clauses = list_union_ptr(scan_clauses, bitmapqualorig);
|
||||
scan_clauses = list_union(scan_clauses, bitmapqualorig);
|
||||
}
|
||||
|
||||
/* Reduce RestrictInfo list to bare expressions */
|
||||
scan_clauses = get_actual_clauses(scan_clauses);
|
||||
|
||||
/*
|
||||
* The qpqual list must contain all restrictions not automatically
|
||||
* handled by the index. All the predicates in the indexquals will be
|
||||
@@ -1322,7 +1318,38 @@ create_nestloop_plan(Query *root,
|
||||
select_nonredundant_join_clauses(root,
|
||||
joinrestrictclauses,
|
||||
linitial(indexclauses),
|
||||
best_path->jointype);
|
||||
IS_OUTER_JOIN(best_path->jointype));
|
||||
}
|
||||
}
|
||||
else if (IsA(best_path->innerjoinpath, BitmapHeapPath))
|
||||
{
|
||||
/*
|
||||
* Same deal for bitmapped index scans.
|
||||
*/
|
||||
BitmapHeapPath *innerpath = (BitmapHeapPath *) best_path->innerjoinpath;
|
||||
|
||||
if (innerpath->isjoininner)
|
||||
{
|
||||
List *bitmapquals;
|
||||
List *bitmapclauses;
|
||||
ListCell *l;
|
||||
|
||||
bitmapquals = create_bitmap_qual(innerpath->bitmapqual);
|
||||
|
||||
/* must convert qual list to restrictinfos ... painful ... */
|
||||
bitmapclauses = NIL;
|
||||
foreach(l, bitmapquals)
|
||||
{
|
||||
bitmapclauses = lappend(bitmapclauses,
|
||||
make_restrictinfo((Expr *) lfirst(l),
|
||||
true, true));
|
||||
}
|
||||
|
||||
joinrestrictclauses =
|
||||
select_nonredundant_join_clauses(root,
|
||||
joinrestrictclauses,
|
||||
bitmapclauses,
|
||||
IS_OUTER_JOIN(best_path->jointype));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planagg.c,v 1.3 2005/04/12 05:11:28 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planagg.c,v 1.4 2005/04/22 21:58:31 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -343,7 +343,10 @@ build_minmax_path(Query *root, RelOptInfo *rel, MinMaxAggInfo *info)
|
||||
* to build the path, it's convenient to extract that first and then
|
||||
* look through it for the equality restrictions.
|
||||
*/
|
||||
restrictclauses = group_clauses_by_indexkey(index);
|
||||
restrictclauses = group_clauses_by_indexkey(index,
|
||||
index->rel->baserestrictinfo,
|
||||
NIL,
|
||||
NULL);
|
||||
|
||||
if (list_length(restrictclauses) < indexcol)
|
||||
continue; /* definitely haven't got enough */
|
||||
@@ -376,7 +379,8 @@ build_minmax_path(Query *root, RelOptInfo *rel, MinMaxAggInfo *info)
|
||||
new_path = create_index_path(root, index,
|
||||
restrictclauses,
|
||||
NIL,
|
||||
indexscandir);
|
||||
indexscandir,
|
||||
false);
|
||||
|
||||
/*
|
||||
* Estimate actual cost of fetching just one row.
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.107 2005/04/19 22:35:16 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.108 2005/04/22 21:58:31 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -44,6 +44,10 @@ typedef struct
|
||||
static void fix_expr_references(Plan *plan, Node *node);
|
||||
static bool fix_expr_references_walker(Node *node, void *context);
|
||||
static void set_join_references(Join *join, List *rtable);
|
||||
static void set_inner_join_references(Plan *inner_plan,
|
||||
List *rtable,
|
||||
List *outer_tlist,
|
||||
bool tlists_have_non_vars);
|
||||
static void set_uppernode_references(Plan *plan, Index subvarno);
|
||||
static bool targetlist_has_non_vars(List *tlist);
|
||||
static List *join_references(List *clauses,
|
||||
@@ -325,7 +329,7 @@ fix_expr_references_walker(Node *node, void *context)
|
||||
*
|
||||
* In the case of a nestloop with inner indexscan, we will also need to
|
||||
* apply the same transformation to any outer vars appearing in the
|
||||
* quals of the child indexscan.
|
||||
* quals of the child indexscan. set_inner_join_references does that.
|
||||
*
|
||||
* 'join' is a join plan node
|
||||
* 'rtable' is the associated range table
|
||||
@@ -365,62 +369,11 @@ set_join_references(Join *join, List *rtable)
|
||||
/* Now do join-type-specific stuff */
|
||||
if (IsA(join, NestLoop))
|
||||
{
|
||||
if (IsA(inner_plan, IndexScan))
|
||||
{
|
||||
/*
|
||||
* An index is being used to reduce the number of tuples
|
||||
* scanned in the inner relation. If there are join clauses
|
||||
* being used with the index, we must update their outer-rel
|
||||
* var nodes to refer to the outer side of the join.
|
||||
*/
|
||||
IndexScan *innerscan = (IndexScan *) inner_plan;
|
||||
List *indxqualorig = innerscan->indxqualorig;
|
||||
|
||||
/* No work needed if indxqual refers only to its own rel... */
|
||||
if (NumRelids((Node *) indxqualorig) > 1)
|
||||
{
|
||||
Index innerrel = innerscan->scan.scanrelid;
|
||||
|
||||
/* only refs to outer vars get changed in the inner qual */
|
||||
innerscan->indxqualorig = join_references(indxqualorig,
|
||||
rtable,
|
||||
outer_tlist,
|
||||
NIL,
|
||||
innerrel,
|
||||
tlists_have_non_vars);
|
||||
innerscan->indxqual = join_references(innerscan->indxqual,
|
||||
rtable,
|
||||
outer_tlist,
|
||||
NIL,
|
||||
innerrel,
|
||||
tlists_have_non_vars);
|
||||
|
||||
/*
|
||||
* We must fix the inner qpqual too, if it has join
|
||||
* clauses (this could happen if special operators are
|
||||
* involved: some indxquals may get rechecked as qpquals).
|
||||
*/
|
||||
if (NumRelids((Node *) inner_plan->qual) > 1)
|
||||
inner_plan->qual = join_references(inner_plan->qual,
|
||||
rtable,
|
||||
outer_tlist,
|
||||
NIL,
|
||||
innerrel,
|
||||
tlists_have_non_vars);
|
||||
}
|
||||
}
|
||||
else if (IsA(inner_plan, TidScan))
|
||||
{
|
||||
TidScan *innerscan = (TidScan *) inner_plan;
|
||||
Index innerrel = innerscan->scan.scanrelid;
|
||||
|
||||
innerscan->tideval = join_references(innerscan->tideval,
|
||||
rtable,
|
||||
outer_tlist,
|
||||
NIL,
|
||||
innerrel,
|
||||
tlists_have_non_vars);
|
||||
}
|
||||
/* This processing is split out to handle possible recursion */
|
||||
set_inner_join_references(inner_plan,
|
||||
rtable,
|
||||
outer_tlist,
|
||||
tlists_have_non_vars);
|
||||
}
|
||||
else if (IsA(join, MergeJoin))
|
||||
{
|
||||
@@ -446,6 +399,178 @@ set_join_references(Join *join, List *rtable)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* set_inner_join_references
|
||||
* Handle join references appearing in an inner indexscan's quals
|
||||
*
|
||||
* To handle bitmap-scan plan trees, we have to be able to recurse down
|
||||
* to the bottom BitmapIndexScan nodes, so this is split out as a separate
|
||||
* function.
|
||||
*/
|
||||
static void
|
||||
set_inner_join_references(Plan *inner_plan,
|
||||
List *rtable,
|
||||
List *outer_tlist,
|
||||
bool tlists_have_non_vars)
|
||||
{
|
||||
if (IsA(inner_plan, IndexScan))
|
||||
{
|
||||
/*
|
||||
* An index is being used to reduce the number of tuples
|
||||
* scanned in the inner relation. If there are join clauses
|
||||
* being used with the index, we must update their outer-rel
|
||||
* var nodes to refer to the outer side of the join.
|
||||
*/
|
||||
IndexScan *innerscan = (IndexScan *) inner_plan;
|
||||
List *indxqualorig = innerscan->indxqualorig;
|
||||
|
||||
/* No work needed if indxqual refers only to its own rel... */
|
||||
if (NumRelids((Node *) indxqualorig) > 1)
|
||||
{
|
||||
Index innerrel = innerscan->scan.scanrelid;
|
||||
|
||||
/* only refs to outer vars get changed in the inner qual */
|
||||
innerscan->indxqualorig = join_references(indxqualorig,
|
||||
rtable,
|
||||
outer_tlist,
|
||||
NIL,
|
||||
innerrel,
|
||||
tlists_have_non_vars);
|
||||
innerscan->indxqual = join_references(innerscan->indxqual,
|
||||
rtable,
|
||||
outer_tlist,
|
||||
NIL,
|
||||
innerrel,
|
||||
tlists_have_non_vars);
|
||||
|
||||
/*
|
||||
* We must fix the inner qpqual too, if it has join
|
||||
* clauses (this could happen if special operators are
|
||||
* involved: some indxquals may get rechecked as qpquals).
|
||||
*/
|
||||
if (NumRelids((Node *) inner_plan->qual) > 1)
|
||||
inner_plan->qual = join_references(inner_plan->qual,
|
||||
rtable,
|
||||
outer_tlist,
|
||||
NIL,
|
||||
innerrel,
|
||||
tlists_have_non_vars);
|
||||
}
|
||||
}
|
||||
else if (IsA(inner_plan, BitmapIndexScan))
|
||||
{
|
||||
/*
|
||||
* Same, but index is being used within a bitmap plan.
|
||||
*/
|
||||
BitmapIndexScan *innerscan = (BitmapIndexScan *) inner_plan;
|
||||
List *indxqualorig = innerscan->indxqualorig;
|
||||
|
||||
/* No work needed if indxqual refers only to its own rel... */
|
||||
if (NumRelids((Node *) indxqualorig) > 1)
|
||||
{
|
||||
Index innerrel = innerscan->scan.scanrelid;
|
||||
|
||||
/* only refs to outer vars get changed in the inner qual */
|
||||
innerscan->indxqualorig = join_references(indxqualorig,
|
||||
rtable,
|
||||
outer_tlist,
|
||||
NIL,
|
||||
innerrel,
|
||||
tlists_have_non_vars);
|
||||
innerscan->indxqual = join_references(innerscan->indxqual,
|
||||
rtable,
|
||||
outer_tlist,
|
||||
NIL,
|
||||
innerrel,
|
||||
tlists_have_non_vars);
|
||||
/* no need to fix inner qpqual */
|
||||
Assert(inner_plan->qual == NIL);
|
||||
}
|
||||
}
|
||||
else if (IsA(inner_plan, BitmapHeapScan))
|
||||
{
|
||||
/*
|
||||
* The inner side is a bitmap scan plan. Fix the top node,
|
||||
* and recurse to get the lower nodes.
|
||||
*/
|
||||
BitmapHeapScan *innerscan = (BitmapHeapScan *) inner_plan;
|
||||
List *bitmapqualorig = innerscan->bitmapqualorig;
|
||||
|
||||
/* No work needed if bitmapqual refers only to its own rel... */
|
||||
if (NumRelids((Node *) bitmapqualorig) > 1)
|
||||
{
|
||||
Index innerrel = innerscan->scan.scanrelid;
|
||||
|
||||
/* only refs to outer vars get changed in the inner qual */
|
||||
innerscan->bitmapqualorig = join_references(bitmapqualorig,
|
||||
rtable,
|
||||
outer_tlist,
|
||||
NIL,
|
||||
innerrel,
|
||||
tlists_have_non_vars);
|
||||
|
||||
/*
|
||||
* We must fix the inner qpqual too, if it has join
|
||||
* clauses (this could happen if special operators are
|
||||
* involved: some indxquals may get rechecked as qpquals).
|
||||
*/
|
||||
if (NumRelids((Node *) inner_plan->qual) > 1)
|
||||
inner_plan->qual = join_references(inner_plan->qual,
|
||||
rtable,
|
||||
outer_tlist,
|
||||
NIL,
|
||||
innerrel,
|
||||
tlists_have_non_vars);
|
||||
|
||||
/* Now recurse */
|
||||
set_inner_join_references(inner_plan->lefttree,
|
||||
rtable,
|
||||
outer_tlist,
|
||||
tlists_have_non_vars);
|
||||
}
|
||||
}
|
||||
else if (IsA(inner_plan, BitmapAnd))
|
||||
{
|
||||
/* All we need do here is recurse */
|
||||
BitmapAnd *innerscan = (BitmapAnd *) inner_plan;
|
||||
ListCell *l;
|
||||
|
||||
foreach(l, innerscan->bitmapplans)
|
||||
{
|
||||
set_inner_join_references((Plan *) lfirst(l),
|
||||
rtable,
|
||||
outer_tlist,
|
||||
tlists_have_non_vars);
|
||||
}
|
||||
}
|
||||
else if (IsA(inner_plan, BitmapOr))
|
||||
{
|
||||
/* All we need do here is recurse */
|
||||
BitmapOr *innerscan = (BitmapOr *) inner_plan;
|
||||
ListCell *l;
|
||||
|
||||
foreach(l, innerscan->bitmapplans)
|
||||
{
|
||||
set_inner_join_references((Plan *) lfirst(l),
|
||||
rtable,
|
||||
outer_tlist,
|
||||
tlists_have_non_vars);
|
||||
}
|
||||
}
|
||||
else if (IsA(inner_plan, TidScan))
|
||||
{
|
||||
TidScan *innerscan = (TidScan *) inner_plan;
|
||||
Index innerrel = innerscan->scan.scanrelid;
|
||||
|
||||
innerscan->tideval = join_references(innerscan->tideval,
|
||||
rtable,
|
||||
outer_tlist,
|
||||
NIL,
|
||||
innerrel,
|
||||
tlists_have_non_vars);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* set_uppernode_references
|
||||
* Update the targetlist and quals of an upper-level plan node
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/util/pathnode.c,v 1.118 2005/04/21 19:18:12 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/util/pathnode.c,v 1.119 2005/04/22 21:58:31 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -260,6 +260,9 @@ set_cheapest(RelOptInfo *parent_rel)
|
||||
* but just recycling discarded Path nodes is a very useful savings in
|
||||
* a large join tree. We can recycle the List nodes of pathlist, too.
|
||||
*
|
||||
* BUT: we do not pfree IndexPath objects, since they may be referenced as
|
||||
* children of BitmapHeapPaths as well as being paths in their own right.
|
||||
*
|
||||
* 'parent_rel' is the relation entry to which the path corresponds.
|
||||
* 'new_path' is a potential path for parent_rel.
|
||||
*
|
||||
@@ -349,8 +352,12 @@ add_path(RelOptInfo *parent_rel, Path *new_path)
|
||||
{
|
||||
parent_rel->pathlist = list_delete_cell(parent_rel->pathlist,
|
||||
p1, p1_prev);
|
||||
/* Delete the data pointed-to by the deleted cell */
|
||||
pfree(old_path);
|
||||
/*
|
||||
* Delete the data pointed-to by the deleted cell, if possible
|
||||
*/
|
||||
if (!IsA(old_path, IndexPath))
|
||||
pfree(old_path);
|
||||
/* Advance list pointer */
|
||||
if (p1_prev)
|
||||
p1 = lnext(p1_prev);
|
||||
else
|
||||
@@ -361,6 +368,7 @@ add_path(RelOptInfo *parent_rel, Path *new_path)
|
||||
/* new belongs after this old path if it has cost >= old's */
|
||||
if (costcmp >= 0)
|
||||
insert_after = p1;
|
||||
/* Advance list pointers */
|
||||
p1_prev = p1;
|
||||
p1 = lnext(p1);
|
||||
}
|
||||
@@ -385,7 +393,8 @@ add_path(RelOptInfo *parent_rel, Path *new_path)
|
||||
else
|
||||
{
|
||||
/* Reject and recycle the new path */
|
||||
pfree(new_path);
|
||||
if (!IsA(new_path, IndexPath))
|
||||
pfree(new_path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,55 +427,102 @@ create_seqscan_path(Query *root, RelOptInfo *rel)
|
||||
* Creates a path node for an index scan.
|
||||
*
|
||||
* 'index' is a usable index.
|
||||
* 'restriction_clauses' is a list of lists of RestrictInfo nodes
|
||||
* 'clause_groups' is a list of lists of RestrictInfo nodes
|
||||
* to be used as index qual conditions in the scan.
|
||||
* 'pathkeys' describes the ordering of the path.
|
||||
* 'indexscandir' is ForwardScanDirection or BackwardScanDirection
|
||||
* for an ordered index, or NoMovementScanDirection for
|
||||
* an unordered index.
|
||||
* 'isjoininner' is TRUE if this is a join inner indexscan path.
|
||||
* (pathkeys and indexscandir are ignored if so.)
|
||||
*
|
||||
* Returns the new path node.
|
||||
*/
|
||||
IndexPath *
|
||||
create_index_path(Query *root,
|
||||
IndexOptInfo *index,
|
||||
List *restriction_clauses,
|
||||
List *clause_groups,
|
||||
List *pathkeys,
|
||||
ScanDirection indexscandir)
|
||||
ScanDirection indexscandir,
|
||||
bool isjoininner)
|
||||
{
|
||||
IndexPath *pathnode = makeNode(IndexPath);
|
||||
List *indexquals;
|
||||
RelOptInfo *rel = index->rel;
|
||||
List *indexquals,
|
||||
*allclauses;
|
||||
|
||||
/*
|
||||
* For a join inner scan, there's no point in marking the path with any
|
||||
* pathkeys, since it will only ever be used as the inner path of a
|
||||
* nestloop, and so its ordering does not matter. For the same reason
|
||||
* we don't really care what order it's scanned in. (We could expect
|
||||
* the caller to supply the correct values, but it's easier to force
|
||||
* it here.)
|
||||
*/
|
||||
if (isjoininner)
|
||||
{
|
||||
pathkeys = NIL;
|
||||
indexscandir = NoMovementScanDirection;
|
||||
}
|
||||
|
||||
pathnode->path.pathtype = T_IndexScan;
|
||||
pathnode->path.parent = index->rel;
|
||||
pathnode->path.parent = rel;
|
||||
pathnode->path.pathkeys = pathkeys;
|
||||
|
||||
/* Convert clauses to indexquals the executor can handle */
|
||||
indexquals = expand_indexqual_conditions(index, restriction_clauses);
|
||||
indexquals = expand_indexqual_conditions(index, clause_groups);
|
||||
|
||||
/* Flatten the clause-groups list to produce indexclauses list */
|
||||
restriction_clauses = flatten_clausegroups_list(restriction_clauses);
|
||||
allclauses = flatten_clausegroups_list(clause_groups);
|
||||
|
||||
/*
|
||||
* We are making a pathnode for a single-scan indexscan; therefore,
|
||||
* indexinfo etc should be single-element lists.
|
||||
*/
|
||||
pathnode->indexinfo = list_make1(index);
|
||||
pathnode->indexclauses = list_make1(restriction_clauses);
|
||||
pathnode->indexclauses = list_make1(allclauses);
|
||||
pathnode->indexquals = list_make1(indexquals);
|
||||
|
||||
/* It's not an innerjoin path. */
|
||||
pathnode->isjoininner = false;
|
||||
|
||||
pathnode->isjoininner = isjoininner;
|
||||
pathnode->indexscandir = indexscandir;
|
||||
|
||||
/*
|
||||
* The number of rows is the same as the parent rel's estimate, since
|
||||
* this isn't a join inner indexscan.
|
||||
*/
|
||||
pathnode->rows = index->rel->rows;
|
||||
if (isjoininner)
|
||||
{
|
||||
/*
|
||||
* We must compute the estimated number of output rows for the
|
||||
* indexscan. This is less than rel->rows because of the additional
|
||||
* selectivity of the join clauses. Since clause_groups may
|
||||
* contain both restriction and join clauses, we have to do a set
|
||||
* union to get the full set of clauses that must be considered to
|
||||
* compute the correct selectivity. (Without the union operation,
|
||||
* we might have some restriction clauses appearing twice, which'd
|
||||
* mislead clauselist_selectivity into double-counting their
|
||||
* selectivity. However, since RestrictInfo nodes aren't copied when
|
||||
* linking them into different lists, it should be sufficient to use
|
||||
* pointer comparison to remove duplicates.)
|
||||
*
|
||||
* Always assume the join type is JOIN_INNER; even if some of the join
|
||||
* clauses come from other contexts, that's not our problem.
|
||||
*/
|
||||
allclauses = list_union_ptr(rel->baserestrictinfo, allclauses);
|
||||
pathnode->rows = rel->tuples *
|
||||
clauselist_selectivity(root,
|
||||
allclauses,
|
||||
rel->relid, /* do not use 0! */
|
||||
JOIN_INNER);
|
||||
/* Like costsize.c, force estimate to be at least one row */
|
||||
pathnode->rows = clamp_row_est(pathnode->rows);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* The number of rows is the same as the parent rel's estimate,
|
||||
* since this isn't a join inner indexscan.
|
||||
*/
|
||||
pathnode->rows = rel->rows;
|
||||
}
|
||||
|
||||
cost_index(pathnode, root, index, indexquals, false);
|
||||
cost_index(pathnode, root, index, indexquals, isjoininner);
|
||||
|
||||
return pathnode;
|
||||
}
|
||||
@@ -480,7 +536,8 @@ create_index_path(Query *root,
|
||||
BitmapHeapPath *
|
||||
create_bitmap_heap_path(Query *root,
|
||||
RelOptInfo *rel,
|
||||
Path *bitmapqual)
|
||||
Path *bitmapqual,
|
||||
bool isjoininner)
|
||||
{
|
||||
BitmapHeapPath *pathnode = makeNode(BitmapHeapPath);
|
||||
|
||||
@@ -489,15 +546,36 @@ create_bitmap_heap_path(Query *root,
|
||||
pathnode->path.pathkeys = NIL; /* always unordered */
|
||||
|
||||
pathnode->bitmapqual = bitmapqual;
|
||||
pathnode->isjoininner = isjoininner;
|
||||
|
||||
/* It's not an innerjoin path. */
|
||||
pathnode->isjoininner = false;
|
||||
if (isjoininner)
|
||||
{
|
||||
/*
|
||||
* We must compute the estimated number of output rows for the
|
||||
* indexscan. This is less than rel->rows because of the additional
|
||||
* selectivity of the join clauses. We make use of the selectivity
|
||||
* estimated for the bitmap to do this; this isn't really quite
|
||||
* right since there may be restriction conditions not included
|
||||
* in the bitmap ...
|
||||
*/
|
||||
Cost indexTotalCost;
|
||||
Selectivity indexSelectivity;
|
||||
|
||||
/*
|
||||
* The number of rows is the same as the parent rel's estimate, since
|
||||
* this isn't a join inner indexscan.
|
||||
*/
|
||||
pathnode->rows = rel->rows;
|
||||
cost_bitmap_tree_node(bitmapqual, &indexTotalCost, &indexSelectivity);
|
||||
pathnode->rows = rel->tuples * indexSelectivity;
|
||||
if (pathnode->rows > rel->rows)
|
||||
pathnode->rows = rel->rows;
|
||||
/* Like costsize.c, force estimate to be at least one row */
|
||||
pathnode->rows = clamp_row_est(pathnode->rows);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* The number of rows is the same as the parent rel's estimate,
|
||||
* since this isn't a join inner indexscan.
|
||||
*/
|
||||
pathnode->rows = rel->rows;
|
||||
}
|
||||
|
||||
cost_bitmap_heap_scan(&pathnode->path, root, rel, bitmapqual, false);
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/util/plancat.c,v 1.105 2005/04/14 20:03:24 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/util/plancat.c,v 1.106 2005/04/22 21:58:31 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -194,10 +194,6 @@ get_relation_info(Oid relationObjectId, RelOptInfo *rel)
|
||||
info->tuples = rel->tuples;
|
||||
}
|
||||
|
||||
/* initialize cached join info to empty */
|
||||
info->outer_relids = NULL;
|
||||
info->inner_paths = NIL;
|
||||
|
||||
index_close(indexRelation);
|
||||
|
||||
indexinfos = lcons(info, indexinfos);
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/util/relnode.c,v 1.64 2004/12/31 22:00:23 pgsql Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/util/relnode.c,v 1.65 2005/04/22 21:58:31 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -461,7 +461,8 @@ build_joinrel_restrictlist(Query *root,
|
||||
* previous clauses (see optimizer/README for discussion). We detect
|
||||
* that case and omit the redundant clause from the result list.
|
||||
*/
|
||||
result = remove_redundant_join_clauses(root, rlist, jointype);
|
||||
result = remove_redundant_join_clauses(root, rlist,
|
||||
IS_OUTER_JOIN(jointype));
|
||||
|
||||
list_free(rlist);
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.32 2005/03/28 00:58:24 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.33 2005/04/22 21:58:31 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -31,7 +31,7 @@ static Expr *make_sub_restrictinfos(Expr *clause,
|
||||
static RestrictInfo *join_clause_is_redundant(Query *root,
|
||||
RestrictInfo *rinfo,
|
||||
List *reference_list,
|
||||
JoinType jointype);
|
||||
bool isouterjoin);
|
||||
|
||||
|
||||
/*
|
||||
@@ -49,27 +49,19 @@ static RestrictInfo *join_clause_is_redundant(Query *root,
|
||||
RestrictInfo *
|
||||
make_restrictinfo(Expr *clause, bool is_pushed_down, bool valid_everywhere)
|
||||
{
|
||||
Expr *orclause;
|
||||
|
||||
/*
|
||||
* If it's an OR clause, build a modified copy with RestrictInfos
|
||||
* inserted above each subclause of the top-level AND/OR structure.
|
||||
*/
|
||||
if (or_clause((Node *) clause))
|
||||
{
|
||||
orclause = make_sub_restrictinfos(clause,
|
||||
is_pushed_down,
|
||||
valid_everywhere);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Shouldn't be an AND clause, else AND/OR flattening messed up */
|
||||
Assert(!and_clause((Node *) clause));
|
||||
return (RestrictInfo *) make_sub_restrictinfos(clause,
|
||||
is_pushed_down,
|
||||
valid_everywhere);
|
||||
|
||||
orclause = NULL;
|
||||
}
|
||||
/* Shouldn't be an AND clause, else AND/OR flattening messed up */
|
||||
Assert(!and_clause((Node *) clause));
|
||||
|
||||
return make_restrictinfo_internal(clause, orclause,
|
||||
return make_restrictinfo_internal(clause, NULL,
|
||||
is_pushed_down, valid_everywhere);
|
||||
}
|
||||
|
||||
@@ -198,6 +190,12 @@ make_restrictinfo_internal(Expr *clause, Expr *orclause,
|
||||
|
||||
/*
|
||||
* Recursively insert sub-RestrictInfo nodes into a boolean expression.
|
||||
*
|
||||
* We put RestrictInfos above simple (non-AND/OR) clauses and above
|
||||
* sub-OR clauses, but not above sub-AND clauses, because there's no need.
|
||||
* This may seem odd but it is closely related to the fact that we use
|
||||
* implicit-AND lists at top level of RestrictInfo lists. Only ORs and
|
||||
* simple clauses are valid RestrictInfos.
|
||||
*/
|
||||
static Expr *
|
||||
make_sub_restrictinfos(Expr *clause, bool is_pushed_down,
|
||||
@@ -213,7 +211,10 @@ make_sub_restrictinfos(Expr *clause, bool is_pushed_down,
|
||||
make_sub_restrictinfos(lfirst(temp),
|
||||
is_pushed_down,
|
||||
valid_everywhere));
|
||||
return make_orclause(orlist);
|
||||
return (Expr *) make_restrictinfo_internal(clause,
|
||||
make_orclause(orlist),
|
||||
is_pushed_down,
|
||||
valid_everywhere);
|
||||
}
|
||||
else if (and_clause((Node *) clause))
|
||||
{
|
||||
@@ -314,7 +315,7 @@ get_actual_join_clauses(List *restrictinfo_list,
|
||||
*/
|
||||
List *
|
||||
remove_redundant_join_clauses(Query *root, List *restrictinfo_list,
|
||||
JoinType jointype)
|
||||
bool isouterjoin)
|
||||
{
|
||||
List *result = NIL;
|
||||
ListCell *item;
|
||||
@@ -341,7 +342,7 @@ remove_redundant_join_clauses(Query *root, List *restrictinfo_list,
|
||||
RestrictInfo *prevrinfo;
|
||||
|
||||
/* is it redundant with any prior clause? */
|
||||
prevrinfo = join_clause_is_redundant(root, rinfo, result, jointype);
|
||||
prevrinfo = join_clause_is_redundant(root, rinfo, result, isouterjoin);
|
||||
if (prevrinfo == NULL)
|
||||
{
|
||||
/* no, so add it to result list */
|
||||
@@ -377,7 +378,7 @@ List *
|
||||
select_nonredundant_join_clauses(Query *root,
|
||||
List *restrictinfo_list,
|
||||
List *reference_list,
|
||||
JoinType jointype)
|
||||
bool isouterjoin)
|
||||
{
|
||||
List *result = NIL;
|
||||
ListCell *item;
|
||||
@@ -387,7 +388,7 @@ select_nonredundant_join_clauses(Query *root,
|
||||
RestrictInfo *rinfo = (RestrictInfo *) lfirst(item);
|
||||
|
||||
/* drop it if redundant with any reference clause */
|
||||
if (join_clause_is_redundant(root, rinfo, reference_list, jointype) != NULL)
|
||||
if (join_clause_is_redundant(root, rinfo, reference_list, isouterjoin) != NULL)
|
||||
continue;
|
||||
|
||||
/* otherwise, add it to result list */
|
||||
@@ -429,7 +430,7 @@ static RestrictInfo *
|
||||
join_clause_is_redundant(Query *root,
|
||||
RestrictInfo *rinfo,
|
||||
List *reference_list,
|
||||
JoinType jointype)
|
||||
bool isouterjoin)
|
||||
{
|
||||
ListCell *refitem;
|
||||
|
||||
@@ -463,7 +464,7 @@ join_clause_is_redundant(Query *root,
|
||||
if (rinfo->left_pathkey == refrinfo->left_pathkey &&
|
||||
rinfo->right_pathkey == refrinfo->right_pathkey &&
|
||||
(rinfo->is_pushed_down == refrinfo->is_pushed_down ||
|
||||
!IS_OUTER_JOIN(jointype)))
|
||||
!isouterjoin))
|
||||
{
|
||||
/* Yup, it's redundant */
|
||||
return refrinfo;
|
||||
|
||||
Reference in New Issue
Block a user