diff options
Diffstat (limited to 'src/backend/optimizer')
26 files changed, 1926 insertions, 1551 deletions
diff --git a/src/backend/optimizer/geqo/geqo_eval.c b/src/backend/optimizer/geqo/geqo_eval.c index 1c70e4bcd8d..6b04eb49ea6 100644 --- a/src/backend/optimizer/geqo/geqo_eval.c +++ b/src/backend/optimizer/geqo/geqo_eval.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: geqo_eval.c,v 1.48 2000/02/15 20:49:14 tgl Exp $ + * $Id: geqo_eval.c,v 1.49 2000/04/12 17:15:18 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -99,8 +99,8 @@ geqo_eval(Query *root, Gene *tour, int num_gene) /* * compute fitness * - * XXX geqo does not currently support optimization for partial - * result retrieval --- how to fix? + * XXX geqo does not currently support optimization for partial result + * retrieval --- how to fix? */ fitness = joinrel->cheapest_total_path->total_cost; @@ -135,7 +135,7 @@ gimme_tree(Query *root, Gene *tour, int rel_count, int num_gene, RelOptInfo *old /* tour[0] = 3; tour[1] = 1; tour[2] = 2 */ base_rel_index = (int) tour[rel_count]; - inner_rel = (RelOptInfo *) nth(base_rel_index-1, root->base_rel_list); + inner_rel = (RelOptInfo *) nth(base_rel_index - 1, root->base_rel_list); if (rel_count == 0) { /* processing first join with @@ -145,15 +145,15 @@ gimme_tree(Query *root, Gene *tour, int rel_count, int num_gene, RelOptInfo *old } else { /* tree main part */ - List *acceptable_rels = lcons(inner_rel, NIL); + List *acceptable_rels = lcons(inner_rel, NIL); new_rel = make_rels_by_clause_joins(root, old_rel, acceptable_rels); - if (! new_rel) + if (!new_rel) { new_rel = make_rels_by_clauseless_joins(root, old_rel, acceptable_rels); - if (! new_rel) + if (!new_rel) elog(ERROR, "gimme_tree: failed to construct join rel"); } diff --git a/src/backend/optimizer/path/_deadcode/predmig.c b/src/backend/optimizer/path/_deadcode/predmig.c index 377a836d9a1..434780d664c 100644 --- a/src/backend/optimizer/path/_deadcode/predmig.c +++ b/src/backend/optimizer/path/_deadcode/predmig.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/_deadcode/Attic/predmig.c,v 1.6 2000/01/26 05:56:36 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/_deadcode/Attic/predmig.c,v 1.7 2000/04/12 17:15:21 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -485,7 +485,7 @@ xfunc_form_groups(Query *queryInfo, Stream root, Stream bottom) } -/* ------------------- UTILITY FUNCTIONS ------------------------- */ +/* ------------------- UTILITY FUNCTIONS ------------------------- */ /* ** xfunc_free_stream diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index 572ef00d2e8..a2e636395e2 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.59 2000/02/15 20:49:16 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.60 2000/04/12 17:15:19 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -24,8 +24,10 @@ #ifdef GEQO bool enable_geqo = true; + #else bool enable_geqo = false; + #endif int geqo_rels = GEQO_RELS; @@ -36,6 +38,7 @@ static RelOptInfo *make_one_rel_by_joins(Query *root, int levels_needed); #ifdef OPTIMIZER_DEBUG static void debug_print_rel(Query *root, RelOptInfo *rel); + #endif @@ -64,6 +67,7 @@ make_one_rel(Query *root) if (levels_needed == 1) { + /* * Single relation, no more processing is required. */ @@ -71,6 +75,7 @@ make_one_rel(Query *root) } else { + /* * Generate join tree. */ @@ -100,8 +105,8 @@ set_base_rel_pathlist(Query *root) /* * Generate paths and add them to the rel's pathlist. * - * Note: add_path() will discard any paths that are dominated - * by another available path, keeping only those paths that are + * Note: add_path() will discard any paths that are dominated by + * another available path, keeping only those paths that are * superior along at least one dimension of cost or sortedness. */ @@ -116,9 +121,10 @@ set_base_rel_pathlist(Query *root) rel->baserestrictinfo, rel->joininfo); - /* Note: create_or_index_paths depends on create_index_paths - * to have marked OR restriction clauses with relevant indices; - * this is why it doesn't need to be given the list of indices. + /* + * Note: create_or_index_paths depends on create_index_paths to + * have marked OR restriction clauses with relevant indices; this + * is why it doesn't need to be given the list of indices. */ create_or_index_paths(root, rel, rel->baserestrictinfo); @@ -153,11 +159,11 @@ make_one_rel_by_joins(Query *root, int levels_needed) return geqo(root); /* - * We employ a simple "dynamic programming" algorithm: we first - * find all ways to build joins of two base relations, then all ways - * to build joins of three base relations (from two-base-rel joins - * and other base rels), then four-base-rel joins, and so on until - * we have considered all ways to join all N relations into one rel. + * We employ a simple "dynamic programming" algorithm: we first find + * all ways to build joins of two base relations, then all ways to + * build joins of three base relations (from two-base-rel joins and + * other base rels), then four-base-rel joins, and so on until we have + * considered all ways to join all N relations into one rel. */ for (lev = 2; lev <= levels_needed; lev++) @@ -185,9 +191,10 @@ make_one_rel_by_joins(Query *root, int levels_needed) rel = (RelOptInfo *) lfirst(x); #ifdef NOT_USED + /* - * * for each expensive predicate in each path in each distinct - * rel, * consider doing pullup -- JMH + * * for each expensive predicate in each path in each + * distinct rel, * consider doing pullup -- JMH */ if (XfuncMode != XFUNC_NOPULL && XfuncMode != XFUNC_OFF) xfunc_trypullup(rel); diff --git a/src/backend/optimizer/path/clausesel.c b/src/backend/optimizer/path/clausesel.c index 985155edf92..d430059a1e0 100644 --- a/src/backend/optimizer/path/clausesel.c +++ b/src/backend/optimizer/path/clausesel.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.33 2000/03/23 23:35:47 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.34 2000/04/12 17:15:19 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -28,17 +28,18 @@ * Data structure for accumulating info about possible range-query * clause pairs in clauselist_selectivity. */ -typedef struct RangeQueryClause { - struct RangeQueryClause *next; /* next in linked list */ +typedef struct RangeQueryClause +{ + struct RangeQueryClause *next; /* next in linked list */ Node *var; /* The common variable of the clauses */ bool have_lobound; /* found a low-bound clause yet? */ bool have_hibound; /* found a high-bound clause yet? */ - Selectivity lobound; /* Selectivity of a var > something clause */ - Selectivity hibound; /* Selectivity of a var < something clause */ + Selectivity lobound; /* Selectivity of a var > something clause */ + Selectivity hibound; /* Selectivity of a var < something clause */ } RangeQueryClause; static void addRangeClause(RangeQueryClause **rqlist, Node *clause, - int flag, bool isLTsel, Selectivity s2); + int flag, bool isLTsel, Selectivity s2); /**************************************************************************** @@ -59,7 +60,7 @@ restrictlist_selectivity(Query *root, int varRelid) { List *clauselist = get_actual_clauses(restrictinfo_list); - Selectivity result; + Selectivity result; result = clauselist_selectivity(root, clauselist, varRelid); freeList(clauselist); @@ -75,7 +76,7 @@ restrictlist_selectivity(Query *root, * See clause_selectivity() for the meaning of the varRelid parameter. * * Our basic approach is to take the product of the selectivities of the - * subclauses. However, that's only right if the subclauses have independent + * subclauses. However, that's only right if the subclauses have independent * probabilities, and in reality they are often NOT independent. So, * we want to be smarter where we can. @@ -92,7 +93,7 @@ restrictlist_selectivity(Query *root, * see that hisel is the fraction of the range below the high bound, while * losel is the fraction above the low bound; so hisel can be interpreted * directly as a 0..1 value but we need to convert losel to 1-losel before - * interpreting it as a value. Then the available range is 1-losel to hisel.) + * interpreting it as a value. Then the available range is 1-losel to hisel.) * If the calculation yields zero or negative, however, we chicken out and * use the default interpretation; that probably means that one or both * selectivities is a default estimate rather than an actual range value. @@ -104,9 +105,9 @@ clauselist_selectivity(Query *root, List *clauses, int varRelid) { - Selectivity s1 = 1.0; - RangeQueryClause *rqlist = NULL; - List *clist; + Selectivity s1 = 1.0; + RangeQueryClause *rqlist = NULL; + List *clist; /* * Initial scan over clauses. Anything that doesn't look like a @@ -116,13 +117,13 @@ clauselist_selectivity(Query *root, foreach(clist, clauses) { Node *clause = (Node *) lfirst(clist); - Selectivity s2; + Selectivity s2; /* - * See if it looks like a restriction clause with a constant. - * (If it's not a constant we can't really trust the selectivity!) - * NB: for consistency of results, this fragment of code had - * better match what clause_selectivity() would do. + * See if it looks like a restriction clause with a constant. (If + * it's not a constant we can't really trust the selectivity!) NB: + * for consistency of results, this fragment of code had better + * match what clause_selectivity() would do. */ if (varRelid != 0 || NumRelids(clause) == 1) { @@ -147,11 +148,12 @@ clauselist_selectivity(Query *root, root->rtable), attno, constval, flag); + /* - * If we reach here, we have computed the same result - * that clause_selectivity would, so we can just use s2 - * if it's the wrong oprrest. But if it's the right - * oprrest, add the clause to rqlist for later processing. + * If we reach here, we have computed the same result that + * clause_selectivity would, so we can just use s2 if it's + * the wrong oprrest. But if it's the right oprrest, add + * the clause to rqlist for later processing. */ switch (oprrest) { @@ -166,7 +168,7 @@ clauselist_selectivity(Query *root, s1 = s1 * s2; break; } - continue; /* drop to loop bottom */ + continue; /* drop to loop bottom */ } } /* Not the right form, so treat it generically. */ @@ -179,12 +181,12 @@ clauselist_selectivity(Query *root, */ while (rqlist != NULL) { - RangeQueryClause *rqnext; + RangeQueryClause *rqnext; if (rqlist->have_lobound && rqlist->have_hibound) { /* Successfully matched a pair of range clauses */ - Selectivity s2 = rqlist->hibound + rqlist->lobound - 1.0; + Selectivity s2 = rqlist->hibound + rqlist->lobound - 1.0; /* * A zero or slightly negative s2 should be converted into a @@ -199,14 +201,20 @@ clauselist_selectivity(Query *root, { if (s2 < -0.01) { - /* No data available --- use a default estimate that + + /* + * No data available --- use a default estimate that * is small, but not real small. */ s2 = 0.01; } else { - /* It's just roundoff error; use a small positive value */ + + /* + * It's just roundoff error; use a small positive + * value + */ s2 = 1.0e-10; } } @@ -239,15 +247,15 @@ static void addRangeClause(RangeQueryClause **rqlist, Node *clause, int flag, bool isLTsel, Selectivity s2) { - RangeQueryClause *rqelem; - Node *var; - bool is_lobound; + RangeQueryClause *rqelem; + Node *var; + bool is_lobound; /* get_relattval sets flag&SEL_RIGHT if the var is on the LEFT. */ if (flag & SEL_RIGHT) { var = (Node *) get_leftop((Expr *) clause); - is_lobound = ! isLTsel; /* x < something is high bound */ + is_lobound = !isLTsel; /* x < something is high bound */ } else { @@ -257,23 +265,27 @@ addRangeClause(RangeQueryClause **rqlist, Node *clause, for (rqelem = *rqlist; rqelem; rqelem = rqelem->next) { - /* We use full equal() here because the "var" might be a function + + /* + * We use full equal() here because the "var" might be a function * of one or more attributes of the same relation... */ - if (! equal(var, rqelem->var)) + if (!equal(var, rqelem->var)) continue; /* Found the right group to put this clause in */ if (is_lobound) { - if (! rqelem->have_lobound) + if (!rqelem->have_lobound) { rqelem->have_lobound = true; rqelem->lobound = s2; } else { - /* We have found two similar clauses, such as - * x < y AND x < z. Keep only the more restrictive one. + + /* + * We have found two similar clauses, such as x < y AND x + * < z. Keep only the more restrictive one. */ if (rqelem->lobound > s2) rqelem->lobound = s2; @@ -281,15 +293,17 @@ addRangeClause(RangeQueryClause **rqlist, Node *clause, } else { - if (! rqelem->have_hibound) + if (!rqelem->have_hibound) { rqelem->have_hibound = true; rqelem->hibound = s2; } else { - /* We have found two similar clauses, such as - * x > y AND x > z. Keep only the more restrictive one. + + /* + * We have found two similar clauses, such as x > y AND x + * > z. Keep only the more restrictive one. */ if (rqelem->hibound > s2) rqelem->hibound = s2; @@ -331,7 +345,7 @@ addRangeClause(RangeQueryClause **rqlist, Node *clause, * nestloop join's inner relation --- varRelid should then be the ID of the * inner relation. * - * When varRelid is 0, all variables are treated as variables. This + * When varRelid is 0, all variables are treated as variables. This * is appropriate for ordinary join clauses and restriction clauses. */ Selectivity @@ -339,12 +353,13 @@ clause_selectivity(Query *root, Node *clause, int varRelid) { - Selectivity s1 = 1.0; /* default for any unhandled clause type */ + Selectivity s1 = 1.0; /* default for any unhandled clause type */ if (clause == NULL) return s1; if (IsA(clause, Var)) { + /* * we have a bool Var. This is exactly equivalent to the clause: * reln.attribute = 't' so we compute the selectivity as if that @@ -352,7 +367,7 @@ clause_selectivity(Query *root, * didn't want to have to do system cache look ups to find out all * of that info. */ - Index varno = ((Var *) clause)->varno; + Index varno = ((Var *) clause)->varno; if (varRelid == 0 || varRelid == (int) varno) s1 = restriction_selectivity(F_EQSEL, @@ -377,7 +392,7 @@ clause_selectivity(Query *root, { /* inverse of the selectivity of the underlying clause */ s1 = 1.0 - clause_selectivity(root, - (Node*) get_notclausearg((Expr*) clause), + (Node *) get_notclausearg((Expr *) clause), varRelid); } else if (and_clause(clause)) @@ -389,18 +404,21 @@ clause_selectivity(Query *root, } else if (or_clause(clause)) { + /* * Selectivities for an 'or' clause are computed as s1+s2 - s1*s2 - * to account for the probable overlap of selected tuple sets. - * XXX is this too conservative? + * to account for the probable overlap of selected tuple sets. XXX + * is this too conservative? */ - List *arg; + List *arg; + s1 = 0.0; foreach(arg, ((Expr *) clause)->args) { - Selectivity s2 = clause_selectivity(root, + Selectivity s2 = clause_selectivity(root, (Node *) lfirst(arg), varRelid); + s1 = s1 + s2 - s1 * s2; } } @@ -411,17 +429,20 @@ clause_selectivity(Query *root, if (varRelid != 0) { + /* - * If we are considering a nestloop join then all clauses - * are restriction clauses, since we are only interested in - * the one relation. + * If we are considering a nestloop join then all clauses are + * restriction clauses, since we are only interested in the + * one relation. */ is_join_clause = false; } else { + /* - * Otherwise, it's a join if there's more than one relation used. + * Otherwise, it's a join if there's more than one relation + * used. */ is_join_clause = (NumRelids(clause) > 1); } @@ -432,8 +453,8 @@ clause_selectivity(Query *root, RegProcedure oprjoin = get_oprjoin(opno); /* - * if the oprjoin procedure is missing for whatever reason, use a - * selectivity of 0.5 + * if the oprjoin procedure is missing for whatever reason, + * use a selectivity of 0.5 */ if (!oprjoin) s1 = (Selectivity) 0.5; @@ -460,8 +481,8 @@ clause_selectivity(Query *root, RegProcedure oprrest = get_oprrest(opno); /* - * if the oprrest procedure is missing for whatever reason, use a - * selectivity of 0.5 + * if the oprrest procedure is missing for whatever reason, + * use a selectivity of 0.5 */ if (!oprrest) s1 = (Selectivity) 0.5; @@ -484,6 +505,7 @@ clause_selectivity(Query *root, } else if (is_funcclause(clause)) { + /* * This is not an operator, so we guess at the selectivity. THIS * IS A HACK TO GET V4 OUT THE DOOR. FUNCS SHOULD BE ABLE TO HAVE @@ -493,6 +515,7 @@ clause_selectivity(Query *root, } else if (is_subplan(clause)) { + /* * Just for the moment! FIX ME! - vadim 02/04/98 */ diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index f6bdcb828b9..6ecfb2a4713 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -19,7 +19,7 @@ * * Obviously, taking constants for these values is an oversimplification, * but it's tough enough to get any useful estimates even at this level of - * detail. Note that all of these parameters are user-settable, in case + * detail. Note that all of these parameters are user-settable, in case * the default values are drastically off for a particular platform. * * We compute two separate costs for each path: @@ -37,12 +37,12 @@ * will be no zero divide.) RelOptInfos, Paths, and Plans themselves never * account for LIMIT. * - * + * * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.56 2000/04/09 04:31:36 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.57 2000/04/12 17:15:19 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -118,6 +118,7 @@ cost_seqscan(Path *path, RelOptInfo *baserel) /* disk costs */ if (lfirsti(baserel->relids) < 0) { + /* * cost of sequentially scanning a materialized temporary relation */ @@ -125,15 +126,17 @@ cost_seqscan(Path *path, RelOptInfo *baserel) } else { + /* * The cost of reading a page sequentially is 1.0, by definition. * Note that the Unix kernel will typically do some amount of - * read-ahead optimization, so that this cost is less than the true - * cost of reading a page from disk. We ignore that issue here, - * but must take it into account when estimating the cost of + * read-ahead optimization, so that this cost is less than the + * true cost of reading a page from disk. We ignore that issue + * here, but must take it into account when estimating the cost of * non-sequential accesses! */ - run_cost += baserel->pages; /* sequential fetches with cost 1.0 */ + run_cost += baserel->pages; /* sequential fetches with cost + * 1.0 */ } /* CPU costs */ @@ -151,7 +154,7 @@ cost_seqscan(Path *path, RelOptInfo *baserel) * * The simplistic model that the cost is random_page_cost is what we want * to use for large relations; but for small ones that is a serious - * overestimate because of the effects of caching. This routine tries to + * overestimate because of the effects of caching. This routine tries to * account for that. * * Unfortunately we don't have any good way of estimating the effective cache @@ -221,12 +224,12 @@ cost_index(Path *path, Query *root, Cost cpu_per_tuple; Cost indexStartupCost; Cost indexTotalCost; - Selectivity indexSelectivity; + Selectivity indexSelectivity; double tuples_fetched; double pages_fetched; /* Should only be applied to base relations */ - Assert(IsA(baserel, RelOptInfo) && IsA(index, IndexOptInfo)); + Assert(IsA(baserel, RelOptInfo) &&IsA(index, IndexOptInfo)); Assert(length(baserel->relids) == 1); if (!enable_indexscan && !is_injoin) @@ -234,8 +237,9 @@ cost_index(Path *path, Query *root, /* * Call index-access-method-specific code to estimate the processing - * cost for scanning the index, as well as the selectivity of the index - * (ie, the fraction of main-table tuples we will have to retrieve). + * cost for scanning the index, as well as the selectivity of the + * index (ie, the fraction of main-table tuples we will have to + * retrieve). */ fmgr(index->amcostestimate, root, baserel, index, indexQuals, &indexStartupCost, &indexTotalCost, &indexSelectivity); @@ -249,17 +253,18 @@ cost_index(Path *path, Query *root, * * If the number of tuples is much smaller than the number of pages in * the relation, each tuple will cost a separate nonsequential fetch. - * If it is comparable or larger, then probably we will be able to avoid - * some fetches. We use a growth rate of log(#tuples/#pages + 1) --- - * probably totally bogus, but intuitively it gives the right shape of - * curve at least. + * If it is comparable or larger, then probably we will be able to + * avoid some fetches. We use a growth rate of log(#tuples/#pages + + * 1) --- probably totally bogus, but intuitively it gives the right + * shape of curve at least. * * XXX if the relation has recently been "clustered" using this index, - * then in fact the target tuples will be highly nonuniformly distributed, - * and we will be seriously overestimating the scan cost! Currently we - * have no way to know whether the relation has been clustered, nor how - * much it's been modified since the last clustering, so we ignore this - * effect. Would be nice to do better someday. + * then in fact the target tuples will be highly nonuniformly + * distributed, and we will be seriously overestimating the scan cost! + * Currently we have no way to know whether the relation has been + * clustered, nor how much it's been modified since the last + * clustering, so we ignore this effect. Would be nice to do better + * someday. */ tuples_fetched = indexSelectivity * baserel->tuples; @@ -274,8 +279,8 @@ cost_index(Path *path, Query *root, pages_fetched = tuples_fetched; /* - * Now estimate one nonsequential access per page fetched, - * plus appropriate CPU costs per tuple. + * Now estimate one nonsequential access per page fetched, plus + * appropriate CPU costs per tuple. */ /* disk costs for main table */ @@ -283,16 +288,18 @@ cost_index(Path *path, Query *root, /* CPU costs */ cpu_per_tuple = cpu_tuple_cost + baserel->baserestrictcost; + /* - * Normally the indexquals will be removed from the list of restriction - * clauses that we have to evaluate as qpquals, so we should subtract - * their costs from baserestrictcost. For a lossy index, however, we - * will have to recheck all the quals and so mustn't subtract anything. - * Also, if we are doing a join then some of the indexquals are join - * clauses and shouldn't be subtracted. Rather than work out exactly - * how much to subtract, we don't subtract anything in that case either. + * Normally the indexquals will be removed from the list of + * restriction clauses that we have to evaluate as qpquals, so we + * should subtract their costs from baserestrictcost. For a lossy + * index, however, we will have to recheck all the quals and so + * mustn't subtract anything. Also, if we are doing a join then some + * of the indexquals are join clauses and shouldn't be subtracted. + * Rather than work out exactly how much to subtract, we don't + * subtract anything in that case either. */ - if (! index->lossy && ! is_injoin) + if (!index->lossy && !is_injoin) cpu_per_tuple -= cost_qual_eval(indexQuals); run_cost += cpu_per_tuple * tuples_fetched; @@ -326,7 +333,7 @@ cost_tidscan(Path *path, RelOptInfo *baserel, List *tideval) path->startup_cost = startup_cost; path->total_cost = startup_cost + run_cost; } - + /* * cost_sort * Determines and returns the cost of sorting a relation. @@ -341,7 +348,7 @@ cost_tidscan(Path *path, RelOptInfo *baserel, List *tideval) * If the total volume exceeds SortMem, we switch to a tape-style merge * algorithm. There will still be about t*log2(t) tuple comparisons in * total, but we will also need to write and read each tuple once per - * merge pass. We expect about ceil(log6(r)) merge passes where r is the + * merge pass. We expect about ceil(log6(r)) merge passes where r is the * number of initial runs formed (log6 because tuplesort.c uses six-tape * merging). Since the average initial run should be about twice SortMem, * we have @@ -385,8 +392,8 @@ cost_sort(Path *path, List *pathkeys, double tuples, int width) /* * CPU costs * - * Assume about two operator evals per tuple comparison - * and N log2 N comparisons + * Assume about two operator evals per tuple comparison and N log2 N + * comparisons */ startup_cost += 2.0 * cpu_operator_cost * tuples * LOG2(tuples); @@ -408,7 +415,7 @@ cost_sort(Path *path, List *pathkeys, double tuples, int width) /* * Note: should we bother to assign a nonzero run_cost to reflect the - * overhead of extracting tuples from the sort result? Probably not + * overhead of extracting tuples from the sort result? Probably not * worth worrying about. */ path->startup_cost = startup_cost; @@ -440,19 +447,22 @@ cost_nestloop(Path *path, startup_cost += disable_cost; /* cost of source data */ + /* - * NOTE: we assume that the inner path's startup_cost is paid once, not - * over again on each restart. This is certainly correct if the inner - * path is materialized. Are there any cases where it is wrong? + * NOTE: we assume that the inner path's startup_cost is paid once, + * not over again on each restart. This is certainly correct if the + * inner path is materialized. Are there any cases where it is wrong? */ startup_cost += outer_path->startup_cost + inner_path->startup_cost; run_cost += outer_path->total_cost - outer_path->startup_cost; run_cost += outer_path->parent->rows * (inner_path->total_cost - inner_path->startup_cost); - /* Number of tuples processed (not number emitted!). If inner path is + /* + * Number of tuples processed (not number emitted!). If inner path is * an indexscan, be sure to use its estimated output row count, which - * may be lower than the restriction-clause-only row count of its parent. + * may be lower than the restriction-clause-only row count of its + * parent. */ if (IsA(inner_path, IndexPath)) ntuples = ((IndexPath *) inner_path)->rows; @@ -498,11 +508,12 @@ cost_mergejoin(Path *path, startup_cost += disable_cost; /* cost of source data */ + /* * Note we are assuming that each source tuple is fetched just once, - * which is not right in the presence of equal keys. If we had a way of - * estimating the proportion of equal keys, we could apply a correction - * factor... + * which is not right in the presence of equal keys. If we had a way + * of estimating the proportion of equal keys, we could apply a + * correction factor... */ if (outersortkeys) /* do we need to sort outer? */ { @@ -537,10 +548,10 @@ cost_mergejoin(Path *path, } /* - * Estimate the number of tuples to be processed in the mergejoin itself - * as one per tuple in the two source relations. This could be a drastic - * underestimate if there are many equal-keyed tuples in either relation, - * but we have no good way of estimating that... + * Estimate the number of tuples to be processed in the mergejoin + * itself as one per tuple in the two source relations. This could be + * a drastic underestimate if there are many equal-keyed tuples in + * either relation, but we have no good way of estimating that... */ ntuples = outer_path->parent->rows + inner_path->parent->rows; @@ -575,9 +586,9 @@ cost_hashjoin(Path *path, Cost cpu_per_tuple; double ntuples; double outerbytes = relation_byte_size(outer_path->parent->rows, - outer_path->parent->width); + outer_path->parent->width); double innerbytes = relation_byte_size(inner_path->parent->rows, - inner_path->parent->width); + inner_path->parent->width); long hashtablebytes = SortMem * 1024L; if (!enable_hashjoin) @@ -592,7 +603,8 @@ cost_hashjoin(Path *path, startup_cost += cpu_operator_cost * inner_path->parent->rows; run_cost += cpu_operator_cost * outer_path->parent->rows; - /* the number of tuple comparisons needed is the number of outer + /* + * the number of tuple comparisons needed is the number of outer * tuples times the typical hash bucket size, which we estimate * conservatively as the inner disbursion times the inner tuple count. */ @@ -601,9 +613,9 @@ cost_hashjoin(Path *path, /* * Estimate the number of tuples that get through the hashing filter - * as one per tuple in the two source relations. This could be a drastic - * underestimate if there are many equal-keyed tuples in either relation, - * but we have no good way of estimating that... + * as one per tuple in the two source relations. This could be a + * drastic underestimate if there are many equal-keyed tuples in + * either relation, but we have no good way of estimating that... */ ntuples = outer_path->parent->rows + inner_path->parent->rows; @@ -614,33 +626,31 @@ cost_hashjoin(Path *path, /* * if inner relation is too big then we will need to "batch" the join, * which implies writing and reading most of the tuples to disk an - * extra time. Charge one cost unit per page of I/O (correct since - * it should be nice and sequential...). Writing the inner rel counts - * as startup cost, all the rest as run cost. + * extra time. Charge one cost unit per page of I/O (correct since it + * should be nice and sequential...). Writing the inner rel counts as + * startup cost, all the rest as run cost. */ if (innerbytes > hashtablebytes) { - double outerpages = page_size(outer_path->parent->rows, - outer_path->parent->width); - double innerpages = page_size(inner_path->parent->rows, - inner_path->parent->width); + double outerpages = page_size(outer_path->parent->rows, + outer_path->parent->width); + double innerpages = page_size(inner_path->parent->rows, + inner_path->parent->width); startup_cost += innerpages; run_cost += innerpages + 2 * outerpages; } /* - * Bias against putting larger relation on inside. We don't want - * an absolute prohibition, though, since larger relation might have + * Bias against putting larger relation on inside. We don't want an + * absolute prohibition, though, since larger relation might have * better disbursion --- and we can't trust the size estimates - * unreservedly, anyway. Instead, inflate the startup cost by - * the square root of the size ratio. (Why square root? No real good + * unreservedly, anyway. Instead, inflate the startup cost by the + * square root of the size ratio. (Why square root? No real good * reason, but it seems reasonable...) */ if (innerbytes > outerbytes && outerbytes > 0) - { startup_cost *= sqrt(innerbytes / outerbytes); - } path->startup_cost = startup_cost; path->total_cost = startup_cost + run_cost; @@ -656,7 +666,7 @@ cost_hashjoin(Path *path, Cost cost_qual_eval(List *quals) { - Cost total = 0; + Cost total = 0; cost_qual_eval_walker((Node *) quals, &total); return total; @@ -667,10 +677,11 @@ cost_qual_eval_walker(Node *node, Cost *total) { if (node == NULL) return false; + /* * Our basic strategy is to charge one cpu_operator_cost for each - * operator or function node in the given tree. Vars and Consts - * are charged zero, and so are boolean operators (AND, OR, NOT). + * operator or function node in the given tree. Vars and Consts are + * charged zero, and so are boolean operators (AND, OR, NOT). * Simplistic, but a lot better than no model at all. * * Should we try to account for the possibility of short-circuit @@ -678,7 +689,7 @@ cost_qual_eval_walker(Node *node, Cost *total) */ if (IsA(node, Expr)) { - Expr *expr = (Expr *) node; + Expr *expr = (Expr *) node; switch (expr->opType) { @@ -691,17 +702,19 @@ cost_qual_eval_walker(Node *node, Cost *total) case NOT_EXPR: break; case SUBPLAN_EXPR: + /* - * A subplan node in an expression indicates that the subplan - * will be executed on each evaluation, so charge accordingly. - * (We assume that sub-selects that can be executed as - * InitPlans have already been removed from the expression.) + * A subplan node in an expression indicates that the + * subplan will be executed on each evaluation, so charge + * accordingly. (We assume that sub-selects that can be + * executed as InitPlans have already been removed from + * the expression.) * * NOTE: this logic should agree with the estimates used by - * make_subplan() in plan/subselect.c. + * make_subplan() in plan/subselect.c. */ { - SubPlan *subplan = (SubPlan *) expr->oper; + SubPlan *subplan = (SubPlan *) expr->oper; Plan *plan = subplan->plan; Cost subcost; @@ -730,13 +743,14 @@ cost_qual_eval_walker(Node *node, Cost *total) } /* fall through to examine args of Expr node */ } + /* - * expression_tree_walker doesn't know what to do with RestrictInfo nodes, - * but we just want to recurse through them. + * expression_tree_walker doesn't know what to do with RestrictInfo + * nodes, but we just want to recurse through them. */ if (IsA(node, RestrictInfo)) { - RestrictInfo *restrictinfo = (RestrictInfo *) node; + RestrictInfo *restrictinfo = (RestrictInfo *) node; return cost_qual_eval_walker((Node *) restrictinfo->clause, total); } @@ -755,7 +769,7 @@ cost_qual_eval_walker(Node *node, Cost *total) * * We set the following fields of the rel node: * rows: the estimated number of output tuples (after applying - * restriction clauses). + * restriction clauses). * width: the estimated average output tuple width in bytes. * baserestrictcost: estimated cost of evaluating baserestrictinfo clauses. */ @@ -769,9 +783,11 @@ set_baserel_size_estimates(Query *root, RelOptInfo *rel) restrictlist_selectivity(root, rel->baserestrictinfo, lfirsti(rel->relids)); + /* * Force estimate to be at least one row, to make explain output look - * better and to avoid possible divide-by-zero when interpolating cost. + * better and to avoid possible divide-by-zero when interpolating + * cost. */ if (rel->rows < 1.0) rel->rows = 1.0; @@ -812,10 +828,10 @@ set_joinrel_size_estimates(Query *root, RelOptInfo *rel, temp = outer_rel->rows * inner_rel->rows; /* - * Apply join restrictivity. Note that we are only considering clauses - * that become restriction clauses at this join level; we are not - * double-counting them because they were not considered in estimating - * the sizes of the component rels. + * Apply join restrictivity. Note that we are only considering + * clauses that become restriction clauses at this join level; we are + * not double-counting them because they were not considered in + * estimating the sizes of the component rels. */ temp *= restrictlist_selectivity(root, restrictlist, @@ -823,7 +839,8 @@ set_joinrel_size_estimates(Query *root, RelOptInfo *rel, /* * Force estimate to be at least one row, to make explain output look - * better and to avoid possible divide-by-zero when interpolating cost. + * better and to avoid possible divide-by-zero when interpolating + * cost. */ if (temp < 1.0) temp = 1.0; @@ -833,8 +850,8 @@ set_joinrel_size_estimates(Query *root, RelOptInfo *rel, * 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. + * taught to remove unneeded columns from join targetlists, go back to + * using set_rel_width here. */ rel->width = outer_rel->width + inner_rel->width; } @@ -859,7 +876,7 @@ set_rel_width(Query *root, RelOptInfo *rel) * compute_attribute_width * Given a target list entry, find the size in bytes of the attribute. * - * If a field is variable-length, we make a default assumption. Would be + * If a field is variable-length, we make a default assumption. Would be * better if VACUUM recorded some stats about the average field width... * also, we have access to the atttypmod, but fail to use it... */ diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c index 8c63d9e1c38..98c5112f7c3 100644 --- a/src/backend/optimizer/path/indxpath.c +++ b/src/backend/optimizer/path/indxpath.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.81 2000/03/22 22:08:33 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.82 2000/04/12 17:15:19 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -46,62 +46,63 @@ #define is_indexable_operator(clause,opclass,relam,indexkey_on_left) \ (indexable_operator(clause,opclass,relam,indexkey_on_left) != InvalidOid) -typedef enum { +typedef enum +{ Prefix_None, Prefix_Partial, Prefix_Exact } Prefix_Status; static void match_index_orclauses(RelOptInfo *rel, IndexOptInfo *index, - List *restrictinfo_list); + List *restrictinfo_list); static List *match_index_orclause(RelOptInfo *rel, IndexOptInfo *index, - List *or_clauses, - List *other_matching_indices); + List *or_clauses, + List *other_matching_indices); static bool match_or_subclause_to_indexkey(RelOptInfo *rel, - IndexOptInfo *index, - Expr *clause); + IndexOptInfo *index, + Expr *clause); static List *group_clauses_by_indexkey(RelOptInfo *rel, IndexOptInfo *index, - int *indexkeys, Oid *classes, - List *restrictinfo_list); + int *indexkeys, Oid *classes, + List *restrictinfo_list); static List *group_clauses_by_ikey_for_joins(RelOptInfo *rel, - IndexOptInfo *index, - int *indexkeys, Oid *classes, - List *join_cinfo_list, - List *restr_cinfo_list); + IndexOptInfo *index, + int *indexkeys, Oid *classes, + List *join_cinfo_list, + List *restr_cinfo_list); static bool match_clause_to_indexkey(RelOptInfo *rel, IndexOptInfo *index, - int indexkey, Oid opclass, - Expr *clause, bool join); + int indexkey, Oid opclass, + Expr *clause, bool join); static bool pred_test(List *predicate_list, List *restrictinfo_list, - List *joininfo_list); + List *joininfo_list); static bool one_pred_test(Expr *predicate, List *restrictinfo_list); static bool one_pred_clause_expr_test(Expr *predicate, Node *clause); static bool one_pred_clause_test(Expr *predicate, Node *clause); static bool clause_pred_clause_test(Expr *predicate, Node *clause); static void indexable_joinclauses(RelOptInfo *rel, IndexOptInfo *index, - List *joininfo_list, List *restrictinfo_list, - List **clausegroups, List **outerrelids); + List *joininfo_list, List *restrictinfo_list, + List **clausegroups, List **outerrelids); static List *index_innerjoin(Query *root, RelOptInfo *rel, IndexOptInfo *index, - List *clausegroup_list, List *outerrelids_list); + List *clausegroup_list, List *outerrelids_list); static bool useful_for_mergejoin(RelOptInfo *rel, IndexOptInfo *index, - List *joininfo_list); + List *joininfo_list); static bool useful_for_ordering(Query *root, RelOptInfo *rel, - IndexOptInfo *index, - ScanDirection scandir); + IndexOptInfo *index, + ScanDirection scandir); static bool match_index_to_operand(int indexkey, Var *operand, - RelOptInfo *rel, IndexOptInfo *index); + RelOptInfo *rel, IndexOptInfo *index); static bool function_index_operand(Expr *funcOpnd, RelOptInfo *rel, - IndexOptInfo *index); + IndexOptInfo *index); static bool match_special_index_operator(Expr *clause, Oid opclass, Oid relam, - bool indexkey_on_left); + bool indexkey_on_left); static Prefix_Status like_fixed_prefix(char *patt, char **prefix); static Prefix_Status regex_fixed_prefix(char *patt, bool case_insensitive, - char **prefix); + char **prefix); static List *prefix_quals(Var *leftop, Oid expr_op, - char *prefix, Prefix_Status pstatus); -static char *make_greater_string(const char * str, Oid datatype); -static Oid find_operator(const char * opname, Oid datatype); -static Datum string_to_datum(const char * str, Oid datatype); -static Const *string_to_const(const char * str, Oid datatype); -static bool string_lessthan(const char * str1, const char * str2, - Oid datatype); + char *prefix, Prefix_Status pstatus); +static char *make_greater_string(const char *str, Oid datatype); +static Oid find_operator(const char *opname, Oid datatype); +static Datum string_to_datum(const char *str, Oid datatype); +static Const *string_to_const(const char *str, Oid datatype); +static bool string_lessthan(const char *str1, const char *str2, + Oid datatype); /* @@ -153,34 +154,34 @@ create_index_paths(Query *root, List *joinouterrelids; /* - * If this is a partial index, we can only use it if it passes - * the predicate test. + * If this is a partial index, we can only use it if it passes the + * predicate test. */ if (index->indpred != NIL) if (!pred_test(index->indpred, restrictinfo_list, joininfo_list)) continue; /* - * 1. Try matching the index against subclauses of restriction 'or' - * clauses (ie, 'or' clauses that reference only this relation). - * The restrictinfo nodes for the 'or' clauses are marked with lists - * of the matching indices. No paths are actually created now; - * that will be done in orindxpath.c after all indexes for the rel - * have been examined. (We need to do it that way because we can - * potentially use a different index for each subclause of an 'or', - * so we can't build a path for an 'or' clause until all indexes have - * been matched against it.) + * 1. Try matching the index against subclauses of restriction + * 'or' clauses (ie, 'or' clauses that reference only this + * relation). The restrictinfo nodes for the 'or' clauses are + * marked with lists of the matching indices. No paths are + * actually created now; that will be done in orindxpath.c after + * all indexes for the rel have been examined. (We need to do it + * that way because we can potentially use a different index for + * each subclause of an 'or', so we can't build a path for an 'or' + * clause until all indexes have been matched against it.) * * We don't even think about special handling of 'or' clauses that - * involve more than one relation (ie, are join clauses). - * Can we do anything useful with those? + * involve more than one relation (ie, are join clauses). Can we + * do anything useful with those? */ match_index_orclauses(rel, index, restrictinfo_list); /* - * 2. If the keys of this index match any of the available non-'or' - * restriction clauses, then create a path using those clauses - * as indexquals. + * 2. If the keys of this index match any of the available + * non-'or' restriction clauses, then create a path using those + * clauses as indexquals. */ restrictclauses = group_clauses_by_indexkey(rel, index, @@ -191,7 +192,7 @@ create_index_paths(Query *root, if (restrictclauses != NIL) add_path(rel, (Path *) create_index_path(root, rel, index, restrictclauses, - NoMovementScanDirection)); + NoMovementScanDirection)); /* * 3. If this index can be used for a mergejoin, then create an @@ -205,16 +206,17 @@ create_index_paths(Query *root, if (restrictclauses == NIL) { if (useful_for_mergejoin(rel, index, joininfo_list) || - useful_for_ordering(root, rel, index, ForwardScanDirection)) + useful_for_ordering(root, rel, index, ForwardScanDirection)) add_path(rel, (Path *) create_index_path(root, rel, index, NIL, ForwardScanDirection)); } + /* - * Currently, backwards scan is never considered except for the case - * of matching a query result ordering. Possibly should consider - * it in other places? + * Currently, backwards scan is never considered except for the + * case of matching a query result ordering. Possibly should + * consider it in other places? */ if (useful_for_ordering(root, rel, index, BackwardScanDirection)) add_path(rel, (Path *) @@ -223,11 +225,11 @@ create_index_paths(Query *root, BackwardScanDirection)); /* - * 4. Create an innerjoin index path for each combination of - * other rels used in available join clauses. These paths will - * be considered as the inner side of nestloop joins against - * those sets of other rels. indexable_joinclauses() finds sets - * of clauses that can be used with each combination of outer rels, + * 4. Create an innerjoin index path for each combination of other + * rels used in available join clauses. These paths will be + * considered as the inner side of nestloop joins against those + * sets of other rels. indexable_joinclauses() finds sets of + * clauses that can be used with each combination of outer rels, * and index_innerjoin builds the paths themselves. We add the * paths to the rel's innerjoin list, NOT to the result list. */ @@ -247,7 +249,7 @@ create_index_paths(Query *root, /**************************************************************************** - * ---- ROUTINES TO PROCESS 'OR' CLAUSES ---- + * ---- ROUTINES TO PROCESS 'OR' CLAUSES ---- ****************************************************************************/ @@ -280,6 +282,7 @@ match_index_orclauses(RelOptInfo *rel, if (restriction_is_or_clause(restrictinfo)) { + /* * Add this index to the subclause index list for each * subclause that it matches. @@ -309,7 +312,7 @@ match_index_orclauses(RelOptInfo *rel, * that have already been matched to subclauses within this * particular 'or' clause (i.e., a list previously generated by * this routine), or NIL if this routine has not previously been - * run for this 'or' clause. + * run for this 'or' clause. * * Returns a list of the form ((a b c) (d e f) nil (g h) ...) where * a,b,c are nodes of indices that match the first subclause in @@ -326,7 +329,8 @@ match_index_orclause(RelOptInfo *rel, List *index_list; List *clist; - /* first time through, we create list of same length as OR clause, + /* + * first time through, we create list of same length as OR clause, * containing an empty sublist for each subclause. */ if (!other_matching_indices) @@ -374,8 +378,8 @@ match_or_subclause_to_indexkey(RelOptInfo *rel, IndexOptInfo *index, Expr *clause) { - int indexkey = index->indexkeys[0]; - Oid opclass = index->classlist[0]; + int indexkey = index->indexkeys[0]; + Oid opclass = index->classlist[0]; if (and_clause((Node *) clause)) { @@ -400,10 +404,10 @@ match_or_subclause_to_indexkey(RelOptInfo *rel, * used as indexquals. * * In the simplest case this just means making a one-element list of the - * given opclause. However, if the OR subclause is an AND, we have to + * given opclause. However, if the OR subclause is an AND, we have to * scan it to find the opclause(s) that match the index. (There should * be at least one, if match_or_subclause_to_indexkey succeeded, but there - * could be more.) Also, we apply expand_indexqual_conditions() to convert + * could be more.) Also, we apply expand_indexqual_conditions() to convert * any special matching opclauses to indexable operators. * * The passed-in clause is not changed. @@ -413,9 +417,9 @@ extract_or_indexqual_conditions(RelOptInfo *rel, IndexOptInfo *index, Expr *orsubclause) { - List *quals = NIL; - int indexkey = index->indexkeys[0]; - Oid opclass = index->classlist[0]; + List *quals = NIL; + int indexkey = index->indexkeys[0]; + Oid opclass = index->classlist[0]; if (and_clause((Node *) orsubclause)) { @@ -514,8 +518,9 @@ group_clauses_by_indexkey(RelOptInfo *rel, clausegroup = lappend(clausegroup, rinfo); } - /* If no clauses match this key, we're done; we don't want to - * look at keys to its right. + /* + * If no clauses match this key, we're done; we don't want to look + * at keys to its right. */ if (clausegroup == NIL) break; @@ -533,7 +538,7 @@ group_clauses_by_indexkey(RelOptInfo *rel, /* * group_clauses_by_ikey_for_joins - * Generates a list of join clauses that can be used with an index + * Generates a list of join clauses that can be used with an index * to scan the inner side of a nestloop join. * * This is much like group_clauses_by_indexkey(), but we consider both @@ -593,8 +598,9 @@ group_clauses_by_ikey_for_joins(RelOptInfo *rel, clausegroup = lappend(clausegroup, rinfo); } - /* If no clauses match this key, we're done; we don't want to - * look at keys to its right. + /* + * If no clauses match this key, we're done; we don't want to look + * at keys to its right. */ if (clausegroup == NIL) break; @@ -607,8 +613,8 @@ group_clauses_by_ikey_for_joins(RelOptInfo *rel, } while (!DoneMatchingIndexKeys(indexkeys, index)); /* - * if no join clause was matched then there ain't clauses for - * joins at all. + * if no join clause was matched then there ain't clauses for joins at + * all. */ if (!jfound) { @@ -623,8 +629,8 @@ group_clauses_by_ikey_for_joins(RelOptInfo *rel, /* * match_clause_to_indexkey() - * Determines whether a restriction or join clause matches - * a key of an index. + * Determines whether a restriction or join clause matches + * a key of an index. * * To match, the clause: @@ -673,43 +679,46 @@ match_clause_to_indexkey(RelOptInfo *rel, *rightop; /* Clause must be a binary opclause. */ - if (! is_opclause((Node *) clause)) + if (!is_opclause((Node *) clause)) return false; leftop = get_leftop(clause); rightop = get_rightop(clause); - if (! leftop || ! rightop) + if (!leftop || !rightop) return false; if (!join) { + /* * Not considering joins, so check for clauses of the form: * (indexkey operator constant) or (constant operator indexkey). * We will accept a Param as being constant. */ - if ((IsA(rightop, Const) || IsA(rightop, Param)) && + if ((IsA(rightop, Const) ||IsA(rightop, Param)) && match_index_to_operand(indexkey, leftop, rel, index)) { if (is_indexable_operator(clause, opclass, index->relam, true)) return true; + /* - * If we didn't find a member of the index's opclass, - * see whether it is a "special" indexable operator. + * If we didn't find a member of the index's opclass, see + * whether it is a "special" indexable operator. */ if (match_special_index_operator(clause, opclass, index->relam, true)) return true; return false; } - if ((IsA(leftop, Const) || IsA(leftop, Param)) && + if ((IsA(leftop, Const) ||IsA(leftop, Param)) && match_index_to_operand(indexkey, rightop, rel, index)) { if (is_indexable_operator(clause, opclass, index->relam, false)) return true; + /* - * If we didn't find a member of the index's opclass, - * see whether it is a "special" indexable operator. + * If we didn't find a member of the index's opclass, see + * whether it is a "special" indexable operator. */ if (match_special_index_operator(clause, opclass, index->relam, false)) @@ -719,20 +728,21 @@ match_clause_to_indexkey(RelOptInfo *rel, } else { + /* - * Check for an indexqual that could be handled by a nestloop join. - * We need the index key to be compared against an expression - * that uses none of the indexed relation's vars. + * Check for an indexqual that could be handled by a nestloop + * join. We need the index key to be compared against an + * expression that uses none of the indexed relation's vars. */ if (match_index_to_operand(indexkey, leftop, rel, index)) { List *othervarnos = pull_varnos((Node *) rightop); bool isIndexable; - isIndexable = ! intMember(lfirsti(rel->relids), othervarnos); + isIndexable = !intMember(lfirsti(rel->relids), othervarnos); freeList(othervarnos); if (isIndexable && - is_indexable_operator(clause, opclass, index->relam, true)) + is_indexable_operator(clause, opclass, index->relam, true)) return true; } else if (match_index_to_operand(indexkey, rightop, rel, index)) @@ -740,10 +750,10 @@ match_clause_to_indexkey(RelOptInfo *rel, List *othervarnos = pull_varnos((Node *) leftop); bool isIndexable; - isIndexable = ! intMember(lfirsti(rel->relids), othervarnos); + isIndexable = !intMember(lfirsti(rel->relids), othervarnos); freeList(othervarnos); if (isIndexable && - is_indexable_operator(clause, opclass, index->relam, false)) + is_indexable_operator(clause, opclass, index->relam, false)) return true; } } @@ -768,7 +778,7 @@ match_clause_to_indexkey(RelOptInfo *rel, * * Returns the OID of the matching operator, or InvalidOid if no match. * Note that the returned OID will be different from the one in the given - * expression if we used a binary-compatible substitution. Also note that + * expression if we used a binary-compatible substitution. Also note that * if indexkey_on_left is FALSE (meaning we need to commute), the returned * OID is *not* commuted; it can be plugged directly into the given clause. */ @@ -818,13 +828,14 @@ indexable_operator(Expr *clause, Oid opclass, Oid relam, if (HeapTupleIsValid(newop)) { - Oid new_expr_op = oprid(newop); + Oid new_expr_op = oprid(newop); if (new_expr_op != expr_op) { + /* - * OK, we found a binary-compatible operator of the same name; - * now does it match the index? + * OK, we found a binary-compatible operator of the same + * name; now does it match the index? */ if (indexkey_on_left) commuted_op = new_expr_op; @@ -883,12 +894,12 @@ useful_for_mergejoin(RelOptInfo *rel, { if (restrictinfo->left_sortop == ordering[0] && match_index_to_operand(indexkeys[0], - get_leftop(restrictinfo->clause), + get_leftop(restrictinfo->clause), rel, index)) return true; if (restrictinfo->right_sortop == ordering[0] && match_index_to_operand(indexkeys[0], - get_rightop(restrictinfo->clause), + get_rightop(restrictinfo->clause), rel, index)) return true; } @@ -1127,7 +1138,7 @@ one_pred_clause_test(Expr *predicate, Node *clause) */ static StrategyNumber -BT_implic_table[BTMaxStrategyNumber][BTMaxStrategyNumber] = { + BT_implic_table[BTMaxStrategyNumber][BTMaxStrategyNumber] = { {2, 2, 0, 0, 0}, {1, 2, 0, 0, 0}, {1, 2, 3, 4, 5}, @@ -1346,13 +1357,13 @@ clause_pred_clause_test(Expr *predicate, Node *clause) * rel's restrictinfo list. Therefore, every clause in the group references * the current rel plus the same set of other rels (except for the restrict * clauses, which only reference the current rel). Therefore, this set - * of clauses could be used as an indexqual if the relation is scanned + * of clauses could be used as an indexqual if the relation is scanned * as the inner side of a nestloop join when the outer side contains * (at least) all those "other rels". * * XXX Actually, given that we are considering a join that requires an * outer rel set (A,B,C), we should use all qual clauses that reference - * any subset of these rels, not just the full set or none. This is + * any subset of these rels, not just the full set or none. This is * doable with a doubly nested loop over joininfo_list; is it worth it? * * Returns two parallel lists of the same length: the clause groups, @@ -1430,10 +1441,11 @@ index_innerjoin(Query *root, RelOptInfo *rel, IndexOptInfo *index, pathnode->path.pathtype = T_IndexScan; pathnode->path.parent = rel; + /* * 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. + * it will only ever be used as the inner path of a nestloop, and + * so its ordering does not matter. */ pathnode->path.pathkeys = NIL; @@ -1441,7 +1453,8 @@ index_innerjoin(Query *root, RelOptInfo *rel, IndexOptInfo *index, /* expand special operators to indexquals the executor can handle */ indexquals = expand_indexqual_conditions(indexquals); - /* Note that we are making a pathnode for a single-scan indexscan; + /* + * Note that we are making a pathnode for a single-scan indexscan; * therefore, both indexid and indexqual should be single-element * lists. */ @@ -1456,14 +1469,15 @@ index_innerjoin(Query *root, RelOptInfo *rel, IndexOptInfo *index, /* * 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 clausegroup 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. (We can't just nconc the two lists; - * then we might have some restriction clauses appearing twice, - * which'd mislead restrictlist_selectivity into double-counting - * their selectivity.) + * indexscan. This is less than rel->rows because of the + * additional selectivity of the join clauses. Since clausegroup + * 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. (We can't just + * nconc the two lists; then we might have some restriction + * clauses appearing twice, which'd mislead + * restrictlist_selectivity into double-counting their + * selectivity.) */ pathnode->rows = rel->tuples * restrictlist_selectivity(root, @@ -1490,7 +1504,7 @@ index_innerjoin(Query *root, RelOptInfo *rel, IndexOptInfo *index, * match_index_to_operand() * Generalized test for a match between an index's key * and the operand on one side of a restriction or join clause. - * Now check for functional indices as well. + * Now check for functional indices as well. */ static bool match_index_to_operand(int indexkey, @@ -1500,6 +1514,7 @@ match_index_to_operand(int indexkey, { if (index->indproc == InvalidOid) { + /* * Normal index. */ @@ -1530,7 +1545,7 @@ function_index_operand(Expr *funcOpnd, RelOptInfo *rel, IndexOptInfo *index) /* * sanity check, make sure we know what we're dealing with here. */ - if (funcOpnd == NULL || ! IsA(funcOpnd, Expr) || + if (funcOpnd == NULL || !IsA(funcOpnd, Expr) || funcOpnd->opType != FUNC_EXPR || funcOpnd->oper == NULL || indexKeys == NULL) return false; @@ -1550,9 +1565,9 @@ function_index_operand(Expr *funcOpnd, RelOptInfo *rel, IndexOptInfo *index) i = 0; foreach(arg, funcargs) { - Var *var = (Var *) lfirst(arg); + Var *var = (Var *) lfirst(arg); - if (! IsA(var, Var)) + if (!IsA(var, Var)) return false; if (indexKeys[i] == 0) return false; @@ -1578,10 +1593,10 @@ function_index_operand(Expr *funcOpnd, RelOptInfo *rel, IndexOptInfo *index) * indexscan machinery. The key idea is that these operators allow us * to derive approximate indexscan qual clauses, such that any tuples * that pass the operator clause itself must also satisfy the simpler - * indexscan condition(s). Then we can use the indexscan machinery + * indexscan condition(s). Then we can use the indexscan machinery * to avoid scanning as much of the table as we'd otherwise have to, * while applying the original operator as a qpqual condition to ensure - * we deliver only the tuples we want. (In essence, we're using a regular + * we deliver only the tuples we want. (In essence, we're using a regular * index as if it were a lossy index.) * * An example of what we're doing is @@ -1630,11 +1645,12 @@ match_special_index_operator(Expr *clause, Oid opclass, Oid relam, char *patt; char *prefix; - /* Currently, all known special operators require the indexkey - * on the left, but this test could be pushed into the switch statement - * if some are added that do not... + /* + * Currently, all known special operators require the indexkey on the + * left, but this test could be pushed into the switch statement if + * some are added that do not... */ - if (! indexkey_on_left) + if (!indexkey_on_left) return false; /* we know these will succeed */ @@ -1643,7 +1659,7 @@ match_special_index_operator(Expr *clause, Oid opclass, Oid relam, expr_op = ((Oper *) clause->oper)->opno; /* again, required for all current special ops: */ - if (! IsA(rightop, Const) || + if (!IsA(rightop, Const) || ((Const *) rightop)->constisnull) return false; constvalue = ((Const *) rightop)->constvalue; @@ -1657,7 +1673,8 @@ match_special_index_operator(Expr *clause, Oid opclass, Oid relam, /* the right-hand const is type text for all of these */ patt = textout((text *) DatumGetPointer(constvalue)); isIndexable = like_fixed_prefix(patt, &prefix) != Prefix_None; - if (prefix) pfree(prefix); + if (prefix) + pfree(prefix); pfree(patt); break; @@ -1668,7 +1685,8 @@ match_special_index_operator(Expr *clause, Oid opclass, Oid relam, /* the right-hand const is type text for all of these */ patt = textout((text *) DatumGetPointer(constvalue)); isIndexable = regex_fixed_prefix(patt, false, &prefix) != Prefix_None; - if (prefix) pfree(prefix); + if (prefix) + pfree(prefix); pfree(patt); break; @@ -1679,13 +1697,14 @@ match_special_index_operator(Expr *clause, Oid opclass, Oid relam, /* the right-hand const is type text for all of these */ patt = textout((text *) DatumGetPointer(constvalue)); isIndexable = regex_fixed_prefix(patt, true, &prefix) != Prefix_None; - if (prefix) pfree(prefix); + if (prefix) + pfree(prefix); pfree(patt); break; } /* done if the expression doesn't look indexable */ - if (! isIndexable) + if (!isIndexable) return false; /* @@ -1699,32 +1718,32 @@ match_special_index_operator(Expr *clause, Oid opclass, Oid relam, case OID_TEXT_LIKE_OP: case OID_TEXT_REGEXEQ_OP: case OID_TEXT_ICREGEXEQ_OP: - if (! op_class(find_operator(">=", TEXTOID), opclass, relam) || - ! op_class(find_operator("<", TEXTOID), opclass, relam)) + if (!op_class(find_operator(">=", TEXTOID), opclass, relam) || + !op_class(find_operator("<", TEXTOID), opclass, relam)) isIndexable = false; break; case OID_BPCHAR_LIKE_OP: case OID_BPCHAR_REGEXEQ_OP: case OID_BPCHAR_ICREGEXEQ_OP: - if (! op_class(find_operator(">=", BPCHAROID), opclass, relam) || - ! op_class(find_operator("<", BPCHAROID), opclass, relam)) + if (!op_class(find_operator(">=", BPCHAROID), opclass, relam) || + !op_class(find_operator("<", BPCHAROID), opclass, relam)) isIndexable = false; break; case OID_VARCHAR_LIKE_OP: case OID_VARCHAR_REGEXEQ_OP: case OID_VARCHAR_ICREGEXEQ_OP: - if (! op_class(find_operator(">=", VARCHAROID), opclass, relam) || - ! op_class(find_operator("<", VARCHAROID), opclass, relam)) + if (!op_class(find_operator(">=", VARCHAROID), opclass, relam) || + !op_class(find_operator("<", VARCHAROID), opclass, relam)) isIndexable = false; break; case OID_NAME_LIKE_OP: case OID_NAME_REGEXEQ_OP: case OID_NAME_ICREGEXEQ_OP: - if (! op_class(find_operator(">=", NAMEOID), opclass, relam) || - ! op_class(find_operator("<", NAMEOID), opclass, relam)) + if (!op_class(find_operator(">=", NAMEOID), opclass, relam) || + !op_class(find_operator("<", NAMEOID), opclass, relam)) isIndexable = false; break; } @@ -1736,7 +1755,7 @@ match_special_index_operator(Expr *clause, Oid opclass, Oid relam, * expand_indexqual_conditions * Given a list of (implicitly ANDed) indexqual clauses, * expand any "special" index operators into clauses that the indexscan - * machinery will know what to do with. Clauses that were not + * machinery will know what to do with. Clauses that were not * recognized by match_special_index_operator() must be passed through * unchanged. */ @@ -1749,6 +1768,7 @@ expand_indexqual_conditions(List *indexquals) foreach(q, indexquals) { Expr *clause = (Expr *) lfirst(q); + /* we know these will succeed */ Var *leftop = get_leftop(clause); Var *rightop = get_rightop(clause); @@ -1760,11 +1780,13 @@ expand_indexqual_conditions(List *indexquals) switch (expr_op) { - /* - * LIKE and regex operators are not members of any index opclass, - * so if we find one in an indexqual list we can assume that - * it was accepted by match_special_index_operator(). - */ + + /* + * LIKE and regex operators are not members of any index + * opclass, so if we find one in an indexqual list we can + * assume that it was accepted by + * match_special_index_operator(). + */ case OID_TEXT_LIKE_OP: case OID_BPCHAR_LIKE_OP: case OID_VARCHAR_LIKE_OP: @@ -1776,7 +1798,8 @@ expand_indexqual_conditions(List *indexquals) resultquals = nconc(resultquals, prefix_quals(leftop, expr_op, prefix, pstatus)); - if (prefix) pfree(prefix); + if (prefix) + pfree(prefix); pfree(patt); break; @@ -1791,7 +1814,8 @@ expand_indexqual_conditions(List *indexquals) resultquals = nconc(resultquals, prefix_quals(leftop, expr_op, prefix, pstatus)); - if (prefix) pfree(prefix); + if (prefix) + pfree(prefix); pfree(patt); break; @@ -1806,7 +1830,8 @@ expand_indexqual_conditions(List *indexquals) resultquals = nconc(resultquals, prefix_quals(leftop, expr_op, prefix, pstatus)); - if (prefix) pfree(prefix); + if (prefix) + pfree(prefix); pfree(patt); break; @@ -1833,7 +1858,7 @@ like_fixed_prefix(char *patt, char **prefix) int pos, match_pos; - *prefix = match = palloc(strlen(patt)+1); + *prefix = match = palloc(strlen(patt) + 1); match_pos = 0; for (pos = 0; patt[pos]; pos++) @@ -1849,14 +1874,15 @@ like_fixed_prefix(char *patt, char **prefix) if (patt[pos] == '\0') break; } + /* - * NOTE: this code used to think that %% meant a literal %, - * but textlike() itself does not think that, and the SQL92 - * spec doesn't say any such thing either. + * NOTE: this code used to think that %% meant a literal %, but + * textlike() itself does not think that, and the SQL92 spec + * doesn't say any such thing either. */ match[match_pos++] = patt[pos]; } - + match[match_pos] = '\0'; /* in LIKE, an empty pattern is an exact match! */ @@ -1905,7 +1931,7 @@ regex_fixed_prefix(char *patt, bool case_insensitive, } /* OK, allocate space for pattern */ - *prefix = match = palloc(strlen(patt)+1); + *prefix = match = palloc(strlen(patt) + 1); match_pos = 0; /* note start at pos 1 to skip leading ^ */ @@ -1916,9 +1942,11 @@ regex_fixed_prefix(char *patt, bool case_insensitive, patt[pos] == '*' || patt[pos] == '[' || patt[pos] == '$' || - /* XXX I suspect isalpha() is not an adequately locale-sensitive - * test for characters that can vary under case folding? - */ + + /* + * XXX I suspect isalpha() is not an adequately locale-sensitive + * test for characters that can vary under case folding? + */ (case_insensitive && isalpha(patt[pos]))) break; if (patt[pos] == '\\') @@ -1932,9 +1960,9 @@ regex_fixed_prefix(char *patt, bool case_insensitive, match[match_pos] = '\0'; - if (patt[pos] == '$' && patt[pos+1] == '\0') + if (patt[pos] == '$' && patt[pos + 1] == '\0') return Prefix_Exact; /* pattern specifies exact match */ - + if (match_pos > 0) return Prefix_Partial; return Prefix_None; @@ -2020,7 +2048,8 @@ prefix_quals(Var *leftop, Oid expr_op, result = lcons(expr, NIL); /* - * If we can create a string larger than the prefix, say "x < greaterstr". + * If we can create a string larger than the prefix, say "x < + * greaterstr". */ greaterstr = make_greater_string(prefix, datatype); if (greaterstr) @@ -2058,17 +2087,20 @@ prefix_quals(Var *leftop, Oid expr_op, * given "foos" and return "foot", will this actually be greater than "fooss"? */ static char * -make_greater_string(const char * str, Oid datatype) +make_greater_string(const char *str, Oid datatype) { char *workstr; int len; - /* Make a modifiable copy, which will be our return value if successful */ + /* + * Make a modifiable copy, which will be our return value if + * successful + */ workstr = pstrdup((char *) str); while ((len = strlen(workstr)) > 0) { - unsigned char *lastchar = (unsigned char *) (workstr + len - 1); + unsigned char *lastchar = (unsigned char *) (workstr + len - 1); /* * Try to generate a larger string by incrementing the last byte. @@ -2077,14 +2109,15 @@ make_greater_string(const char * str, Oid datatype) { (*lastchar)++; if (string_lessthan(str, workstr, datatype)) - return workstr; /* Success! */ + return workstr; /* Success! */ } + /* - * Truncate off the last character, which might be more than 1 byte - * in MULTIBYTE case. + * Truncate off the last character, which might be more than 1 + * byte in MULTIBYTE case. */ #ifdef MULTIBYTE - len = pg_mbcliplen((const unsigned char *) workstr, len, len-1); + len = pg_mbcliplen((const unsigned char *) workstr, len, len - 1); workstr[len] = '\0'; #else *lastchar = '\0'; @@ -2102,7 +2135,7 @@ make_greater_string(const char * str, Oid datatype) /* See if there is a binary op of the given name for the given datatype */ static Oid -find_operator(const char * opname, Oid datatype) +find_operator(const char *opname, Oid datatype) { HeapTuple optup; @@ -2122,10 +2155,12 @@ find_operator(const char * opname, Oid datatype) * returned value should be pfree'd if no longer needed. */ static Datum -string_to_datum(const char * str, Oid datatype) +string_to_datum(const char *str, Oid datatype) { - /* We cheat a little by assuming that textin() will do for - * bpchar and varchar constants too... + + /* + * We cheat a little by assuming that textin() will do for bpchar and + * varchar constants too... */ if (datatype == NAMEOID) return PointerGetDatum(namein((char *) str)); @@ -2137,7 +2172,7 @@ string_to_datum(const char * str, Oid datatype) * Generate a Const node of the appropriate type from a C string. */ static Const * -string_to_const(const char * str, Oid datatype) +string_to_const(const char *str, Oid datatype) { Datum conval = string_to_datum(str, datatype); @@ -2151,7 +2186,7 @@ string_to_const(const char * str, Oid datatype) * "<" operator function, to ensure we get the right result... */ static bool -string_lessthan(const char * str1, const char * str2, Oid datatype) +string_lessthan(const char *str1, const char *str2, Oid datatype) { Datum datum1 = string_to_datum(str1, datatype); Datum datum2 = string_to_datum(str2, datatype); diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c index 92614374968..64e9443ab18 100644 --- a/src/backend/optimizer/path/joinpath.c +++ b/src/backend/optimizer/path/joinpath.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.53 2000/02/18 23:47:19 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.54 2000/04/12 17:15:19 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -28,25 +28,27 @@ #include "utils/lsyscache.h" static void sort_inner_and_outer(Query *root, RelOptInfo *joinrel, - RelOptInfo *outerrel, RelOptInfo *innerrel, - List *restrictlist, List *mergeclause_list); + RelOptInfo *outerrel, RelOptInfo *innerrel, + List *restrictlist, List *mergeclause_list); static void match_unsorted_outer(Query *root, RelOptInfo *joinrel, - RelOptInfo *outerrel, RelOptInfo *innerrel, - List *restrictlist, List *mergeclause_list); + RelOptInfo *outerrel, RelOptInfo *innerrel, + List *restrictlist, List *mergeclause_list); + #ifdef NOT_USED static void match_unsorted_inner(Query *root, RelOptInfo *joinrel, - RelOptInfo *outerrel, RelOptInfo *innerrel, - List *restrictlist, List *mergeclause_list); + RelOptInfo *outerrel, RelOptInfo *innerrel, + List *restrictlist, List *mergeclause_list); + #endif static void hash_inner_and_outer(Query *root, RelOptInfo *joinrel, - RelOptInfo *outerrel, RelOptInfo *innerrel, - List *restrictlist); + RelOptInfo *outerrel, RelOptInfo *innerrel, + List *restrictlist); static Path *best_innerjoin(List *join_paths, List *outer_relid); static Selectivity estimate_disbursion(Query *root, Var *var); static List *select_mergejoin_clauses(RelOptInfo *joinrel, - RelOptInfo *outerrel, - RelOptInfo *innerrel, - List *restrictlist); + RelOptInfo *outerrel, + RelOptInfo *innerrel, + List *restrictlist); /* @@ -79,32 +81,33 @@ add_paths_to_joinrel(Query *root, restrictlist); /* - * 1. Consider mergejoin paths where both relations must be - * explicitly sorted. + * 1. Consider mergejoin paths where both relations must be explicitly + * sorted. */ sort_inner_and_outer(root, joinrel, outerrel, innerrel, restrictlist, mergeclause_list); /* - * 2. Consider paths where the outer relation need not be - * explicitly sorted. This includes both nestloops and - * mergejoins where the outer path is already ordered. + * 2. Consider paths where the outer relation need not be explicitly + * sorted. This includes both nestloops and mergejoins where the outer + * path is already ordered. */ match_unsorted_outer(root, joinrel, outerrel, innerrel, restrictlist, mergeclause_list); #ifdef NOT_USED + /* - * 3. Consider paths where the inner relation need not be - * explicitly sorted. This includes mergejoins only - * (nestloops were already built in match_unsorted_outer). + * 3. Consider paths where the inner relation need not be explicitly + * sorted. This includes mergejoins only (nestloops were already + * built in match_unsorted_outer). * - * Diked out as redundant 2/13/2000 -- tgl. There isn't any - * really significant difference between the inner and outer - * side of a mergejoin, so match_unsorted_inner creates no paths - * that aren't equivalent to those made by match_unsorted_outer - * when add_paths_to_joinrel() is invoked with the two rels given - * in the other order. + * Diked out as redundant 2/13/2000 -- tgl. There isn't any really + * significant difference between the inner and outer side of a + * mergejoin, so match_unsorted_inner creates no paths that aren't + * equivalent to those made by match_unsorted_outer when + * add_paths_to_joinrel() is invoked with the two rels given in the + * other order. */ match_unsorted_inner(root, joinrel, outerrel, innerrel, restrictlist, mergeclause_list); @@ -144,31 +147,31 @@ sort_inner_and_outer(Query *root, /* * Each possible ordering of the available mergejoin clauses will - * generate a differently-sorted result path at essentially the - * same cost. We have no basis for choosing one over another at - * this level of joining, but some sort orders may be more useful - * than others for higher-level mergejoins. Generating a path here - * for *every* permutation of mergejoin clauses doesn't seem like - * a winning strategy, however; the cost in planning time is too high. + * generate a differently-sorted result path at essentially the same + * cost. We have no basis for choosing one over another at this level + * of joining, but some sort orders may be more useful than others for + * higher-level mergejoins. Generating a path here for *every* + * permutation of mergejoin clauses doesn't seem like a winning + * strategy, however; the cost in planning time is too high. * * For now, we generate one path for each mergejoin clause, listing that - * clause first and the rest in random order. This should allow at least - * a one-clause mergejoin without re-sorting against any other possible - * mergejoin partner path. But if we've not guessed the right ordering - * of secondary clauses, we may end up evaluating clauses as qpquals when - * they could have been done as mergeclauses. We need to figure out a - * better way. (Two possible approaches: look at all the relevant index - * relations to suggest plausible sort orders, or make just one output - * path and somehow mark it as having a sort-order that can be rearranged - * freely.) + * clause first and the rest in random order. This should allow at + * least a one-clause mergejoin without re-sorting against any other + * possible mergejoin partner path. But if we've not guessed the + * right ordering of secondary clauses, we may end up evaluating + * clauses as qpquals when they could have been done as mergeclauses. + * We need to figure out a better way. (Two possible approaches: look + * at all the relevant index relations to suggest plausible sort + * orders, or make just one output path and somehow mark it as having + * a sort-order that can be rearranged freely.) */ foreach(i, mergeclause_list) { - RestrictInfo *restrictinfo = lfirst(i); - List *curclause_list; - List *outerkeys; - List *innerkeys; - List *merge_pathkeys; + RestrictInfo *restrictinfo = lfirst(i); + List *curclause_list; + List *outerkeys; + List *innerkeys; + List *merge_pathkeys; /* Make a mergeclause list with this guy first. */ if (i != mergeclause_list) @@ -176,13 +179,14 @@ sort_inner_and_outer(Query *root, lremove(restrictinfo, listCopy(mergeclause_list))); else - curclause_list = mergeclause_list; /* no work at first one... */ + curclause_list = mergeclause_list; /* no work at first one... */ - /* Build sort pathkeys for both sides. + /* + * Build sort pathkeys for both sides. * - * Note: it's possible that the cheapest paths will already be - * sorted properly. create_mergejoin_path will detect that case - * and suppress an explicit sort step, so we needn't do so here. + * Note: it's possible that the cheapest paths will already be sorted + * properly. create_mergejoin_path will detect that case and + * suppress an explicit sort step, so we needn't do so here. */ outerkeys = make_pathkeys_for_mergeclauses(root, curclause_list, @@ -198,8 +202,8 @@ sort_inner_and_outer(Query *root, /* * And now we can make the path. We only consider the cheapest- * total-cost input paths, since we are assuming here that a sort - * is required. We will consider cheapest-startup-cost input paths - * later, and only if they don't need a sort. + * is required. We will consider cheapest-startup-cost input + * paths later, and only if they don't need a sort. */ add_path(joinrel, (Path *) create_mergejoin_path(joinrel, @@ -225,7 +229,7 @@ sort_inner_and_outer(Query *root, * inner path, one on the cheapest-startup-cost inner path (if different), * and one on the best inner-indexscan path (if any). * - * We also consider mergejoins if mergejoin clauses are available. We have + * We also consider mergejoins if mergejoin clauses are available. We have * two ways to generate the inner path for a mergejoin: sort the cheapest * inner path, or use an inner path that is already suitably ordered for the * merge. If we have several mergeclauses, it could be that there is no inner @@ -255,8 +259,8 @@ match_unsorted_outer(Query *root, List *i; /* - * Get the best innerjoin indexpath (if any) for this outer rel. - * It's the same for all outer paths. + * Get the best innerjoin indexpath (if any) for this outer rel. It's + * the same for all outer paths. */ bestinnerjoin = best_innerjoin(innerrel->innerjoin, outerrel->relids); @@ -274,8 +278,8 @@ match_unsorted_outer(Query *root, /* * The result will have this sort order (even if it is implemented - * as a nestloop, and even if some of the mergeclauses are implemented - * by qpquals rather than as true mergeclauses): + * as a nestloop, and even if some of the mergeclauses are + * implemented by qpquals rather than as true mergeclauses): */ merge_pathkeys = build_join_pathkeys(outerpath->pathkeys, joinrel->targetlist, @@ -318,11 +322,12 @@ match_unsorted_outer(Query *root, /* Compute the required ordering of the inner path */ innersortkeys = make_pathkeys_for_mergeclauses(root, mergeclauses, - innerrel->targetlist); + innerrel->targetlist); /* - * Generate a mergejoin on the basis of sorting the cheapest inner. - * Since a sort will be needed, only cheapest total cost matters. + * Generate a mergejoin on the basis of sorting the cheapest + * inner. Since a sort will be needed, only cheapest total cost + * matters. */ add_path(joinrel, (Path *) create_mergejoin_path(joinrel, @@ -335,11 +340,11 @@ match_unsorted_outer(Query *root, innersortkeys)); /* - * Look for presorted inner paths that satisfy the mergeclause list - * or any truncation thereof. Here, we consider both cheap startup - * cost and cheap total cost. + * Look for presorted inner paths that satisfy the mergeclause + * list or any truncation thereof. Here, we consider both cheap + * startup cost and cheap total cost. */ - trialsortkeys = listCopy(innersortkeys); /* modifiable copy */ + trialsortkeys = listCopy(innersortkeys); /* modifiable copy */ cheapest_startup_inner = NULL; cheapest_total_inner = NULL; num_mergeclauses = length(mergeclauses); @@ -349,8 +354,9 @@ match_unsorted_outer(Query *root, Path *innerpath; List *newclauses = NIL; - /* Look for an inner path ordered well enough to merge with - * the first 'clausecnt' mergeclauses. NB: trialsortkeys list + /* + * Look for an inner path ordered well enough to merge with + * the first 'clausecnt' mergeclauses. NB: trialsortkeys list * is modified destructively, which is why we made a copy... */ trialsortkeys = ltruncate(clausecnt, trialsortkeys); @@ -391,14 +397,16 @@ match_unsorted_outer(Query *root, /* Found a cheap (or even-cheaper) sorted path */ if (innerpath != cheapest_total_inner) { - /* Avoid rebuilding clause list if we already made one; - * saves memory in big join trees... + + /* + * Avoid rebuilding clause list if we already made + * one; saves memory in big join trees... */ if (newclauses == NIL) { if (clausecnt < num_mergeclauses) newclauses = ltruncate(clausecnt, - listCopy(mergeclauses)); + listCopy(mergeclauses)); else newclauses = mergeclauses; } @@ -461,11 +469,12 @@ match_unsorted_inner(Query *root, /* Compute the required ordering of the outer path */ outersortkeys = make_pathkeys_for_mergeclauses(root, mergeclauses, - outerrel->targetlist); + outerrel->targetlist); /* - * Generate a mergejoin on the basis of sorting the cheapest outer. - * Since a sort will be needed, only cheapest total cost matters. + * Generate a mergejoin on the basis of sorting the cheapest + * outer. Since a sort will be needed, only cheapest total cost + * matters. */ merge_pathkeys = build_join_pathkeys(outersortkeys, joinrel->targetlist, @@ -479,10 +488,11 @@ match_unsorted_inner(Query *root, mergeclauses, outersortkeys, NIL)); + /* * Now generate mergejoins based on already-sufficiently-ordered - * outer paths. There's likely to be some redundancy here with paths - * already generated by merge_unsorted_outer ... but since + * outer paths. There's likely to be some redundancy here with + * paths already generated by merge_unsorted_outer ... but since * merge_unsorted_outer doesn't consider all permutations of the * mergeclause list, it may fail to notice that this particular * innerpath could have been used with this outerpath. @@ -491,7 +501,8 @@ match_unsorted_inner(Query *root, outersortkeys, TOTAL_COST); if (totalouterpath == NULL) - continue; /* there won't be a startup-cost path either */ + continue; /* there won't be a startup-cost path + * either */ merge_pathkeys = build_join_pathkeys(totalouterpath->pathkeys, joinrel->targetlist, @@ -552,8 +563,8 @@ hash_inner_and_outer(Query *root, List *i; /* - * Scan the join's restrictinfo list to find hashjoinable clauses - * that are usable with this pair of sub-relations. Since we currently + * Scan the join's restrictinfo list to find hashjoinable clauses that + * are usable with this pair of sub-relations. Since we currently * accept only var-op-var clauses as hashjoinable, we need only check * the membership of the vars to determine whether a particular clause * can be used with this pair of sub-relations. This code would need @@ -568,7 +579,7 @@ hash_inner_and_outer(Query *root, *right, *inner; List *hashclauses; - Selectivity innerdisbursion; + Selectivity innerdisbursion; if (restrictinfo->hashjoinoperator == InvalidOid) continue; /* not hashjoinable */ @@ -595,9 +606,9 @@ hash_inner_and_outer(Query *root, innerdisbursion = estimate_disbursion(root, inner); /* - * We consider both the cheapest-total-cost and cheapest-startup-cost - * outer paths. There's no need to consider any but the cheapest- - * total-cost inner path, however. + * We consider both the cheapest-total-cost and + * cheapest-startup-cost outer paths. There's no need to consider + * any but the cheapest- total-cost inner path, however. */ add_path(joinrel, (Path *) create_hashjoin_path(joinrel, @@ -644,7 +655,8 @@ best_innerjoin(List *join_paths, Relids outer_relids) Assert(IsA(path, IndexPath)); - /* path->joinrelids is the set of base rels that must be part of + /* + * path->joinrelids is the set of base rels that must be part of * outer_relids in order to use this inner path, because those * rels are used in the index join quals of this inner path. */ @@ -661,7 +673,7 @@ best_innerjoin(List *join_paths, Relids outer_relids) * * We use a default of 0.1 if we can't figure out anything better. * This will typically discourage use of a hash rather strongly, - * if the inner relation is large. We do not want to hash unless + * if the inner relation is large. We do not want to hash unless * we know that the inner rel is well-dispersed (or the alternatives * seem much worse). */ @@ -670,7 +682,7 @@ estimate_disbursion(Query *root, Var *var) { Oid relid; - if (! IsA(var, Var)) + if (!IsA(var, Var)) return 0.1; relid = getrelid(var->varno, root->rtable); @@ -690,7 +702,7 @@ estimate_disbursion(Query *root, Var *var) * Since we currently allow only plain Vars as the left and right sides * of mergejoin clauses, this test is relatively simple. This routine * would need to be upgraded to support more-complex expressions - * as sides of mergejoins. In theory, we could allow arbitrarily complex + * as sides of mergejoins. In theory, we could allow arbitrarily complex * expressions in mergejoins, so long as one side uses only vars from one * sub-relation and the other side uses only vars from the other. */ diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c index e872b67623a..09003eb9fa8 100644 --- a/src/backend/optimizer/path/joinrels.c +++ b/src/backend/optimizer/path/joinrels.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.43 2000/02/07 04:40:59 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.44 2000/04/12 17:15:20 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -22,7 +22,7 @@ static RelOptInfo *make_join_rel(Query *root, RelOptInfo *rel1, - RelOptInfo *rel2); + RelOptInfo *rel2); /* @@ -44,22 +44,23 @@ make_rels_by_joins(Query *root, int level) /* * First, consider left-sided and right-sided plans, in which rels of * exactly level-1 member relations are joined against base relations. - * We prefer to join using join clauses, but if we find a rel of level-1 - * members that has no join clauses, we will generate Cartesian-product - * joins against all base rels not already contained in it. + * We prefer to join using join clauses, but if we find a rel of + * level-1 members that has no join clauses, we will generate + * Cartesian-product joins against all base rels not already contained + * in it. * * In the first pass (level == 2), we try to join each base rel to each * base rel that appears later in base_rel_list. (The mirror-image - * joins are handled automatically by make_join_rel.) In later passes, - * we try to join rels of size level-1 from join_rel_list to each - * base rel in base_rel_list. + * joins are handled automatically by make_join_rel.) In later + * passes, we try to join rels of size level-1 from join_rel_list to + * each base rel in base_rel_list. * * We assume that the rels already present in join_rel_list appear in * decreasing order of level (number of members). This should be true * since we always add new higher-level rels to the front of the list. */ if (level == 2) - r = root->base_rel_list; /* level-1 is base rels */ + r = root->base_rel_list;/* level-1 is base rels */ else r = root->join_rel_list; for (; r != NIL; r = lnext(r)) @@ -68,21 +69,23 @@ make_rels_by_joins(Query *root, int level) int old_level = length(old_rel->relids); List *other_rels; - if (old_level != level-1) + if (old_level != level - 1) break; if (level == 2) - other_rels = lnext(r); /* only consider remaining base rels */ + other_rels = lnext(r); /* only consider remaining base + * rels */ else - other_rels = root->base_rel_list; /* consider all base rels */ + other_rels = root->base_rel_list; /* consider all base rels */ if (old_rel->joininfo != NIL) { + /* - * Note that if all available join clauses for this rel require - * more than one other rel, we will fail to make any joins against - * it here. That's OK; it'll be considered by "bushy plan" join - * code in a higher-level pass. + * Note that if all available join clauses for this rel + * require more than one other rel, we will fail to make any + * joins against it here. That's OK; it'll be considered by + * "bushy plan" join code in a higher-level pass. */ make_rels_by_clause_joins(root, old_rel, @@ -90,6 +93,7 @@ make_rels_by_joins(Query *root, int level) } else { + /* * Oops, we have a relation that is not joined to any other * relation. Cartesian product time. @@ -103,10 +107,11 @@ make_rels_by_joins(Query *root, int level) /* * Now, consider "bushy plans" in which relations of k base rels are * joined to relations of level-k base rels, for 2 <= k <= level-2. - * The previous loop left r pointing to the first rel of level level-2. + * The previous loop left r pointing to the first rel of level + * level-2. * - * We only consider bushy-plan joins for pairs of rels where there is - * a suitable join clause, in order to avoid unreasonable growth of + * We only consider bushy-plan joins for pairs of rels where there is a + * suitable join clause, in order to avoid unreasonable growth of * planning time. */ for (; r != NIL; r = lnext(r)) @@ -115,8 +120,9 @@ make_rels_by_joins(Query *root, int level) int old_level = length(old_rel->relids); List *r2; - /* We can quit once past the halfway point (make_join_rel took care - * of making the opposite-direction joins) + /* + * We can quit once past the halfway point (make_join_rel took + * care of making the opposite-direction joins) */ if (old_level * 2 < level) break; @@ -137,8 +143,10 @@ make_rels_by_joins(Query *root, int level) { List *i; - /* OK, we can build a rel of the right level from this pair of - * rels. Do so if there is at least one usable join clause. + /* + * OK, we can build a rel of the right level from this + * pair of rels. Do so if there is at least one usable + * join clause. */ foreach(i, old_rel->joininfo) { @@ -192,7 +200,7 @@ make_rels_by_clause_joins(Query *root, foreach(j, other_rels) { - RelOptInfo *other_rel = (RelOptInfo *) lfirst(j); + RelOptInfo *other_rel = (RelOptInfo *) lfirst(j); if (is_subseti(unjoined_relids, other_rel->relids)) result = make_join_rel(root, old_rel, other_rel); @@ -251,8 +259,8 @@ make_rels_by_clauseless_joins(Query *root, static RelOptInfo * make_join_rel(Query *root, RelOptInfo *rel1, RelOptInfo *rel2) { - RelOptInfo *joinrel; - List *restrictlist; + RelOptInfo *joinrel; + List *restrictlist; /* * Find or build the join RelOptInfo, and compute the restrictlist diff --git a/src/backend/optimizer/path/orindxpath.c b/src/backend/optimizer/path/orindxpath.c index e2ae3f0577a..85e96d6b86c 100644 --- a/src/backend/optimizer/path/orindxpath.c +++ b/src/backend/optimizer/path/orindxpath.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.38 2000/03/22 22:08:33 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.39 2000/04/12 17:15:20 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -27,14 +27,14 @@ static void best_or_subclause_indices(Query *root, RelOptInfo *rel, - List *subclauses, List *indices, - IndexPath *pathnode); + List *subclauses, List *indices, + IndexPath *pathnode); static void best_or_subclause_index(Query *root, RelOptInfo *rel, - Expr *subclause, List *indices, - List **retIndexQual, - Oid *retIndexid, - Cost *retStartupCost, - Cost *retTotalCost); + Expr *subclause, List *indices, + List **retIndexQual, + Oid *retIndexid, + Cost *retStartupCost, + Cost *retTotalCost); /* @@ -61,8 +61,8 @@ create_or_index_paths(Query *root, /* * Check to see if this clause is an 'or' clause, and, if so, * whether or not each of the subclauses within the 'or' clause - * has been matched by an index. The information used was - * saved by create_index_paths(). + * has been matched by an index. The information used was saved + * by create_index_paths(). */ if (restriction_is_or_clause(clausenode) && clausenode->subclauseindices) @@ -80,6 +80,7 @@ create_or_index_paths(Query *root, } if (all_indexable) { + /* * OK, build an IndexPath for this OR clause, using the * best available index for each subclause. @@ -88,19 +89,23 @@ create_or_index_paths(Query *root, pathnode->path.pathtype = T_IndexScan; pathnode->path.parent = rel; + /* - * This is an IndexScan, but the overall result will consist - * of tuples extracted in multiple passes (one for each - * subclause of the OR), so the result cannot be claimed - * to have any particular ordering. + * This is an IndexScan, but the overall result will + * consist of tuples extracted in multiple passes (one for + * each subclause of the OR), so the result cannot be + * claimed to have any particular ordering. */ pathnode->path.pathkeys = NIL; - /* We don't actually care what order the index scans in ... */ + /* + * We don't actually care what order the index scans in + * ... + */ pathnode->indexscandir = NoMovementScanDirection; /* This isn't a nestloop innerjoin, so: */ - pathnode->joinrelids = NIL; /* no join clauses here */ + pathnode->joinrelids = NIL; /* no join clauses here */ pathnode->rows = rel->rows; best_or_subclause_indices(root, @@ -125,7 +130,7 @@ create_or_index_paths(Query *root, * This routine also creates the indexqual and indexid lists that will * be needed by the executor. The indexqual list has one entry for each * scan of the base rel, which is a sublist of indexqual conditions to - * apply in that scan. The implicit semantics are AND across each sublist + * apply in that scan. The implicit semantics are AND across each sublist * of quals, and OR across the toplevel list (note that the executor * takes care not to return any single tuple more than once). The indexid * list gives the OID of the index to be used in each scan. @@ -181,7 +186,7 @@ best_or_subclause_indices(Query *root, pathnode->indexqual = lappend(pathnode->indexqual, best_indexqual); pathnode->indexid = lappendi(pathnode->indexid, best_indexid); - if (slist == subclauses) /* first scan? */ + if (slist == subclauses)/* first scan? */ pathnode->path.startup_cost = best_startup_cost; pathnode->path.total_cost += best_total_cost; diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c index d5fbf82eb50..580675a85b7 100644 --- a/src/backend/optimizer/path/pathkeys.c +++ b/src/backend/optimizer/path/pathkeys.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.20 2000/02/18 23:47:19 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.21 2000/04/12 17:15:20 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -28,7 +28,7 @@ static PathKeyItem *makePathKeyItem(Node *key, Oid sortop); static List *make_canonical_pathkey(Query *root, PathKeyItem *item); static Var *find_indexkey_var(Query *root, RelOptInfo *rel, - AttrNumber varattno); + AttrNumber varattno); /*-------------------- @@ -42,8 +42,8 @@ static Var *find_indexkey_var(Query *root, RelOptInfo *rel, * of scanning the relation and the resulting ordering of the tuples. * Sequential scan Paths have NIL pathkeys, indicating no known ordering. * Index scans have Path.pathkeys that represent the chosen index's ordering, - * if any. A single-key index would create a pathkey with a single sublist, - * e.g. ( (tab1.indexkey1/sortop1) ). A multi-key index generates a sublist + * if any. A single-key index would create a pathkey with a single sublist, + * e.g. ( (tab1.indexkey1/sortop1) ). A multi-key index generates a sublist * per key, e.g. ( (tab1.indexkey1/sortop1) (tab1.indexkey2/sortop2) ) which * shows major sort by indexkey1 (ordering by sortop1) and minor sort by * indexkey2 with sortop2. @@ -56,10 +56,10 @@ static Var *find_indexkey_var(Query *root, RelOptInfo *rel, * ordering operators used. * * Things get more interesting when we consider joins. Suppose we do a - * mergejoin between A and B using the mergeclause A.X = B.Y. The output + * mergejoin between A and B using the mergeclause A.X = B.Y. The output * of the mergejoin is sorted by X --- but it is also sorted by Y. We * represent this fact by listing both keys in a single pathkey sublist: - * ( (A.X/xsortop B.Y/ysortop) ). This pathkey asserts that the major + * ( (A.X/xsortop B.Y/ysortop) ). This pathkey asserts that the major * sort order of the Path can be taken to be *either* A.X or B.Y. * They are equal, so they are both primary sort keys. By doing this, * we allow future joins to use either var as a pre-sorted key, so upper @@ -120,12 +120,12 @@ static Var *find_indexkey_var(Query *root, RelOptInfo *rel, * We did implement pathkeys just as described above, and found that the * planner spent a huge amount of time comparing pathkeys, because the * representation of pathkeys as unordered lists made it expensive to decide - * whether two were equal or not. So, we've modified the representation + * whether two were equal or not. So, we've modified the representation * as described next. * * If we scan the WHERE clause for equijoin clauses (mergejoinable clauses) * during planner startup, we can construct lists of equivalent pathkey items - * for the query. There could be more than two items per equivalence set; + * for the query. There could be more than two items per equivalence set; * for example, WHERE A.X = B.Y AND B.Y = C.Z AND D.R = E.S creates the * equivalence sets { A.X B.Y C.Z } and { D.R E.S } (plus associated sortops). * Any pathkey item that belongs to an equivalence set implies that all the @@ -147,20 +147,20 @@ static Var *find_indexkey_var(Query *root, RelOptInfo *rel, * equivalence set, we instantly add all the other vars equivalenced to it, * whether they appear yet in the pathkey's relation or not. And we also * mandate that the pathkey sublist appear in the same order as the - * equivalence set it comes from. (In practice, we simply return a pointer + * equivalence set it comes from. (In practice, we simply return a pointer * to the relevant equivalence set without building any new sublist at all.) * This makes comparing pathkeys very simple and fast, and saves a lot of * work and memory space for pathkey construction as well. * * Note that pathkey sublists having just one item still exist, and are - * not expected to be equal() to any equivalence set. This occurs when + * not expected to be equal() to any equivalence set. This occurs when * we describe a sort order that involves a var that's not mentioned in * any equijoin clause of the WHERE. We could add singleton sets containing * such vars to the query's list of equivalence sets, but there's little * point in doing so. * * By the way, it's OK and even useful for us to build equivalence sets - * that mention multiple vars from the same relation. For example, if + * that mention multiple vars from the same relation. For example, if * we have WHERE A.X = A.Y and we are scanning A using an index on X, * we can legitimately conclude that the path is sorted by Y as well; * and this could be handy if Y is the variable used in other join clauses @@ -179,7 +179,7 @@ static Var *find_indexkey_var(Query *root, RelOptInfo *rel, static PathKeyItem * makePathKeyItem(Node *key, Oid sortop) { - PathKeyItem *item = makeNode(PathKeyItem); + PathKeyItem *item = makeNode(PathKeyItem); item->key = key; item->sortop = sortop; @@ -219,11 +219,13 @@ add_equijoined_keys(Query *root, RestrictInfo *restrictinfo) /* We might see a clause X=X; don't make a single-element list from it */ if (equal(item1, item2)) return; + /* - * Our plan is to make a two-element set, then sweep through the existing - * equijoin sets looking for matches to item1 or item2. When we find one, - * we remove that set from equi_key_list and union it into our new set. - * When done, we add the new set to the front of equi_key_list. + * Our plan is to make a two-element set, then sweep through the + * existing equijoin sets looking for matches to item1 or item2. When + * we find one, we remove that set from equi_key_list and union it + * into our new set. When done, we add the new set to the front of + * equi_key_list. * * This is a standard UNION-FIND problem, for which there exist better * data structures than simple lists. If this code ever proves to be @@ -240,8 +242,11 @@ add_equijoined_keys(Query *root, RestrictInfo *restrictinfo) { /* Found a set to merge into our new set */ newset = LispUnion(newset, curset); - /* Remove old set from equi_key_list. NOTE this does not change - * lnext(cursetlink), so the outer foreach doesn't break. + + /* + * Remove old set from equi_key_list. NOTE this does not + * change lnext(cursetlink), so the outer foreach doesn't + * break. */ root->equi_key_list = lremove(curset, root->equi_key_list); freeList(curset); /* might as well recycle old cons cells */ @@ -256,7 +261,7 @@ add_equijoined_keys(Query *root, RestrictInfo *restrictinfo) * Given a PathKeyItem, find the equi_key_list subset it is a member of, * if any. If so, return a pointer to that sublist, which is the * canonical representation (for this query) of that PathKeyItem's - * equivalence set. If it is not found, return a single-element list + * equivalence set. If it is not found, return a single-element list * containing the PathKeyItem (when the item has no equivalence peers, * we just allow it to be a standalone list). * @@ -293,13 +298,13 @@ canonicalize_pathkeys(Query *root, List *pathkeys) foreach(i, pathkeys) { - List *pathkey = (List *) lfirst(i); - PathKeyItem *item; + List *pathkey = (List *) lfirst(i); + PathKeyItem *item; /* - * It's sufficient to look at the first entry in the sublist; - * if there are more entries, they're already part of an - * equivalence set by definition. + * It's sufficient to look at the first entry in the sublist; if + * there are more entries, they're already part of an equivalence + * set by definition. */ Assert(pathkey != NIL); item = (PathKeyItem *) lfirst(pathkey); @@ -319,12 +324,12 @@ canonicalize_pathkeys(Query *root, List *pathkeys) * one is "better" than the other. * * A pathkey can be considered better than another if it is a superset: - * it contains all the keys of the other plus more. For example, either + * it contains all the keys of the other plus more. For example, either * ((A) (B)) or ((A B)) is better than ((A)). * * Because we actually only expect to see canonicalized pathkey sublists, * we don't have to do the full two-way-subset-inclusion test on each - * pair of sublists that is implied by the above statement. Instead we + * pair of sublists that is implied by the above statement. Instead we * just do an equal(). In the normal case where multi-element sublists * are pointers into the root's equi_key_list, equal() will be very fast: * it will recognize pointer equality when the sublists are the same, @@ -345,23 +350,25 @@ compare_pathkeys(List *keys1, List *keys2) List *subkey1 = lfirst(key1); List *subkey2 = lfirst(key2); - /* We will never have two subkeys where one is a subset of the other, - * because of the canonicalization explained above. Either they are - * equal or they ain't. + /* + * We will never have two subkeys where one is a subset of the + * other, because of the canonicalization explained above. Either + * they are equal or they ain't. */ - if (! equal(subkey1, subkey2)) - return PATHKEYS_DIFFERENT; /* no need to keep looking */ + if (!equal(subkey1, subkey2)) + return PATHKEYS_DIFFERENT; /* no need to keep looking */ } - /* If we reached the end of only one list, the other is longer and - * therefore not a subset. (We assume the additional sublist(s) - * of the other list are not NIL --- no pathkey list should ever have - * a NIL sublist.) + /* + * If we reached the end of only one list, the other is longer and + * therefore not a subset. (We assume the additional sublist(s) of + * the other list are not NIL --- no pathkey list should ever have a + * NIL sublist.) */ if (key1 == NIL && key2 == NIL) return PATHKEYS_EQUAL; if (key1 != NIL) - return PATHKEYS_BETTER1; /* key1 is longer */ + return PATHKEYS_BETTER1;/* key1 is longer */ return PATHKEYS_BETTER2; /* key2 is longer */ } @@ -375,8 +382,8 @@ pathkeys_contained_in(List *keys1, List *keys2) { switch (compare_pathkeys(keys1, keys2)) { - case PATHKEYS_EQUAL: - case PATHKEYS_BETTER2: + case PATHKEYS_EQUAL: + case PATHKEYS_BETTER2: return true; default: break; @@ -448,7 +455,7 @@ get_cheapest_fractional_path_for_pathkeys(List *paths, * do that first. */ if (matched_path != NULL && - compare_fractional_path_costs(matched_path, path, fraction) <= 0) + compare_fractional_path_costs(matched_path, path, fraction) <= 0) continue; if (pathkeys_contained_in(pathkeys, path->pathkeys)) @@ -469,7 +476,7 @@ get_cheapest_fractional_path_for_pathkeys(List *paths, * its "ordering" field, and we will return NIL.) * * If 'scandir' is BackwardScanDirection, attempt to build pathkeys - * representing a backwards scan of the index. Return NIL if can't do it. + * representing a backwards scan of the index. Return NIL if can't do it. */ List * build_index_pathkeys(Query *root, @@ -527,7 +534,7 @@ build_index_pathkeys(Query *root, /* Normal non-functional index */ while (*indexkeys != 0 && *ordering != InvalidOid) { - Var *relvar = find_indexkey_var(root, rel, *indexkeys); + Var *relvar = find_indexkey_var(root, rel, *indexkeys); sortop = *ordering; if (ScanDirectionIsBackward(scandir)) @@ -569,9 +576,9 @@ find_indexkey_var(Query *root, RelOptInfo *rel, AttrNumber varattno) foreach(temp, rel->targetlist) { - Var *tle_var = get_expr(lfirst(temp)); + Var *tle_var = get_expr(lfirst(temp)); - if (IsA(tle_var, Var) && tle_var->varattno == varattno) + if (IsA(tle_var, Var) &&tle_var->varattno == varattno) return tle_var; } @@ -606,11 +613,12 @@ build_join_pathkeys(List *outer_pathkeys, List *join_rel_tlist, List *equi_key_list) { + /* * This used to be quite a complex bit of code, but now that all - * pathkey sublists start out life canonicalized, we don't have to - * do a darn thing here! The inner-rel vars we used to need to add - * are *already* part of the outer pathkey! + * pathkey sublists start out life canonicalized, we don't have to do + * a darn thing here! The inner-rel vars we used to need to add are + * *already* part of the outer pathkey! * * I'd remove the routine entirely, but maybe someday we'll need it... */ @@ -644,16 +652,17 @@ make_pathkeys_for_sortclauses(List *sortclauses, foreach(i, sortclauses) { - SortClause *sortcl = (SortClause *) lfirst(i); - Node *sortkey; - PathKeyItem *pathkey; + SortClause *sortcl = (SortClause *) lfirst(i); + Node *sortkey; + PathKeyItem *pathkey; sortkey = get_sortgroupclause_expr(sortcl, tlist); pathkey = makePathKeyItem(sortkey, sortcl->sortop); + /* * The pathkey becomes a one-element sublist, for now; - * canonicalize_pathkeys() might replace it with a longer - * sublist later. + * canonicalize_pathkeys() might replace it with a longer sublist + * later. */ pathkeys = lappend(pathkeys, lcons(pathkey, NIL)); } @@ -691,28 +700,28 @@ find_mergeclauses_for_pathkeys(List *pathkeys, List *restrictinfos) foreach(i, pathkeys) { - List *pathkey = lfirst(i); - RestrictInfo *matched_restrictinfo = NULL; - List *j; + List *pathkey = lfirst(i); + RestrictInfo *matched_restrictinfo = NULL; + List *j; /* - * We can match any of the keys in this pathkey sublist, - * since they're all equivalent. And we can match against - * either left or right side of any mergejoin clause we haven't - * used yet. For the moment we use a dumb "greedy" algorithm - * with no backtracking. Is it worth being any smarter to - * make a longer list of usable mergeclauses? Probably not. + * We can match any of the keys in this pathkey sublist, since + * they're all equivalent. And we can match against either left + * or right side of any mergejoin clause we haven't used yet. For + * the moment we use a dumb "greedy" algorithm with no + * backtracking. Is it worth being any smarter to make a longer + * list of usable mergeclauses? Probably not. */ foreach(j, pathkey) { - PathKeyItem *keyitem = lfirst(j); - Node *key = keyitem->key; - Oid keyop = keyitem->sortop; - List *k; + PathKeyItem *keyitem = lfirst(j); + Node *key = keyitem->key; + Oid keyop = keyitem->sortop; + List *k; foreach(k, restrictinfos) { - RestrictInfo *restrictinfo = lfirst(k); + RestrictInfo *restrictinfo = lfirst(k); Assert(restrictinfo->mergejoinoperator != InvalidOid); @@ -720,7 +729,7 @@ find_mergeclauses_for_pathkeys(List *pathkeys, List *restrictinfos) equal(key, get_leftop(restrictinfo->clause))) || (keyop == restrictinfo->right_sortop && equal(key, get_rightop(restrictinfo->clause)))) && - ! member(restrictinfo, mergeclauses)) + !member(restrictinfo, mergeclauses)) { matched_restrictinfo = restrictinfo; break; @@ -732,11 +741,12 @@ find_mergeclauses_for_pathkeys(List *pathkeys, List *restrictinfos) /* * If we didn't find a mergeclause, we're done --- any additional - * sort-key positions in the pathkeys are useless. (But we can + * sort-key positions in the pathkeys are useless. (But we can * still mergejoin if we found at least one mergeclause.) */ - if (! matched_restrictinfo) + if (!matched_restrictinfo) break; + /* * If we did find a usable mergeclause for this sort-key position, * add it to result list. @@ -756,7 +766,7 @@ find_mergeclauses_for_pathkeys(List *pathkeys, List *restrictinfos) * 'mergeclauses' is a list of RestrictInfos for mergejoin clauses * that will be used in a merge join. * 'tlist' is a relation target list for either the inner or outer - * side of the proposed join rel. (Not actually needed anymore) + * side of the proposed join rel. (Not actually needed anymore) * * Returns a pathkeys list that can be applied to the indicated relation. * @@ -785,24 +795,26 @@ make_pathkeys_for_mergeclauses(Query *root, /* * Find the key and sortop needed for this mergeclause. * - * Both sides of the mergeclause should appear in one of the - * query's pathkey equivalence classes, so it doesn't matter - * which one we use here. + * Both sides of the mergeclause should appear in one of the query's + * pathkey equivalence classes, so it doesn't matter which one we + * use here. */ key = (Node *) get_leftop(restrictinfo->clause); sortop = restrictinfo->left_sortop; + /* - * Find pathkey sublist for this sort item. We expect to find - * the canonical set including the mergeclause's left and right - * sides; if we get back just the one item, something is rotten. + * Find pathkey sublist for this sort item. We expect to find the + * canonical set including the mergeclause's left and right sides; + * if we get back just the one item, something is rotten. */ item = makePathKeyItem(key, sortop); pathkey = make_canonical_pathkey(root, item); Assert(length(pathkey) > 1); + /* - * Since the item we just made is not in the returned canonical set, - * we can free it --- this saves a useful amount of storage in a - * big join tree. + * Since the item we just made is not in the returned canonical + * set, we can free it --- this saves a useful amount of storage + * in a big join tree. */ pfree(item); diff --git a/src/backend/optimizer/path/tidpath.c b/src/backend/optimizer/path/tidpath.c index 1e7dc43473b..7824e0e3d2f 100644 --- a/src/backend/optimizer/path/tidpath.c +++ b/src/backend/optimizer/path/tidpath.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/tidpath.c,v 1.5 2000/02/15 20:49:17 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/tidpath.c,v 1.6 2000/04/12 17:15:20 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -37,30 +37,34 @@ #include "utils/lsyscache.h" static void create_tidscan_joinpaths(RelOptInfo *rel); -static List *TidqualFromRestrictinfo(List *relids, List *restrictinfo); -static bool isEvaluable(int varno, Node *node); -static Node *TidequalClause(int varno, Expr *node); -static List *TidqualFromExpr(int varno, Expr *expr); +static List *TidqualFromRestrictinfo(List *relids, List *restrictinfo); +static bool isEvaluable(int varno, Node *node); +static Node *TidequalClause(int varno, Expr *node); +static List *TidqualFromExpr(int varno, Expr *expr); static -bool isEvaluable(int varno, Node *node) +bool +isEvaluable(int varno, Node *node) { - List *lst; - Expr *expr; + List *lst; + Expr *expr; - if (IsA(node, Const)) return true; - if (IsA(node, Param)) return true; + if (IsA(node, Const)) + return true; + if (IsA(node, Param)) + return true; if (IsA(node, Var)) { - Var *var = (Var *)node; + Var *var = (Var *) node; if (var->varno == varno) return false; return true; } - if (!is_funcclause(node)) return false; - expr = (Expr *)node; - foreach (lst, expr->args) + if (!is_funcclause(node)) + return false; + expr = (Expr *) node; + foreach(lst, expr->args) { if (!isEvaluable(varno, lfirst(lst))) return false; @@ -72,53 +76,60 @@ bool isEvaluable(int varno, Node *node) /* * The 2nd parameter should be an opclause * Extract the right node if the opclause is CTID= .... - * or the left node if the opclause is ....=CTID + * or the left node if the opclause is ....=CTID */ static -Node *TidequalClause(int varno, Expr *node) +Node * +TidequalClause(int varno, Expr *node) { - Node *rnode = 0, *arg1, *arg2, *arg; - Oper *oper; - Var *var; - Const *aconst; - Param *param; - Expr *expr; + Node *rnode = 0, + *arg1, + *arg2, + *arg; + Oper *oper; + Var *var; + Const *aconst; + Param *param; + Expr *expr; - if (!node->oper) return rnode; - if (!node->args) return rnode; - if (length(node->args) != 2) return rnode; - oper = (Oper *) node->oper; + if (!node->oper) + return rnode; + if (!node->args) + return rnode; + if (length(node->args) != 2) + return rnode; + oper = (Oper *) node->oper; if (oper->opno != TIDEqualOperator) return rnode; arg1 = lfirst(node->args); arg2 = lsecond(node->args); - arg = (Node *)0; + arg = (Node *) 0; if (IsA(arg1, Var)) { var = (Var *) arg1; if (var->varno == varno && - var->varattno == SelfItemPointerAttributeNumber && - var->vartype == TIDOID) + var->varattno == SelfItemPointerAttributeNumber && + var->vartype == TIDOID) arg = arg2; else if (var->varnoold == varno && - var->varoattno == SelfItemPointerAttributeNumber && - var->vartype == TIDOID) + var->varoattno == SelfItemPointerAttributeNumber && + var->vartype == TIDOID) arg = arg2; } if ((!arg) && IsA(arg2, Var)) { var = (Var *) arg2; if (var->varno == varno && - var->varattno == SelfItemPointerAttributeNumber && - var->vartype == TIDOID) + var->varattno == SelfItemPointerAttributeNumber && + var->vartype == TIDOID) arg = arg1; } if (!arg) return rnode; switch (nodeTag(arg)) { - case T_Const: + case T_Const: aconst = (Const *) arg; if (aconst->consttype != TIDOID) return rnode; @@ -126,27 +137,29 @@ Node *TidequalClause(int varno, Expr *node) return rnode; rnode = arg; break; - case T_Param: + case T_Param: param = (Param *) arg; if (param->paramtype != TIDOID) return rnode; rnode = arg; break; - case T_Var: + case T_Var: var = (Var *) arg; if (var->varno == varno || - var->vartype != TIDOID) + var->vartype != TIDOID) return rnode; rnode = arg; break; - case T_Expr: + case T_Expr: expr = (Expr *) arg; - if (expr->typeOid != TIDOID) return rnode; - if (expr->opType != FUNC_EXPR) return rnode; - if (isEvaluable(varno, (Node *)expr)) + if (expr->typeOid != TIDOID) + return rnode; + if (expr->opType != FUNC_EXPR) + return rnode; + if (isEvaluable(varno, (Node *) expr)) rnode = arg; break; - default: + default: break; } return rnode; @@ -160,43 +173,43 @@ Node *TidequalClause(int varno, Expr *node) * When the expr node is an and_clause,we return the list of * CTID values if we could extract the CTID values from a member * node. - */ + */ static -List *TidqualFromExpr(int varno, Expr *expr) +List * +TidqualFromExpr(int varno, Expr *expr) { - List *rlst = NIL, *lst, *frtn; - Node *node = (Node *) expr, *rnode; + List *rlst = NIL, + *lst, + *frtn; + Node *node = (Node *) expr, + *rnode; if (is_opclause(node)) { rnode = TidequalClause(varno, expr); if (rnode) - { rlst = lcons(rnode, rlst); - } } else if (and_clause(node)) { - foreach (lst, expr->args) + foreach(lst, expr->args) { node = lfirst(lst); - if (!IsA(node, Expr)) + if (!IsA(node, Expr)) continue; - rlst = TidqualFromExpr(varno, (Expr *)node); + rlst = TidqualFromExpr(varno, (Expr *) node); if (rlst) break; } } else if (or_clause(node)) { - foreach (lst, expr->args) + foreach(lst, expr->args) { node = lfirst(lst); if (IsA(node, Expr) && - (frtn = TidqualFromExpr(varno, (Expr *)node)) ) - { + (frtn = TidqualFromExpr(varno, (Expr *) node))) rlst = nconc(rlst, frtn); - } else { if (rlst) @@ -207,24 +220,26 @@ List *TidqualFromExpr(int varno, Expr *expr) } } return rlst; -} +} static List * TidqualFromRestrictinfo(List *relids, List *restrictinfo) { - List *lst, *rlst = NIL; - int varno; - Node *node; - Expr *expr; + List *lst, + *rlst = NIL; + int varno; + Node *node; + Expr *expr; if (length(relids) != 1) return NIL; varno = lfirsti(relids); - foreach (lst, restrictinfo) + foreach(lst, restrictinfo) { node = lfirst(lst); - if (!IsA(node, RestrictInfo)) continue; - expr = ((RestrictInfo *)node)->clause; + if (!IsA(node, RestrictInfo)) + continue; + expr = ((RestrictInfo *) node)->clause; rlst = TidqualFromExpr(varno, expr); if (rlst) break; @@ -241,20 +256,20 @@ TidqualFromRestrictinfo(List *relids, List *restrictinfo) static void create_tidscan_joinpaths(RelOptInfo *rel) { - List *rlst = NIL, - *lst; + List *rlst = NIL, + *lst; - foreach (lst, rel->joininfo) + foreach(lst, rel->joininfo) { JoinInfo *joininfo = (JoinInfo *) lfirst(lst); - List *restinfo, - *tideval; + List *restinfo, + *tideval; restinfo = joininfo->jinfo_restrictinfo; tideval = TidqualFromRestrictinfo(rel->relids, restinfo); if (length(tideval) == 1) { - TidPath *pathnode = makeNode(TidPath); + TidPath *pathnode = makeNode(TidPath); pathnode->path.pathtype = T_TidScan; pathnode->path.parent = rel; @@ -278,9 +293,9 @@ create_tidscan_joinpaths(RelOptInfo *rel) void create_tidscan_paths(Query *root, RelOptInfo *rel) { - List *tideval = TidqualFromRestrictinfo(rel->relids, - rel->baserestrictinfo); - + List *tideval = TidqualFromRestrictinfo(rel->relids, + rel->baserestrictinfo); + if (tideval) add_path(rel, (Path *) create_tidscan_path(rel, tideval)); create_tidscan_joinpaths(rel); diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 3fab7f08b87..9cd8a11159a 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.88 2000/04/04 01:21:47 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.89 2000/04/12 17:15:21 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -32,15 +32,15 @@ static List *switch_outer(List *clauses); -static int set_tlist_sort_info(List *tlist, List *pathkeys); +static int set_tlist_sort_info(List *tlist, List *pathkeys); static Scan *create_scan_node(Query *root, Path *best_path, List *tlist); static Join *create_join_node(Query *root, JoinPath *best_path, List *tlist); static SeqScan *create_seqscan_node(Path *best_path, List *tlist, List *scan_clauses); static IndexScan *create_indexscan_node(Query *root, IndexPath *best_path, - List *tlist, List *scan_clauses); + List *tlist, List *scan_clauses); static TidScan *create_tidscan_node(TidPath *best_path, List *tlist, - List *scan_clauses); + List *scan_clauses); static NestLoop *create_nestloop_node(NestPath *best_path, List *tlist, List *clauses, Plan *outer_node, List *outer_tlist, Plan *inner_node, List *inner_tlist); @@ -52,16 +52,16 @@ static HashJoin *create_hashjoin_node(HashPath *best_path, List *tlist, Plan *inner_node, List *inner_tlist); static List *fix_indxqual_references(List *indexquals, IndexPath *index_path); static List *fix_indxqual_sublist(List *indexqual, int baserelid, Oid relam, - Form_pg_index index); + Form_pg_index index); static Node *fix_indxqual_operand(Node *node, int baserelid, - Form_pg_index index, - Oid *opclass); + Form_pg_index index, + Oid *opclass); static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid, - List *indxid, List *indxqual, - List *indxqualorig, - ScanDirection indexscandir); + List *indxid, List *indxqual, + List *indxqualorig, + ScanDirection indexscandir); static TidScan *make_tidscan(List *qptlist, List *qpqual, Index scanrelid, - List *tideval); + List *tideval); static NestLoop *make_nestloop(List *qptlist, List *qpqual, Plan *lefttree, Plan *righttree); static HashJoin *make_hashjoin(List *tlist, List *qpqual, @@ -166,8 +166,8 @@ create_scan_node(Query *root, Path *best_path, List *tlist) case T_TidScan: node = (Scan *) create_tidscan_node((TidPath *) best_path, - tlist, - scan_clauses); + tlist, + scan_clauses); break; default: @@ -242,6 +242,7 @@ create_join_node(Query *root, JoinPath *best_path, List *tlist) } #ifdef NOT_USED + /* * * Expensive function pullups may have pulled local predicates * * into this path node. Put them in the qpqual of the plan node. * @@ -250,7 +251,7 @@ create_join_node(Query *root, JoinPath *best_path, List *tlist) if (get_loc_restrictinfo(best_path) != NIL) set_qpqual((Plan) retval, nconc(get_qpqual((Plan) retval), - get_actual_clauses(get_loc_restrictinfo(best_path)))); + get_actual_clauses(get_loc_restrictinfo(best_path)))); #endif return retval; @@ -345,17 +346,17 @@ create_indexscan_node(Query *root, * for lossy indices the indxqual predicates need to be double-checked * after the index fetches the best-guess tuples. * - * Since the indexquals were generated from the restriction clauses - * given by scan_clauses, there will normally be some duplications - * between the lists. We get rid of the duplicates, then add back - * if lossy. + * Since the indexquals were generated from the restriction clauses given + * by scan_clauses, there will normally be some duplications between + * the lists. We get rid of the duplicates, then add back if lossy. */ if (length(indxqual) > 1) { + /* * Build an expression representation of the indexqual, expanding - * the implicit OR and AND semantics of the first- and second-level - * lists. + * the implicit OR and AND semantics of the first- and + * second-level lists. */ List *orclauses = NIL; List *orclause; @@ -374,8 +375,11 @@ create_indexscan_node(Query *root, } else if (indxqual != NIL) { - /* Here, we can simply treat the first sublist as an independent - * set of qual expressions, since there is no top-level OR behavior. + + /* + * Here, we can simply treat the first sublist as an independent + * set of qual expressions, since there is no top-level OR + * behavior. */ List *indxqual_list = lfirst(indxqual); @@ -387,8 +391,9 @@ create_indexscan_node(Query *root, else qpqual = scan_clauses; - /* The executor needs a copy with the indexkey on the left of each clause - * and with index attr numbers substituted for table ones. + /* + * The executor needs a copy with the indexkey on the left of each + * clause and with index attr numbers substituted for table ones. */ fixed_indxqual = fix_indxqual_references(indxqual, best_path); @@ -410,11 +415,11 @@ create_indexscan_node(Query *root, static TidScan * make_tidscan(List *qptlist, List *qpqual, - Index scanrelid, + Index scanrelid, List *tideval) { - TidScan *node = makeNode(TidScan); - Plan *plan = &node->scan.plan; + TidScan *node = makeNode(TidScan); + Plan *plan = &node->scan.plan; /* cost should be inserted by caller */ plan->state = (EState *) NULL; @@ -423,7 +428,8 @@ make_tidscan(List *qptlist, plan->lefttree = NULL; plan->righttree = NULL; node->scan.scanrelid = scanrelid; - node->tideval = copyObject(tideval); /* XXX do we really need a copy? */ + node->tideval = copyObject(tideval); /* XXX do we really need a + * copy? */ node->needRescan = false; node->scan.scanstate = (CommonScanState *) NULL; @@ -438,8 +444,8 @@ make_tidscan(List *qptlist, static TidScan * create_tidscan_node(TidPath *best_path, List *tlist, List *scan_clauses) { - TidScan *scan_node; - Index scan_relid; + TidScan *scan_node; + Index scan_relid; /* there should be exactly one base rel involved... */ Assert(length(best_path->path.parent->relids) == 1); @@ -452,7 +458,7 @@ create_tidscan_node(TidPath *best_path, List *tlist, List *scan_clauses) best_path->tideval); if (best_path->unjoined_relids) - scan_node->needRescan = true; + scan_node->needRescan = true; copy_path_costsize(&scan_node->scan.plan, &best_path->path); @@ -467,7 +473,7 @@ create_tidscan_node(TidPath *best_path, List *tlist, List *scan_clauses) * once we have changed a Var node to refer to a subplan output rather than * the original relation, it is no longer equal() to an unmodified Var node * for the same var. So, we cannot easily compare reference-adjusted qual - * clauses to clauses that have not been adjusted. Fortunately, that + * clauses to clauses that have not been adjusted. Fortunately, that * doesn't seem to be necessary; all the decisions are made before we do * the reference adjustments. * @@ -493,6 +499,7 @@ create_nestloop_node(NestPath *best_path, if (IsA(inner_node, IndexScan)) { + /* * An index is being used to reduce the number of tuples scanned * in the inner relation. If there are join clauses being used @@ -522,12 +529,13 @@ create_nestloop_node(NestPath *best_path, { Index innerrel = innerscan->scan.scanrelid; - /* Remove redundant tests from my clauses, if possible. - * Note we must compare against indxqualorig not the "fixed" - * indxqual (which has index attnos instead of relation attnos, - * and may have been commuted as well). + /* + * Remove redundant tests from my clauses, if possible. Note + * we must compare against indxqualorig not the "fixed" + * indxqual (which has index attnos instead of relation + * attnos, and may have been commuted as well). */ - if (length(indxqualorig) == 1) /* single indexscan? */ + if (length(indxqualorig) == 1) /* single indexscan? */ clauses = set_difference(clauses, lfirst(indxqualorig)); /* only refs to outer vars get changed in the inner indexqual */ @@ -549,17 +557,20 @@ create_nestloop_node(NestPath *best_path, } else if (IsA(inner_node, TidScan)) { - List *inner_tideval = ((TidScan *) inner_node)->tideval; - TidScan *innerscan = (TidScan *) inner_node; + List *inner_tideval = ((TidScan *) inner_node)->tideval; + TidScan *innerscan = (TidScan *) inner_node; + ((TidScan *) inner_node)->tideval = join_references(inner_tideval, outer_tlist, inner_tlist, innerscan->scan.scanrelid); - } + } else if (IsA_Join(inner_node)) { + /* * Materialize the inner join for speed reasons. * * XXX It is probably *not* always fastest to materialize an inner - * join --- how can we estimate whether this is a good thing to do? + * join --- how can we estimate whether this is a good thing to + * do? */ inner_node = (Plan *) make_noname(inner_tlist, NIL, @@ -595,9 +606,9 @@ create_mergejoin_node(MergePath *best_path, mergeclauses = get_actual_clauses(best_path->path_mergeclauses); /* - * Remove the mergeclauses from the list of join qual clauses, - * leaving the list of quals that must be checked as qpquals. - * Set those clauses to contain INNER/OUTER var references. + * Remove the mergeclauses from the list of join qual clauses, leaving + * the list of quals that must be checked as qpquals. Set those + * clauses to contain INNER/OUTER var references. */ qpqual = join_references(set_difference(clauses, mergeclauses), outer_tlist, @@ -655,16 +666,16 @@ create_hashjoin_node(HashPath *best_path, /* * NOTE: there will always be exactly one hashclause in the list - * best_path->path_hashclauses (cf. hash_inner_and_outer()). - * We represent it as a list anyway, for convenience with routines - * that want to work on lists of clauses. + * best_path->path_hashclauses (cf. hash_inner_and_outer()). We + * represent it as a list anyway, for convenience with routines that + * want to work on lists of clauses. */ hashclauses = get_actual_clauses(best_path->path_hashclauses); /* - * Remove the hashclauses from the list of join qual clauses, - * leaving the list of quals that must be checked as qpquals. - * Set those clauses to contain INNER/OUTER var references. + * Remove the hashclauses from the list of join qual clauses, leaving + * the list of quals that must be checked as qpquals. Set those + * clauses to contain INNER/OUTER var references. */ qpqual = join_references(set_difference(clauses, hashclauses), outer_tlist, @@ -779,7 +790,7 @@ fix_indxqual_references(List *indexquals, IndexPath *index_path) * * For each qual clause, commute if needed to put the indexkey operand on the * left, and then change its varno. (We do not need to change the other side - * of the clause.) Also change the operator if necessary. + * of the clause.) Also change the operator if necessary. */ static List * fix_indxqual_sublist(List *indexqual, int baserelid, Oid relam, @@ -803,14 +814,16 @@ fix_indxqual_sublist(List *indexqual, int baserelid, Oid relam, length(clause->args) != 2) elog(ERROR, "fix_indxqual_sublist: indexqual clause is not binary opclause"); - /* Which side is the indexkey on? + /* + * Which side is the indexkey on? * * get_relattval sets flag&SEL_RIGHT if the indexkey is on the LEFT. */ get_relattval((Node *) clause, baserelid, &relid, &attno, &constval, &flag); - /* Make a copy that will become the fixed clause. + /* + * Make a copy that will become the fixed clause. * * We used to try to do a shallow copy here, but that fails if there * is a subplan in the arguments of the opclause. So just do a @@ -822,18 +835,19 @@ fix_indxqual_sublist(List *indexqual, int baserelid, Oid relam, if ((flag & SEL_RIGHT) == 0) CommuteClause(newclause); - /* Now, determine which index attribute this is, - * change the indexkey operand as needed, - * and get the index opclass. + /* + * Now, determine which index attribute this is, change the + * indexkey operand as needed, and get the index opclass. */ lfirst(newclause->args) = fix_indxqual_operand(lfirst(newclause->args), baserelid, index, &opclass); - /* Substitute the appropriate operator if the expression operator - * is merely binary-compatible with the index. This shouldn't fail, - * since indxpath.c found it before... + /* + * Substitute the appropriate operator if the expression operator + * is merely binary-compatible with the index. This shouldn't + * fail, since indxpath.c found it before... */ newopno = indexable_operator(newclause, opclass, relam, true); if (newopno == InvalidOid) @@ -861,12 +875,14 @@ fix_indxqual_operand(Node *node, int baserelid, Form_pg_index index, if (index->indkey[pos] == varatt) { Node *newnode = copyObject(node); + ((Var *) newnode)->varattno = pos + 1; *opclass = index->indclass[pos]; return newnode; } } } + /* * Oops, this Var isn't the indexkey! */ @@ -876,13 +892,13 @@ fix_indxqual_operand(Node *node, int baserelid, Form_pg_index index, /* * Else, it must be a func expression representing a functional index. * - * Currently, there is no need for us to do anything here for - * functional indexes. If nodeIndexscan.c sees a func clause as the left - * or right-hand toplevel operand of an indexqual, it assumes that that is - * a reference to the functional index's value and makes the appropriate - * substitution. (It would be cleaner to make the substitution here, I - * think --- suspect this issue if a join clause involving a function call - * misbehaves...) + * Currently, there is no need for us to do anything here for functional + * indexes. If nodeIndexscan.c sees a func clause as the left or + * right-hand toplevel operand of an indexqual, it assumes that that + * is a reference to the functional index's value and makes the + * appropriate substitution. (It would be cleaner to make the + * substitution here, I think --- suspect this issue if a join clause + * involving a function call misbehaves...) */ /* indclass[0] is the only class of a functional index */ @@ -915,6 +931,7 @@ switch_outer(List *clauses) Assert(op && IsA(op, Var)); if (var_is_outer(op)) { + /* * Duplicate just enough of the structure to allow commuting * the clause without changing the original list. Could use @@ -954,21 +971,21 @@ set_tlist_sort_info(List *tlist, List *pathkeys) foreach(i, pathkeys) { - List *keysublist = (List *) lfirst(i); - PathKeyItem *pathkey = NULL; - Resdom *resdom = NULL; - List *j; + List *keysublist = (List *) lfirst(i); + PathKeyItem *pathkey = NULL; + Resdom *resdom = NULL; + List *j; /* * We can sort by any one of the sort key items listed in this - * sublist. For now, we take the first one that corresponds to - * an available Var in the tlist. + * sublist. For now, we take the first one that corresponds to an + * available Var in the tlist. * * XXX if we have a choice, is there any way of figuring out which * might be cheapest to execute? (For example, int4lt is likely - * much cheaper to execute than numericlt, but both might appear in - * the same pathkey sublist...) Not clear that we ever will have - * a choice in practice, so it may not matter. + * much cheaper to execute than numericlt, but both might appear + * in the same pathkey sublist...) Not clear that we ever will + * have a choice in practice, so it may not matter. */ foreach(j, keysublist) { @@ -982,12 +999,12 @@ set_tlist_sort_info(List *tlist, List *pathkeys) elog(ERROR, "set_tlist_sort_info: cannot find tlist item to sort"); /* - * The resdom might be already marked as a sort key, if the pathkeys - * contain duplicate entries. (This can happen in scenarios where - * multiple mergejoinable clauses mention the same var, for example.) - * In that case the current pathkey is essentially a no-op, because - * only one value can be seen within any subgroup where it would be - * consulted. We can ignore it. + * The resdom might be already marked as a sort key, if the + * pathkeys contain duplicate entries. (This can happen in + * scenarios where multiple mergejoinable clauses mention the same + * var, for example.) In that case the current pathkey is + * essentially a no-op, because only one value can be seen within + * any subgroup where it would be consulted. We can ignore it. */ if (resdom->reskey == 0) { @@ -1195,7 +1212,9 @@ make_hash(List *tlist, Var *hashkey, Plan *lefttree) Plan *plan = &node->plan; copy_plan_costsize(plan, lefttree); - /* For plausibility, make startup & total costs equal total cost of + + /* + * For plausibility, make startup & total costs equal total cost of * input plan; this only affects EXPLAIN display not decisions. */ plan->startup_cost = plan->total_cost; @@ -1237,7 +1256,7 @@ make_sort(List *tlist, Oid nonameid, Plan *lefttree, int keycount) Plan *plan = &node->plan; Path sort_path; /* dummy for result of cost_sort */ - copy_plan_costsize(plan, lefttree); /* only care about copying size */ + copy_plan_costsize(plan, lefttree); /* only care about copying size */ cost_sort(&sort_path, NIL, lefttree->plan_rows, lefttree->plan_width); plan->startup_cost = sort_path.startup_cost + lefttree->total_cost; plan->total_cost = sort_path.total_cost + lefttree->total_cost; @@ -1262,9 +1281,11 @@ make_material(List *tlist, Plan *plan = &node->plan; copy_plan_costsize(plan, lefttree); - /* For plausibility, make startup & total costs equal total cost of - * input plan; this only affects EXPLAIN display not decisions. - * XXX shouldn't we charge some additional cost for materialization? + + /* + * For plausibility, make startup & total costs equal total cost of + * input plan; this only affects EXPLAIN display not decisions. XXX + * shouldn't we charge some additional cost for materialization? */ plan->startup_cost = plan->total_cost; plan->state = (EState *) NULL; @@ -1285,18 +1306,21 @@ make_agg(List *tlist, List *qual, Plan *lefttree) Plan *plan = &node->plan; copy_plan_costsize(plan, lefttree); + /* - * Charge one cpu_operator_cost per aggregate function per input tuple. + * Charge one cpu_operator_cost per aggregate function per input + * tuple. */ plan->total_cost += cpu_operator_cost * plan->plan_rows * (length(pull_agg_clause((Node *) tlist)) + length(pull_agg_clause((Node *) qual))); + /* * We will produce a single output tuple if the input is not a Group, * and a tuple per group otherwise. For now, estimate the number of - * groups as 10% of the number of tuples --- bogus, but how to do better? - * (Note we assume the input Group node is in "tuplePerGroup" mode, - * so it didn't reduce its row count already.) + * groups as 10% of the number of tuples --- bogus, but how to do + * better? (Note we assume the input Group node is in "tuplePerGroup" + * mode, so it didn't reduce its row count already.) */ if (IsA(lefttree, Group)) plan->plan_rows *= 0.1; @@ -1326,19 +1350,21 @@ make_group(List *tlist, Plan *plan = &node->plan; copy_plan_costsize(plan, lefttree); + /* - * Charge one cpu_operator_cost per comparison per input tuple. - * We assume all columns get compared at most of the tuples. + * Charge one cpu_operator_cost per comparison per input tuple. We + * assume all columns get compared at most of the tuples. */ plan->total_cost += cpu_operator_cost * plan->plan_rows * ngrp; + /* - * If tuplePerGroup (which is named exactly backwards) is true, - * we will return all the input tuples, so the input node's row count - * is OK. Otherwise, we'll return only one tuple from each group. - * For now, estimate the number of groups as 10% of the number of - * tuples --- bogus, but how to do better? + * If tuplePerGroup (which is named exactly backwards) is true, we + * will return all the input tuples, so the input node's row count is + * OK. Otherwise, we'll return only one tuple from each group. For + * now, estimate the number of groups as 10% of the number of tuples + * --- bogus, but how to do better? */ - if (! tuplePerGroup) + if (!tuplePerGroup) plan->plan_rows *= 0.1; plan->state = (EState *) NULL; @@ -1369,11 +1395,13 @@ make_unique(List *tlist, Plan *lefttree, List *distinctList) List *slitem; copy_plan_costsize(plan, lefttree); + /* - * Charge one cpu_operator_cost per comparison per input tuple. - * We assume all columns get compared at most of the tuples. + * Charge one cpu_operator_cost per comparison per input tuple. We + * assume all columns get compared at most of the tuples. */ plan->total_cost += cpu_operator_cost * plan->plan_rows * numCols; + /* * As for Group, we make the unsupported assumption that there will be * 10% as many tuples out as in. @@ -1388,14 +1416,17 @@ make_unique(List *tlist, Plan *lefttree, List *distinctList) node->nonameid = _NONAME_RELATION_ID_; node->keycount = 0; - /* convert SortClause list into array of attr indexes, as wanted by exec */ + /* + * convert SortClause list into array of attr indexes, as wanted by + * exec + */ Assert(numCols > 0); uniqColIdx = (AttrNumber *) palloc(sizeof(AttrNumber) * numCols); foreach(slitem, distinctList) { - SortClause *sortcl = (SortClause *) lfirst(slitem); - TargetEntry *tle = get_sortgroupclause_tle(sortcl, tlist); + SortClause *sortcl = (SortClause *) lfirst(slitem); + TargetEntry *tle = get_sortgroupclause_tle(sortcl, tlist); uniqColIdx[keyno++] = tle->resdom->resno; } diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c index 6b6f3971719..207981b527f 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.45 2000/02/15 20:49:18 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.46 2000/04/12 17:15:21 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -30,7 +30,7 @@ static void add_restrict_and_join_to_rel(Query *root, Node *clause); static void add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo, - Relids join_relids); + Relids join_relids); static void add_vars_to_targetlist(Query *root, List *vars); static void check_mergejoinable(RestrictInfo *restrictinfo); static void check_hashjoinable(RestrictInfo *restrictinfo); @@ -83,7 +83,7 @@ add_vars_to_targetlist(Query *root, List *vars) * * If we have a range variable in the FROM clause that does not appear * in the target list nor qualifications, we must add it to the base - * relation list so that it will be joined. For instance, "select f.x + * relation list so that it will be joined. For instance, "select f.x * from foo f, foo f2" is a join of f and f2. Note that if we have * "select foo.x from foo f", it also gets turned into a join (between * foo as foo and foo as f). @@ -106,13 +106,15 @@ add_missing_rels_to_query(Query *root) { RelOptInfo *rel = get_base_rel(root, varno); - /* If the rel isn't otherwise referenced, give it a dummy + /* + * If the rel isn't otherwise referenced, give it a dummy * targetlist consisting of its own OID. */ if (rel->targetlist == NIL) { Var *var = makeVar(varno, ObjectIdAttributeNumber, OIDOID, -1, 0); + add_var_to_tlist(rel, var); } } @@ -142,14 +144,14 @@ add_restrict_and_join_to_rels(Query *root, List *clauses) List *clause; foreach(clause, clauses) - add_restrict_and_join_to_rel(root, (Node*) lfirst(clause)); + add_restrict_and_join_to_rel(root, (Node *) lfirst(clause)); } /* * add_restrict_and_join_to_rel * Add clause information to either the 'RestrictInfo' or 'JoinInfo' field * (depending on whether the clause is a join) of each base relation - * mentioned in the clause. A RestrictInfo node is created and added to + * mentioned in the clause. A RestrictInfo node is created and added to * the appropriate list for each rel. Also, if the clause uses a * mergejoinable operator, enter the left- and right-side expressions * into the query's lists of equijoined vars. @@ -175,6 +177,7 @@ add_restrict_and_join_to_rel(Query *root, Node *clause) if (length(relids) == 1) { + /* * There is only one relation participating in 'clause', so * 'clause' must be a restriction clause for that relation. @@ -183,21 +186,24 @@ add_restrict_and_join_to_rel(Query *root, Node *clause) rel->baserestrictinfo = lcons(restrictinfo, rel->baserestrictinfo); + /* * Check for a "mergejoinable" clause even though it's not a join - * clause. This is so that we can recognize that "a.x = a.y" makes - * x and y eligible to be considered equal, even when they belong - * to the same rel. Without this, we would not recognize that - * "a.x = a.y AND a.x = b.z AND a.y = c.q" allows us to consider - * z and q equal after their rels are joined. + * clause. This is so that we can recognize that "a.x = a.y" + * makes x and y eligible to be considered equal, even when they + * belong to the same rel. Without this, we would not recognize + * that "a.x = a.y AND a.x = b.z AND a.y = c.q" allows us to + * consider z and q equal after their rels are joined. */ check_mergejoinable(restrictinfo); } else { + /* * 'clause' is a join clause, since there is more than one atom in - * the relid list. Set additional RestrictInfo fields for joining. + * the relid list. Set additional RestrictInfo fields for + * joining. * * We need the merge info whether or not mergejoin is enabled (for * constructing equijoined-var lists), but we don't bother setting @@ -206,16 +212,19 @@ add_restrict_and_join_to_rel(Query *root, Node *clause) check_mergejoinable(restrictinfo); if (enable_hashjoin) check_hashjoinable(restrictinfo); + /* - * Add clause to the join lists of all the relevant - * relations. (If, perchance, 'clause' contains NO vars, then - * nothing will happen...) + * Add clause to the join lists of all the relevant relations. + * (If, perchance, 'clause' contains NO vars, then nothing will + * happen...) */ add_join_info_to_rels(root, restrictinfo, relids); + /* - * Add vars used in the join clause to targetlists of member relations, - * so that they will be emitted by the plan nodes that scan those - * relations (else they won't be available at the join node!). + * Add vars used in the join clause to targetlists of member + * relations, so that they will be emitted by the plan nodes that + * scan those relations (else they won't be available at the join + * node!). */ add_vars_to_targetlist(root, vars); } @@ -267,7 +276,7 @@ add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo, joininfo = find_joininfo_node(get_base_rel(root, cur_relid), unjoined_relids); joininfo->jinfo_restrictinfo = lcons(restrictinfo, - joininfo->jinfo_restrictinfo); + joininfo->jinfo_restrictinfo); } } @@ -296,16 +305,16 @@ check_mergejoinable(RestrictInfo *restrictinfo) leftOp, rightOp; - if (! is_opclause((Node *) clause)) + if (!is_opclause((Node *) clause)) return; left = get_leftop(clause); right = get_rightop(clause); /* caution: is_opclause accepts more than I do, so check it */ - if (! right) + if (!right) return; /* unary opclauses need not apply */ - if (!IsA(left, Var) || !IsA(right, Var)) + if (!IsA(left, Var) ||!IsA(right, Var)) return; opno = ((Oper *) clause->oper)->opno; @@ -339,16 +348,16 @@ check_hashjoinable(RestrictInfo *restrictinfo) *right; Oid opno; - if (! is_opclause((Node *) clause)) + if (!is_opclause((Node *) clause)) return; left = get_leftop(clause); right = get_rightop(clause); /* caution: is_opclause accepts more than I do, so check it */ - if (! right) + if (!right) return; /* unary opclauses need not apply */ - if (!IsA(left, Var) || !IsA(right, Var)) + if (!IsA(left, Var) ||!IsA(right, Var)) return; opno = ((Oper *) clause->oper)->opno; @@ -356,7 +365,5 @@ check_hashjoinable(RestrictInfo *restrictinfo) if (op_hashjoinable(opno, left->vartype, right->vartype)) - { restrictinfo->hashjoinoperator = opno; - } } diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c index 4377359ddcc..0e05c945380 100644 --- a/src/backend/optimizer/plan/planmain.c +++ b/src/backend/optimizer/plan/planmain.c @@ -14,7 +14,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.54 2000/03/24 21:40:43 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.55 2000/04/12 17:15:22 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -31,7 +31,7 @@ static Plan *subplanner(Query *root, List *flat_tlist, List *qual, - double tuple_fraction); + double tuple_fraction); /*-------------------- @@ -55,12 +55,12 @@ static Plan *subplanner(Query *root, List *flat_tlist, List *qual, * Query field and not a passed parameter is that the low-level routines * in indxpath.c need to see it.) The pathkeys value passed to query_planner * has not yet been "canonicalized", since the necessary info does not get - * computed until subplanner() scans the qual clauses. We canonicalize it + * computed until subplanner() scans the qual clauses. We canonicalize it * inside subplanner() as soon as that task is done. The output value * will be in canonical form as well. * * tuple_fraction is interpreted as follows: - * 0 (or less): expect all tuples to be retrieved (normal case) + * 0 (or less): expect all tuples to be retrieved (normal case) * 0 < tuple_fraction < 1: expect the given fraction of tuples available * from the plan to be retrieved * tuple_fraction >= 1: tuple_fraction is the absolute number of tuples @@ -91,7 +91,7 @@ query_planner(Query *root, if (root->commandType != CMD_SELECT) elog(ERROR, "Empty range table for non-SELECT query"); - root->query_pathkeys = NIL; /* signal unordered result */ + root->query_pathkeys = NIL; /* signal unordered result */ /* Make childless Result node to evaluate given tlist. */ return (Plan *) make_result(tlist, (Node *) qual, (Plan *) NULL); @@ -115,8 +115,8 @@ query_planner(Query *root, * * All subplan nodes will have "flat" (var-only) tlists. * - * This implies that all expression evaluations are done at the root - * of the plan tree. Once upon a time there was code to try to push + * This implies that all expression evaluations are done at the root of + * the plan tree. Once upon a time there was code to try to push * expensive function calls down to lower plan nodes, but that's dead * code and has been for a long time... */ @@ -132,9 +132,10 @@ query_planner(Query *root, */ if (constant_qual) { + /* - * The result node will also be responsible for evaluating - * the originally requested tlist. + * The result node will also be responsible for evaluating the + * originally requested tlist. */ subplan = (Plan *) make_result(tlist, (Node *) constant_qual, @@ -142,9 +143,11 @@ query_planner(Query *root, } else { + /* * Replace the toplevel plan node's flattened target list with the - * targetlist given by my caller, so that expressions are evaluated. + * targetlist given by my caller, so that expressions are + * evaluated. */ subplan->targetlist = tlist; } @@ -180,8 +183,9 @@ subplanner(Query *root, * Initialize the targetlist and qualification, adding entries to * base_rel_list as relation references are found (e.g., in the * qualification, the targetlist, etc.). Restrict and join clauses - * are added to appropriate lists belonging to the mentioned relations, - * and we also build lists of equijoined keys for pathkey construction. + * are added to appropriate lists belonging to the mentioned + * relations, and we also build lists of equijoined keys for pathkey + * construction. */ root->base_rel_list = NIL; root->join_rel_list = NIL; @@ -192,9 +196,9 @@ subplanner(Query *root, add_missing_rels_to_query(root); /* - * We should now have all the pathkey equivalence sets built, - * so it's now possible to convert the requested query_pathkeys - * to canonical form. + * We should now have all the pathkey equivalence sets built, so it's + * now possible to convert the requested query_pathkeys to canonical + * form. */ root->query_pathkeys = canonicalize_pathkeys(root, root->query_pathkeys); @@ -203,20 +207,22 @@ subplanner(Query *root, */ final_rel = make_one_rel(root); - if (! final_rel) + if (!final_rel) { + /* * We expect to end up here for a trivial INSERT ... VALUES query - * (which will have a target relation, so it gets past query_planner's - * check for empty range table; but the target rel is unreferenced - * and not marked inJoinSet, so we find there is nothing to join). - * + * (which will have a target relation, so it gets past + * query_planner's check for empty range table; but the target rel + * is unreferenced and not marked inJoinSet, so we find there is + * nothing to join). + * * It's also possible to get here if the query was rewritten by the - * rule processor (creating rangetable entries not marked inJoinSet) - * but the rules either did nothing or were simplified to nothing - * by constant-expression folding. So, don't complain. + * rule processor (creating rangetable entries not marked + * inJoinSet) but the rules either did nothing or were simplified + * to nothing by constant-expression folding. So, don't complain. */ - root->query_pathkeys = NIL; /* signal unordered result */ + root->query_pathkeys = NIL; /* signal unordered result */ /* Make childless Result node to evaluate given tlist. */ return (Plan *) make_result(flat_tlist, (Node *) qual, (Plan *) NULL); @@ -246,16 +252,16 @@ subplanner(Query *root, #endif /* - * Now that we have an estimate of the final rel's size, we can convert - * a tuple_fraction specified as an absolute count (ie, a LIMIT option) - * into a fraction of the total tuples. + * Now that we have an estimate of the final rel's size, we can + * convert a tuple_fraction specified as an absolute count (ie, a + * LIMIT option) into a fraction of the total tuples. */ if (tuple_fraction >= 1.0) tuple_fraction /= final_rel->rows; /* * Determine the cheapest path, independently of any ordering - * considerations. We do, however, take into account whether the + * considerations. We do, however, take into account whether the * whole plan is expected to be evaluated or not. */ if (tuple_fraction <= 0.0 || tuple_fraction >= 1.0) @@ -271,8 +277,8 @@ subplanner(Query *root, /* * Select the best path and create a subplan to execute it. * - * If no special sort order is wanted, or if the cheapest path is - * already appropriately ordered, we use the cheapest path found above. + * If no special sort order is wanted, or if the cheapest path is already + * appropriately ordered, we use the cheapest path found above. */ if (root->query_pathkeys == NIL || pathkeys_contained_in(root->query_pathkeys, @@ -284,7 +290,8 @@ subplanner(Query *root, /* * Otherwise, look to see if we have an already-ordered path that is - * cheaper than doing an explicit sort on the cheapest-total-cost path. + * cheaper than doing an explicit sort on the cheapest-total-cost + * path. */ cheapestpath = final_rel->cheapest_total_path; presortedpath = @@ -310,11 +317,11 @@ subplanner(Query *root, } /* - * Nothing for it but to sort the cheapest-total-cost path --- but we let - * the caller do that. union_planner has to be able to add a sort node - * anyway, so no need for extra code here. (Furthermore, the given - * pathkeys might involve something we can't compute here, such as an - * aggregate function...) + * Nothing for it but to sort the cheapest-total-cost path --- but we + * let the caller do that. union_planner has to be able to add a sort + * node anyway, so no need for extra code here. (Furthermore, the + * given pathkeys might involve something we can't compute here, such + * as an aggregate function...) */ root->query_pathkeys = cheapestpath->pathkeys; return create_plan(root, cheapestpath); diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index b8871d5801e..a92d439ee52 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.78 2000/03/21 05:12:01 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.79 2000/04/12 17:15:22 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -38,10 +38,10 @@ static List *make_subplanTargetList(Query *parse, List *tlist, - AttrNumber **groupColIdx); + AttrNumber **groupColIdx); static Plan *make_groupplan(List *group_tlist, bool tuplePerGroup, - List *groupClause, AttrNumber *grpColIdx, - bool is_presorted, Plan *subplan); + List *groupClause, AttrNumber *grpColIdx, + bool is_presorted, Plan *subplan); static Plan *make_sortplan(List *tlist, List *sortcls, Plan *plannode); /***************************************************************************** @@ -64,7 +64,7 @@ planner(Query *parse) transformKeySetQuery(parse); /* primary planning entry point (may recurse for subplans) */ - result_plan = subquery_planner(parse, -1.0 /* default case */); + result_plan = subquery_planner(parse, -1.0 /* default case */ ); Assert(PlannerQueryLevel == 1); @@ -110,21 +110,22 @@ planner(Query *parse) Plan * subquery_planner(Query *parse, double tuple_fraction) { + /* * A HAVING clause without aggregates is equivalent to a WHERE clause - * (except it can only refer to grouped fields). If there are no - * aggs anywhere in the query, then we don't want to create an Agg - * plan node, so merge the HAVING condition into WHERE. (We used to + * (except it can only refer to grouped fields). If there are no aggs + * anywhere in the query, then we don't want to create an Agg plan + * node, so merge the HAVING condition into WHERE. (We used to * consider this an error condition, but it seems to be legal SQL.) */ - if (parse->havingQual != NULL && ! parse->hasAggs) + if (parse->havingQual != NULL && !parse->hasAggs) { if (parse->qual == NULL) parse->qual = parse->havingQual; else parse->qual = (Node *) make_andclause(lappend(lcons(parse->qual, NIL), - parse->havingQual)); + parse->havingQual)); parse->havingQual = NULL; } @@ -144,8 +145,8 @@ subquery_planner(Query *parse, double tuple_fraction) /* * Canonicalize the qual, and convert it to implicit-AND format. * - * XXX Is there any value in re-applying eval_const_expressions - * after canonicalize_qual? + * XXX Is there any value in re-applying eval_const_expressions after + * canonicalize_qual? */ parse->qual = (Node *) canonicalize_qual((Expr *) parse->qual, true); #ifdef OPTIMIZER_DEBUG @@ -169,15 +170,17 @@ subquery_planner(Query *parse, double tuple_fraction) if (parse->groupClause != NIL) { + /* - * Check for ungrouped variables passed to subplans. - * Note we do NOT do this for subplans in WHERE; it's legal - * there because WHERE is evaluated pre-GROUP. + * Check for ungrouped variables passed to subplans. Note we + * do NOT do this for subplans in WHERE; it's legal there + * because WHERE is evaluated pre-GROUP. * - * An interesting fine point: if we reassigned a HAVING qual - * into WHERE above, then we will accept references to ungrouped - * vars from subplans in the HAVING qual. This is not entirely - * consistent, but it doesn't seem particularly harmful... + * An interesting fine point: if we reassigned a HAVING qual into + * WHERE above, then we will accept references to ungrouped + * vars from subplans in the HAVING qual. This is not + * entirely consistent, but it doesn't seem particularly + * harmful... */ check_subplans_for_ungrouped_vars((Node *) parse->targetList, parse); @@ -218,8 +221,8 @@ subquery_planner(Query *parse, double tuple_fraction) * tuple_fraction is the fraction of tuples we expect will be retrieved * * tuple_fraction is interpreted as follows: - * < 0: determine fraction by inspection of query (normal case) - * 0: expect all tuples to be retrieved + * < 0: determine fraction by inspection of query (normal case) + * 0: expect all tuples to be retrieved * 0 < tuple_fraction < 1: expect the given fraction of tuples available * from the plan to be retrieved * tuple_fraction >= 1: tuple_fraction is the absolute number of tuples @@ -251,13 +254,18 @@ union_planner(Query *parse, parse->commandType, parse->resultRelation, parse->rtable); + /* - * We leave current_pathkeys NIL indicating we do not know sort order. - * Actually, for a normal UNION we have done an explicit sort; ought - * to change interface to plan_union_queries to pass that info back! + * We leave current_pathkeys NIL indicating we do not know sort + * order. Actually, for a normal UNION we have done an explicit + * sort; ought to change interface to plan_union_queries to pass + * that info back! */ - /* Calculate pathkeys that represent grouping/ordering requirements */ + /* + * Calculate pathkeys that represent grouping/ordering + * requirements + */ group_pathkeys = make_pathkeys_for_sortclauses(parse->groupClause, tlist); sort_pathkeys = make_pathkeys_for_sortclauses(parse->sortClause, @@ -280,13 +288,13 @@ union_planner(Query *parse, rt_index); /* - * Fix up outer target list. NOTE: unlike the case for non-inherited - * query, we pass the unfixed tlist to subplans, which do their own - * fixing. But we still want to fix the outer target list afterwards. - * I *think* this is correct --- doing the fix before recursing is - * definitely wrong, because preprocess_targetlist() will do the - * wrong thing if invoked twice on the same list. Maybe that is a bug? - * tgl 6/6/99 + * Fix up outer target list. NOTE: unlike the case for + * non-inherited query, we pass the unfixed tlist to subplans, + * which do their own fixing. But we still want to fix the outer + * target list afterwards. I *think* this is correct --- doing the + * fix before recursing is definitely wrong, because + * preprocess_targetlist() will do the wrong thing if invoked + * twice on the same list. Maybe that is a bug? tgl 6/6/99 */ tlist = preprocess_targetlist(tlist, parse->commandType, @@ -295,12 +303,16 @@ union_planner(Query *parse, if (parse->rowMark != NULL) elog(ERROR, "SELECT FOR UPDATE is not supported for inherit queries"); + /* - * We leave current_pathkeys NIL indicating we do not know sort order - * of the Append-ed results. + * We leave current_pathkeys NIL indicating we do not know sort + * order of the Append-ed results. */ - /* Calculate pathkeys that represent grouping/ordering requirements */ + /* + * Calculate pathkeys that represent grouping/ordering + * requirements + */ group_pathkeys = make_pathkeys_for_sortclauses(parse->groupClause, tlist); sort_pathkeys = make_pathkeys_for_sortclauses(parse->sortClause, @@ -358,7 +370,10 @@ union_planner(Query *parse, */ sub_tlist = make_subplanTargetList(parse, tlist, &groupColIdx); - /* Calculate pathkeys that represent grouping/ordering requirements */ + /* + * Calculate pathkeys that represent grouping/ordering + * requirements + */ group_pathkeys = make_pathkeys_for_sortclauses(parse->groupClause, tlist); sort_pathkeys = make_pathkeys_for_sortclauses(parse->sortClause, @@ -368,11 +383,12 @@ union_planner(Query *parse, * Figure out whether we need a sorted result from query_planner. * * If we have a GROUP BY clause, then we want a result sorted - * properly for grouping. Otherwise, if there is an ORDER BY clause, - * we want to sort by the ORDER BY clause. (Note: if we have both, - * and ORDER BY is a superset of GROUP BY, it would be tempting to - * request sort by ORDER BY --- but that might just leave us failing - * to exploit an available sort order at all. Needs more thought...) + * properly for grouping. Otherwise, if there is an ORDER BY + * clause, we want to sort by the ORDER BY clause. (Note: if we + * have both, and ORDER BY is a superset of GROUP BY, it would be + * tempting to request sort by ORDER BY --- but that might just + * leave us failing to exploit an available sort order at all. + * Needs more thought...) */ if (parse->groupClause) parse->query_pathkeys = group_pathkeys; @@ -382,15 +398,16 @@ union_planner(Query *parse, parse->query_pathkeys = NIL; /* - * Figure out whether we expect to retrieve all the tuples that the - * plan can generate, or to stop early due to a LIMIT or other - * factors. If the caller passed a value >= 0, believe that value, - * else do our own examination of the query context. + * Figure out whether we expect to retrieve all the tuples that + * the plan can generate, or to stop early due to a LIMIT or other + * factors. If the caller passed a value >= 0, believe that + * value, else do our own examination of the query context. */ if (tuple_fraction < 0.0) { /* Initial assumption is we need all the tuples */ tuple_fraction = 0.0; + /* * Check for a LIMIT clause. */ @@ -430,33 +447,37 @@ union_planner(Query *parse, } else { + /* - * COUNT is a PARAM ... don't know exactly what the limit - * will be, but for lack of a better idea assume 10% - * of the plan's result is wanted. + * COUNT is a PARAM ... don't know exactly what the + * limit will be, but for lack of a better idea assume + * 10% of the plan's result is wanted. */ tuple_fraction = 0.10; } } + /* * Check for a retrieve-into-portal, ie DECLARE CURSOR. * * We have no real idea how many tuples the user will ultimately - * FETCH from a cursor, but it seems a good bet that he doesn't - * want 'em all. Optimize for 10% retrieval (you gotta better - * number?) + * FETCH from a cursor, but it seems a good bet that he + * doesn't want 'em all. Optimize for 10% retrieval (you + * gotta better number?) */ if (parse->isPortal) tuple_fraction = 0.10; } + /* * Adjust tuple_fraction if we see that we are going to apply * grouping/aggregation/etc. This is not overridable by the - * caller, since it reflects plan actions that this routine - * will certainly take, not assumptions about context. + * caller, since it reflects plan actions that this routine will + * certainly take, not assumptions about context. */ if (parse->groupClause) { + /* * In GROUP BY mode, we have the little problem that we don't * really know how many input tuples will be needed to make a @@ -464,33 +485,42 @@ union_planner(Query *parse, * input count. For lack of a better idea, assume 25% of the * input data will be processed if there is any output limit. * However, if the caller gave us a fraction rather than an - * absolute count, we can keep using that fraction (which amounts - * to assuming that all the groups are about the same size). + * absolute count, we can keep using that fraction (which + * amounts to assuming that all the groups are about the same + * size). */ if (tuple_fraction >= 1.0) tuple_fraction = 0.25; + /* * If both GROUP BY and ORDER BY are specified, we will need * two levels of sort --- and, therefore, certainly need to * read all the input tuples --- unless ORDER BY is a subset * of GROUP BY. (Although we are comparing non-canonicalized * pathkeys here, it should be OK since they will both contain - * only single-element sublists at this point. See pathkeys.c.) + * only single-element sublists at this point. See + * pathkeys.c.) */ if (parse->groupClause && parse->sortClause && - ! pathkeys_contained_in(sort_pathkeys, group_pathkeys)) + !pathkeys_contained_in(sort_pathkeys, group_pathkeys)) tuple_fraction = 0.0; } else if (parse->hasAggs) { - /* Ungrouped aggregate will certainly want all the input tuples. */ + + /* + * Ungrouped aggregate will certainly want all the input + * tuples. + */ tuple_fraction = 0.0; } else if (parse->distinctClause) { + /* * SELECT DISTINCT, like GROUP, will absorb an unpredictable - * number of input tuples per output tuple. Handle the same way. + * number of input tuples per output tuple. Handle the same + * way. */ if (tuple_fraction >= 1.0) tuple_fraction = 0.25; @@ -502,14 +532,15 @@ union_planner(Query *parse, (List *) parse->qual, tuple_fraction); - /* query_planner returns actual sort order (which is not + /* + * query_planner returns actual sort order (which is not * necessarily what we requested) in query_pathkeys. */ current_pathkeys = parse->query_pathkeys; } /* query_planner returns NULL if it thinks plan is bogus */ - if (! result_plan) + if (!result_plan) elog(ERROR, "union_planner: failed to create plan"); /* @@ -539,9 +570,9 @@ union_planner(Query *parse, /* * If there are aggregates then the Group node should just return - * the same set of vars as the subplan did (but we can exclude - * any GROUP BY expressions). If there are no aggregates - * then the Group node had better compute the final tlist. + * the same set of vars as the subplan did (but we can exclude any + * GROUP BY expressions). If there are no aggregates then the + * Group node had better compute the final tlist. */ if (parse->hasAggs) group_tlist = flatten_tlist(result_plan->targetlist); @@ -549,8 +580,8 @@ union_planner(Query *parse, group_tlist = tlist; /* - * Figure out whether the path result is already ordered the way we - * need it --- if so, no need for an explicit sort step. + * Figure out whether the path result is already ordered the way + * we need it --- if so, no need for an explicit sort step. */ if (pathkeys_contained_in(group_pathkeys, current_pathkeys)) { @@ -559,7 +590,9 @@ union_planner(Query *parse, } else { - /* We will need to do an explicit sort by the GROUP BY clause. + + /* + * We will need to do an explicit sort by the GROUP BY clause. * make_groupplan will do the work, but set current_pathkeys * to indicate the resulting order. */ @@ -594,10 +627,8 @@ union_planner(Query *parse, */ if (parse->sortClause) { - if (! pathkeys_contained_in(sort_pathkeys, current_pathkeys)) - { + if (!pathkeys_contained_in(sort_pathkeys, current_pathkeys)) result_plan = make_sortplan(tlist, parse->sortClause, result_plan); - } } /* @@ -633,7 +664,7 @@ union_planner(Query *parse, * we want to pass this targetlist to the subplan: * a,b,c,d,a+b * where the a+b target will be used by the Sort/Group steps, and the - * other targets will be used for computing the final results. (In the + * other targets will be used for computing the final results. (In the * above example we could theoretically suppress the a and b targets and * use only a+b, but it's not really worth the trouble.) * @@ -675,8 +706,9 @@ make_subplanTargetList(Query *parse, /* * If grouping, create sub_tlist entries for all GROUP BY expressions - * (GROUP BY items that are simple Vars should be in the list already), - * and make an array showing where the group columns are in the sub_tlist. + * (GROUP BY items that are simple Vars should be in the list + * already), and make an array showing where the group columns are in + * the sub_tlist. */ numCols = length(parse->groupClause); if (numCols > 0) @@ -690,10 +722,10 @@ make_subplanTargetList(Query *parse, foreach(gl, parse->groupClause) { - GroupClause *grpcl = (GroupClause *) lfirst(gl); - Node *groupexpr = get_sortgroupclause_expr(grpcl, tlist); - TargetEntry *te = NULL; - List *sl; + GroupClause *grpcl = (GroupClause *) lfirst(gl); + Node *groupexpr = get_sortgroupclause_expr(grpcl, tlist); + TargetEntry *te = NULL; + List *sl; /* Find or make a matching sub_tlist entry */ foreach(sl, sub_tlist) @@ -702,7 +734,7 @@ make_subplanTargetList(Query *parse, if (equal(groupexpr, te->expr)) break; } - if (! sl) + if (!sl) { te = makeTargetEntry(makeResdom(length(sub_tlist) + 1, exprType(groupexpr), @@ -739,8 +771,9 @@ make_groupplan(List *group_tlist, { int numCols = length(groupClause); - if (! is_presorted) + if (!is_presorted) { + /* * The Sort node always just takes a copy of the subplan's tlist * plus ordering information. (This might seem inefficient if the @@ -755,14 +788,14 @@ make_groupplan(List *group_tlist, foreach(gl, groupClause) { - GroupClause *grpcl = (GroupClause *) lfirst(gl); - TargetEntry *te = nth(grpColIdx[keyno]-1, sort_tlist); - Resdom *resdom = te->resdom; + GroupClause *grpcl = (GroupClause *) lfirst(gl); + TargetEntry *te = nth(grpColIdx[keyno] - 1, sort_tlist); + Resdom *resdom = te->resdom; /* - * Check for the possibility of duplicate group-by clauses --- the - * parser should have removed 'em, but the Sort executor will get - * terribly confused if any get through! + * Check for the possibility of duplicate group-by clauses --- + * the parser should have removed 'em, but the Sort executor + * will get terribly confused if any get through! */ if (resdom->reskey == 0) { @@ -808,8 +841,8 @@ make_sortplan(List *tlist, List *sortcls, Plan *plannode) /* * Check for the possibility of duplicate order-by clauses --- the - * parser should have removed 'em, but the executor will get terribly - * confused if any get through! + * parser should have removed 'em, but the executor will get + * terribly confused if any get through! */ if (resdom->reskey == 0) { diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 756333e0059..a72fa0e74f0 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.61 2000/04/04 01:21:47 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.62 2000/04/12 17:15:22 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -24,13 +24,15 @@ #include "optimizer/tlist.h" #include "optimizer/var.h" -typedef struct { +typedef struct +{ List *outer_tlist; List *inner_tlist; Index acceptable_rel; } join_references_context; -typedef struct { +typedef struct +{ Index subvarno; List *subplanTargetList; } replace_vars_with_subplan_refs_context; @@ -38,12 +40,12 @@ typedef struct { static void set_join_references(Join *join); static void set_uppernode_references(Plan *plan, Index subvarno); static Node *join_references_mutator(Node *node, - join_references_context *context); + join_references_context *context); static Node *replace_vars_with_subplan_refs(Node *node, - Index subvarno, - List *subplanTargetList); + Index subvarno, + List *subplanTargetList); static Node *replace_vars_with_subplan_refs_mutator(Node *node, - replace_vars_with_subplan_refs_context *context); + replace_vars_with_subplan_refs_context *context); static bool fix_opids_walker(Node *node, void *context); /***************************************************************************** @@ -56,7 +58,7 @@ static bool fix_opids_walker(Node *node, void *context); * set_plan_references * This is the final processing pass of the planner/optimizer. The plan * tree is complete; we just have to adjust some representational details - * for the convenience of the executor. We update Vars in upper plan nodes + * for the convenience of the executor. We update Vars in upper plan nodes * to refer to the outputs of their subplans, and we compute regproc OIDs * for operators (ie, we look up the function that implements each op). * We must also build lists of all the subplan nodes present in each @@ -74,7 +76,8 @@ set_plan_references(Plan *plan) if (plan == NULL) return; - /* We must rebuild the plan's list of subplan nodes, since we are + /* + * We must rebuild the plan's list of subplan nodes, since we are * copying/mutating its expression trees. */ plan->subPlan = NIL; @@ -92,10 +95,10 @@ set_plan_references(Plan *plan) fix_opids((Node *) ((IndexScan *) plan)->indxqualorig); plan->subPlan = nconc(plan->subPlan, - pull_subplans((Node *) ((IndexScan *) plan)->indxqual)); + pull_subplans((Node *) ((IndexScan *) plan)->indxqual)); plan->subPlan = nconc(plan->subPlan, - pull_subplans((Node *) ((IndexScan *) plan)->indxqualorig)); + pull_subplans((Node *) ((IndexScan *) plan)->indxqualorig)); break; case T_NestLoop: set_join_references((Join *) plan); @@ -105,24 +108,26 @@ set_plan_references(Plan *plan) fix_opids((Node *) ((MergeJoin *) plan)->mergeclauses); plan->subPlan = nconc(plan->subPlan, - pull_subplans((Node *) ((MergeJoin *) plan)->mergeclauses)); + pull_subplans((Node *) ((MergeJoin *) plan)->mergeclauses)); break; case T_HashJoin: set_join_references((Join *) plan); fix_opids((Node *) ((HashJoin *) plan)->hashclauses); plan->subPlan = nconc(plan->subPlan, - pull_subplans((Node *) ((HashJoin *) plan)->hashclauses)); + pull_subplans((Node *) ((HashJoin *) plan)->hashclauses)); break; case T_Material: case T_Sort: case T_Unique: case T_Hash: - /* These plan types don't actually bother to evaluate their + + /* + * These plan types don't actually bother to evaluate their * targetlists or quals (because they just return their * unmodified input tuples). The optimizer is lazy about - * creating really valid targetlists for them. Best to - * just leave the targetlist alone. + * creating really valid targetlists for them. Best to just + * leave the targetlist alone. */ break; case T_Agg: @@ -130,7 +135,9 @@ set_plan_references(Plan *plan) set_uppernode_references(plan, (Index) 0); break; case T_Result: - /* Result may or may not have a subplan; no need to fix up + + /* + * Result may or may not have a subplan; no need to fix up * subplan references if it hasn't got one... * * XXX why does Result use a different subvarno from Agg/Group? @@ -144,9 +151,7 @@ set_plan_references(Plan *plan) break; case T_Append: foreach(pl, ((Append *) plan)->appendplans) - { set_plan_references((Plan *) lfirst(pl)); - } break; case T_TidScan: /* nothing special */ @@ -158,8 +163,8 @@ set_plan_references(Plan *plan) } /* - * For all plan types, fix operators in targetlist and qual expressions, - * and find subplans therein. + * For all plan types, fix operators in targetlist and qual + * expressions, and find subplans therein. */ fix_opids((Node *) plan->targetlist); fix_opids((Node *) plan->qual); @@ -176,20 +181,21 @@ set_plan_references(Plan *plan) * NOTE: it is essential that we recurse into subplans AFTER we set * subplan references in this plan's tlist and quals. If we did the * reference-adjustments bottom-up, then we would fail to match this - * plan's var nodes against the already-modified nodes of the subplans. + * plan's var nodes against the already-modified nodes of the + * subplans. */ set_plan_references(plan->lefttree); set_plan_references(plan->righttree); foreach(pl, plan->initPlan) { - SubPlan *sp = (SubPlan *) lfirst(pl); + SubPlan *sp = (SubPlan *) lfirst(pl); Assert(IsA(sp, SubPlan)); set_plan_references(sp->plan); } foreach(pl, plan->subPlan) { - SubPlan *sp = (SubPlan *) lfirst(pl); + SubPlan *sp = (SubPlan *) lfirst(pl); Assert(IsA(sp, SubPlan)); set_plan_references(sp->plan); @@ -325,9 +331,10 @@ join_references_mutator(Node *node, newvar->varattno = resdom->resno; return (Node *) newvar; } + /* - * Var not in either tlist --- either raise an error, - * or return the Var unmodified. + * Var not in either tlist --- either raise an error, or return + * the Var unmodified. */ if (var->varno != context->acceptable_rel) elog(ERROR, "join_references: variable not in subplan target lists"); @@ -370,7 +377,7 @@ replace_vars_with_subplan_refs(Node *node, static Node * replace_vars_with_subplan_refs_mutator(Node *node, - replace_vars_with_subplan_refs_context *context) + replace_vars_with_subplan_refs_context *context) { if (node == NULL) return NULL; @@ -414,7 +421,7 @@ fix_opids(Node *node) } static bool -fix_opids_walker (Node *node, void *context) +fix_opids_walker(Node *node, void *context) { if (node == NULL) return false; diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index b77d8b586f6..3493bfda245 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.34 2000/04/04 01:21:47 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.35 2000/04/12 17:15:22 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -82,19 +82,19 @@ replace_var(Var *var) varlevel = PlannerQueryLevel - var->varlevelsup; /* - * If there's already a PlannerParamVar entry for this same Var, - * just use it. NOTE: in situations involving UNION or inheritance, - * it is possible for the same varno/varlevel to refer to different RTEs - * in different parts of the parsetree, so that different fields might - * end up sharing the same Param number. As long as we check the vartype - * as well, I believe that this sort of aliasing will cause no trouble. - * The correct field should get stored into the Param slot at execution - * in each part of the tree. + * If there's already a PlannerParamVar entry for this same Var, just + * use it. NOTE: in situations involving UNION or inheritance, it is + * possible for the same varno/varlevel to refer to different RTEs in + * different parts of the parsetree, so that different fields might + * end up sharing the same Param number. As long as we check the + * vartype as well, I believe that this sort of aliasing will cause no + * trouble. The correct field should get stored into the Param slot at + * execution in each part of the tree. */ i = 0; foreach(ppv, PlannerParamVar) { - Var *pvar = lfirst(ppv); + Var *pvar = lfirst(ppv); if (pvar->varno == var->varno && pvar->varattno == var->varattno && @@ -104,7 +104,7 @@ replace_var(Var *var) i++; } - if (! ppv) + if (!ppv) { /* Nope, so make a new one */ i = new_param(var, varlevel); @@ -137,23 +137,25 @@ make_subplan(SubLink *slink) PlannerQueryLevel++; /* we become child */ /* - * For an EXISTS subplan, tell lower-level planner to expect that - * only the first tuple will be retrieved. For ALL and ANY subplans, - * we will be able to stop evaluating if the test condition fails, - * so very often not all the tuples will be retrieved; for lack of a - * better idea, specify 50% retrieval. For EXPR and MULTIEXPR subplans, - * use default behavior (we're only expecting one row out, anyway). + * For an EXISTS subplan, tell lower-level planner to expect that only + * the first tuple will be retrieved. For ALL and ANY subplans, we + * will be able to stop evaluating if the test condition fails, so + * very often not all the tuples will be retrieved; for lack of a + * better idea, specify 50% retrieval. For EXPR and MULTIEXPR + * subplans, use default behavior (we're only expecting one row out, + * anyway). * * NOTE: if you change these numbers, also change cost_qual_eval_walker() * in path/costsize.c. * - * XXX If an ALL/ANY subplan is uncorrelated, we may decide to materialize - * its result below. In that case it would've been better to specify - * full retrieval. At present, however, we can only detect correlation - * or lack of it after we've made the subplan :-(. Perhaps detection - * of correlation should be done as a separate step. Meanwhile, we don't - * want to be too optimistic about the percentage of tuples retrieved, - * for fear of selecting a plan that's bad for the materialization case. + * XXX If an ALL/ANY subplan is uncorrelated, we may decide to + * materialize its result below. In that case it would've been better + * to specify full retrieval. At present, however, we can only detect + * correlation or lack of it after we've made the subplan :-(. Perhaps + * detection of correlation should be done as a separate step. + * Meanwhile, we don't want to be too optimistic about the percentage + * of tuples retrieved, for fear of selecting a plan that's bad for + * the materialization case. */ if (slink->subLinkType == EXISTS_SUBLINK) tuple_fraction = 1.0; /* just like a LIMIT 1 */ @@ -167,8 +169,8 @@ make_subplan(SubLink *slink) /* * Assign subPlan, extParam and locParam to plan nodes. At the moment, - * SS_finalize_plan doesn't handle initPlan-s and so we assign them - * to the topmost plan node and take care about its extParam too. + * SS_finalize_plan doesn't handle initPlan-s and so we assign them to + * the topmost plan node and take care about its extParam too. */ (void) SS_finalize_plan(plan); plan->initPlan = PlannerInitPlan; @@ -206,8 +208,8 @@ make_subplan(SubLink *slink) /* * Un-correlated or undirect correlated plans of EXISTS, EXPR, or - * MULTIEXPR types can be used as initPlans. For EXISTS or EXPR, - * we just produce a Param referring to the result of evaluating the + * MULTIEXPR types can be used as initPlans. For EXISTS or EXPR, we + * just produce a Param referring to the result of evaluating the * initPlan. For MULTIEXPR, we must build an AND or OR-clause of the * individual comparison operators, using the appropriate lefthand * side expressions and Params for the initPlan's target items. @@ -228,6 +230,7 @@ make_subplan(SubLink *slink) else if (node->parParam == NIL && slink->subLinkType == EXPR_SUBLINK) { TargetEntry *te = lfirst(plan->targetlist); + /* need a var node just to pass to new_param()... */ Var *var = makeVar(0, 0, te->resdom->restype, te->resdom->restypmod, 0); @@ -247,14 +250,15 @@ make_subplan(SubLink *slink) int i = 0; /* - * Convert oper list of Opers into a list of Exprs, using - * lefthand arguments and Params representing inside results. + * Convert oper list of Opers into a list of Exprs, using lefthand + * arguments and Params representing inside results. */ foreach(lst, slink->oper) { Oper *oper = (Oper *) lfirst(lst); Node *lefthand = nth(i, slink->lefthand); TargetEntry *te = nth(i, plan->targetlist); + /* need a var node just to pass to new_param()... */ Var *var = makeVar(0, 0, te->resdom->restype, te->resdom->restypmod, 0); @@ -273,7 +277,9 @@ make_subplan(SubLink *slink) tup = get_operator_tuple(oper->opno); Assert(HeapTupleIsValid(tup)); opform = (Form_pg_operator) GETSTRUCT(tup); - /* Note: we use make_operand in case runtime type conversion + + /* + * Note: we use make_operand in case runtime type conversion * function calls must be inserted for this operator! */ left = make_operand("", lefthand, @@ -304,15 +310,16 @@ make_subplan(SubLink *slink) int i = 0; /* - * We can't convert subplans of ALL_SUBLINK or ANY_SUBLINK types to - * initPlans, even when they are uncorrelated or undirect correlated, - * because we need to scan the output of the subplan for each outer - * tuple. However, we have the option to tack a MATERIAL node onto - * the top of an uncorrelated/undirect correlated subplan, which lets - * us do the work of evaluating the subplan only once. We do this - * if the subplan's top plan node is anything more complicated than - * a plain sequential scan, and we do it even for seqscan if the - * qual appears selective enough to eliminate many tuples. + * We can't convert subplans of ALL_SUBLINK or ANY_SUBLINK types + * to initPlans, even when they are uncorrelated or undirect + * correlated, because we need to scan the output of the subplan + * for each outer tuple. However, we have the option to tack a + * MATERIAL node onto the top of an uncorrelated/undirect + * correlated subplan, which lets us do the work of evaluating the + * subplan only once. We do this if the subplan's top plan node + * is anything more complicated than a plain sequential scan, and + * we do it even for seqscan if the qual appears selective enough + * to eliminate many tuples. */ if (node->parParam == NIL) { @@ -336,10 +343,12 @@ make_subplan(SubLink *slink) break; case T_Material: case T_Sort: - /* Don't add another Material node if there's one already, - * nor if the top node is a Sort, since Sort materializes - * its output anyway. (I doubt either case can happen in - * practice for a subplan, but...) + + /* + * Don't add another Material node if there's one + * already, nor if the top node is a Sort, since Sort + * materializes its output anyway. (I doubt either + * case can happen in practice for a subplan, but...) */ use_material = false; break; @@ -359,7 +368,7 @@ make_subplan(SubLink *slink) /* * Make expression of SUBPLAN type */ - expr->typeOid = BOOLOID; /* bogus, but we don't really care */ + expr->typeOid = BOOLOID;/* bogus, but we don't really care */ expr->opType = SUBPLAN_EXPR; expr->oper = (Node *) node; @@ -371,17 +380,20 @@ make_subplan(SubLink *slink) Var *var = nth(lfirsti(lst), PlannerParamVar); var = (Var *) copyObject(var); - /* Must fix absolute-level varlevelsup from the - * PlannerParamVar entry. But since var is at current - * subplan level, this is easy: + + /* + * Must fix absolute-level varlevelsup from the + * PlannerParamVar entry. But since var is at current subplan + * level, this is easy: */ var->varlevelsup = 0; args = lappend(args, var); } expr->args = args; + /* - * Convert oper list of Opers into a list of Exprs, using - * lefthand arguments and Consts representing inside results. + * Convert oper list of Opers into a list of Exprs, using lefthand + * arguments and Consts representing inside results. */ foreach(lst, slink->oper) { @@ -395,8 +407,8 @@ make_subplan(SubLink *slink) *right; /* - * XXX really ought to fill in constlen and constbyval correctly, - * but right now ExecEvalExpr won't look at them... + * XXX really ought to fill in constlen and constbyval + * correctly, but right now ExecEvalExpr won't look at them... */ con = makeConst(te->resdom->restype, 0, 0, true, 0, 0, 0); @@ -404,7 +416,9 @@ make_subplan(SubLink *slink) tup = get_operator_tuple(oper->opno); Assert(HeapTupleIsValid(tup)); opform = (Form_pg_operator) GETSTRUCT(tup); - /* Note: we use make_operand in case runtime type conversion + + /* + * Note: we use make_operand in case runtime type conversion * function calls must be inserted for this operator! */ left = make_operand("", lefthand, @@ -450,9 +464,10 @@ set_unioni(List *l1, List *l2) * check in make_subplan to see whether a subselect has any subselects. */ -typedef struct finalize_primnode_results { - List *subplans; /* List of subplans found in expr */ - List *paramids; /* List of PARAM_EXEC paramids found */ +typedef struct finalize_primnode_results +{ + List *subplans; /* List of subplans found in expr */ + List *paramids; /* List of PARAM_EXEC paramids found */ } finalize_primnode_results; static bool @@ -464,16 +479,16 @@ finalize_primnode(Node *node, finalize_primnode_results *results) { if (((Param *) node)->paramkind == PARAM_EXEC) { - int paramid = (int) ((Param *) node)->paramid; + int paramid = (int) ((Param *) node)->paramid; - if (! intMember(paramid, results->paramids)) + if (!intMember(paramid, results->paramids)) results->paramids = lconsi(paramid, results->paramids); } return false; /* no more to do here */ } if (is_subplan(node)) { - SubPlan *subplan = (SubPlan *) ((Expr *) node)->oper; + SubPlan *subplan = (SubPlan *) ((Expr *) node)->oper; List *lst; /* Add subplan to subplans list */ @@ -486,7 +501,7 @@ finalize_primnode(Node *node, finalize_primnode_results *results) /* note varlevelsup is absolute level number */ if (var->varlevelsup < PlannerQueryLevel && - ! intMember(paramid, results->paramids)) + !intMember(paramid, results->paramids)) results->paramids = lconsi(paramid, results->paramids); } /* fall through to recurse into subplan args */ @@ -533,7 +548,7 @@ Node * SS_process_sublinks(Node *expr) { /* No setup needed for tree walk, so away we go */ - return process_sublinks_mutator(expr, NULL); + return process_sublinks_mutator(expr, NULL); } static Node * @@ -543,25 +558,26 @@ process_sublinks_mutator(Node *node, void *context) return NULL; if (IsA(node, SubLink)) { - SubLink *sublink = (SubLink *) node; + SubLink *sublink = (SubLink *) node; - /* First, scan the lefthand-side expressions, if any. - * This is a tad klugy since we modify the input SubLink node, - * but that should be OK (make_subplan does it too!) + /* + * First, scan the lefthand-side expressions, if any. This is a + * tad klugy since we modify the input SubLink node, but that + * should be OK (make_subplan does it too!) */ sublink->lefthand = (List *) process_sublinks_mutator((Node *) sublink->lefthand, context); /* Now build the SubPlan node and make the expr to return */ return make_subplan(sublink); } + /* * Note that we will never see a SubPlan expression in the input - * (since this is the very routine that creates 'em to begin with). - * So the code in expression_tree_mutator() that might do - * inappropriate things with SubPlans or SubLinks will not be - * exercised. + * (since this is the very routine that creates 'em to begin with). So + * the code in expression_tree_mutator() that might do inappropriate + * things with SubPlans or SubLinks will not be exercised. */ - Assert(! is_subplan(node)); + Assert(!is_subplan(node)); return expression_tree_mutator(node, process_sublinks_mutator, @@ -581,12 +597,13 @@ SS_finalize_plan(Plan *plan) results.subplans = NIL; /* initialize lists to NIL */ results.paramids = NIL; + /* * When we call finalize_primnode, results.paramids lists are - * automatically merged together. But when recursing to self, - * we have to do it the hard way. We want the paramids list - * to include params in subplans as well as at this level. - * (We don't care about finding subplans of subplans, though.) + * automatically merged together. But when recursing to self, we have + * to do it the hard way. We want the paramids list to include params + * in subplans as well as at this level. (We don't care about finding + * subplans of subplans, though.) */ /* Find params and subplans in targetlist and qual */ @@ -604,13 +621,15 @@ SS_finalize_plan(Plan *plan) case T_Append: foreach(lst, ((Append *) plan)->appendplans) results.paramids = set_unioni(results.paramids, - SS_finalize_plan((Plan *) lfirst(lst))); + SS_finalize_plan((Plan *) lfirst(lst))); break; case T_IndexScan: finalize_primnode((Node *) ((IndexScan *) plan)->indxqual, &results); - /* we need not look at indxqualorig, since it will have the + + /* + * we need not look at indxqualorig, since it will have the * same param references as indxqual, and we aren't really * concerned yet about having a complete subplan list. */ @@ -633,7 +652,7 @@ SS_finalize_plan(Plan *plan) case T_TidScan: finalize_primnode((Node *) ((TidScan *) plan)->tideval, - &results); + &results); break; case T_Agg: diff --git a/src/backend/optimizer/prep/prepqual.c b/src/backend/optimizer/prep/prepqual.c index 5f92c545cce..fae694dc264 100644 --- a/src/backend/optimizer/prep/prepqual.c +++ b/src/backend/optimizer/prep/prepqual.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepqual.c,v 1.23 2000/02/27 19:45:44 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepqual.c,v 1.24 2000/04/12 17:15:23 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -33,7 +33,7 @@ static Expr *and_normalize(List *andlist); static Expr *qual_cleanup(Expr *qual); static List *remove_duplicates(List *list); static void count_bool_nodes(Expr *qual, double *nodes, - double *cnfnodes, double *dnfnodes); + double *cnfnodes, double *dnfnodes); /***************************************************************************** * @@ -71,7 +71,7 @@ static void count_bool_nodes(Expr *qual, double *nodes, * * If 'removeAndFlag' is true then it removes explicit AND at the top level, * producing a list of implicitly-ANDed conditions. Otherwise, a regular - * boolean expression is returned. Since most callers pass 'true', we + * boolean expression is returned. Since most callers pass 'true', we * prefer to declare the result as List *, not Expr *. * * XXX This code could be much smarter, at the cost of also being slower, @@ -95,12 +95,14 @@ canonicalize_qual(Expr *qual, bool removeAndFlag) if (qual == NULL) return NIL; - /* Flatten AND and OR groups throughout the tree. - * This improvement is always worthwhile, so do it unconditionally. + /* + * Flatten AND and OR groups throughout the tree. This improvement is + * always worthwhile, so do it unconditionally. */ qual = flatten_andors(qual); - /* Push down NOTs. We do this only in the top-level boolean + /* + * Push down NOTs. We do this only in the top-level boolean * expression, without examining arguments of operators/functions. * Even so, it might not be a win if we are unable to find negators * for all the operators involved; perhaps we should compare before- @@ -109,21 +111,24 @@ canonicalize_qual(Expr *qual, bool removeAndFlag) newqual = find_nots(qual); /* - * Choose whether to convert to CNF, or DNF, or leave well enough alone. + * Choose whether to convert to CNF, or DNF, or leave well enough + * alone. * * We make an approximate estimate of the number of bottom-level nodes * that will appear in the CNF and DNF forms of the query. */ count_bool_nodes(newqual, &nodes, &cnfnodes, &dnfnodes); + /* * First heuristic is to forget about *both* normal forms if there are * a huge number of terms in the qual clause. This would only happen - * with machine-generated queries, presumably; and most likely such - * a query is already in either CNF or DNF. + * with machine-generated queries, presumably; and most likely such a + * query is already in either CNF or DNF. */ cnfok = dnfok = true; if (nodes >= 500.0) cnfok = dnfok = false; + /* * Second heuristic is to forget about either CNF or DNF if it shows * unreasonable growth compared to the original form of the qual, @@ -134,15 +139,17 @@ canonicalize_qual(Expr *qual, bool removeAndFlag) cnfok = false; if (dnfnodes >= 4.0 * nodes) dnfok = false; + /* - * Third heuristic is to prefer DNF if top level is already an OR, - * and only one relation is mentioned, and DNF is no larger than - * the CNF representation. (Pretty shaky; can we improve on this?) + * Third heuristic is to prefer DNF if top level is already an OR, and + * only one relation is mentioned, and DNF is no larger than the CNF + * representation. (Pretty shaky; can we improve on this?) */ if (cnfok && dnfok && dnfnodes <= cnfnodes && or_clause((Node *) newqual) && NumRelids((Node *) newqual) == 1) cnfok = false; + /* * Otherwise, we prefer CNF. * @@ -150,20 +157,26 @@ canonicalize_qual(Expr *qual, bool removeAndFlag) */ if (cnfok) { - /* Normalize into conjunctive normal form, and clean up the result. */ + + /* + * Normalize into conjunctive normal form, and clean up the + * result. + */ newqual = qual_cleanup(find_ors(newqual)); } else if (dnfok) { - /* Normalize into disjunctive normal form, and clean up the result. */ + + /* + * Normalize into disjunctive normal form, and clean up the + * result. + */ newqual = qual_cleanup(find_ands(newqual)); } /* Convert to implicit-AND list if requested */ if (removeAndFlag) - { newqual = (Expr *) make_ands_implicit(newqual); - } return (List *) newqual; } @@ -177,7 +190,7 @@ canonicalize_qual(Expr *qual, bool removeAndFlag) * * If 'removeAndFlag' is true then it removes explicit AND at the top level, * producing a list of implicitly-ANDed conditions. Otherwise, a regular - * boolean expression is returned. Since most callers pass 'true', we + * boolean expression is returned. Since most callers pass 'true', we * prefer to declare the result as List *, not Expr *. */ List * @@ -188,11 +201,14 @@ cnfify(Expr *qual, bool removeAndFlag) if (qual == NULL) return NIL; - /* Flatten AND and OR groups throughout the tree. - * This improvement is always worthwhile. + /* + * Flatten AND and OR groups throughout the tree. This improvement is + * always worthwhile. */ newqual = flatten_andors(qual); - /* Push down NOTs. We do this only in the top-level boolean + + /* + * Push down NOTs. We do this only in the top-level boolean * expression, without examining arguments of operators/functions. */ newqual = find_nots(newqual); @@ -202,9 +218,7 @@ cnfify(Expr *qual, bool removeAndFlag) newqual = qual_cleanup(newqual); if (removeAndFlag) - { newqual = (Expr *) make_ands_implicit(newqual); - } return (List *) newqual; } @@ -227,11 +241,14 @@ dnfify(Expr *qual) if (qual == NULL) return NULL; - /* Flatten AND and OR groups throughout the tree. - * This improvement is always worthwhile. + /* + * Flatten AND and OR groups throughout the tree. This improvement is + * always worthwhile. */ newqual = flatten_andors(qual); - /* Push down NOTs. We do this only in the top-level boolean + + /* + * Push down NOTs. We do this only in the top-level boolean * expression, without examining arguments of operators/functions. */ newqual = find_nots(newqual); @@ -280,13 +297,13 @@ flatten_andors(Expr *qual) foreach(arg, qual->args) { - Expr *subexpr = flatten_andors((Expr *) lfirst(arg)); + Expr *subexpr = flatten_andors((Expr *) lfirst(arg)); /* - * Note: we can destructively nconc the subexpression's arglist - * because we know the recursive invocation of flatten_andors - * will have built a new arglist not shared with any other expr. - * Otherwise we'd need a listCopy here. + * Note: we can destructively nconc the subexpression's + * arglist because we know the recursive invocation of + * flatten_andors will have built a new arglist not shared + * with any other expr. Otherwise we'd need a listCopy here. */ if (and_clause((Node *) subexpr)) out_list = nconc(out_list, subexpr->args); @@ -302,13 +319,13 @@ flatten_andors(Expr *qual) foreach(arg, qual->args) { - Expr *subexpr = flatten_andors((Expr *) lfirst(arg)); + Expr *subexpr = flatten_andors((Expr *) lfirst(arg)); /* - * Note: we can destructively nconc the subexpression's arglist - * because we know the recursive invocation of flatten_andors - * will have built a new arglist not shared with any other expr. - * Otherwise we'd need a listCopy here. + * Note: we can destructively nconc the subexpression's + * arglist because we know the recursive invocation of + * flatten_andors will have built a new arglist not shared + * with any other expr. Otherwise we'd need a listCopy here. */ if (or_clause((Node *) subexpr)) out_list = nconc(out_list, subexpr->args); @@ -354,13 +371,13 @@ pull_ors(List *orlist) foreach(arg, orlist) { - Expr *subexpr = (Expr *) lfirst(arg); + Expr *subexpr = (Expr *) lfirst(arg); /* * Note: we can destructively nconc the subexpression's arglist - * because we know the recursive invocation of pull_ors - * will have built a new arglist not shared with any other expr. - * Otherwise we'd need a listCopy here. + * because we know the recursive invocation of pull_ors will have + * built a new arglist not shared with any other expr. Otherwise + * we'd need a listCopy here. */ if (or_clause((Node *) subexpr)) out_list = nconc(out_list, pull_ors(subexpr->args)); @@ -385,13 +402,13 @@ pull_ands(List *andlist) foreach(arg, andlist) { - Expr *subexpr = (Expr *) lfirst(arg); + Expr *subexpr = (Expr *) lfirst(arg); /* * Note: we can destructively nconc the subexpression's arglist - * because we know the recursive invocation of pull_ands - * will have built a new arglist not shared with any other expr. - * Otherwise we'd need a listCopy here. + * because we know the recursive invocation of pull_ands will have + * built a new arglist not shared with any other expr. Otherwise + * we'd need a listCopy here. */ if (and_clause((Node *) subexpr)) out_list = nconc(out_list, pull_ands(subexpr->args)); @@ -407,7 +424,7 @@ pull_ands(List *andlist) * For 'NOT' clauses, apply push_not() to try to push down the 'NOT'. * For all other clause types, simply recurse. * - * Returns the modified qualification. AND/OR flatness is preserved. + * Returns the modified qualification. AND/OR flatness is preserved. */ static Expr * find_nots(Expr *qual) @@ -468,7 +485,8 @@ static Expr * push_nots(Expr *qual) { if (qual == NULL) - return make_notclause(qual); /* XXX is this right? Or possible? */ + return make_notclause(qual); /* XXX is this right? Or + * possible? */ /* * Negate an operator clause if possible: ("NOT" (< A B)) => (> A B) @@ -486,6 +504,7 @@ push_nots(Expr *qual) InvalidOid, oper->opresulttype, 0, NULL); + return make_opclause(op, get_leftop(qual), get_rightop(qual)); } else @@ -496,7 +515,7 @@ push_nots(Expr *qual) /*-------------------- * Apply DeMorgan's Laws: * ("NOT" ("AND" A B)) => ("OR" ("NOT" A) ("NOT" B)) - * ("NOT" ("OR" A B)) => ("AND" ("NOT" A) ("NOT" B)) + * ("NOT" ("OR" A B)) => ("AND" ("NOT" A) ("NOT" B)) * i.e., swap AND for OR and negate all the subclauses. *-------------------- */ @@ -518,6 +537,7 @@ push_nots(Expr *qual) } else if (not_clause((Node *) qual)) { + /* * Another 'not' cancels this 'not', so eliminate the 'not' and * stop negating this branch. But search the subexpression for @@ -527,6 +547,7 @@ push_nots(Expr *qual) } else { + /* * We don't know how to negate anything else, place a 'not' at * this level. @@ -544,7 +565,7 @@ push_nots(Expr *qual) * Note that 'or' clauses will always be turned into 'and' clauses * if they contain any 'and' subclauses. * - * Returns the modified qualification. AND/OR flatness is preserved. + * Returns the modified qualification. AND/OR flatness is preserved. */ static Expr * find_ors(Expr *qual) @@ -601,17 +622,17 @@ or_normalize(List *orlist) return lfirst(orlist); /* single-expression OR (can this happen?) */ /* - * If we have a choice of AND clauses, pick the one with the - * most subclauses. Because we initialized num_subclauses = 1, - * any AND clauses with only one arg will be ignored as useless. + * If we have a choice of AND clauses, pick the one with the most + * subclauses. Because we initialized num_subclauses = 1, any AND + * clauses with only one arg will be ignored as useless. */ foreach(temp, orlist) { - Expr *clause = lfirst(temp); + Expr *clause = lfirst(temp); if (and_clause((Node *) clause)) { - int nclauses = length(clause->args); + int nclauses = length(clause->args); if (nclauses > num_subclauses) { @@ -622,12 +643,13 @@ or_normalize(List *orlist) } /* if there's no suitable AND clause, we can't transform the OR */ - if (! distributable) + if (!distributable) return make_orclause(orlist); - /* Caution: lremove destructively modifies the input orlist. - * This should be OK, since or_normalize is only called with - * freshly constructed lists that are not referenced elsewhere. + /* + * Caution: lremove destructively modifies the input orlist. This + * should be OK, since or_normalize is only called with freshly + * constructed lists that are not referenced elsewhere. */ orlist = lremove(distributable, orlist); @@ -635,11 +657,12 @@ or_normalize(List *orlist) { Expr *andclause = lfirst(temp); - /* pull_ors is needed here in case andclause has a top-level OR. - * Then we recursively apply or_normalize, since there might - * be an AND subclause in the resulting OR-list. - * Note: we rely on pull_ors to build a fresh list, - * and not damage the given orlist. + /* + * pull_ors is needed here in case andclause has a top-level OR. + * Then we recursively apply or_normalize, since there might be an + * AND subclause in the resulting OR-list. Note: we rely on + * pull_ors to build a fresh list, and not damage the given + * orlist. */ andclause = or_normalize(pull_ors(lcons(andclause, orlist))); andclauses = lappend(andclauses, andclause); @@ -658,7 +681,7 @@ or_normalize(List *orlist) * Note that 'and' clauses will always be turned into 'or' clauses * if they contain any 'or' subclauses. * - * Returns the modified qualification. AND/OR flatness is preserved. + * Returns the modified qualification. AND/OR flatness is preserved. */ static Expr * find_ands(Expr *qual) @@ -712,20 +735,21 @@ and_normalize(List *andlist) if (andlist == NIL) return NULL; /* probably can't happen */ if (lnext(andlist) == NIL) - return lfirst(andlist); /* single-expression AND (can this happen?) */ + return lfirst(andlist); /* single-expression AND (can this + * happen?) */ /* - * If we have a choice of OR clauses, pick the one with the - * most subclauses. Because we initialized num_subclauses = 1, - * any OR clauses with only one arg will be ignored as useless. + * If we have a choice of OR clauses, pick the one with the most + * subclauses. Because we initialized num_subclauses = 1, any OR + * clauses with only one arg will be ignored as useless. */ foreach(temp, andlist) { - Expr *clause = lfirst(temp); + Expr *clause = lfirst(temp); if (or_clause((Node *) clause)) { - int nclauses = length(clause->args); + int nclauses = length(clause->args); if (nclauses > num_subclauses) { @@ -736,12 +760,13 @@ and_normalize(List *andlist) } /* if there's no suitable OR clause, we can't transform the AND */ - if (! distributable) + if (!distributable) return make_andclause(andlist); - /* Caution: lremove destructively modifies the input andlist. - * This should be OK, since and_normalize is only called with - * freshly constructed lists that are not referenced elsewhere. + /* + * Caution: lremove destructively modifies the input andlist. This + * should be OK, since and_normalize is only called with freshly + * constructed lists that are not referenced elsewhere. */ andlist = lremove(distributable, andlist); @@ -749,11 +774,12 @@ and_normalize(List *andlist) { Expr *orclause = lfirst(temp); - /* pull_ands is needed here in case orclause has a top-level AND. - * Then we recursively apply and_normalize, since there might - * be an OR subclause in the resulting AND-list. - * Note: we rely on pull_ands to build a fresh list, - * and not damage the given andlist. + /* + * pull_ands is needed here in case orclause has a top-level AND. + * Then we recursively apply and_normalize, since there might be + * an OR subclause in the resulting AND-list. Note: we rely on + * pull_ands to build a fresh list, and not damage the given + * andlist. */ orclause = and_normalize(pull_ands(lcons(orclause, andlist))); orclauses = lappend(orclauses, orclause); @@ -767,7 +793,7 @@ and_normalize(List *andlist) * qual_cleanup * Fix up a qualification by removing duplicate entries (which could be * created during normalization, if identical subexpressions from different - * parts of the tree are brought together). Also, check for AND and OR + * parts of the tree are brought together). Also, check for AND and OR * clauses with only one remaining subexpression, and simplify. * * Returns the modified qualification. @@ -828,7 +854,7 @@ remove_duplicates(List *list) foreach(i, list) { - if (! member(lfirst(i), result)) + if (!member(lfirst(i), result)) result = lappend(result, lfirst(i)); } return result; @@ -855,7 +881,9 @@ count_bool_nodes(Expr *qual, double *dnfnodes) { List *temp; - double subnodes, subcnfnodes, subdnfnodes; + double subnodes, + subcnfnodes, + subdnfnodes; if (and_clause((Node *) qual)) { @@ -864,13 +892,15 @@ count_bool_nodes(Expr *qual, foreach(temp, qual->args) { - count_bool_nodes(lfirst(temp), + count_bool_nodes(lfirst(temp), &subnodes, &subcnfnodes, &subdnfnodes); *nodes += subnodes; *cnfnodes += subcnfnodes; *dnfnodes *= subdnfnodes; } - /* we could get dnfnodes < cnfnodes here, if all the sub-nodes are + + /* + * we could get dnfnodes < cnfnodes here, if all the sub-nodes are * simple ones with count 1. Make sure dnfnodes isn't too small. */ if (*dnfnodes < *cnfnodes) @@ -883,13 +913,15 @@ count_bool_nodes(Expr *qual, foreach(temp, qual->args) { - count_bool_nodes(lfirst(temp), + count_bool_nodes(lfirst(temp), &subnodes, &subcnfnodes, &subdnfnodes); *nodes += subnodes; *cnfnodes *= subcnfnodes; *dnfnodes += subdnfnodes; } - /* we could get cnfnodes < dnfnodes here, if all the sub-nodes are + + /* + * we could get cnfnodes < dnfnodes here, if all the sub-nodes are * simple ones with count 1. Make sure cnfnodes isn't too small. */ if (*cnfnodes < *dnfnodes) diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c index 22ed6f418f7..22f06bb61a9 100644 --- a/src/backend/optimizer/prep/preptlist.c +++ b/src/backend/optimizer/prep/preptlist.c @@ -4,7 +4,7 @@ * Routines to preprocess the parse tree target list * * This module takes care of altering the query targetlist as needed for - * INSERT, UPDATE, and DELETE queries. For INSERT and UPDATE queries, + * INSERT, UPDATE, and DELETE queries. For INSERT and UPDATE queries, * the targetlist must contain an entry for each attribute of the target * relation in the correct order. For both UPDATE and DELETE queries, * we need a junk targetlist entry holding the CTID attribute --- the @@ -15,7 +15,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.35 2000/03/09 05:00:24 inoue Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.36 2000/04/12 17:15:23 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -31,7 +31,7 @@ static List *expand_targetlist(List *tlist, int command_type, - Index result_relation, List *range_table); + Index result_relation, List *range_table); /* @@ -46,6 +46,7 @@ preprocess_targetlist(List *tlist, Index result_relation, List *range_table) { + /* * for heap_formtuple to work, the targetlist must match the exact * order of the attributes. We also need to fill in any missing @@ -56,11 +57,11 @@ preprocess_targetlist(List *tlist, result_relation, range_table); /* - * for "update" and "delete" queries, add ctid of the result - * relation into the target list so that the ctid will propagate - * through execution and ExecutePlan() will be able to identify - * the right tuple to replace or delete. This extra field is - * marked "junk" so that it is not stored back into the tuple. + * for "update" and "delete" queries, add ctid of the result relation + * into the target list so that the ctid will propagate through + * execution and ExecutePlan() will be able to identify the right + * tuple to replace or delete. This extra field is marked "junk" so + * that it is not stored back into the tuple. */ if (command_type == CMD_UPDATE || command_type == CMD_DELETE) { @@ -78,7 +79,8 @@ preprocess_targetlist(List *tlist, var = makeVar(result_relation, SelfItemPointerAttributeNumber, TIDOID, -1, 0); - /* For an UPDATE, expand_targetlist already created a fresh tlist. + /* + * For an UPDATE, expand_targetlist already created a fresh tlist. * For DELETE, better do a listCopy so that we don't destructively * modify the original tlist (is this really necessary?). */ @@ -117,11 +119,11 @@ expand_targetlist(List *tlist, int command_type, List *temp; /* - * Keep a map of which tlist items we have transferred to new list. - * +1 here keeps palloc from complaining if old_tlist_len=0. + * Keep a map of which tlist items we have transferred to new list. +1 + * here keeps palloc from complaining if old_tlist_len=0. */ - tlistentry_used = (bool *) palloc((old_tlist_len+1) * sizeof(bool)); - memset(tlistentry_used, 0, (old_tlist_len+1) * sizeof(bool)); + tlistentry_used = (bool *) palloc((old_tlist_len + 1) * sizeof(bool)); + memset(tlistentry_used, 0, (old_tlist_len + 1) * sizeof(bool)); /* * Scan the tuple description in the relation's relcache entry to make @@ -133,9 +135,9 @@ expand_targetlist(List *tlist, int command_type, for (attrno = 1; attrno <= numattrs; attrno++) { - Form_pg_attribute att_tup = rel->rd_att->attrs[attrno-1]; - char *attrname = NameStr(att_tup->attname); - TargetEntry *new_tle = NULL; + Form_pg_attribute att_tup = rel->rd_att->attrs[attrno - 1]; + char *attrname = NameStr(att_tup->attname); + TargetEntry *new_tle = NULL; /* * We match targetlist entries to attributes using the resname. @@ -143,22 +145,22 @@ expand_targetlist(List *tlist, int command_type, old_tlist_index = 0; foreach(temp, tlist) { - TargetEntry *old_tle = (TargetEntry *) lfirst(temp); - Resdom *resdom = old_tle->resdom; + TargetEntry *old_tle = (TargetEntry *) lfirst(temp); + Resdom *resdom = old_tle->resdom; - if (! tlistentry_used[old_tlist_index] && + if (!tlistentry_used[old_tlist_index] && strcmp(resdom->resname, attrname) == 0 && - ! resdom->resjunk) + !resdom->resjunk) { + /* * We can recycle the old TLE+resdom if right resno; else - * make a new one to avoid modifying the old tlist structure. - * (Is preserving old tlist actually necessary?) + * make a new one to avoid modifying the old tlist + * structure. (Is preserving old tlist actually + * necessary?) */ if (resdom->resno == attrno) - { new_tle = old_tle; - } else { resdom = (Resdom *) copyObject((Node *) resdom); @@ -173,14 +175,15 @@ expand_targetlist(List *tlist, int command_type, if (new_tle == NULL) { + /* * Didn't find a matching tlist entry, so make one. * - * For INSERT, generate a constant of the default value for - * the attribute type, or NULL if no default value. + * For INSERT, generate a constant of the default value for the + * attribute type, or NULL if no default value. * - * For UPDATE, generate a Var reference to the existing value - * of the attribute, so that it gets copied to the new tuple. + * For UPDATE, generate a Var reference to the existing value of + * the attribute, so that it gets copied to the new tuple. */ Oid atttype = att_tup->atttypid; int32 atttypmod = att_tup->atttypmod; @@ -188,92 +191,96 @@ expand_targetlist(List *tlist, int command_type, switch (command_type) { case CMD_INSERT: - { + { #ifdef _DROP_COLUMN_HACK__ - Datum typedefault; + Datum typedefault; + #else - Datum typedefault = get_typdefault(atttype); -#endif /* _DROP_COLUMN_HACK__ */ - int typlen; - Const *temp_const; + Datum typedefault = get_typdefault(atttype); + +#endif /* _DROP_COLUMN_HACK__ */ + int typlen; + Const *temp_const; #ifdef _DROP_COLUMN_HACK__ - if (COLUMN_IS_DROPPED(att_tup)) - typedefault = PointerGetDatum(NULL); - else - typedefault = get_typdefault(atttype); -#endif /* _DROP_COLUMN_HACK__ */ - if (typedefault == PointerGetDatum(NULL)) - typlen = 0; - else - { - /* - * Since this is an append or replace, the size of - * any set attribute is the size of the OID used to - * represent it. - */ - if (att_tup->attisset) - typlen = get_typlen(OIDOID); + if (COLUMN_IS_DROPPED(att_tup)) + typedefault = PointerGetDatum(NULL); + else + typedefault = get_typdefault(atttype); +#endif /* _DROP_COLUMN_HACK__ */ + if (typedefault == PointerGetDatum(NULL)) + typlen = 0; else - typlen = get_typlen(atttype); + { + + /* + * Since this is an append or replace, the + * size of any set attribute is the size of + * the OID used to represent it. + */ + if (att_tup->attisset) + typlen = get_typlen(OIDOID); + else + typlen = get_typlen(atttype); + } + + temp_const = makeConst(atttype, + typlen, + typedefault, + (typedefault == PointerGetDatum(NULL)), + false, + false, /* not a set */ + false); + + new_tle = makeTargetEntry(makeResdom(attrno, + atttype, + -1, + pstrdup(attrname), + 0, + (Oid) 0, + false), + (Node *) temp_const); + break; } - - temp_const = makeConst(atttype, - typlen, - typedefault, - (typedefault == PointerGetDatum(NULL)), - false, - false, /* not a set */ - false); - - new_tle = makeTargetEntry(makeResdom(attrno, - atttype, - -1, - pstrdup(attrname), - 0, - (Oid) 0, - false), - (Node *) temp_const); - break; - } case CMD_UPDATE: - { - Var *temp_var; + { + Var *temp_var; #ifdef _DROP_COLUMN_HACK__ - Node *temp_node = (Node *) NULL; - if (COLUMN_IS_DROPPED(att_tup)) - { - temp_node = (Node *)makeConst(atttype, 0, - PointerGetDatum(NULL), - true, - false, - false, /* not a set */ - false); - } - else -#endif /* _DROP_COLUMN_HACK__ */ - temp_var = makeVar(result_relation, attrno, atttype, - atttypmod, 0); + Node *temp_node = (Node *) NULL; + + if (COLUMN_IS_DROPPED(att_tup)) + { + temp_node = (Node *) makeConst(atttype, 0, + PointerGetDatum(NULL), + true, + false, + false, /* not a set */ + false); + } + else +#endif /* _DROP_COLUMN_HACK__ */ + temp_var = makeVar(result_relation, attrno, atttype, + atttypmod, 0); #ifdef _DROP_COLUMN_HACK__ - if (!temp_node) - temp_node = (Node *) temp_var; -#endif /* _DROP_COLUMN_HACK__ */ - - new_tle = makeTargetEntry(makeResdom(attrno, - atttype, - atttypmod, - pstrdup(attrname), - 0, - (Oid) 0, - false), + if (!temp_node) + temp_node = (Node *) temp_var; +#endif /* _DROP_COLUMN_HACK__ */ + + new_tle = makeTargetEntry(makeResdom(attrno, + atttype, + atttypmod, + pstrdup(attrname), + 0, + (Oid) 0, + false), #ifdef _DROP_COLUMN_HACK__ - temp_node); + temp_node); #else - (Node *) temp_var); -#endif /* _DROP_COLUMN_HACK__ */ - break; - } + (Node *) temp_var); +#endif /* _DROP_COLUMN_HACK__ */ + break; + } default: elog(ERROR, "expand_targetlist: unexpected command_type"); break; @@ -285,18 +292,19 @@ expand_targetlist(List *tlist, int command_type, /* * Copy all unprocessed tlist entries to the end of the new tlist, - * making sure they are marked resjunk = true. Typical junk entries - * include ORDER BY or GROUP BY expressions (are these actually possible - * in an INSERT or UPDATE?), system attribute references, etc. + * making sure they are marked resjunk = true. Typical junk entries + * include ORDER BY or GROUP BY expressions (are these actually + * possible in an INSERT or UPDATE?), system attribute references, + * etc. */ old_tlist_index = 0; foreach(temp, tlist) { - TargetEntry *old_tle = (TargetEntry *) lfirst(temp); + TargetEntry *old_tle = (TargetEntry *) lfirst(temp); - if (! tlistentry_used[old_tlist_index]) + if (!tlistentry_used[old_tlist_index]) { - Resdom *resdom; + Resdom *resdom; resdom = (Resdom *) copyObject((Node *) old_tle->resdom); resdom->resno = attrno++; diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index e866a5032fc..cd2baf6bbb8 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.47 2000/03/21 05:12:06 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.48 2000/04/12 17:15:23 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -26,7 +26,8 @@ #include "parser/parsetree.h" #include "utils/lsyscache.h" -typedef struct { +typedef struct +{ Index rt_index; int sublevels_up; Oid old_relid; @@ -40,11 +41,11 @@ static RangeTblEntry *new_rangetable_entry(Oid new_relid, RangeTblEntry *old_entry); static void fix_parsetree_attnums(Index rt_index, Oid old_relid, Oid new_relid, Query *parsetree); -static bool fix_parsetree_attnums_walker (Node *node, - fix_parsetree_attnums_context *context); +static bool fix_parsetree_attnums_walker(Node *node, + fix_parsetree_attnums_context *context); static Append *make_append(List *appendplans, List *unionrtables, - Index rt_index, - List *inheritrtable, List *tlist); + Index rt_index, + List *inheritrtable, List *tlist); /* @@ -122,11 +123,11 @@ plan_union_queries(Query *parse) /* Is this a simple one */ if (!union_all_found || !union_found || - /* A trailing UNION negates the effect of earlier UNION ALLs */ + /* A trailing UNION negates the effect of earlier UNION ALLs */ !last_union_all_flag) { List *hold_unionClause = parse->unionClause; - double tuple_fraction = -1.0; /* default processing */ + double tuple_fraction = -1.0; /* default processing */ /* we will do sorting later, so don't do it now */ if (!union_all_found || @@ -134,6 +135,7 @@ plan_union_queries(Query *parse) { parse->sortClause = NIL; parse->distinctClause = NIL; + /* * force lower-level planning to assume that all tuples will * be retrieved, even if it sees a LIMIT in the query node. @@ -149,8 +151,9 @@ plan_union_queries(Query *parse) { Query *union_query = lfirst(ulist); - /* use subquery_planner here because the union'd queries - * have not been preprocessed yet. My goodness this is messy... + /* + * use subquery_planner here because the union'd queries have + * not been preprocessed yet. My goodness this is messy... */ union_plans = lappend(union_plans, subquery_planner(union_query, @@ -178,8 +181,8 @@ plan_union_queries(Query *parse) * Recursion, but UNION only. The last one is a UNION, so it will * not come here in recursion. * - * XXX is it OK to pass default -1 to union_planner in this path, - * or should we force a tuple_fraction value? + * XXX is it OK to pass default -1 to union_planner in this path, or + * should we force a tuple_fraction value? */ union_plans = lcons(union_planner(parse, -1.0), NIL); union_rts = lcons(parse->rtable, NIL); @@ -189,11 +192,12 @@ plan_union_queries(Query *parse) { Query *union_all_query = lfirst(ulist); - /* use subquery_planner here because the union'd queries - * have not been preprocessed yet. My goodness this is messy... + /* + * use subquery_planner here because the union'd queries have + * not been preprocessed yet. My goodness this is messy... */ union_plans = lappend(union_plans, - subquery_planner(union_all_query, -1.0)); + subquery_planner(union_all_query, -1.0)); union_rts = lappend(union_rts, union_all_query->rtable); } } @@ -201,9 +205,11 @@ plan_union_queries(Query *parse) /* We have already split UNION and UNION ALL and we made it consistent */ if (!last_union_all_flag) { - /* Need SELECT DISTINCT behavior to implement UNION. - * Put back the held sortClause, add any missing columns to the - * sort clause, and set distinctClause properly. + + /* + * Need SELECT DISTINCT behavior to implement UNION. Put back the + * held sortClause, add any missing columns to the sort clause, + * and set distinctClause properly. */ List *slitem; @@ -215,7 +221,7 @@ plan_union_queries(Query *parse) SortClause *scl = (SortClause *) lfirst(slitem); TargetEntry *tle = get_sortgroupclause_tle(scl, parse->targetList); - if (! tle->resdom->resjunk) + if (!tle->resdom->resjunk) parse->distinctClause = lappend(parse->distinctClause, copyObject(scl)); } @@ -226,9 +232,10 @@ plan_union_queries(Query *parse) parse->distinctClause = NIL; } - /* Make sure we don't try to apply the first query's grouping stuff - * to the Append node, either. Basically we don't want union_planner - * to do anything when we return control, except add the top sort/unique + /* + * Make sure we don't try to apply the first query's grouping stuff to + * the Append node, either. Basically we don't want union_planner to + * do anything when we return control, except add the top sort/unique * nodes for DISTINCT processing if this wasn't UNION ALL, or the top * sort node if it was UNION ALL with a user-provided sort clause. */ @@ -259,13 +266,13 @@ plan_union_queries(Query *parse) * * If grouping, aggregation, or sorting is specified in the parent plan, * the subplans should not do any of those steps --- we must do those - * operations just once above the APPEND node. The given tlist has been + * operations just once above the APPEND node. The given tlist has been * modified appropriately to remove group/aggregate expressions, but the * Query node still has the relevant fields set. We remove them in the * copies used for subplans (see plan_inherit_query). * * NOTE: this can be invoked recursively if more than one inheritance wildcard - * is present. At each level of recursion, the first wildcard remaining in + * is present. At each level of recursion, the first wildcard remaining in * the rangetable is expanded. */ Append * @@ -282,8 +289,8 @@ plan_inherit_queries(Query *parse, List *tlist, Index rt_index) /* * Remove the flag for this relation, since we're about to handle it. - * XXX destructive change to parent parse tree, but necessary to prevent - * infinite recursion. + * XXX destructive change to parent parse tree, but necessary to + * prevent infinite recursion. */ rt_entry->inh = false; @@ -313,42 +320,44 @@ plan_inherit_query(Relids relids, List *union_plans = NIL; List *union_rtentries = NIL; List *save_tlist = root->targetList; - double tuple_fraction; + double tuple_fraction; List *i; /* * Avoid making copies of the root's tlist, which we aren't going to - * use anyway (we are going to make copies of the passed tlist, instead). + * use anyway (we are going to make copies of the passed tlist, + * instead). */ root->targetList = NIL; /* - * If we are going to need sorting or grouping at the top level, - * force lower-level planners to assume that all tuples will be - * retrieved. + * If we are going to need sorting or grouping at the top level, force + * lower-level planners to assume that all tuples will be retrieved. */ if (root->distinctClause || root->sortClause || root->groupClause || root->hasAggs) - tuple_fraction = 0.0; /* will need all tuples from each subplan */ + tuple_fraction = 0.0; /* will need all tuples from each subplan */ else - tuple_fraction = -1.0; /* default behavior is OK (I think) */ + tuple_fraction = -1.0; /* default behavior is OK (I think) */ foreach(i, relids) { int relid = lfirsti(i); + /* - * Make a modifiable copy of the original query, - * and replace the target rangetable entry with a new one - * identifying this child table. + * Make a modifiable copy of the original query, and replace the + * target rangetable entry with a new one identifying this child + * table. */ Query *new_root = copyObject(root); RangeTblEntry *new_rt_entry = new_rangetable_entry(relid, rt_entry); rt_store(rt_index, new_root->rtable, new_rt_entry); + /* - * Insert (a modifiable copy of) the desired simplified tlist - * into the subquery + * Insert (a modifiable copy of) the desired simplified tlist into + * the subquery */ new_root->targetList = copyObject(tlist); @@ -360,14 +369,15 @@ plan_inherit_query(Relids relids, new_root->sortClause = NIL; new_root->groupClause = NIL; new_root->havingQual = NULL; - new_root->hasAggs = false; /* shouldn't be any left ... */ + new_root->hasAggs = false; /* shouldn't be any left ... */ /* * Update attribute numbers in case child has different ordering * of columns than parent (as can happen after ALTER TABLE). * * XXX This is a crock, and it doesn't really work. It'd be better - * to fix ALTER TABLE to preserve consistency of attribute numbering. + * to fix ALTER TABLE to preserve consistency of attribute + * numbering. */ fix_parsetree_attnums(rt_index, rt_entry->relid, @@ -397,23 +407,24 @@ find_all_inheritors(Oid parentrel) List *unexamined_relids = lconsi(parentrel, NIL); /* - * While the queue of unexamined relids is nonempty, remove the - * first element, mark it examined, and find its direct descendants. - * NB: cannot use foreach(), since we modify the queue inside loop. + * While the queue of unexamined relids is nonempty, remove the first + * element, mark it examined, and find its direct descendants. NB: + * cannot use foreach(), since we modify the queue inside loop. */ while (unexamined_relids != NIL) { - Oid currentrel = lfirsti(unexamined_relids); - List *currentchildren; + Oid currentrel = lfirsti(unexamined_relids); + List *currentchildren; unexamined_relids = lnext(unexamined_relids); examined_relids = lappendi(examined_relids, currentrel); currentchildren = find_inheritance_children(currentrel); + /* - * Add to the queue only those children not already seen. - * This could probably be simplified to a plain nconc, - * because our inheritance relationships should always be a - * strict tree, no? Should never find any matches, ISTM... + * Add to the queue only those children not already seen. This + * could probably be simplified to a plain nconc, because our + * inheritance relationships should always be a strict tree, no? + * Should never find any matches, ISTM... */ currentchildren = set_differencei(currentchildren, examined_relids); unexamined_relids = LispUnioni(unexamined_relids, currentchildren); @@ -477,7 +488,7 @@ new_rangetable_entry(Oid new_relid, RangeTblEntry *old_entry) * 'old_relid' in 'parsetree' with the attribute numbers from * 'new_relid'. * - * The parsetree is MODIFIED IN PLACE. This is OK only because + * The parsetree is MODIFIED IN PLACE. This is OK only because * plan_inherit_query made a copy of the tree for us to hack upon. */ static void @@ -495,6 +506,7 @@ fix_parsetree_attnums(Index rt_index, context.old_relid = old_relid; context.new_relid = new_relid; context.sublevels_up = 0; + /* * We must scan both the targetlist and qual, but we know the * havingQual is empty, so we can ignore it. @@ -504,7 +516,7 @@ fix_parsetree_attnums(Index rt_index, } /* - * Adjust varnos for child tables. This routine makes it possible for + * Adjust varnos for child tables. This routine makes it possible for * child tables to have different column positions for the "same" attribute * as a parent, which helps ALTER TABLE ADD COLUMN. Unfortunately this isn't * nearly enough to make it work transparently; there are other places where @@ -513,8 +525,8 @@ fix_parsetree_attnums(Index rt_index, * ALTER TABLE... */ static bool -fix_parsetree_attnums_walker (Node *node, - fix_parsetree_attnums_context *context) +fix_parsetree_attnums_walker(Node *node, + fix_parsetree_attnums_context *context) { if (node == NULL) return false; @@ -534,9 +546,10 @@ fix_parsetree_attnums_walker (Node *node, } if (IsA(node, SubLink)) { + /* - * Standard expression_tree_walker will not recurse into subselect, - * but here we must do so. + * Standard expression_tree_walker will not recurse into + * subselect, but here we must do so. */ SubLink *sub = (SubLink *) node; @@ -588,9 +601,9 @@ make_append(List *appendplans, node->plan.plan_width = 0; foreach(subnode, appendplans) { - Plan *subplan = (Plan *) lfirst(subnode); + Plan *subplan = (Plan *) lfirst(subnode); - if (subnode == appendplans) /* first node? */ + if (subnode == appendplans) /* first node? */ node->plan.startup_cost = subplan->startup_cost; node->plan.total_cost += subplan->total_cost; node->plan.plan_rows += subplan->plan_rows; diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index d429db93a03..7ddbe4190cc 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.64 2000/04/04 01:21:46 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.65 2000/04/12 17:15:24 momjian Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -46,9 +46,9 @@ static bool pull_agg_clause_walker(Node *node, List **listptr); static bool contain_subplans_walker(Node *node, void *context); static bool pull_subplans_walker(Node *node, List **listptr); static bool check_subplans_for_ungrouped_vars_walker(Node *node, - Query *context); -static int is_single_func(Node *node); -static Node *eval_const_expressions_mutator (Node *node, void *context); + Query *context); +static int is_single_func(Node *node); +static Node *eval_const_expressions_mutator(Node *node, void *context); static Expr *simplify_op_or_func(Expr *expr, List *args); @@ -340,18 +340,19 @@ make_ands_explicit(List *andclauses) List * make_ands_implicit(Expr *clause) { + /* * NB: because the parser sets the qual field to NULL in a query that * has no WHERE clause, we must consider a NULL input clause as TRUE, - * even though one might more reasonably think it FALSE. Grumble. - * If this causes trouble, consider changing the parser's behavior. + * even though one might more reasonably think it FALSE. Grumble. If + * this causes trouble, consider changing the parser's behavior. */ if (clause == NULL) return NIL; /* NULL -> NIL list == TRUE */ else if (and_clause((Node *) clause)) return clause->args; else if (IsA(clause, Const) && - ! ((Const *) clause)->constisnull && + !((Const *) clause)->constisnull && DatumGetInt32(((Const *) clause)->constvalue)) return NIL; /* constant TRUE input -> NIL list */ else @@ -381,7 +382,8 @@ contain_agg_clause_walker(Node *node, void *context) if (node == NULL) return false; if (IsA(node, Aggref)) - return true; /* abort the tree traversal and return true */ + return true; /* abort the tree traversal and return + * true */ return expression_tree_walker(node, contain_agg_clause_walker, context); } @@ -411,12 +413,14 @@ pull_agg_clause_walker(Node *node, List **listptr) if (IsA(node, Aggref)) { *listptr = lappend(*listptr, node); + /* * Complain if the aggregate's argument contains any aggregates; * nested agg functions are semantically nonsensical. */ if (contain_agg_clause(((Aggref *) node)->target)) elog(ERROR, "Aggregate function calls may not be nested"); + /* * Having checked that, we need not recurse into the argument. */ @@ -454,7 +458,8 @@ contain_subplans_walker(Node *node, void *context) if (node == NULL) return false; if (is_subplan(node) || IsA(node, SubLink)) - return true; /* abort the tree traversal and return true */ + return true; /* abort the tree traversal and return + * true */ return expression_tree_walker(node, contain_subplans_walker, context); } @@ -462,7 +467,7 @@ contain_subplans_walker(Node *node, void *context) * pull_subplans * Recursively pulls all subplans from an expression tree. * - * Returns list of subplan nodes found. Note the nodes themselves are not + * Returns list of subplan nodes found. Note the nodes themselves are not * copied, only referenced. */ List * @@ -507,7 +512,11 @@ void check_subplans_for_ungrouped_vars(Node *clause, Query *query) { - /* No special setup needed; context for walker is just the Query pointer */ + + /* + * No special setup needed; context for walker is just the Query + * pointer + */ check_subplans_for_ungrouped_vars_walker(clause, query); } @@ -517,17 +526,19 @@ check_subplans_for_ungrouped_vars_walker(Node *node, { if (node == NULL) return false; + /* - * We can ignore Vars other than in subplan args lists, - * since the parser already checked 'em. + * We can ignore Vars other than in subplan args lists, since the + * parser already checked 'em. */ if (is_subplan(node)) { + /* * The args list of the subplan node represents attributes from * outside passed into the sublink. */ - List *t; + List *t; foreach(t, ((Expr *) node)->args) { @@ -539,10 +550,10 @@ check_subplans_for_ungrouped_vars_walker(Node *node, /* * We do not care about args that are not local variables; * params or outer-level vars are not our responsibility to - * check. (The outer-level query passing them to us needs - * to worry, instead.) + * check. (The outer-level query passing them to us needs to + * worry, instead.) */ - if (! IsA(thisarg, Var)) + if (!IsA(thisarg, Var)) continue; var = (Var *) thisarg; if (var->varlevelsup > 0) @@ -554,8 +565,8 @@ check_subplans_for_ungrouped_vars_walker(Node *node, contained_in_group_clause = false; foreach(gl, context->groupClause) { - GroupClause *gcl = lfirst(gl); - Node *groupexpr; + GroupClause *gcl = lfirst(gl); + Node *groupexpr; groupexpr = get_sortgroupclause_expr(gcl, context->targetList); @@ -569,14 +580,14 @@ check_subplans_for_ungrouped_vars_walker(Node *node, if (!contained_in_group_clause) { /* Found an ungrouped argument. Complain. */ - RangeTblEntry *rte; - char *attname; + RangeTblEntry *rte; + char *attname; Assert(var->varno > 0 && var->varno <= length(context->rtable)); rte = rt_fetch(var->varno, context->rtable); attname = get_attname(rte->relid, var->varattno); - if (! attname) + if (!attname) elog(ERROR, "cache lookup of attribute %d in relation %u failed", var->varattno, rte->relid); elog(ERROR, "Sub-SELECT uses un-GROUPed attribute %s.%s from outer query", @@ -585,7 +596,7 @@ check_subplans_for_ungrouped_vars_walker(Node *node, } } return expression_tree_walker(node, - check_subplans_for_ungrouped_vars_walker, + check_subplans_for_ungrouped_vars_walker, (void *) context); } @@ -697,7 +708,7 @@ NumRelids(Node *clause) * is referenced in the clause). The routine checks that the * expression is of the form (var op something) or (something op var) * where the var is an attribute of the specified relation, or - * a function of a var of the specified relation. If so, it + * a function of a var of the specified relation. If so, it * returns the following info: * the found relation number (same as targetrelid unless that is 0) * the found var number (or InvalidAttrNumber if a function) @@ -707,7 +718,7 @@ NumRelids(Node *clause) * specifically 0 for the relid and attno, 0 for the constant value. * * Note that negative attno values are *not* invalid, but represent - * system attributes such as OID. It's sufficient to check for relid=0 + * system attributes such as OID. It's sufficient to check for relid=0 * to determine whether the routine succeeded. */ void @@ -785,15 +796,13 @@ default_results: *flag |= SEL_CONSTANT; } else - { *constval = 0; - } } /* * is_single_func - * If the given expression is a function of a single relation, - * return the relation number; else return 0 + * If the given expression is a function of a single relation, + * return the relation number; else return 0 */ static int is_single_func(Node *node) @@ -804,7 +813,7 @@ is_single_func(Node *node) if (length(varnos) == 1) { - int funcvarno = lfirsti(varnos); + int funcvarno = lfirsti(varnos); freeList(varnos); return funcvarno; @@ -922,7 +931,7 @@ CommuteClause(Expr *clause) * expression tree, for example "2 + 2" => "4". More interestingly, * we can reduce certain boolean expressions even when they contain * non-constant subexpressions: "x OR true" => "true" no matter what - * the subexpression x is. (XXX We assume that no such subexpression + * the subexpression x is. (XXX We assume that no such subexpression * will have important side-effects, which is not necessarily a good * assumption in the presence of user-defined functions; do we need a * pg_proc flag that prevents discarding the execution of a function?) @@ -954,7 +963,7 @@ eval_const_expressions(Node *node) } static Node * -eval_const_expressions_mutator (Node *node, void *context) +eval_const_expressions_mutator(Node *node, void *context) { if (node == NULL) return NULL; @@ -963,21 +972,22 @@ eval_const_expressions_mutator (Node *node, void *context) Expr *expr = (Expr *) node; List *args; Const *const_input; - Expr *newexpr; + Expr *newexpr; /* * Reduce constants in the Expr's arguments. We know args is - * either NIL or a List node, so we can call expression_tree_mutator - * directly rather than recursing to self. + * either NIL or a List node, so we can call + * expression_tree_mutator directly rather than recursing to self. */ args = (List *) expression_tree_mutator((Node *) expr->args, - eval_const_expressions_mutator, + eval_const_expressions_mutator, (void *) context); switch (expr->opType) { case OP_EXPR: case FUNC_EXPR: + /* * Code for op/func case is pretty bulky, so split it out * as a separate function. @@ -985,123 +995,131 @@ eval_const_expressions_mutator (Node *node, void *context) newexpr = simplify_op_or_func(expr, args); if (newexpr) /* successfully simplified it */ return (Node *) newexpr; - /* else fall out to build new Expr node with simplified args */ - break; - case OR_EXPR: - { + /* - * OR arguments are handled as follows: - * non constant: keep - * FALSE: drop (does not affect result) - * TRUE: force result to TRUE - * NULL: keep only one - * We keep one NULL input because ExecEvalOr returns - * NULL when no input is TRUE and at least one is NULL. + * else fall out to build new Expr node with simplified + * args */ - List *newargs = NIL; - List *arg; - bool haveNull = false; - bool forceTrue = false; - - foreach(arg, args) + break; + case OR_EXPR: { - if (! IsA(lfirst(arg), Const)) + + /* + * OR arguments are handled as follows: non constant: + * keep FALSE: drop (does not affect result) TRUE: + * force result to TRUE NULL: keep only one We keep + * one NULL input because ExecEvalOr returns NULL when + * no input is TRUE and at least one is NULL. + */ + List *newargs = NIL; + List *arg; + bool haveNull = false; + bool forceTrue = false; + + foreach(arg, args) { - newargs = lappend(newargs, lfirst(arg)); - continue; + if (!IsA(lfirst(arg), Const)) + { + newargs = lappend(newargs, lfirst(arg)); + continue; + } + const_input = (Const *) lfirst(arg); + if (const_input->constisnull) + haveNull = true; + else if (DatumGetInt32(const_input->constvalue)) + forceTrue = true; + /* otherwise, we can drop the constant-false input */ } - const_input = (Const *) lfirst(arg); - if (const_input->constisnull) - haveNull = true; - else if (DatumGetInt32(const_input->constvalue)) - forceTrue = true; - /* otherwise, we can drop the constant-false input */ + + /* + * We could return TRUE before falling out of the + * loop, but this coding method will be easier to + * adapt if we ever add a notion of non-removable + * functions. We'd need to check all the inputs for + * non-removability. + */ + if (forceTrue) + return MAKEBOOLCONST(true, false); + if (haveNull) + newargs = lappend(newargs, MAKEBOOLCONST(false, true)); + /* If all the inputs are FALSE, result is FALSE */ + if (newargs == NIL) + return MAKEBOOLCONST(false, false); + /* If only one nonconst-or-NULL input, it's the result */ + if (lnext(newargs) == NIL) + return (Node *) lfirst(newargs); + /* Else we still need an OR node */ + return (Node *) make_orclause(newargs); } - /* - * We could return TRUE before falling out of the loop, - * but this coding method will be easier to adapt if - * we ever add a notion of non-removable functions. - * We'd need to check all the inputs for non-removability. - */ - if (forceTrue) - return MAKEBOOLCONST(true, false); - if (haveNull) - newargs = lappend(newargs, MAKEBOOLCONST(false, true)); - /* If all the inputs are FALSE, result is FALSE */ - if (newargs == NIL) - return MAKEBOOLCONST(false, false); - /* If only one nonconst-or-NULL input, it's the result */ - if (lnext(newargs) == NIL) - return (Node *) lfirst(newargs); - /* Else we still need an OR node */ - return (Node *) make_orclause(newargs); - } case AND_EXPR: - { - /* - * AND arguments are handled as follows: - * non constant: keep - * TRUE: drop (does not affect result) - * FALSE: force result to FALSE - * NULL: keep only one - * We keep one NULL input because ExecEvalAnd returns - * NULL when no input is FALSE and at least one is NULL. - */ - List *newargs = NIL; - List *arg; - bool haveNull = false; - bool forceFalse = false; - - foreach(arg, args) { - if (! IsA(lfirst(arg), Const)) + + /* + * AND arguments are handled as follows: non constant: + * keep TRUE: drop (does not affect result) FALSE: + * force result to FALSE NULL: keep only one We keep + * one NULL input because ExecEvalAnd returns NULL + * when no input is FALSE and at least one is NULL. + */ + List *newargs = NIL; + List *arg; + bool haveNull = false; + bool forceFalse = false; + + foreach(arg, args) { - newargs = lappend(newargs, lfirst(arg)); - continue; + if (!IsA(lfirst(arg), Const)) + { + newargs = lappend(newargs, lfirst(arg)); + continue; + } + const_input = (Const *) lfirst(arg); + if (const_input->constisnull) + haveNull = true; + else if (!DatumGetInt32(const_input->constvalue)) + forceFalse = true; + /* otherwise, we can drop the constant-true input */ } - const_input = (Const *) lfirst(arg); - if (const_input->constisnull) - haveNull = true; - else if (! DatumGetInt32(const_input->constvalue)) - forceFalse = true; - /* otherwise, we can drop the constant-true input */ + + /* + * We could return FALSE before falling out of the + * loop, but this coding method will be easier to + * adapt if we ever add a notion of non-removable + * functions. We'd need to check all the inputs for + * non-removability. + */ + if (forceFalse) + return MAKEBOOLCONST(false, false); + if (haveNull) + newargs = lappend(newargs, MAKEBOOLCONST(false, true)); + /* If all the inputs are TRUE, result is TRUE */ + if (newargs == NIL) + return MAKEBOOLCONST(true, false); + /* If only one nonconst-or-NULL input, it's the result */ + if (lnext(newargs) == NIL) + return (Node *) lfirst(newargs); + /* Else we still need an AND node */ + return (Node *) make_andclause(newargs); } - /* - * We could return FALSE before falling out of the loop, - * but this coding method will be easier to adapt if - * we ever add a notion of non-removable functions. - * We'd need to check all the inputs for non-removability. - */ - if (forceFalse) - return MAKEBOOLCONST(false, false); - if (haveNull) - newargs = lappend(newargs, MAKEBOOLCONST(false, true)); - /* If all the inputs are TRUE, result is TRUE */ - if (newargs == NIL) - return MAKEBOOLCONST(true, false); - /* If only one nonconst-or-NULL input, it's the result */ - if (lnext(newargs) == NIL) - return (Node *) lfirst(newargs); - /* Else we still need an AND node */ - return (Node *) make_andclause(newargs); - } case NOT_EXPR: Assert(length(args) == 1); - if (! IsA(lfirst(args), Const)) + if (!IsA(lfirst(args), Const)) break; const_input = (Const *) lfirst(args); /* NOT NULL => NULL */ if (const_input->constisnull) return MAKEBOOLCONST(false, true); /* otherwise pretty easy */ - return MAKEBOOLCONST(! DatumGetInt32(const_input->constvalue), + return MAKEBOOLCONST(!DatumGetInt32(const_input->constvalue), false); case SUBPLAN_EXPR: + /* * Safety measure per notes at head of this routine: - * return a SubPlan unchanged. Too late to do anything + * return a SubPlan unchanged. Too late to do anything * with it. The arglist simplification above was wasted - * work (the list probably only contains Var nodes anyway). + * work (the list probably only contains Var nodes + * anyway). */ return (Node *) expr; default: @@ -1112,25 +1130,26 @@ eval_const_expressions_mutator (Node *node, void *context) /* * If we break out of the above switch on opType, then the - * expression cannot be simplified any further, so build - * and return a replacement Expr node using the - * possibly-simplified arguments and the original oper node. - * Can't use make_clause() here because we want to be sure - * the typeOid field is preserved... + * expression cannot be simplified any further, so build and + * return a replacement Expr node using the possibly-simplified + * arguments and the original oper node. Can't use make_clause() + * here because we want to be sure the typeOid field is + * preserved... */ newexpr = makeNode(Expr); - newexpr->typeOid = expr->typeOid; - newexpr->opType = expr->opType; - newexpr->oper = expr->oper; - newexpr->args = args; - return (Node *) newexpr; + newexpr->typeOid = expr->typeOid; + newexpr->opType = expr->opType; + newexpr->oper = expr->oper; + newexpr->args = args; + return (Node *) newexpr; } if (IsA(node, RelabelType)) { + /* * If we can simplify the input to a constant, then we don't need - * the RelabelType node anymore: just change the type field of - * the Const node. Otherwise, copy the RelabelType node. + * the RelabelType node anymore: just change the type field of the + * Const node. Otherwise, copy the RelabelType node. */ RelabelType *relabel = (RelabelType *) node; Node *arg; @@ -1138,13 +1157,15 @@ eval_const_expressions_mutator (Node *node, void *context) arg = eval_const_expressions_mutator(relabel->arg, context); if (arg && IsA(arg, Const)) { - Const *con = (Const *) arg; + Const *con = (Const *) arg; con->consttype = relabel->resulttype; + /* * relabel's resulttypmod is discarded, which is OK for now; * if the type actually needs a runtime length coercion then - * there should be a function call to do it just above this node. + * there should be a function call to do it just above this + * node. */ return (Node *) con; } @@ -1160,15 +1181,15 @@ eval_const_expressions_mutator (Node *node, void *context) } if (IsA(node, CaseExpr)) { + /* - * CASE expressions can be simplified if there are constant condition - * clauses: - * FALSE (or NULL): drop the alternative - * TRUE: drop all remaining alternatives - * If the first non-FALSE alternative is a constant TRUE, we can - * simplify the entire CASE to that alternative's expression. - * If there are no non-FALSE alternatives, we simplify the entire - * CASE to the default result (ELSE result). + * CASE expressions can be simplified if there are constant + * condition clauses: FALSE (or NULL): drop the alternative TRUE: + * drop all remaining alternatives If the first non-FALSE + * alternative is a constant TRUE, we can simplify the entire CASE + * to that alternative's expression. If there are no non-FALSE + * alternatives, we simplify the entire CASE to the default result + * (ELSE result). */ CaseExpr *caseexpr = (CaseExpr *) node; CaseExpr *newcase; @@ -1181,26 +1202,29 @@ eval_const_expressions_mutator (Node *node, void *context) { /* Simplify this alternative's condition and result */ CaseWhen *casewhen = (CaseWhen *) - expression_tree_mutator((Node *) lfirst(arg), - eval_const_expressions_mutator, - (void *) context); + expression_tree_mutator((Node *) lfirst(arg), + eval_const_expressions_mutator, + (void *) context); + Assert(IsA(casewhen, CaseWhen)); if (casewhen->expr == NULL || - ! IsA(casewhen->expr, Const)) + !IsA(casewhen->expr, Const)) { newargs = lappend(newargs, casewhen); continue; } const_input = (Const *) casewhen->expr; if (const_input->constisnull || - ! DatumGetInt32(const_input->constvalue)) + !DatumGetInt32(const_input->constvalue)) continue; /* drop alternative with FALSE condition */ + /* - * Found a TRUE condition. If it's the first (un-dropped) + * Found a TRUE condition. If it's the first (un-dropped) * alternative, the CASE reduces to just this alternative. */ if (newargs == NIL) return casewhen->result; + /* * Otherwise, add it to the list, and drop all the rest. */ @@ -1211,7 +1235,11 @@ eval_const_expressions_mutator (Node *node, void *context) /* Simplify the default result */ defresult = eval_const_expressions_mutator(caseexpr->defresult, context); - /* If no non-FALSE alternatives, CASE reduces to the default result */ + + /* + * If no non-FALSE alternatives, CASE reduces to the default + * result + */ if (newargs == NIL) return defresult; /* Otherwise we need a new CASE node */ @@ -1224,21 +1252,21 @@ eval_const_expressions_mutator (Node *node, void *context) } if (IsA(node, Iter)) { + /* - * The argument of an Iter is normally a function call. - * We must not try to eliminate the function, but we - * can try to simplify its arguments. If, by chance, - * the arg is NOT a function then we go ahead and try to - * simplify it (by falling into expression_tree_mutator). - * Is that the right thing? + * The argument of an Iter is normally a function call. We must + * not try to eliminate the function, but we can try to simplify + * its arguments. If, by chance, the arg is NOT a function then + * we go ahead and try to simplify it (by falling into + * expression_tree_mutator). Is that the right thing? */ Iter *iter = (Iter *) node; if (is_funcclause(iter->iterexpr)) { - Expr *func = (Expr *) iter->iterexpr; - Expr *newfunc; - Iter *newiter; + Expr *func = (Expr *) iter->iterexpr; + Expr *newfunc; + Iter *newiter; newfunc = makeNode(Expr); newfunc->typeOid = func->typeOid; @@ -1254,12 +1282,13 @@ eval_const_expressions_mutator (Node *node, void *context) return (Node *) newiter; } } + /* * For any node type not handled above, we recurse using - * expression_tree_mutator, which will copy the node unchanged - * but try to simplify its arguments (if any) using this routine. - * For example: we cannot eliminate an ArrayRef node, but we - * might be able to simplify constant expressions in its subscripts. + * expression_tree_mutator, which will copy the node unchanged but try + * to simplify its arguments (if any) using this routine. For example: + * we cannot eliminate an ArrayRef node, but we might be able to + * simplify constant expressions in its subscripts. */ return expression_tree_mutator(node, eval_const_expressions_mutator, (void *) context); @@ -1289,31 +1318,32 @@ simplify_op_or_func(Expr *expr, List *args) HeapTuple func_tuple; Form_pg_proc funcform; Type resultType; - Expr *newexpr; + Expr *newexpr; Datum const_val; bool const_is_null; bool isDone; /* - * For an operator or function, we cannot simplify unless all the inputs - * are constants. (XXX possible future improvement: if the op/func is - * strict and at least one input is NULL, we could simplify to NULL. - * But we do not currently have any way to know if the op/func is strict - * or not. For now, a NULL input is treated the same as any other - * constant node.) + * For an operator or function, we cannot simplify unless all the + * inputs are constants. (XXX possible future improvement: if the + * op/func is strict and at least one input is NULL, we could simplify + * to NULL. But we do not currently have any way to know if the + * op/func is strict or not. For now, a NULL input is treated the + * same as any other constant node.) */ foreach(arg, args) { - if (! IsA(lfirst(arg), Const)) + if (!IsA(lfirst(arg), Const)) return NULL; } + /* - * Get the function procedure's OID and look to see - * whether it is marked proiscachable. + * Get the function procedure's OID and look to see whether it is + * marked proiscachable. */ if (expr->opType == OP_EXPR) { - Oper *oper = (Oper *) expr->oper; + Oper *oper = (Oper *) expr->oper; replace_opid(oper); /* OK to scribble on input to this extent */ funcid = oper->opid; @@ -1321,7 +1351,7 @@ simplify_op_or_func(Expr *expr, List *args) } else { - Func *func = (Func *) expr->oper; + Func *func = (Func *) expr->oper; funcid = func->funcid; result_typeid = func->functype; @@ -1333,21 +1363,23 @@ simplify_op_or_func(Expr *expr, List *args) if (!HeapTupleIsValid(func_tuple)) elog(ERROR, "Function OID %u does not exist", funcid); funcform = (Form_pg_proc) GETSTRUCT(func_tuple); - if (! funcform->proiscachable) + if (!funcform->proiscachable) return NULL; + /* * Also check to make sure it doesn't return a set. */ if (funcform->proretset) return NULL; + /* * OK, looks like we can simplify this operator/function. * * We use the executor's routine ExecEvalExpr() to avoid duplication of * code and ensure we get the same result as the executor would get. * - * Build a new Expr node containing the already-simplified arguments. - * The only other setup needed here is the replace_opid() that we already + * Build a new Expr node containing the already-simplified arguments. The + * only other setup needed here is the replace_opid() that we already * did for the OP_EXPR case. */ newexpr = makeNode(Expr); @@ -1355,21 +1387,23 @@ simplify_op_or_func(Expr *expr, List *args) newexpr->opType = expr->opType; newexpr->oper = expr->oper; newexpr->args = args; + /* * It is OK to pass econtext = NULL because none of the ExecEvalExpr() * code used in this situation will use econtext. That might seem - * fortuitous, but it's not so unreasonable --- a constant expression does - * not depend on context, by definition, n'est ce pas? + * fortuitous, but it's not so unreasonable --- a constant expression + * does not depend on context, by definition, n'est ce pas? */ const_val = ExecEvalExpr((Node *) newexpr, NULL, &const_is_null, &isDone); Assert(isDone); /* if this isn't set, we blew it... */ pfree(newexpr); + /* * Make the constant result node. * - * XXX would it be better to take the result type from the - * pg_proc tuple, rather than the Oper or Func node? + * XXX would it be better to take the result type from the pg_proc tuple, + * rather than the Oper or Func node? */ resultType = typeidType(result_typeid); return (Expr *) makeConst(result_typeid, typeLen(resultType), @@ -1426,8 +1460,8 @@ simplify_op_or_func(Expr *expr, List *args) * * The walker routine should return "false" to continue the tree walk, or * "true" to abort the walk and immediately return "true" to the top-level - * caller. This can be used to short-circuit the traversal if the walker - * has found what it came for. "false" is returned to the top-level caller + * caller. This can be used to short-circuit the traversal if the walker + * has found what it came for. "false" is returned to the top-level caller * iff no invocation of the walker returned "true". * * The node types handled by expression_tree_walker include all those @@ -1454,16 +1488,16 @@ simplify_op_or_func(Expr *expr, List *args) */ bool -expression_tree_walker(Node *node, bool (*walker) (), void *context) + expression_tree_walker(Node *node, bool (*walker) (), void *context) { List *temp; /* - * The walker has already visited the current node, - * and so we need only recurse into any sub-nodes it has. + * The walker has already visited the current node, and so we need + * only recurse into any sub-nodes it has. * - * We assume that the walker is not interested in List nodes per se, - * so when we expect a List we just recurse directly to self without + * We assume that the walker is not interested in List nodes per se, so + * when we expect a List we just recurse directly to self without * bothering to call the walker. */ if (node == NULL) @@ -1478,7 +1512,7 @@ expression_tree_walker(Node *node, bool (*walker) (), void *context) break; case T_Expr: { - Expr *expr = (Expr *) node; + Expr *expr = (Expr *) node; if (expr->opType == SUBPLAN_EXPR) { @@ -1500,6 +1534,7 @@ expression_tree_walker(Node *node, bool (*walker) (), void *context) case T_ArrayRef: { ArrayRef *aref = (ArrayRef *) node; + /* recurse directly for upper/lower array index lists */ if (expression_tree_walker((Node *) aref->refupperindexpr, walker, context)) @@ -1519,10 +1554,12 @@ expression_tree_walker(Node *node, bool (*walker) (), void *context) case T_CaseExpr: { CaseExpr *caseexpr = (CaseExpr *) node; + /* we assume walker doesn't care about CaseWhens, either */ foreach(temp, caseexpr->args) { CaseWhen *when = (CaseWhen *) lfirst(temp); + Assert(IsA(when, CaseWhen)); if (walker(when->expr, context)) return true; @@ -1538,12 +1575,14 @@ expression_tree_walker(Node *node, bool (*walker) (), void *context) break; case T_SubLink: { - SubLink *sublink = (SubLink *) node; + SubLink *sublink = (SubLink *) node; - /* If the SubLink has already been processed by subselect.c, - * it will have lefthand=NIL, and we only need to look at - * the oper list. Otherwise we only need to look at lefthand - * (the Oper nodes in the oper list are deemed uninteresting). + /* + * If the SubLink has already been processed by + * subselect.c, it will have lefthand=NIL, and we only + * need to look at the oper list. Otherwise we only need + * to look at lefthand (the Oper nodes in the oper list + * are deemed uninteresting). */ if (sublink->lefthand) return walker((Node *) sublink->lefthand, context); @@ -1628,18 +1667,19 @@ expression_tree_walker(Node *node, bool (*walker) (), void *context) */ Node * -expression_tree_mutator(Node *node, Node * (*mutator) (), void *context) + expression_tree_mutator(Node *node, Node *(*mutator) (), void *context) { + /* - * The mutator has already decided not to modify the current node, - * but we must call the mutator for any sub-nodes. + * The mutator has already decided not to modify the current node, but + * we must call the mutator for any sub-nodes. */ #define FLATCOPY(newnode, node, nodetype) \ ( (newnode) = makeNode(nodetype), \ memcpy((newnode), (node), sizeof(nodetype)) ) -#define CHECKFLATCOPY(newnode, node, nodetype) \ +#define CHECKFLATCOPY(newnode, node, nodetype) \ ( AssertMacro(IsA((node), nodetype)), \ (newnode) = makeNode(nodetype), \ memcpy((newnode), (node), sizeof(nodetype)) ) @@ -1659,31 +1699,41 @@ expression_tree_mutator(Node *node, Node * (*mutator) (), void *context) return (Node *) copyObject(node); case T_Expr: { - Expr *expr = (Expr *) node; - Expr *newnode; + Expr *expr = (Expr *) node; + Expr *newnode; FLATCOPY(newnode, expr, Expr); if (expr->opType == SUBPLAN_EXPR) { - SubLink *oldsublink = ((SubPlan *) expr->oper)->sublink; - SubPlan *newsubplan; + SubLink *oldsublink = ((SubPlan *) expr->oper)->sublink; + SubPlan *newsubplan; /* flat-copy the oper node, which is a SubPlan */ CHECKFLATCOPY(newsubplan, expr->oper, SubPlan); newnode->oper = (Node *) newsubplan; /* likewise its SubLink node */ CHECKFLATCOPY(newsubplan->sublink, oldsublink, SubLink); - /* transform args list (params to be passed to subplan) */ + + /* + * transform args list (params to be passed to + * subplan) + */ MUTATE(newnode->args, expr->args, List *); /* transform sublink's oper list as well */ - MUTATE(newsubplan->sublink->oper, oldsublink->oper, List*); - /* but not the subplan itself, which is referenced as-is */ + MUTATE(newsubplan->sublink->oper, oldsublink->oper, List *); + + /* + * but not the subplan itself, which is referenced + * as-is + */ } else { - /* for other Expr node types, just transform args list, - * linking to original oper node (OK?) + + /* + * for other Expr node types, just transform args + * list, linking to original oper node (OK?) */ MUTATE(newnode->args, expr->args, List *); } @@ -1692,8 +1742,8 @@ expression_tree_mutator(Node *node, Node * (*mutator) (), void *context) break; case T_Aggref: { - Aggref *aggref = (Aggref *) node; - Aggref *newnode; + Aggref *aggref = (Aggref *) node; + Aggref *newnode; FLATCOPY(newnode, aggref, Aggref); MUTATE(newnode->target, aggref->target, Node *); @@ -1702,8 +1752,8 @@ expression_tree_mutator(Node *node, Node * (*mutator) (), void *context) break; case T_Iter: { - Iter *iter = (Iter *) node; - Iter *newnode; + Iter *iter = (Iter *) node; + Iter *newnode; FLATCOPY(newnode, iter, Iter); MUTATE(newnode->iterexpr, iter->iterexpr, Node *); @@ -1763,12 +1813,14 @@ expression_tree_mutator(Node *node, Node * (*mutator) (), void *context) break; case T_SubLink: { - /* A "bare" SubLink (note we will not come here if we found - * a SUBPLAN_EXPR node above it). Transform the lefthand side, - * but not the oper list nor the subquery. + + /* + * A "bare" SubLink (note we will not come here if we + * found a SUBPLAN_EXPR node above it). Transform the + * lefthand side, but not the oper list nor the subquery. */ - SubLink *sublink = (SubLink *) node; - SubLink *newnode; + SubLink *sublink = (SubLink *) node; + SubLink *newnode; FLATCOPY(newnode, sublink, SubLink); MUTATE(newnode->lefthand, sublink->lefthand, List *); @@ -1777,9 +1829,12 @@ expression_tree_mutator(Node *node, Node * (*mutator) (), void *context) break; case T_List: { - /* We assume the mutator isn't interested in the list nodes - * per se, so just invoke it on each list element. - * NOTE: this would fail badly on a list with integer elements! + + /* + * We assume the mutator isn't interested in the list + * nodes per se, so just invoke it on each list element. + * NOTE: this would fail badly on a list with integer + * elements! */ List *resultlist = NIL; List *temp; @@ -1795,9 +1850,13 @@ expression_tree_mutator(Node *node, Node * (*mutator) (), void *context) break; case T_TargetEntry: { - /* We mutate the expression, but not the resdom, by default. */ - TargetEntry *targetentry = (TargetEntry *) node; - TargetEntry *newnode; + + /* + * We mutate the expression, but not the resdom, by + * default. + */ + TargetEntry *targetentry = (TargetEntry *) node; + TargetEntry *newnode; FLATCOPY(newnode, targetentry, TargetEntry); MUTATE(newnode->expr, targetentry->expr, Node *); diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index 400c813125c..69a28138d8a 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.62 2000/03/22 22:08:35 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.63 2000/04/12 17:15:24 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -42,6 +42,7 @@ compare_path_costs(Path *path1, Path *path2, CostSelector criterion) return -1; if (path1->startup_cost > path2->startup_cost) return +1; + /* * If paths have the same startup cost (not at all unlikely), * order them by total cost. @@ -57,6 +58,7 @@ compare_path_costs(Path *path1, Path *path2, CostSelector criterion) return -1; if (path1->total_cost > path2->total_cost) return +1; + /* * If paths have the same total cost, order them by startup cost. */ @@ -172,7 +174,8 @@ set_cheapest(RelOptInfo *parent_rel) void add_path(RelOptInfo *parent_rel, Path *new_path) { - bool accept_new = true; /* unless we find a superior old path */ + bool accept_new = true; /* unless we find a superior old + * path */ List *p1_prev = NIL; List *p1; @@ -184,36 +187,39 @@ add_path(RelOptInfo *parent_rel, Path *new_path) foreach(p1, parent_rel->pathlist) { Path *old_path = (Path *) lfirst(p1); - bool remove_old = false; /* unless new proves superior */ + bool remove_old = false; /* unless new proves superior */ int costcmp; costcmp = compare_path_costs(new_path, old_path, TOTAL_COST); + /* - * If the two paths compare differently for startup and total cost, - * then we want to keep both, and we can skip the (much slower) - * comparison of pathkeys. If they compare the same, proceed with - * the pathkeys comparison. Note this test relies on the fact that - * compare_path_costs will only return 0 if both costs are equal - * (and, therefore, there's no need to call it twice in that case). + * If the two paths compare differently for startup and total + * cost, then we want to keep both, and we can skip the (much + * slower) comparison of pathkeys. If they compare the same, + * proceed with the pathkeys comparison. Note this test relies on + * the fact that compare_path_costs will only return 0 if both + * costs are equal (and, therefore, there's no need to call it + * twice in that case). */ if (costcmp == 0 || - costcmp == compare_path_costs(new_path, old_path, STARTUP_COST)) + costcmp == compare_path_costs(new_path, old_path, STARTUP_COST)) { switch (compare_pathkeys(new_path->pathkeys, old_path->pathkeys)) { case PATHKEYS_EQUAL: if (costcmp < 0) - remove_old = true; /* new dominates old */ + remove_old = true; /* new dominates old */ else - accept_new = false; /* old equals or dominates new */ + accept_new = false; /* old equals or dominates + * new */ break; case PATHKEYS_BETTER1: if (costcmp <= 0) - remove_old = true; /* new dominates old */ + remove_old = true; /* new dominates old */ break; case PATHKEYS_BETTER2: if (costcmp >= 0) - accept_new = false; /* old dominates new */ + accept_new = false; /* old dominates new */ break; case PATHKEYS_DIFFERENT: /* keep both paths, since they have different ordering */ @@ -241,7 +247,7 @@ add_path(RelOptInfo *parent_rel, Path *new_path) * scanning the pathlist; we will not add new_path, and we assume * new_path cannot dominate any other elements of the pathlist. */ - if (! accept_new) + if (!accept_new) break; } @@ -315,12 +321,14 @@ create_index_path(Query *root, if (pathnode->path.pathkeys == NIL) { /* No ordering available from index, is that OK? */ - if (! ScanDirectionIsNoMovement(indexscandir)) + if (!ScanDirectionIsNoMovement(indexscandir)) elog(ERROR, "create_index_path: failed to create ordered index scan"); } else { - /* The index is ordered, and build_index_pathkeys defaulted to + + /* + * The index is ordered, and build_index_pathkeys defaulted to * forward scan, so make sure we mark the pathnode properly. */ if (ScanDirectionIsNoMovement(indexscandir)) @@ -341,11 +349,11 @@ create_index_path(Query *root, pathnode->indexscandir = indexscandir; /* - * This routine is only used to generate "standalone" indexpaths, - * not nestloop inner indexpaths. So joinrelids is always NIL - * and the number of rows is the same as the parent rel's estimate. + * This routine is only used to generate "standalone" indexpaths, not + * nestloop inner indexpaths. So joinrelids is always NIL and the + * number of rows is the same as the parent rel's estimate. */ - pathnode->joinrelids = NIL; /* no join clauses here */ + pathnode->joinrelids = NIL; /* no join clauses here */ pathnode->rows = rel->rows; cost_index(&pathnode->path, root, rel, index, indexquals, false); @@ -359,20 +367,23 @@ create_index_path(Query *root, * pathnode. * */ -TidPath * +TidPath * create_tidscan_path(RelOptInfo *rel, List *tideval) { - TidPath *pathnode = makeNode(TidPath); + TidPath *pathnode = makeNode(TidPath); pathnode->path.pathtype = T_TidScan; pathnode->path.parent = rel; pathnode->path.pathkeys = NIL; - pathnode->tideval = copyObject(tideval); /* is copy really necessary? */ + pathnode->tideval = copyObject(tideval); /* is copy really + * necessary? */ pathnode->unjoined_relids = NIL; cost_tidscan(&pathnode->path, rel, tideval); - /* divide selectivity for each clause to get an equal selectivity - * as IndexScan does OK ? + + /* + * divide selectivity for each clause to get an equal selectivity as + * IndexScan does OK ? */ return pathnode; @@ -485,7 +496,7 @@ create_mergejoin_path(RelOptInfo *joinrel, * 'innerdisbursion' is an estimate of the disbursion of the inner hash key * */ -HashPath * +HashPath * create_hashjoin_path(RelOptInfo *joinrel, Path *outer_path, Path *inner_path, diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index 716c31ab0f1..e9d7690e00c 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.49 2000/02/18 09:30:09 inoue Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.50 2000/04/12 17:15:24 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -50,7 +50,7 @@ relation_info(Query *root, Index relid, Form_pg_class relation; relationTuple = SearchSysCacheTuple(RELOID, - ObjectIdGetDatum(relationObjectId), + ObjectIdGetDatum(relationObjectId), 0, 0, 0); if (!HeapTupleIsValid(relationTuple)) elog(ERROR, "relation_info: Relation %u not found", @@ -81,7 +81,7 @@ find_secondary_indexes(Query *root, Index relid) Oid indrelid = getrelid(relid, root->rtable); Relation relation; HeapScanDesc scan; - ScanKeyData indexKey; + ScanKeyData indexKey; HeapTuple indexTuple; /* Scan pg_index for tuples describing indexes of this rel */ @@ -97,27 +97,28 @@ find_secondary_indexes(Query *root, Index relid) while (HeapTupleIsValid(indexTuple = heap_getnext(scan, 0))) { - Form_pg_index index = (Form_pg_index) GETSTRUCT(indexTuple); - IndexOptInfo *info = makeNode(IndexOptInfo); - int i; - Relation indexRelation; - Oid relam; - uint16 amorderstrategy; + Form_pg_index index = (Form_pg_index) GETSTRUCT(indexTuple); + IndexOptInfo *info = makeNode(IndexOptInfo); + int i; + Relation indexRelation; + Oid relam; + uint16 amorderstrategy; /* * Need to make these arrays large enough to be sure there is a * terminating 0 at the end of each one. */ - info->classlist = (Oid *) palloc(sizeof(Oid) * (INDEX_MAX_KEYS+1)); - info->indexkeys = (int *) palloc(sizeof(int) * (INDEX_MAX_KEYS+1)); - info->ordering = (Oid *) palloc(sizeof(Oid) * (INDEX_MAX_KEYS+1)); + info->classlist = (Oid *) palloc(sizeof(Oid) * (INDEX_MAX_KEYS + 1)); + info->indexkeys = (int *) palloc(sizeof(int) * (INDEX_MAX_KEYS + 1)); + info->ordering = (Oid *) palloc(sizeof(Oid) * (INDEX_MAX_KEYS + 1)); /* Extract info from the pg_index tuple */ info->indexoid = index->indexrelid; - info->indproc = index->indproc; /* functional index ?? */ - if (VARSIZE(&index->indpred) != 0) /* partial index ?? */ + info->indproc = index->indproc; /* functional index ?? */ + if (VARSIZE(&index->indpred) != 0) /* partial index ?? */ { char *predString = fmgr(F_TEXTOUT, &index->indpred); + info->indpred = (List *) stringToNode(predString); pfree(predString); } @@ -143,26 +144,25 @@ find_secondary_indexes(Query *root, Index relid) index_close(indexRelation); /* - * Fetch the ordering operators associated with the index, - * if any. + * Fetch the ordering operators associated with the index, if any. */ - MemSet(info->ordering, 0, sizeof(Oid) * (INDEX_MAX_KEYS+1)); + MemSet(info->ordering, 0, sizeof(Oid) * (INDEX_MAX_KEYS + 1)); if (amorderstrategy != 0) { for (i = 0; i < INDEX_MAX_KEYS && index->indclass[i]; i++) { - HeapTuple amopTuple; - Form_pg_amop amop; + HeapTuple amopTuple; + Form_pg_amop amop; amopTuple = SearchSysCacheTuple(AMOPSTRATEGY, ObjectIdGetDatum(relam), - ObjectIdGetDatum(index->indclass[i]), + ObjectIdGetDatum(index->indclass[i]), UInt16GetDatum(amorderstrategy), 0); if (!HeapTupleIsValid(amopTuple)) elog(ERROR, "find_secondary_indexes: no amop %u %u %d", - relam, index->indclass[i], (int) amorderstrategy); + relam, index->indclass[i], (int) amorderstrategy); amop = (Form_pg_amop) GETSTRUCT(amopTuple); info->ordering[i] = amop->amopopr; } diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index 694a1b905e1..da7059ce915 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.25 2000/02/18 23:47:31 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.26 2000/04/12 17:15:24 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -24,15 +24,15 @@ static List *new_join_tlist(List *tlist, int first_resdomno); static List *build_joinrel_restrictlist(RelOptInfo *joinrel, - RelOptInfo *outer_rel, - RelOptInfo *inner_rel); + RelOptInfo *outer_rel, + RelOptInfo *inner_rel); static void build_joinrel_joinlist(RelOptInfo *joinrel, - RelOptInfo *outer_rel, - RelOptInfo *inner_rel); + RelOptInfo *outer_rel, + RelOptInfo *inner_rel); static List *subbuild_joinrel_restrictlist(RelOptInfo *joinrel, - List *joininfo_list); + List *joininfo_list); static void subbuild_joinrel_joinlist(RelOptInfo *joinrel, - List *joininfo_list); + List *joininfo_list); /* @@ -50,7 +50,10 @@ get_base_rel(Query *root, int relid) { rel = (RelOptInfo *) lfirst(baserels); - /* We know length(rel->relids) == 1 for all members of base_rel_list */ + /* + * We know length(rel->relids) == 1 for all members of + * base_rel_list + */ if (lfirsti(rel->relids) == relid) return rel; } @@ -75,18 +78,20 @@ get_base_rel(Query *root, int relid) if (relid < 0) { + /* - * If the relation is a materialized relation, assume - * constants for sizes. + * If the relation is a materialized relation, assume constants + * for sizes. */ rel->pages = _NONAME_RELATION_PAGES_; rel->tuples = _NONAME_RELATION_TUPLES_; } else { + /* - * Otherwise, retrieve relation statistics from the - * system catalogs. + * Otherwise, retrieve relation statistics from the system + * catalogs. */ relation_info(root, relid, &rel->indexed, &rel->pages, &rel->tuples); @@ -162,6 +167,7 @@ get_join_rel(Query *root, if (joinrel) { + /* * Yes, so we only need to figure the restrictlist for this * particular pair of component relations. @@ -198,13 +204,13 @@ get_join_rel(Query *root, * of the outer and inner join relations and then merging the results * together. * - * 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 + * 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. * - * XXX 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. + * XXX 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. */ new_outer_tlist = new_join_tlist(outer_rel->targetlist, 1); new_inner_tlist = new_join_tlist(inner_rel->targetlist, @@ -212,9 +218,9 @@ get_join_rel(Query *root, joinrel->targetlist = nconc(new_outer_tlist, new_inner_tlist); /* - * Construct restrict and join clause lists for the new joinrel. - * (The caller might or might not need the restrictlist, but - * I need it anyway for set_joinrel_size_estimates().) + * Construct restrict and join clause lists for the new joinrel. (The + * caller might or might not need the restrictlist, but I need it + * anyway for set_joinrel_size_estimates().) */ restrictlist = build_joinrel_restrictlist(joinrel, outer_rel, inner_rel); if (restrictlist_ptr) @@ -246,7 +252,7 @@ get_join_rel(Query *root, * * 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 + * anymore. For now, all vars present in either input tlist will be * emitted in the join's tlist. * * 'tlist' is the target list of one of the join relations @@ -286,16 +292,16 @@ new_join_tlist(List *tlist, * the join lists need only be computed once for any join RelOptInfo. * The join lists are fully determined by the set of rels making up the * joinrel, so we should get the same results (up to ordering) from any - * candidate pair of sub-relations. But the restriction list is whatever + * candidate pair of sub-relations. But the restriction list is whatever * is not handled in the sub-relations, so it depends on which * sub-relations are considered. * * If a join clause from an input relation refers to base rels still not * present in the joinrel, then it is still a join clause for the joinrel; - * we put it into an appropriate JoinInfo list for the joinrel. Otherwise, + * we put it into an appropriate JoinInfo list for the joinrel. Otherwise, * the clause is now a restrict clause for the joined relation, and we * return it to the caller of build_joinrel_restrictlist() to be stored in - * join paths made from this pair of sub-relations. (It will not need to + * join paths made from this pair of sub-relations. (It will not need to * be considered further up the join tree.) * * 'joinrel' is a join relation node @@ -304,11 +310,11 @@ new_join_tlist(List *tlist, * * build_joinrel_restrictlist() returns a list of relevant restrictinfos, * whereas build_joinrel_joinlist() stores its results in the joinrel's - * joininfo lists. One or the other must accept each given clause! + * joininfo lists. One or the other must accept each given clause! * * NB: Formerly, we made deep(!) copies of each input RestrictInfo to pass * up to the join relation. I believe this is no longer necessary, because - * RestrictInfo nodes are no longer context-dependent. Instead, just include + * RestrictInfo nodes are no longer context-dependent. Instead, just include * the original nodes in the lists made for the join relation. */ static List * @@ -316,9 +322,10 @@ build_joinrel_restrictlist(RelOptInfo *joinrel, RelOptInfo *outer_rel, RelOptInfo *inner_rel) { + /* - * We must eliminate duplicates, since we will see the - * same clauses arriving from both input relations... + * We must eliminate duplicates, since we will see the same clauses + * arriving from both input relations... */ return LispUnion(subbuild_joinrel_restrictlist(joinrel, outer_rel->joininfo), @@ -348,6 +355,7 @@ subbuild_joinrel_restrictlist(RelOptInfo *joinrel, if (is_subseti(joininfo->unjoined_relids, joinrel->relids)) { + /* * Clauses in this JoinInfo list become restriction clauses * for the joinrel, since they refer to no outside rels. @@ -360,9 +368,10 @@ subbuild_joinrel_restrictlist(RelOptInfo *joinrel, } else { + /* - * These clauses are still join clauses at this level, - * so we ignore them in this routine. + * These clauses are still join clauses at this level, so we + * ignore them in this routine. */ } } @@ -385,18 +394,20 @@ subbuild_joinrel_joinlist(RelOptInfo *joinrel, joinrel->relids); if (new_unjoined_relids == NIL) { + /* * Clauses in this JoinInfo list become restriction clauses - * for the joinrel, since they refer to no outside rels. - * So we can ignore them in this routine. + * for the joinrel, since they refer to no outside rels. So we + * can ignore them in this routine. */ } else { + /* - * These clauses are still join clauses at this level, - * so find or make the appropriate JoinInfo item for the joinrel, - * and add the clauses to it (eliminating duplicates). + * These clauses are still join clauses at this level, so find + * or make the appropriate JoinInfo item for the joinrel, and + * add the clauses to it (eliminating duplicates). */ JoinInfo *new_joininfo; diff --git a/src/backend/optimizer/util/tlist.c b/src/backend/optimizer/util/tlist.c index b4c745b25f3..d4094dac12a 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.43 2000/01/27 18:11:34 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/tlist.c,v 1.44 2000/04/12 17:15:24 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -27,7 +27,7 @@ /* * tlistentry_member * Finds the (first) member of the given tlist whose expression is - * equal() to the given expression. Result is NULL if no such member. + * equal() to the given expression. Result is NULL if no such member. */ TargetEntry * tlistentry_member(Node *node, List *targetlist) @@ -36,7 +36,7 @@ tlistentry_member(Node *node, List *targetlist) foreach(temp, targetlist) { - TargetEntry *tlentry = (TargetEntry *) lfirst(temp); + TargetEntry *tlentry = (TargetEntry *) lfirst(temp); if (equal(node, tlentry->expr)) return tlentry; @@ -87,12 +87,12 @@ tlist_member(Node *node, List *targetlist) void add_var_to_tlist(RelOptInfo *rel, Var *var) { - if (! tlistentry_member((Node *) var, rel->targetlist)) + 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((Var *) copyObject(var), + length(rel->targetlist) + 1)); } } @@ -189,7 +189,7 @@ add_to_flat_tlist(List *tlist, List *vars) { Var *var = lfirst(v); - if (! tlistentry_member((Node *) var, tlist)) + if (!tlistentry_member((Node *) var, tlist)) { Resdom *r; diff --git a/src/backend/optimizer/util/var.c b/src/backend/optimizer/util/var.c index f438845cff9..bed7be7f08a 100644 --- a/src/backend/optimizer/util/var.c +++ b/src/backend/optimizer/util/var.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.25 2000/01/26 05:56:40 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.26 2000/04/12 17:15:24 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -20,7 +20,8 @@ #include "optimizer/var.h" -typedef struct { +typedef struct +{ List *varlist; bool includeUpperVars; } pull_var_clause_context; @@ -28,7 +29,7 @@ typedef struct { static bool pull_varnos_walker(Node *node, List **listptr); static bool contain_var_clause_walker(Node *node, void *context); static bool pull_var_clause_walker(Node *node, - pull_var_clause_context *context); + pull_var_clause_context *context); /* @@ -54,7 +55,8 @@ pull_varnos_walker(Node *node, List **listptr) return false; if (IsA(node, Var)) { - Var *var = (Var *) node; + Var *var = (Var *) node; + if (var->varlevelsup == 0 && !intMember(var->varno, *listptr)) *listptr = lconsi(var->varno, *listptr); return false; @@ -83,7 +85,8 @@ contain_var_clause_walker(Node *node, void *context) if (IsA(node, Var)) { if (((Var *) node)->varlevelsup == 0) - return true; /* abort the tree traversal and return true */ + return true; /* abort the tree traversal and return + * true */ return false; } return expression_tree_walker(node, contain_var_clause_walker, context); @@ -94,7 +97,7 @@ contain_var_clause_walker(Node *node, void *context) * Recursively pulls all var nodes from an expression clause. * * Upper-level vars (with varlevelsup > 0) are included only - * if includeUpperVars is true. Most callers probably want + * if includeUpperVars is true. Most callers probably want * to ignore upper-level vars. * * Returns list of varnodes found. Note the varnodes themselves are not @@ -103,7 +106,7 @@ contain_var_clause_walker(Node *node, void *context) List * pull_var_clause(Node *clause, bool includeUpperVars) { - pull_var_clause_context context; + pull_var_clause_context context; context.varlist = NIL; context.includeUpperVars = includeUpperVars; |