diff --git a/src/backend/nodes/bitmapset.c b/src/backend/nodes/bitmapset.c index 5798b726497da09cf634eb3eb9b38df142130218..45c6ead0fc04caa03454bdf18e766fe253b8f6b3 100644 --- a/src/backend/nodes/bitmapset.c +++ b/src/backend/nodes/bitmapset.c @@ -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 * diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index e6791dddd69f9cbdfe558df126a579cf240a099e..cd04af9f821ee2feb35e6d939829cebda6fef630 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -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); + } } /* diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index 283987b63d53275846f7896ed546d97f68297638..34a3aaf1c949b5a17303ee62bd208f3dececc14a 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -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)) + Assert(IsA(var, Var)); + + /* The width probably hasn't been cached yet, but may as well check */ + if (rel->attr_widths[ndx] > 0) { - Var *var = (Var *) tle->expr; - Oid relid; + tuple_width += rel->attr_widths[ndx]; + continue; + } - relid = getrelid(var->varno, root->rtable); - if (relid != InvalidOid) + 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); diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c index f8e4906c13bda67fb883585827b910f8b45874fc..d4d1179d54576d49ebe9346937942fbb265344f3 100644 --- a/src/backend/optimizer/path/pathkeys.c +++ b/src/backend/optimizer/path/pathkeys.c @@ -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); } diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 81ee7962df90a5c4193d871909cebcd12542c4d6..1cdc3628b20ceb718b4e380e66232c12e77b1076 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -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; diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c index 05d963d94c10f8bb4c3e0a7e3d15995f34210238..cbc1ff365a31b9ddd7c99c91d5248ebdfeb17bc8 100644 --- a/src/backend/optimizer/plan/initsplan.c +++ b/src/backend/optimizer/plan/initsplan.c @@ -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: /* diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index ac6e0f0f35225e3600d53a0437a11f25c08088b2..03674e8990f8088835267b187a7d7ff44cdaf85f 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -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); } } diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index da9497d58a4c5691320392230da12284acf51bdb..9e919079ccf9c2d6d1f18ee34c6bb2a70259826e 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -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)); } /* diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index 36223be93fdc823cc088d742b6588eb7f2a5b567..3f4fe661717dbd405a971aac095fcfdc1e20df89 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -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 * diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index af44cb7f20616aa9a493c426332b2f72518cc53a..0b82569ba375ebde7478a436837aa1d09663e860 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -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; + + FastListInit(&joinrel->reltargetlist); + joinrel->width = 0; - foreach(i, tlist) + 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; - return t_list; + 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]; + } + } + } } /* diff --git a/src/backend/optimizer/util/tlist.c b/src/backend/optimizer/util/tlist.c index 9b10e8e97be34fc03b09c6460a458570e25472b9..7a4422a3594195a30a53b5ba24b8d25b5a994810 100644 --- a/src/backend/optimizer/util/tlist.c +++ b/src/backend/optimizer/util/tlist.c @@ -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 diff --git a/src/include/nodes/bitmapset.h b/src/include/nodes/bitmapset.h index 4e06d7a284a9137d3917f2987fb68725bb1a687d..7974c44aae78540fb4de5320f37b65363d692a18 100644 --- a/src/include/nodes/bitmapset.h +++ b/src/include/nodes/bitmapset.h @@ -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: */ diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index 8392ab505bd66789c88a1d8aa4ff680cf265a40f..2ab9e0f6e7f7062c81902f46cbb6daeeca545a09 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -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; diff --git a/src/include/optimizer/plancat.h b/src/include/optimizer/plancat.h index c6b18ab0da9463bc06cc47c143c63441294ac106..8f7cfbe6ceb67e98040ea648b687b46279928955 100644 --- a/src/include/optimizer/plancat.h +++ b/src/include/optimizer/plancat.h @@ -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); diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h index 106e9d1ce4b549cc4fe7ef873660dabe2bb59b6d..99c9470493df5a3995a8092fa60b57f1c2637b0c 100644 --- a/src/include/optimizer/planmain.h +++ b/src/include/optimizer/planmain.h @@ -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, diff --git a/src/include/optimizer/tlist.h b/src/include/optimizer/tlist.h index e2afc3ac82192fbb2074bb78e14628039aaef964..d22c78f8d8e694cca8f9c822e96792d10ee895a5 100644 --- a/src/include/optimizer/tlist.h +++ b/src/include/optimizer/tlist.h @@ -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);