Restructure building of join relation targetlists so that a join plan

node emits only those vars that are actually needed above it in the
plan tree.  (There were comments in the code suggesting that this was
done at some point in the dim past, but for a long time we have just
made join nodes emit everything that either input emitted.)  Aside from
being marginally more efficient, this fixes the problem noted by Peter
Eisentraut where a join above an IN-implemented-as-join might fail,
because the subplan targetlist constructed in the latter case didn't
meet the expectation of including everything.
Along the way, fix some places that were O(N^2) in the targetlist
length.  This is not all the trouble spots for wide queries by any
means, but it's a step forward.
This commit is contained in:
Tom Lane 2003-06-29 23:05:05 +00:00
parent cf883ea95c
commit 835bb975d8
16 changed files with 334 additions and 221 deletions

View File

@ -14,7 +14,7 @@
* Copyright (c) 2003, PostgreSQL Global Development Group
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/bitmapset.c,v 1.1 2003/02/08 20:20:53 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/bitmapset.c,v 1.2 2003/06/29 23:05:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -388,6 +388,36 @@ bms_overlap(const Bitmapset *a, const Bitmapset *b)
return false;
}
/*
* bms_nonempty_difference - do sets have a nonempty difference?
*/
bool
bms_nonempty_difference(const Bitmapset *a, const Bitmapset *b)
{
int shortlen;
int i;
/* Handle cases where either input is NULL */
if (a == NULL)
return false;
if (b == NULL)
return !bms_is_empty(a);
/* Check words in common */
shortlen = Min(a->nwords, b->nwords);
for (i = 0; i < shortlen; i++)
{
if ((a->words[i] & ~ b->words[i]) != 0)
return true;
}
/* Check extra words in a */
for (; i < a->nwords; i++)
{
if (a->words[i] != 0)
return true;
}
return false;
}
/*
* bms_singleton_member - return the sole integer member of set
*

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.102 2003/04/24 23:43:09 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.103 2003/06/29 23:05:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -235,6 +235,9 @@ set_inherited_rel_pathlist(Query *root, RelOptInfo *rel,
RangeTblEntry *childrte;
Oid childOID;
RelOptInfo *childrel;
List *reltlist;
List *parentvars;
List *childvars;
childrte = rt_fetch(childRTindex, root->rtable);
childOID = childrte->relid;
@ -251,21 +254,24 @@ set_inherited_rel_pathlist(Query *root, RelOptInfo *rel,
* Copy the parent's targetlist and restriction quals to the
* child, with attribute-number adjustment as needed. We don't
* bother to copy the join quals, since we can't do any joining of
* the individual tables.
* the individual tables. Also, we just zap attr_needed rather
* than trying to adjust it; it won't be looked at in the child.
*/
childrel->targetlist = (List *)
adjust_inherited_attrs((Node *) rel->targetlist,
reltlist = FastListValue(&rel->reltargetlist);
reltlist = (List *)
adjust_inherited_attrs((Node *) reltlist,
parentRTindex,
parentOID,
childRTindex,
childOID);
FastListFromList(&childrel->reltargetlist, reltlist);
childrel->attr_needed = NULL;
childrel->baserestrictinfo = (List *)
adjust_inherited_attrs((Node *) rel->baserestrictinfo,
parentRTindex,
parentOID,
childRTindex,
childOID);
childrel->baserestrictcost = rel->baserestrictcost;
/*
* Now compute child access paths, and save the cheapest.
@ -274,10 +280,27 @@ set_inherited_rel_pathlist(Query *root, RelOptInfo *rel,
subpaths = lappend(subpaths, childrel->cheapest_total_path);
/* Also update total size estimates */
/*
* Propagate size information from the child back to the parent.
* For simplicity, we use the largest widths from any child as the
* parent estimates.
*/
rel->rows += childrel->rows;
if (childrel->width > rel->width)
rel->width = childrel->width;
childvars = FastListValue(&childrel->reltargetlist);
foreach(parentvars, FastListValue(&rel->reltargetlist))
{
Var *parentvar = (Var *) lfirst(parentvars);
Var *childvar = (Var *) lfirst(childvars);
int parentndx = parentvar->varattno - rel->min_attr;
int childndx = childvar->varattno - childrel->min_attr;
if (childrel->attr_widths[childndx] > rel->attr_widths[parentndx])
rel->attr_widths[parentndx] = childrel->attr_widths[childndx];
childvars = lnext(childvars);
}
}
/*

View File

@ -49,7 +49,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.108 2003/06/29 00:33:43 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.109 2003/06/29 23:05:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -1792,13 +1792,9 @@ set_joinrel_size_estimates(Query *root, RelOptInfo *rel,
rel->rows = temp;
/*
* We could apply set_rel_width() to compute the output tuple width
* from scratch, but at present it's always just the sum of the input
* widths, so why work harder than necessary? If relnode.c is ever
* taught to remove unneeded columns from join targetlists, go back to
* using set_rel_width here.
* We need not compute the output width here, because build_joinrel_tlist
* already did.
*/
rel->width = outer_rel->width + inner_rel->width;
}
/*
@ -1858,13 +1854,16 @@ set_function_size_estimates(Query *root, RelOptInfo *rel)
/*
* set_rel_width
* Set the estimated output width of the relation.
* Set the estimated output width of a base relation.
*
* NB: this works best on base relations because it prefers to look at
* NB: this works best on plain relations because it prefers to look at
* real Vars. It will fail to make use of pg_statistic info when applied
* to a subquery relation, even if the subquery outputs are simple vars
* that we could have gotten info for. Is it worth trying to be smarter
* about subqueries?
*
* The per-attribute width estimates are cached for possible re-use while
* building join relations.
*/
static void
set_rel_width(Query *root, RelOptInfo *rel)
@ -1872,38 +1871,41 @@ set_rel_width(Query *root, RelOptInfo *rel)
int32 tuple_width = 0;
List *tllist;
foreach(tllist, rel->targetlist)
foreach(tllist, FastListValue(&rel->reltargetlist))
{
TargetEntry *tle = (TargetEntry *) lfirst(tllist);
Var *var = (Var *) lfirst(tllist);
int ndx = var->varattno - rel->min_attr;
Oid relid;
int32 item_width;
/*
* If it's a Var, try to get statistical info from pg_statistic.
*/
if (tle->expr && IsA(tle->expr, Var))
{
Var *var = (Var *) tle->expr;
Oid relid;
Assert(IsA(var, Var));
relid = getrelid(var->varno, root->rtable);
if (relid != InvalidOid)
/* The width probably hasn't been cached yet, but may as well check */
if (rel->attr_widths[ndx] > 0)
{
tuple_width += rel->attr_widths[ndx];
continue;
}
relid = getrelid(var->varno, root->rtable);
if (relid != InvalidOid)
{
item_width = get_attavgwidth(relid, var->varattno);
if (item_width > 0)
{
item_width = get_attavgwidth(relid, var->varattno);
if (item_width > 0)
{
tuple_width += item_width;
continue;
}
rel->attr_widths[ndx] = item_width;
tuple_width += item_width;
continue;
}
}
/*
* Not a Var, or can't find statistics for it. Estimate using
* just the type info.
* Not a plain relation, or can't find statistics for it.
* Estimate using just the type info.
*/
item_width = get_typavgwidth(tle->resdom->restype,
tle->resdom->restypmod);
item_width = get_typavgwidth(var->vartype, var->vartypmod);
Assert(item_width > 0);
rel->attr_widths[ndx] = item_width;
tuple_width += item_width;
}
Assert(tuple_width >= 0);

View File

@ -11,7 +11,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.49 2003/05/28 16:03:56 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.50 2003/06/29 23:05:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -703,20 +703,18 @@ find_indexkey_var(Query *root, RelOptInfo *rel, AttrNumber varattno)
vartypeid;
int32 type_mod;
foreach(temp, rel->targetlist)
foreach(temp, FastListValue(&rel->reltargetlist))
{
Var *tle_var = (Var *) ((TargetEntry *) lfirst(temp))->expr;
Var *var = (Var *) lfirst(temp);
if (IsA(tle_var, Var) &&
tle_var->varattno == varattno)
return tle_var;
if (IsA(var, Var) &&
var->varattno == varattno)
return var;
}
relid = rel->relid;
Assert(relid > 0);
reloid = getrelid(relid, root->rtable);
vartypeid = get_atttype(reloid, varattno);
type_mod = get_atttypmod(reloid, varattno);
get_atttypetypmod(reloid, varattno, &vartypeid, &type_mod);
return makeVar(relid, varattno, vartypeid, type_mod, 0);
}

