diff options
Diffstat (limited to 'src/backend/optimizer/plan')
-rw-r--r-- | src/backend/optimizer/plan/createplan.c | 237 | ||||
-rw-r--r-- | src/backend/optimizer/plan/initsplan.c | 61 | ||||
-rw-r--r-- | src/backend/optimizer/plan/planmain.c | 79 | ||||
-rw-r--r-- | src/backend/optimizer/plan/planner.c | 207 | ||||
-rw-r--r-- | src/backend/optimizer/plan/setrefs.c | 63 | ||||
-rw-r--r-- | src/backend/optimizer/plan/subselect.c | 173 |
6 files changed, 462 insertions, 358 deletions
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: |