View File

@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.146 2003/06/16 02:03:37 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.147 2003/06/29 23:05:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -23,6 +23,7 @@
#include "optimizer/clauses.h"
#include "optimizer/cost.h"
#include "optimizer/paths.h"
#include "optimizer/plancat.h"
#include "optimizer/planmain.h"
#include "optimizer/restrictinfo.h"
#include "optimizer/tlist.h"
@ -34,6 +35,7 @@
static Scan *create_scan_plan(Query *root, Path *best_path);
static List *build_relation_tlist(RelOptInfo *rel);
static bool use_physical_tlist(RelOptInfo *rel);
static void disuse_physical_tlist(Plan *plan, Path *path);
static Join *create_join_plan(Query *root, JoinPath *best_path);
@ -199,20 +201,13 @@ create_scan_plan(Query *root, Path *best_path)
*/
if (use_physical_tlist(rel))
{
int resdomno = 1;
List *v;
tlist = NIL;
foreach(v, rel->varlist)
{
Var *var = (Var *) lfirst(v);
tlist = lappend(tlist, create_tl_element(var, resdomno));
resdomno++;
}
tlist = build_physical_tlist(root, rel);
/* if fail because of dropped cols, use regular method */
if (tlist == NIL)
tlist = build_relation_tlist(rel);
}
else
tlist = rel->targetlist;
tlist = build_relation_tlist(rel);
/*
* Extract the relevant restriction clauses from the parent relation;
@ -266,6 +261,28 @@ create_scan_plan(Query *root, Path *best_path)
return plan;
}
/*
* Build a target list (ie, a list of TargetEntry) for a relation.
*/
static List *
build_relation_tlist(RelOptInfo *rel)
{
FastList tlist;
int resdomno = 1;
List *v;
FastListInit(&tlist);
foreach(v, FastListValue(&rel->reltargetlist))
{
/* Do we really need to copy here? Not sure */
Var *var = (Var *) copyObject(lfirst(v));
FastAppend(&tlist, create_tl_element(var, resdomno));
resdomno++;
}
return FastListValue(&tlist);
}
/*
* use_physical_tlist
* Decide whether to use a tlist matching relation structure,
@ -274,12 +291,12 @@ create_scan_plan(Query *root, Path *best_path)
static bool
use_physical_tlist(RelOptInfo *rel)
{
List *t;
int i;
/*
* Currently, can't do this for subquery or function scans. (This
* is mainly because we don't set up the necessary info when creating
* their RelOptInfo nodes.)
* is mainly because we don't have an equivalent of build_physical_tlist
* for them; worth adding?)
*/
if (rel->rtekind != RTE_RELATION)
return false;
@ -289,26 +306,15 @@ use_physical_tlist(RelOptInfo *rel)
*/
if (rel->reloptkind != RELOPT_BASEREL)
return false;
/*
* Can't do it if relation contains dropped columns. This is detected
* in plancat.c, see notes there.
*/
if (rel->varlist == NIL)
return false;
/*
* Can't do it if any system columns are requested, either. (This could
* possibly be fixed but would take some fragile assumptions in setrefs.c,
* I think.)
*/
foreach(t, rel->targetlist)
for (i = rel->min_attr; i <= 0; i++)
{
TargetEntry *tle = (TargetEntry *) lfirst(t);
Var *var = (Var *) tle->expr;
if (!var || !IsA(var, Var))
return false; /* probably can't happen */
if (var->varattno <= 0)
return false; /* system column! */
if (!bms_is_empty(rel->attr_needed[i - rel->min_attr]))
return false;
}
return true;
}
@ -333,7 +339,7 @@ disuse_physical_tlist(Plan *plan, Path *path)
case T_TidScan:
case T_SubqueryScan:
case T_FunctionScan:
plan->targetlist = path->parent->targetlist;
plan->targetlist = build_relation_tlist(path->parent);
break;
default:
break;
@ -411,7 +417,7 @@ static Append *
create_append_plan(Query *root, AppendPath *best_path)
{
Append *plan;
List *tlist = best_path->path.parent->targetlist;
List *tlist = build_relation_tlist(best_path->path.parent);
List *subplans = NIL;
List *subpaths;
@ -443,7 +449,7 @@ create_result_plan(Query *root, ResultPath *best_path)
Plan *subplan;
if (best_path->path.parent)
tlist = best_path->path.parent->targetlist;
tlist = build_relation_tlist(best_path->path.parent);
else
tlist = NIL; /* will be filled in later */
@ -842,7 +848,7 @@ create_nestloop_plan(Query *root,
Plan *outer_plan,
Plan *inner_plan)
{
List *tlist = best_path->path.parent->targetlist;
List *tlist = build_relation_tlist(best_path->path.parent);
List *joinrestrictclauses = best_path->joinrestrictinfo;
List *joinclauses;
List *otherclauses;
@ -912,7 +918,7 @@ create_mergejoin_plan(Query *root,
Plan *outer_plan,
Plan *inner_plan)
{
List *tlist = best_path->jpath.path.parent->targetlist;
List *tlist = build_relation_tlist(best_path->jpath.path.parent);
List *joinclauses;
List *otherclauses;
List *mergeclauses;
@ -992,7 +998,7 @@ create_hashjoin_plan(Query *root,
Plan *outer_plan,
Plan *inner_plan)
{
List *tlist = best_path->jpath.path.parent->targetlist;
List *tlist = build_relation_tlist(best_path->jpath.path.parent);
List *joinclauses;
List *otherclauses;
List *hashclauses;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.85 2003/03/02 23:46:34 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.86 2003/06/29 23:05:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -40,7 +40,8 @@ static void distribute_qual_to_rels(Query *root, Node *clause,
bool isdeduced,
Relids outerjoin_nonnullable,
Relids qualscope);
static void add_vars_to_targetlist(Query *root, List *vars);
static void add_vars_to_targetlist(Query *root, List *vars,
Relids where_needed);
static bool qual_is_redundant(Query *root, RestrictInfo *restrictinfo,
List *restrictlist);
static void check_mergejoinable(RestrictInfo *restrictinfo);
@ -112,34 +113,54 @@ add_base_rels_to_query(Query *root, Node *jtnode)
/*
* build_base_rel_tlists
* Creates targetlist entries for each var seen in 'tlist' and adds
* them to the tlist of the appropriate rel node.
* Add targetlist entries for each var needed in the query's final tlist
* to the appropriate base relations.
*
* We mark such vars as needed by "relation 0" to ensure that they will
* propagate up through all join plan steps.
*/
void
build_base_rel_tlists(Query *root, List *tlist)
build_base_rel_tlists(Query *root, List *final_tlist)
{
List *tlist_vars = pull_var_clause((Node *) tlist, false);
List *tlist_vars = pull_var_clause((Node *) final_tlist, false);
add_vars_to_targetlist(root, tlist_vars);
freeList(tlist_vars);
if (tlist_vars != NIL)
{
add_vars_to_targetlist(root, tlist_vars, bms_make_singleton(0));
freeList(tlist_vars);
}
}
/*
* add_vars_to_targetlist
* For each variable appearing in the list, add it to the owning
* relation's targetlist if not already present.
* relation's targetlist if not already present, and mark the variable
* as being needed for the indicated join (or for final output if
* where_needed includes "relation 0").
*/
static void
add_vars_to_targetlist(Query *root, List *vars)
add_vars_to_targetlist(Query *root, List *vars, Relids where_needed)
{
List *temp;
Assert(!bms_is_empty(where_needed));
foreach(temp, vars)
{
Var *var = (Var *) lfirst(temp);
RelOptInfo *rel = find_base_rel(root, var->varno);
int attrno = var->varattno;
add_var_to_tlist(rel, var);
Assert(attrno >= rel->min_attr && attrno <= rel->max_attr);
attrno -= rel->min_attr;
if (bms_is_empty(rel->attr_needed[attrno]))
{
/* Variable not yet requested, so add to reltargetlist */
/* XXX is copyObject necessary here? */
FastAppend(&rel->reltargetlist, copyObject(var));
}
rel->attr_needed[attrno] = bms_add_members(rel->attr_needed[attrno],
where_needed);
}
}
@ -575,7 +596,7 @@ distribute_qual_to_rels(Query *root, Node *clause,
* scan those relations (else they won't be available at the join
* node!).
*/
add_vars_to_targetlist(root, vars);
add_vars_to_targetlist(root, vars, relids);
break;
default:
/*

View File

@ -14,7 +14,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.96 2003/06/16 02:03:37 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.97 2003/06/29 23:05:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -800,6 +800,7 @@ adjust_inherited_attrs_mutator(Node *node,
var->varno == context->old_rt_index)
{
var->varno = context->new_rt_index;
var->varnoold = context->new_rt_index;
if (var->varattno > 0)
{
char *attname = get_attname(context->old_relid,
@ -809,6 +810,7 @@ adjust_inherited_attrs_mutator(Node *node,
if (var->varattno == InvalidAttrNumber)
elog(ERROR, "Relation \"%s\" has no column \"%s\"",
get_rel_name(context->new_relid), attname);
var->varoattno = var->varattno;
pfree(attname);
}
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.90 2003/06/15 22:51:45 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.91 2003/06/29 23:05:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -581,7 +581,7 @@ create_unique_path(Query *root, RelOptInfo *rel, Path *subpath)
else
{
pathnode->rows = rel->rows;
numCols = length(rel->targetlist); /* second-best estimate */
numCols = length(FastListValue(&rel->reltargetlist));
}
/*

View File

@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.83 2003/05/28 16:03:56 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.84 2003/06/29 23:05:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -26,6 +26,7 @@
#include "nodes/makefuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/plancat.h"
#include "optimizer/tlist.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteManip.h"
#include "utils/builtins.h"
@ -44,7 +45,8 @@
* Given the Oid of the relation, return the following info into fields
* of the RelOptInfo struct:
*
* varlist list of physical columns (expressed as Vars)
* min_attr lowest valid AttrNumber
* max_attr highest valid AttrNumber
* indexlist list of IndexOptInfos for relation's indexes
* pages number of pages
* tuples number of tuples
@ -52,49 +54,15 @@
void
get_relation_info(Oid relationObjectId, RelOptInfo *rel)
{
Relation relation;
Index varno = rel->relid;
Relation relation;
bool hasindex;
List *varlist = NIL;
List *indexinfos = NIL;
int attrno,
numattrs;
relation = heap_open(relationObjectId, AccessShareLock);
/*
* Make list of physical Vars. But if there are any dropped columns,
* punt and set varlist to NIL. (XXX Ideally we would like to include
* dropped columns so that the varlist models the physical tuples
* of the relation. However this creates problems for ExecTypeFromTL,
* which may be asked to build a tupdesc for a tlist that includes vars
* of no-longer-existent types. In theory we could dig out the required
* info from the pg_attribute entries of the relation, but that data is
* not readily available to ExecTypeFromTL. For now, punt and don't
* apply the physical-tlist optimization when there are dropped cols.)
*/
numattrs = RelationGetNumberOfAttributes(relation);
for (attrno = 1; attrno <= numattrs; attrno++)
{
Form_pg_attribute att_tup = relation->rd_att->attrs[attrno - 1];
if (att_tup->attisdropped)
{
/* found a dropped col, so punt */
varlist = NIL;
break;
}
varlist = lappend(varlist,
makeVar(varno,
attrno,
att_tup->atttypid,
att_tup->atttypmod,
0));
}
rel->varlist = varlist;
rel->min_attr = FirstLowInvalidHeapAttributeNumber + 1;
rel->max_attr = RelationGetNumberOfAttributes(relation);
/*
* Make list of indexes. Ignore indexes on system catalogs if told to.
@ -199,6 +167,65 @@ get_relation_info(Oid relationObjectId, RelOptInfo *rel)
heap_close(relation, AccessShareLock);
}
/*
* build_physical_tlist
*
* Build a targetlist consisting of exactly the relation's user attributes,
* in order. The executor can special-case such tlists to avoid a projection
* step at runtime, so we use such tlists preferentially for scan nodes.
*
* Exception: if there are any dropped columns, we punt and return NIL.
* Ideally we would like to handle the dropped-column case too. However this
* creates problems for ExecTypeFromTL, which may be asked to build a tupdesc
* for a tlist that includes vars of no-longer-existent types. In theory we
* could dig out the required info from the pg_attribute entries of the
* relation, but that data is not readily available to ExecTypeFromTL.
* For now, we don't apply the physical-tlist optimization when there are
* dropped cols.
*/
List *
build_physical_tlist(Query *root, RelOptInfo *rel)
{
Index varno = rel->relid;
RangeTblEntry *rte = rt_fetch(varno, root->rtable);
Relation relation;
FastList tlist;
int attrno,
numattrs;
FastListInit(&tlist);
Assert(rte->rtekind == RTE_RELATION);
relation = heap_open(rte->relid, AccessShareLock);
numattrs = RelationGetNumberOfAttributes(relation);
for (attrno = 1; attrno <= numattrs; attrno++)
{
Form_pg_attribute att_tup = relation->rd_att->attrs[attrno - 1];
if (att_tup->attisdropped)
{
/* found a dropped col, so punt */
FastListInit(&tlist);
break;
}
FastAppend(&tlist,
create_tl_element(makeVar(varno,
attrno,
att_tup->atttypid,
att_tup->atttypmod,
0),
attrno));
}
heap_close(relation, AccessShareLock);
return FastListValue(&tlist);
}
/*
* restriction_selectivity
*

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.48 2003/02/15 20:12:40 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.49 2003/06/29 23:05:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -24,7 +24,7 @@
static RelOptInfo *make_base_rel(Query *root, int relid);
static List *new_join_tlist(List *tlist, int first_resdomno);
static void build_joinrel_tlist(Query *root, RelOptInfo *joinrel);
static List *build_joinrel_restrictlist(Query *root,
RelOptInfo *joinrel,
RelOptInfo *outer_rel,
@ -130,7 +130,7 @@ make_base_rel(Query *root, int relid)
rel->relids = bms_make_singleton(relid);
rel->rows = 0;
rel->width = 0;
rel->targetlist = NIL;
FastListInit(&rel->reltargetlist);
rel->pathlist = NIL;
rel->cheapest_startup_path = NULL;
rel->cheapest_total_path = NULL;
@ -138,7 +138,7 @@ make_base_rel(Query *root, int relid)
rel->pruneable = true;
rel->relid = relid;
rel->rtekind = rte->rtekind;
rel->varlist = NIL;
/* min_attr, max_attr, attr_needed, attr_widths are set below */
rel->indexlist = NIL;
rel->pages = 0;
rel->tuples = 0;
@ -160,7 +160,9 @@ make_base_rel(Query *root, int relid)
break;
case RTE_SUBQUERY:
case RTE_FUNCTION:
/* Subquery or function --- nothing to do here */
/* Subquery or function --- need only set up attr range */
rel->min_attr = 1;
rel->max_attr = length(rte->eref->colnames);
break;
default:
elog(ERROR, "make_base_rel: unsupported RTE kind %d",
@ -168,6 +170,19 @@ make_base_rel(Query *root, int relid)
break;
}
if (rel->max_attr >= rel->min_attr)
{
rel->attr_needed = (Relids *)
palloc0((rel->max_attr - rel->min_attr + 1) * sizeof(Relids));
rel->attr_widths = (int32 *)
palloc0((rel->max_attr - rel->min_attr + 1) * sizeof(int32));
}
else
{
rel->attr_needed = NULL;
rel->attr_widths = NULL;
}
return rel;
}
@ -252,8 +267,6 @@ build_join_rel(Query *root,
{
RelOptInfo *joinrel;
List *restrictlist;
List *new_outer_tlist;
List *new_inner_tlist;
/*
* See if we already have a joinrel for this set of base rels.
@ -283,7 +296,7 @@ build_join_rel(Query *root,
joinrel->relids = bms_copy(joinrelids);
joinrel->rows = 0;
joinrel->width = 0;
joinrel->targetlist = NIL;
FastListInit(&joinrel->reltargetlist);
joinrel->pathlist = NIL;
joinrel->cheapest_startup_path = NULL;
joinrel->cheapest_total_path = NULL;
@ -291,7 +304,10 @@ build_join_rel(Query *root,
joinrel->pruneable = true;
joinrel->relid = 0; /* indicates not a baserel */
joinrel->rtekind = RTE_JOIN;
joinrel->varlist = NIL;
joinrel->min_attr = 0;
joinrel->max_attr = 0;
joinrel->attr_needed = NULL;
joinrel->attr_widths = NULL;
joinrel->indexlist = NIL;
joinrel->pages = 0;
joinrel->tuples = 0;
@ -305,24 +321,10 @@ build_join_rel(Query *root,
joinrel->index_inner_paths = NIL;
/*
* Create a new tlist by removing irrelevant elements from both tlists
* of the outer and inner join relations and then merging the results
* together.
*
* XXX right now we don't remove any irrelevant elements, we just append
* the two tlists together. Someday consider pruning vars from the
* join's targetlist if they are needed only to evaluate restriction
* clauses of this join, and will never be accessed at higher levels
* of the plantree.
*
* NOTE: the tlist order for a join rel will depend on which pair of
* outer and inner rels we first try to build it from. But the
* contents should be the same regardless.
* Create a new tlist containing just the vars that need to be output
* from this join (ie, are needed for higher joinclauses or final output).
*/
new_outer_tlist = new_join_tlist(outer_rel->targetlist, 1);
new_inner_tlist = new_join_tlist(inner_rel->targetlist,
length(new_outer_tlist) + 1);
joinrel->targetlist = nconc(new_outer_tlist, new_inner_tlist);
build_joinrel_tlist(root, joinrel);
/*
* Construct restrict and join clause lists for the new joinrel. (The
@ -353,42 +355,51 @@ build_join_rel(Query *root,
}
/*
* new_join_tlist
* Builds a join relation's target list by keeping those elements that
* will be in the final target list and any other elements that are still
* needed for future joins. For a target list entry to still be needed
* for future joins, its 'joinlist' field must not be empty after removal
* of all relids in 'other_relids'.
* build_joinrel_tlist
* Builds a join relation's target list.
*
* XXX the above comment refers to code that is long dead and gone;
* we don't keep track of joinlists for individual targetlist entries
* anymore. For now, all vars present in either input tlist will be
* emitted in the join's tlist.
* The join's targetlist includes all Vars of its member relations that
* will still be needed above the join.
*
* 'tlist' is the target list of one of the join relations
* 'first_resdomno' is the resdom number to use for the first created
* target list entry
* In a former lifetime, this just merged the tlists of the two member
* relations first presented. While we could still do that, working from
* lists of Vars would mean doing a find_base_rel lookup for each Var.
* It seems more efficient to scan the list of base rels and collect the
* needed vars directly from there.
*
* Returns the new target list.
* We also compute the expected width of the join's output, making use
* of data that was cached at the baserel level by set_rel_width().
*/
static List *
new_join_tlist(List *tlist,
int first_resdomno)
static void
build_joinrel_tlist(Query *root, RelOptInfo *joinrel)
{
int resdomno = first_resdomno - 1;
List *t_list = NIL;
List *i;
Relids relids = joinrel->relids;
List *rels;
List *vars;
foreach(i, tlist)
FastListInit(&joinrel->reltargetlist);
joinrel->width = 0;
foreach(rels, root->base_rel_list)
{
TargetEntry *tle = lfirst(i);
RelOptInfo *baserel = (RelOptInfo *) lfirst(rels);
resdomno += 1;
t_list = lappend(t_list,
create_tl_element((Var *) tle->expr, resdomno));
if (!bms_is_member(baserel->relid, relids))
continue;
foreach(vars, FastListValue(&baserel->reltargetlist))
{
Var *var = (Var *) lfirst(vars);
int ndx = var->varattno - baserel->min_attr;
if (bms_nonempty_difference(baserel->attr_needed[ndx], relids))
{
FastAppend(&joinrel->reltargetlist, var);
Assert(baserel->attr_widths[ndx] > 0);
joinrel->width += baserel->attr_widths[ndx];
}
}
}
return t_list;
}
/*

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/tlist.c,v 1.56 2003/05/06 00:20:32 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/tlist.c,v 1.57 2003/06/29 23:05:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -20,7 +20,7 @@
/*****************************************************************************
* ---------- RELATION node target list routines ----------
* Target list creation and searching utilities
*****************************************************************************/
/*
@ -79,24 +79,6 @@ tlist_member(Node *node, List *targetlist)
return (Resdom *) NULL;
}
/*
* add_var_to_tlist
* Creates a targetlist entry corresponding to the supplied var node
* 'var' and adds the new targetlist entry to the targetlist field of
* 'rel'. No entry is created if 'var' is already in the tlist.
*/
void
add_var_to_tlist(RelOptInfo *rel, Var *var)
{
if (!tlistentry_member((Node *) var, rel->targetlist))
{
/* XXX is copyObject necessary here? */
rel->targetlist = lappend(rel->targetlist,
create_tl_element((Var *) copyObject(var),
length(rel->targetlist) + 1));
}
}
/*
* create_tl_element
* Creates a target list entry node and its associated (resdom var) pair

View File

@ -13,7 +13,7 @@
*
* Copyright (c) 2003, PostgreSQL Global Development Group
*
* $Id: bitmapset.h,v 1.1 2003/02/08 20:20:55 tgl Exp $
* $Id: bitmapset.h,v 1.2 2003/06/29 23:05:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -59,6 +59,7 @@ extern Bitmapset *bms_difference(const Bitmapset *a, const Bitmapset *b);
extern bool bms_is_subset(const Bitmapset *a, const Bitmapset *b);
extern bool bms_is_member(int x, const Bitmapset *a);
extern bool bms_overlap(const Bitmapset *a, const Bitmapset *b);
extern bool bms_nonempty_difference(const Bitmapset *a, const Bitmapset *b);
extern int bms_singleton_member(const Bitmapset *a);
extern int bms_num_members(const Bitmapset *a);
/* optimized tests when we don't need to know exact membership count: */

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: relation.h,v 1.81 2003/06/15 22:51:45 tgl Exp $
* $Id: relation.h,v 1.82 2003/06/29 23:05:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -89,8 +89,8 @@ typedef struct QualCost
* clauses have been applied (ie, output rows of a plan for it)
* width - avg. number of bytes per tuple in the relation after the
* appropriate projections have been done (ie, output width)
* targetlist - List of TargetEntry nodes for the attributes we need
* to output from this relation
* reltargetlist - List of Var nodes for the attributes we need to
* output from this relation (in no particular order)
* pathlist - List of Path nodes, one for each potentially useful
* method of generating the relation
* cheapest_startup_path - the pathlist member with lowest startup cost
@ -107,7 +107,12 @@ typedef struct QualCost
* relid - RTE index (this is redundant with the relids field, but
* is provided for convenience of access)
* rtekind - distinguishes plain relation, subquery, or function RTE
* varlist - list of Vars for physical columns (only if table)
* min_attr, max_attr - range of valid AttrNumbers for rel
* attr_needed - array of bitmapsets indicating the highest joinrel
* in which each attribute is needed; if bit 0 is set then
* the attribute is needed as part of final targetlist
* attr_widths - cache space for per-attribute width estimates;
* zero means not computed yet
* indexlist - list of IndexOptInfo nodes for relation's indexes
* (always NIL if it's not a table)
* pages - number of disk pages in relation (zero if not a table)
@ -183,7 +188,7 @@ typedef struct RelOptInfo
int width; /* estimated avg width of result tuples */
/* materialization information */
List *targetlist;
FastList reltargetlist;
List *pathlist; /* Path structures */
struct Path *cheapest_startup_path;
struct Path *cheapest_total_path;
@ -193,7 +198,10 @@ typedef struct RelOptInfo
/* information about a base rel (not set for join rels!) */
Index relid;
RTEKind rtekind; /* RELATION, SUBQUERY, or FUNCTION */
List *varlist;
AttrNumber min_attr; /* smallest attrno of rel (often <0) */
AttrNumber max_attr; /* largest attrno of rel */
Relids *attr_needed; /* array indexed [min_attr .. max_attr] */
int32 *attr_widths; /* array indexed [min_attr .. max_attr] */
List *indexlist;
long pages;
double tuples;

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: plancat.h,v 1.29 2003/02/03 15:07:08 tgl Exp $
* $Id: plancat.h,v 1.30 2003/06/29 23:05:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -19,6 +19,8 @@
extern void get_relation_info(Oid relationObjectId, RelOptInfo *rel);
extern List *build_physical_tlist(Query *root, RelOptInfo *rel);
extern List *find_inheritance_children(Oid inhparent);
extern bool has_subclass(Oid relationId);

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: planmain.h,v 1.71 2003/06/29 00:33:44 tgl Exp $
* $Id: planmain.h,v 1.72 2003/06/29 23:05:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -56,7 +56,7 @@ extern Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan);
* prototypes for plan/initsplan.c
*/
extern void add_base_rels_to_query(Query *root, Node *jtnode);
extern void build_base_rel_tlists(Query *root, List *tlist);
extern void build_base_rel_tlists(Query *root, List *final_tlist);
extern Relids distribute_quals_to_rels(Query *root, Node *jtnode);
extern void process_implied_equality(Query *root,
Node *item1, Node *item2,

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: tlist.h,v 1.35 2003/05/06 00:20:33 tgl Exp $
* $Id: tlist.h,v 1.36 2003/06/29 23:05:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -16,10 +16,10 @@
#include "nodes/relation.h"
extern TargetEntry *tlistentry_member(Node *node, List *targetlist);
extern Resdom *tlist_member(Node *node, List *targetlist);
extern void add_var_to_tlist(RelOptInfo *rel, Var *var);
extern TargetEntry *create_tl_element(Var *var, int resdomno);
extern List *flatten_tlist(List *tlist);