diff options
24 files changed, 650 insertions, 951 deletions
diff --git a/doc/src/sgml/arch-dev.sgml b/doc/src/sgml/arch-dev.sgml index 7d867c45cae..93f53c51555 100644 --- a/doc/src/sgml/arch-dev.sgml +++ b/doc/src/sgml/arch-dev.sgml @@ -2475,7 +2475,7 @@ having clause} is found. + if(node->plan.qual != NULL) + { + qual_result = -+ ExecQual(fix_opids(node->plan.qual), ++ ExecQual(node->plan.qual, + econtext); + } + if (oneTuple) pfree(oneTuple); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 3644d1ba042..31d3419bee6 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.9 1999/07/17 20:16:52 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.10 1999/08/22 20:14:37 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -24,6 +24,7 @@ #include "catalog/pg_type.h" #include "commands/defrem.h" #include "optimizer/clauses.h" +#include "optimizer/planmain.h" #include "optimizer/prep.h" #include "parser/parsetree.h" #include "utils/builtins.h" @@ -142,7 +143,7 @@ DefineIndex(char *heapRelationName, if (predicate != NULL && rangetable != NIL) { cnfPred = cnfify((Expr *) copyObject(predicate), true); - fix_opids(cnfPred); + fix_opids((Node *) cnfPred); CheckPredicate(cnfPred, rangetable, relationId); } @@ -285,7 +286,7 @@ ExtendIndex(char *indexRelationName, Expr *predicate, List *rangetable) if (rangetable != NIL) { cnfPred = cnfify((Expr *) copyObject(predicate), true); - fix_opids(cnfPred); + fix_opids((Node *) cnfPred); CheckPredicate(cnfPred, rangetable, relationId); } diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index b600c0e3a8f..2936694f679 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -23,7 +23,6 @@ #include "executor/executor.h" #include "executor/nodeAgg.h" #include "optimizer/clauses.h" -#include "optimizer/planmain.h" #include "parser/parse_type.h" #include "utils/syscache.h" @@ -443,7 +442,7 @@ ExecAgg(Agg *node) * qualifications it is ignored and the next group is fetched */ if (node->plan.qual != NULL) - qual_result = ExecQual(fix_opids(node->plan.qual), econtext); + qual_result = ExecQual(node->plan.qual, econtext); else qual_result = false; diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c index 26ebed1d458..ddfef9d5eb4 100644 --- a/src/backend/nodes/makefuncs.c +++ b/src/backend/nodes/makefuncs.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/makefuncs.c,v 1.17 1999/08/21 03:48:58 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/makefuncs.c,v 1.18 1999/08/22 20:14:59 tgl Exp $ * * NOTES * Creator functions in POSTGRES 4.2 are generated automatically. Most of @@ -52,9 +52,7 @@ makeVar(Index varno, AttrNumber varattno, Oid vartype, int32 vartypmod, - Index varlevelsup, - Index varnoold, - AttrNumber varoattno) + Index varlevelsup) { Var *var = makeNode(Var); @@ -63,8 +61,14 @@ makeVar(Index varno, var->vartype = vartype; var->vartypmod = vartypmod; var->varlevelsup = varlevelsup; - var->varnoold = varnoold; - var->varoattno = varoattno; + /* + * Since few if any routines ever create Var nodes with varnoold/varoattno + * different from varno/varattno, we don't provide separate arguments + * for them, but just initialize them to the given varno/varattno. + * This reduces code clutter and chance of error for most callers. + */ + var->varnoold = varno; + var->varoattno = varattno; return var; } diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index 55787062dc5..fcf462b83eb 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -18,7 +18,7 @@ * Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.44 1999/08/06 04:00:15 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.45 1999/08/22 20:14:41 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -431,7 +431,7 @@ compute_rel_size(RelOptInfo *rel) int compute_rel_width(RelOptInfo *rel) { - return compute_targetlist_width(get_actual_tlist(rel->targetlist)); + return compute_targetlist_width(rel->targetlist); } /* @@ -448,8 +448,7 @@ compute_targetlist_width(List *targetlist) foreach(temp_tl, targetlist) { - tuple_width = tuple_width + - compute_attribute_width(lfirst(temp_tl)); + tuple_width += compute_attribute_width(lfirst(temp_tl)); } return tuple_width; } diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c index 41a3ff35b48..b9a982e8283 100644 --- a/src/backend/optimizer/path/pathkeys.c +++ b/src/backend/optimizer/path/pathkeys.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.15 1999/08/21 03:49:01 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.16 1999/08/22 20:14:42 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -24,8 +24,6 @@ #include "utils/lsyscache.h" static PathKeyItem *makePathKeyItem(Node *key, Oid sortop); -static bool pathkeyitem_equal(PathKeyItem *a, PathKeyItem *b); -static bool pathkeyitem_member(PathKeyItem *a, List *l); static Var *find_indexkey_var(int indexkey, List *tlist); static List *build_join_pathkey(List *pathkeys, List *join_rel_tlist, List *joinclauses); @@ -119,45 +117,6 @@ makePathKeyItem(Node *key, Oid sortop) ****************************************************************************/ /* - * Compare two pathkey items for equality. - * - * This is unlike straight equal() because when the two keys are both Vars, - * we want to apply the weaker var_equal() condition (doesn't check varnoold - * or varoattno). But if that fails, try equal() so that we recognize - * functional-index keys. - */ -static bool -pathkeyitem_equal (PathKeyItem *a, PathKeyItem *b) -{ - Assert(a && IsA(a, PathKeyItem)); - Assert(b && IsA(b, PathKeyItem)); - - if (a->sortop != b->sortop) - return false; - if (var_equal((Var *) a->key, (Var *) b->key)) - return true; - return equal(a->key, b->key); -} - -/* - * member() test using pathkeyitem_equal - */ -static bool -pathkeyitem_member (PathKeyItem *a, List *l) -{ - List *i; - - Assert(a && IsA(a, PathKeyItem)); - - foreach(i, l) - { - if (pathkeyitem_equal(a, (PathKeyItem *) lfirst(i))) - return true; - } - return false; -} - -/* * compare_pathkeys * Compare two pathkeys to see if they are equivalent, and if not whether * one is "better" than the other. @@ -191,7 +150,7 @@ compare_pathkeys(List *keys1, List *keys2) { foreach(i, subkey1) { - if (! pathkeyitem_member((PathKeyItem *) lfirst(i), subkey2)) + if (! member(lfirst(i), subkey2)) { key1_subsetof_key2 = false; break; @@ -203,7 +162,7 @@ compare_pathkeys(List *keys1, List *keys2) { foreach(i, subkey2) { - if (! pathkeyitem_member((PathKeyItem *) lfirst(i), subkey1)) + if (! member(lfirst(i), subkey1)) { key2_subsetof_key1 = false; break; @@ -336,8 +295,8 @@ build_index_pathkeys(Query *root, RelOptInfo *rel, RelOptInfo *index) int32 type_mod = get_atttypmod(reloid, varattno); funcargs = lappend(funcargs, - makeVar(relid, varattno, vartypeid, type_mod, - 0, relid, varattno)); + makeVar(relid, varattno, vartypeid, + type_mod, 0)); indexkeys++; } @@ -483,13 +442,13 @@ build_join_pathkey(List *pathkey, foreach(i, pathkey) { PathKeyItem *key = (PathKeyItem *) lfirst(i); - Expr *tlist_key; + Node *tlist_key; Assert(key && IsA(key, PathKeyItem)); - tlist_key = matching_tlist_var((Var *) key->key, join_rel_tlist); + tlist_key = matching_tlist_expr(key->key, join_rel_tlist); if (tlist_key) - new_pathkey = lcons(makePathKeyItem((Node *) tlist_key, + new_pathkey = lcons(makePathKeyItem(tlist_key, key->sortop), new_pathkey); @@ -498,17 +457,17 @@ build_join_pathkey(List *pathkey, RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(j); Expr *joinclause = restrictinfo->clause; /* We assume the clause is a binary opclause... */ - Var *l = get_leftop(joinclause); - Var *r = get_rightop(joinclause); - Var *other_var = NULL; + Node *l = (Node *) get_leftop(joinclause); + Node *r = (Node *) get_rightop(joinclause); + Node *other_var = NULL; Oid other_sortop = InvalidOid; - if (var_equal((Var *) key->key, l)) + if (equal(key->key, l)) { other_var = r; other_sortop = restrictinfo->right_sortop; } - else if (var_equal((Var *) key->key, r)) + else if (equal(key->key, r)) { other_var = l; other_sortop = restrictinfo->left_sortop; @@ -516,9 +475,9 @@ build_join_pathkey(List *pathkey, if (other_var && other_sortop) { - tlist_key = matching_tlist_var(other_var, join_rel_tlist); + tlist_key = matching_tlist_expr(other_var, join_rel_tlist); if (tlist_key) - new_pathkey = lcons(makePathKeyItem((Node *) tlist_key, + new_pathkey = lcons(makePathKeyItem(tlist_key, other_sortop), new_pathkey); } @@ -638,20 +597,17 @@ find_mergeclauses_for_pathkeys(List *pathkeys, List *restrictinfos) foreach(j, pathkey) { PathKeyItem *keyitem = lfirst(j); - Var *keyvar = (Var *) keyitem->key; + Node *key = keyitem->key; List *k; - if (! IsA(keyvar, Var)) - continue; /* for now, only Vars can be mergejoined */ - foreach(k, restrictinfos) { RestrictInfo *restrictinfo = lfirst(k); Assert(restrictinfo->mergejoinoperator != InvalidOid); - if ((var_equal(keyvar, get_leftop(restrictinfo->clause)) || - var_equal(keyvar, get_rightop(restrictinfo->clause))) && + if ((equal(key, get_leftop(restrictinfo->clause)) || + equal(key, get_rightop(restrictinfo->clause))) && ! member(restrictinfo, mergeclauses)) { matched_restrictinfo = restrictinfo; @@ -705,23 +661,24 @@ make_pathkeys_for_mergeclauses(List *mergeclauses, List *tlist) foreach(i, mergeclauses) { RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(i); - Var *key; + Node *key; Oid sortop; + Assert(restrictinfo->mergejoinoperator != InvalidOid); + /* * Find the key and sortop needed for this mergeclause. * * We can use either side of the mergeclause, since we haven't yet * committed to which side will be inner. */ - Assert(restrictinfo->mergejoinoperator != InvalidOid); - key = (Var *) matching_tlist_var(get_leftop(restrictinfo->clause), - tlist); + key = matching_tlist_expr((Node *) get_leftop(restrictinfo->clause), + tlist); sortop = restrictinfo->left_sortop; if (! key) { - key = (Var *) matching_tlist_var(get_rightop(restrictinfo->clause), - tlist); + key = matching_tlist_expr((Node *) get_rightop(restrictinfo->clause), + tlist); sortop = restrictinfo->right_sortop; } if (! key) @@ -730,7 +687,7 @@ make_pathkeys_for_mergeclauses(List *mergeclauses, List *tlist) * Add a pathkey sublist for this sort item */ pathkeys = lappend(pathkeys, - lcons(makePathKeyItem((Node *) key, sortop), + lcons(makePathKeyItem(key, sortop), NIL)); } diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index d1f756fc7c1..ae1e2d3266b 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -1,13 +1,15 @@ /*------------------------------------------------------------------------- * * createplan.c - * Routines to create the desired plan for processing a query + * Routines to create the desired plan for processing a query. + * Planning is complete, we just need to convert the selected + * Path into a Plan. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.74 1999/08/21 03:49:02 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.75 1999/08/22 20:14:47 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -70,13 +72,13 @@ static void copy_costsize(Plan *dest, Plan *src); * every pathnode found: * (1) Create a corresponding plan node containing appropriate id, * target list, and qualification information. - * (2) Modify ALL clauses so that attributes are referenced using - * relative values. - * (3) Target lists are not modified, but will be in another routine. + * (2) Modify qual clauses of join nodes so that subplan attributes are + * referenced using relative values. + * (3) Target lists are not modified, but will be in setrefs.c. * * best_path is the best access path * - * Returns the optimal(?) access plan. + * Returns the access plan. */ Plan * create_plan(Path *best_path) @@ -90,7 +92,7 @@ create_plan(Path *best_path) int tuples; parent_rel = best_path->parent; - tlist = get_actual_tlist(parent_rel->targetlist); + tlist = parent_rel->targetlist; size = parent_rel->size; width = parent_rel->width; pages = parent_rel->pages; @@ -152,9 +154,8 @@ create_scan_node(Path *best_path, List *tlist) /* * Extract the relevant restriction clauses from the parent relation; * the executor must apply all these restrictions during the scan. - * Fix regproc ids in the restriction clauses. */ - scan_clauses = fix_opids(get_actual_clauses(best_path->parent->restrictinfo)); + scan_clauses = get_actual_clauses(best_path->parent->restrictinfo); switch (best_path->pathtype) { @@ -235,7 +236,6 @@ create_join_node(JoinPath *best_path, List *tlist) inner_tlist); break; default: - /* do nothing */ elog(ERROR, "create_join_node: unknown node type", best_path->path.pathtype); } @@ -249,8 +249,7 @@ create_join_node(JoinPath *best_path, List *tlist) if (get_loc_restrictinfo(best_path) != NIL) set_qpqual((Plan) retval, nconc(get_qpqual((Plan) retval), - fix_opids(get_actual_clauses - (get_loc_restrictinfo(best_path))))); + get_actual_clauses(get_loc_restrictinfo(best_path)))); #endif return retval; @@ -282,8 +281,7 @@ create_seqscan_node(Path *best_path, List *tlist, List *scan_clauses) scan_node = make_seqscan(tlist, scan_clauses, - scan_relid, - (Plan *) NULL); + scan_relid); scan_node->plan.cost = best_path->path_cost; @@ -386,17 +384,10 @@ create_indexscan_node(IndexPath *best_path, qpqual = NIL; /* The executor needs a copy with the indexkey on the left of each clause - * and with index attrs substituted for table ones. + * and with index attr numbers substituted for table ones. */ fixed_indxqual = fix_indxqual_references(indxqual, best_path); - /* - * Fix opids in the completed indxquals. - * XXX this ought to only happen at final exit from the planner... - */ - indxqual = fix_opids(indxqual); - fixed_indxqual = fix_opids(fixed_indxqual); - scan_node = make_indexscan(tlist, qpqual, lfirsti(best_path->path.parent->relids), @@ -413,6 +404,21 @@ create_indexscan_node(IndexPath *best_path, * * JOIN METHODS * + * A general note about join_references() processing in these routines: + * 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 + * doesn't seem to be necessary; all the decisions are made before we do + * the reference adjustments. + * + * A cleaner solution would be to not call join_references() here at all, + * but leave it for setrefs.c to do at the end of plan tree construction. + * But that would make switch_outer() much more complicated, and some care + * would be needed to get setrefs.c to do the right thing with nestloop + * inner indexscan quals. So, we do subplan reference adjustment here for + * quals of join nodes (and *only* for quals of join nodes). + * *****************************************************************************/ static NestLoop * @@ -432,7 +438,7 @@ create_nestloop_node(NestPath *best_path, * An index is being used to reduce the number of tuples scanned * in the inner relation. If there are join clauses being used * with the index, we must update their outer-rel var nodes to - * refer to the outer relation. + * refer to the outer side of the join. * * We can also remove those join clauses from the list of clauses * that have to be checked as qpquals at the join node, but only @@ -442,7 +448,12 @@ create_nestloop_node(NestPath *best_path, * Note: if the index is lossy, the same clauses may also be getting * checked as qpquals in the indexscan. We can still remove them * from the nestloop's qpquals, but we gotta update the outer-rel - * vars in the indexscan's qpquals too... + * vars in the indexscan's qpquals too. + * + * Note: we can safely do set_difference() against my clauses and + * join_references() because the innerscan is a primitive plan, + * and therefore has not itself done join_references renumbering + * of the vars in its quals. */ IndexScan *innerscan = (IndexScan *) inner_node; List *indxqualorig = innerscan->indxqualorig; @@ -450,6 +461,8 @@ create_nestloop_node(NestPath *best_path, /* No work needed if indxqual refers only to its own relation... */ if (NumRelids((Node *) indxqualorig) > 1) { + 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, @@ -461,20 +474,28 @@ create_nestloop_node(NestPath *best_path, /* only refs to outer vars get changed in the inner indexqual */ innerscan->indxqualorig = join_references(indxqualorig, outer_tlist, - NIL); + NIL, + innerrel); innerscan->indxqual = join_references(innerscan->indxqual, outer_tlist, - NIL); + NIL, + innerrel); /* fix the inner qpqual too, if it has join clauses */ if (NumRelids((Node *) inner_node->qual) > 1) inner_node->qual = join_references(inner_node->qual, outer_tlist, - NIL); + NIL, + innerrel); } } else if (IsA_Join(inner_node)) { - /* Materialize the inner join for speed reasons */ + /* + * 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? + */ inner_node = (Plan *) make_noname(inner_tlist, NIL, inner_node); @@ -483,7 +504,8 @@ create_nestloop_node(NestPath *best_path, join_node = make_nestloop(tlist, join_references(clauses, outer_tlist, - inner_tlist), + inner_tlist, + (Index) 0), outer_node, inner_node); @@ -513,7 +535,8 @@ create_mergejoin_node(MergePath *best_path, qpqual = join_references(set_difference(clauses, best_path->path_mergeclauses), outer_tlist, - inner_tlist); + inner_tlist, + (Index) 0); /* * Now set the references in the mergeclauses and rearrange them so @@ -521,7 +544,8 @@ create_mergejoin_node(MergePath *best_path, */ mergeclauses = switch_outer(join_references(best_path->path_mergeclauses, outer_tlist, - inner_tlist)); + inner_tlist, + (Index) 0)); /* * Create explicit sort nodes for the outer and inner join paths if @@ -578,7 +602,8 @@ create_hashjoin_node(HashPath *best_path, qpqual = join_references(set_difference(clauses, best_path->path_hashclauses), outer_tlist, - inner_tlist); + inner_tlist, + (Index) 0); /* * Now set the references in the hashclauses and rearrange them so @@ -586,7 +611,8 @@ create_hashjoin_node(HashPath *best_path, */ hashclauses = switch_outer(join_references(best_path->path_hashclauses, outer_tlist, - inner_tlist)); + inner_tlist, + (Index) 0)); /* Now the righthand op of the sole hashclause is the inner hash key. */ innerhashkey = get_rightop(lfirst(hashclauses)); @@ -839,7 +865,7 @@ set_tlist_sort_info(List *tlist, List *pathkeys) { pathkey = lfirst(j); Assert(IsA(pathkey, PathKeyItem)); - resdom = tlist_member((Var *) pathkey->key, tlist); + resdom = tlist_member(pathkey->key, tlist); if (resdom) break; } @@ -939,17 +965,16 @@ make_noname(List *tlist, SeqScan * make_seqscan(List *qptlist, List *qpqual, - Index scanrelid, - Plan *lefttree) + Index scanrelid) { SeqScan *node = makeNode(SeqScan); Plan *plan = &node->plan; - copy_costsize(plan, lefttree); + copy_costsize(plan, NULL); plan->state = (EState *) NULL; plan->targetlist = qptlist; plan->qual = qpqual; - plan->lefttree = lefttree; + plan->lefttree = NULL; plan->righttree = NULL; node->scanrelid = scanrelid; node->scanstate = (CommonScanState *) NULL; @@ -1158,9 +1183,7 @@ make_group(List *tlist, } /* - * A unique node always has a SORT node in the lefttree. - * - * the uniqueAttr argument must be a null-terminated string, + * The uniqueAttr argument must be a null-terminated string, * either the name of the attribute to select unique on * or "*" */ @@ -1186,6 +1209,29 @@ make_unique(List *tlist, Plan *lefttree, char *uniqueAttr) return node; } +Result * +make_result(List *tlist, + Node *resconstantqual, + Plan *subplan) +{ + Result *node = makeNode(Result); + Plan *plan = &node->plan; + +#ifdef NOT_USED + tlist = generate_fjoin(tlist); +#endif + copy_costsize(plan, subplan); + plan->state = (EState *) NULL; + plan->targetlist = tlist; + plan->qual = NIL; + plan->lefttree = subplan; + plan->righttree = NULL; + node->resconstantqual = resconstantqual; + node->resstate = NULL; + + return node; +} + #ifdef NOT_USED List * generate_fjoin(List *tlist) diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c index db97c732070..a89c40b9435 100644 --- a/src/backend/optimizer/plan/initsplan.c +++ b/src/backend/optimizer/plan/initsplan.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.37 1999/08/16 02:17:54 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.38 1999/08/22 20:14:47 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -104,8 +104,7 @@ add_missing_vars_to_tlist(Query *root, List *tlist) /* add it to base_rel_list */ rel = get_base_rel(root, varno); /* give it a dummy tlist entry for its OID */ - var = makeVar(varno, ObjectIdAttributeNumber, - OIDOID, -1, 0, varno, ObjectIdAttributeNumber); + var = makeVar(varno, ObjectIdAttributeNumber, OIDOID, -1, 0); add_var_to_tlist(rel, var); } pfree(relids); diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c index f6f62abfe08..802e5970416 100644 --- a/src/backend/optimizer/plan/planmain.c +++ b/src/backend/optimizer/plan/planmain.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.41 1999/08/21 03:49:03 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.42 1999/08/22 20:14:48 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -27,7 +27,6 @@ static Plan *subplanner(Query *root, List *flat_tlist, List *qual); -static Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan); /* * query_planner @@ -84,11 +83,6 @@ query_planner(Query *root, * topmost result node. */ qual = pull_constant_clauses(qual, &constant_qual); - /* - * The opids for the variable qualifications will be fixed later, but - * someone seems to think that the constant quals need to be fixed here. - */ - fix_opids(constant_qual); /* * Create a target list that consists solely of (resdom var) target @@ -124,8 +118,7 @@ query_planner(Query *root, { SeqScan *scan = make_seqscan(tlist, NIL, - root->resultRelation, - (Plan *) NULL); + root->resultRelation); if (constant_qual != NULL) return ((Plan *) make_result(tlist, @@ -141,14 +134,10 @@ query_planner(Query *root, } /* - * Find the subplan (access path) and destructively modify the target - * list of the newly created subplan to contain the appropriate join - * references. + * Choose the best access path and build a plan for it. */ subplan = subplanner(root, level_tlist, qual); - set_tlist_references(subplan); - /* * Build a result node linking the plan if we have constant quals */ @@ -158,33 +147,24 @@ query_planner(Query *root, (Node *) constant_qual, subplan); - /* - * Fix all varno's of the Result's node target list. - */ - set_tlist_references(subplan); - root->query_pathkeys = NIL; /* result is unordered, no? */ return subplan; } /* - * fix up the flattened target list of the plan root node so that - * expressions are evaluated. this forces expression evaluations that - * may involve expensive function calls to be delayed to the very last - * stage of query execution. this could be bad. but it is joey's - * responsibility to optimally push these expressions down the plan - * tree. -- Wei + * Replace the toplevel plan node's flattened target list with the + * targetlist given by my caller, so that expressions are evaluated. * - * Note: formerly there was a test here to skip the unflatten call if - * we expected union_planner to insert a Group or Agg node above our - * result. However, now union_planner tells us exactly what it wants - * returned, and we just do it. Much cleaner. + * 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... */ else { - subplan->targetlist = unflatten_tlist(tlist, - subplan->targetlist); + subplan->targetlist = tlist; + return subplan; } @@ -330,36 +310,11 @@ subplanner(Query *root, /* Nothing for it but to sort the cheapestpath... * - * we indicate we failed to sort the plan, and let the caller - * stick the appropriate sortplan on top. + * We indicate we failed to sort the plan, and let the caller + * stick the appropriate sort node on top. union_planner has to be + * able to add a sort node anyway, so no need for extra code here. */ root->query_pathkeys = NIL; /* sorry, it ain't sorted */ return create_plan(final_rel->cheapestpath); } - -/***************************************************************************** - * - *****************************************************************************/ - -static Result * -make_result(List *tlist, - Node *resconstantqual, - Plan *subplan) -{ - Result *node = makeNode(Result); - Plan *plan = &node->plan; - -#ifdef NOT_USED - tlist = generate_fjoin(tlist); -#endif - plan->cost = (subplan ? subplan->cost : 0); - plan->state = (EState *) NULL; - plan->targetlist = tlist; - plan->lefttree = subplan; - plan->righttree = NULL; - node->resconstantqual = resconstantqual; - node->resstate = NULL; - - return node; -} diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index b328c40f226..0003262a454 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.63 1999/08/21 03:49:03 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.64 1999/08/22 20:14:48 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -70,6 +70,8 @@ planner(Query *parse) } result_plan->nParamExec = length(PlannerParamVar); + set_plan_references(result_plan); + return result_plan; } @@ -173,8 +175,7 @@ union_planner(Query *parse) 0, true); - var = makeVar(rowmark->rti, -1, TIDOID, - -1, 0, rowmark->rti, -1); + var = makeVar(rowmark->rti, -1, TIDOID, -1, 0); ctid = makeTargetEntry(resdom, (Node *) var); tlist = lappend(tlist, ctid); @@ -279,6 +280,8 @@ union_planner(Query *parse) */ if (parse->havingQual) { + List *ql; + /* convert the havingQual to conjunctive normal form (cnf) */ parse->havingQual = (Node *) cnfify((Expr *) parse->havingQual, true); @@ -295,13 +298,21 @@ union_planner(Query *parse) * Check for ungrouped variables passed to subplans. (Probably * this should be done for the targetlist as well???) */ - check_having_for_ungrouped_vars(parse->havingQual, - parse->groupClause, - parse->targetList); + if (check_subplans_for_ungrouped_vars(parse->havingQual, + parse->groupClause, + parse->targetList)) + elog(ERROR, "Sub-SELECT in HAVING clause must use only GROUPed attributes from outer SELECT"); } - /* Calculate the opfids from the opnos */ - parse->havingQual = (Node *) fix_opids((List *) parse->havingQual); + /* + * Require an aggregate function to appear in each clause of the + * havingQual (else it could have been done as a WHERE constraint). + */ + foreach(ql, (List *) parse->havingQual) + { + if (pull_agg_clause(lfirst(ql)) == NIL) + elog(ERROR, "SELECT/HAVING requires aggregates to be valid"); + } } /* @@ -315,13 +326,6 @@ union_planner(Query *parse) result_plan->qual = (List *) parse->havingQual; /* - * Update vars to refer to subplan result tuples, and - * make sure there is an Aggref in every HAVING clause. - */ - if (!set_agg_tlist_references((Agg *) result_plan)) - elog(ERROR, "SELECT/HAVING requires aggregates to be valid"); - - /* * Assume result is not ordered suitably for ORDER BY. * XXX it might be; improve this! */ @@ -474,7 +478,6 @@ make_groupplan(List *group_tlist, Plan *subplan) { int numCols = length(groupClause); - Group *grpplan; if (! is_sorted) { @@ -515,21 +518,8 @@ make_groupplan(List *group_tlist, keyno); } - /* - * Fix variables in tlist (should be done somewhere else?) - */ - group_tlist = copyObject(group_tlist); /* necessary?? */ - replace_tlist_with_subplan_refs(group_tlist, - (Index) 0, - subplan->targetlist); - - /* - * Make the Group node - */ - grpplan = make_group(group_tlist, tuplePerGroup, numCols, - grpColIdx, subplan); - - return (Plan *) grpplan; + return (Plan *) make_group(group_tlist, tuplePerGroup, numCols, + grpColIdx, subplan); } /* diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 1492df8b030..a983aa32143 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -1,13 +1,14 @@ /*------------------------------------------------------------------------- * * setrefs.c - * Routines to change varno/attno entries to contain references + * Post-processing of a completed plan tree: fix references to subplan + * vars, and compute regproc values for operators * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.56 1999/08/21 03:49:03 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.57 1999/08/22 20:14:48 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -25,36 +26,24 @@ typedef struct { List *outer_tlist; List *inner_tlist; -} replace_joinvar_refs_context; + Index acceptable_rel; +} join_references_context; typedef struct { Index subvarno; List *subplanTargetList; } replace_vars_with_subplan_refs_context; -typedef struct { - List *groupClause; - List *targetList; -} check_having_for_ungrouped_vars_context; - -static void set_join_tlist_references(Join *join); -static void set_nonamescan_tlist_references(SeqScan *nonamescan); -static void set_noname_tlist_references(Noname *noname); -static Node *replace_joinvar_refs(Node *clause, - List *outer_tlist, - List *inner_tlist); -static Node *replace_joinvar_refs_mutator(Node *node, - replace_joinvar_refs_context *context); -static List *tlist_noname_references(Oid nonameid, List *tlist); -static void set_result_tlist_references(Result *resultNode); -static void replace_vars_with_subplan_refs(Node *clause, - Index subvarno, - List *subplanTargetList); -static bool replace_vars_with_subplan_refs_walker(Node *node, +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); +static Node *replace_vars_with_subplan_refs(Node *node, + Index subvarno, + List *subplanTargetList); +static Node *replace_vars_with_subplan_refs_mutator(Node *node, replace_vars_with_subplan_refs_context *context); -static bool pull_agg_clause_walker(Node *node, List **listptr); -static bool check_having_for_ungrouped_vars_walker(Node *node, - check_having_for_ungrouped_vars_context *context); +static bool fix_opids_walker(Node *node, void *context); /***************************************************************************** * @@ -63,340 +52,298 @@ static bool check_having_for_ungrouped_vars_walker(Node *node, *****************************************************************************/ /* - * set_tlist_references - * Modifies the target list of nodes in a plan to reference target lists - * at lower levels. + * 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 + * 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). * - * 'plan' is the plan whose target list and children's target lists will - * be modified + * set_plan_references recursively traverses the whole plan tree. * * Returns nothing of interest, but modifies internal fields of nodes. - * */ void -set_tlist_references(Plan *plan) +set_plan_references(Plan *plan) { + List *pl; + if (plan == NULL) return; - if (IsA_Join(plan)) - set_join_tlist_references((Join *) plan); - else if (IsA(plan, SeqScan) && plan->lefttree && - IsA_Noname(plan->lefttree)) - set_nonamescan_tlist_references((SeqScan *) plan); - else if (IsA_Noname(plan)) - set_noname_tlist_references((Noname *) plan); - else if (IsA(plan, Result)) - set_result_tlist_references((Result *) plan); - else if (IsA(plan, Hash)) - set_tlist_references(plan->lefttree); -} + /* + * Plan-type-specific fixes + */ + switch (nodeTag(plan)) + { + case T_SeqScan: + /* nothing special */ + break; + case T_IndexScan: + fix_opids((Node *) ((IndexScan *) plan)->indxqual); + fix_opids((Node *) ((IndexScan *) plan)->indxqualorig); + break; + case T_NestLoop: + set_join_references((Join *) plan); + break; + case T_MergeJoin: + set_join_references((Join *) plan); + fix_opids((Node *) ((MergeJoin *) plan)->mergeclauses); + break; + case T_HashJoin: + set_join_references((Join *) plan); + fix_opids((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 + * 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. + */ + break; + case T_Agg: + case T_Group: + set_uppernode_references(plan, (Index) 0); + break; + case T_Result: + /* XXX why does Result use a different subvarno? */ + set_uppernode_references(plan, (Index) OUTER); + fix_opids(((Result *) plan)->resconstantqual); + break; + case T_Append: + foreach(pl, ((Append *) plan)->appendplans) + { + set_plan_references((Plan *) lfirst(pl)); + } + break; + default: + elog(ERROR, "set_plan_references: unknown plan type %d", + nodeTag(plan)); + break; + } -/* - * set_join_tlist_references - * Modifies the target list of a join node by setting the varnos and - * varattnos to reference the target list of the outer and inner join - * relations. - * - * Creates a target list for a join node to contain references by setting - * varno values to OUTER or INNER and setting attno values to the - * result domain number of either the corresponding outer or inner join - * tuple. - * - * 'join' is a join plan node - * - * Returns nothing of interest, but modifies internal fields of nodes. - * - */ -static void -set_join_tlist_references(Join *join) -{ - Plan *outer = join->lefttree; - Plan *inner = join->righttree; - List *outer_tlist = ((outer == NULL) ? NIL : outer->targetlist); - List *inner_tlist = ((inner == NULL) ? NIL : inner->targetlist); - List *new_join_targetlist = NIL; - List *qptlist = join->targetlist; - List *entry; + /* + * For all plan types, fix operators in targetlist and qual expressions + */ + fix_opids((Node *) plan->targetlist); + fix_opids((Node *) plan->qual); - foreach(entry, qptlist) + /* + * Now recurse into subplans, if any + * + * 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. + */ + set_plan_references(plan->lefttree); + set_plan_references(plan->righttree); + foreach(pl, plan->initPlan) { - TargetEntry *xtl = (TargetEntry *) lfirst(entry); - Node *joinexpr = replace_joinvar_refs(xtl->expr, - outer_tlist, - inner_tlist); + SubPlan *sp = (SubPlan *) lfirst(pl); - new_join_targetlist = lappend(new_join_targetlist, - makeTargetEntry(xtl->resdom, joinexpr)); + Assert(IsA(sp, SubPlan)); + set_plan_references(sp->plan); } - join->targetlist = new_join_targetlist; + foreach(pl, plan->subPlan) + { + SubPlan *sp = (SubPlan *) lfirst(pl); - set_tlist_references(outer); - set_tlist_references(inner); + Assert(IsA(sp, SubPlan)); + set_plan_references(sp->plan); + } } /* - * set_nonamescan_tlist_references - * Modifies the target list of a node that scans a noname relation (i.e., a - * sort or materialize node) so that the varnos refer to the child noname. + * set_join_references + * Modifies the target list of a join node to reference its subplans, + * by setting the varnos to OUTER or INNER and setting attno values to the + * result domain number of either the corresponding outer or inner join + * tuple item. * - * 'nonamescan' is a seqscan node + * Note: this same transformation has already been applied to the quals + * of the join by createplan.c. It's a little odd to do it here for the + * targetlist and there for the quals, but it's easier that way. (Look + * at switch_outer() and the handling of nestloop inner indexscans to + * see why.) * - * Returns nothing of interest, but modifies internal fields of nodes. + * Because the quals are reference-adjusted sooner, we cannot do equal() + * comparisons between qual and tlist var nodes during the time between + * creation of a plan node by createplan.c and its fixing by this module. + * Fortunately, there doesn't seem to be any need to do that. * + * 'join' is a join plan node */ static void -set_nonamescan_tlist_references(SeqScan *nonamescan) +set_join_references(Join *join) { - Noname *noname = (Noname *) nonamescan->plan.lefttree; + Plan *outer = join->lefttree; + Plan *inner = join->righttree; + List *outer_tlist = ((outer == NULL) ? NIL : outer->targetlist); + List *inner_tlist = ((inner == NULL) ? NIL : inner->targetlist); - nonamescan->plan.targetlist = tlist_noname_references(noname->nonameid, - nonamescan->plan.targetlist); - /* since we know child is a Noname, skip recursion through - * set_tlist_references() and just get the job done - */ - set_noname_tlist_references(noname); + join->targetlist = join_references(join->targetlist, + outer_tlist, + inner_tlist, + (Index) 0); } /* - * set_noname_tlist_references - * The noname's vars are made consistent with (actually, identical to) the - * modified version of the target list of the node from which noname node - * receives its tuples. - * - * 'noname' is a noname (e.g., sort, materialize) plan node - * - * Returns nothing of interest, but modifies internal fields of nodes. + * set_uppernode_references + * Update the targetlist and quals of an upper-level plan node + * to refer to the tuples returned by its lefttree subplan. * + * This is used for single-input plan types like Agg, Group, Result. */ static void -set_noname_tlist_references(Noname *noname) +set_uppernode_references(Plan *plan, Index subvarno) { - Plan *source = noname->plan.lefttree; + Plan *subplan = plan->lefttree; + List *subplanTargetList; - if (source != NULL) - { - set_tlist_references(source); - noname->plan.targetlist = copy_vars(noname->plan.targetlist, - source->targetlist); - } + if (subplan != NULL) + subplanTargetList = subplan->targetlist; else - elog(ERROR, "calling set_noname_tlist_references with empty lefttree"); + subplanTargetList = NIL; + + plan->targetlist = (List *) + replace_vars_with_subplan_refs((Node *) plan->targetlist, + subvarno, + subplanTargetList); + + plan->qual = (List *) + replace_vars_with_subplan_refs((Node *) plan->qual, + subvarno, + subplanTargetList); } /* * join_references - * Creates a new set of join clauses by changing the varno/varattno - * values of variables in the clauses to reference target list values - * from the outer and inner join relation target lists. - * This is just an external interface for replace_joinvar_refs. - * - * 'clauses' is the list of join clauses + * Creates a new set of targetlist entries or join qual clauses by + * changing the varno/varattno values of variables in the clauses + * to reference target list values from the outer and inner join + * relation target lists. + * + * This is used in two different scenarios: a normal join clause, where + * all the Vars in the clause *must* be replaced by OUTER or INNER references; + * and an indexscan being used on the inner side of a nestloop join. + * In the latter case we want to replace the outer-relation Vars by OUTER + * references, but not touch the Vars of the inner relation. + * + * For a normal join, acceptable_rel should be zero so that any failure to + * match a Var will be reported as an error. For the indexscan case, + * pass inner_tlist = NIL and acceptable_rel = the ID of the inner relation. + * + * 'clauses' is the targetlist or list of join clauses * 'outer_tlist' is the target list of the outer join relation - * 'inner_tlist' is the target list of the inner join relation + * 'inner_tlist' is the target list of the inner join relation, or NIL + * 'acceptable_rel' is either zero or the rangetable index of a relation + * whose Vars may appear in the clause without provoking an error. * - * Returns the new join clauses. The original clause structure is + * Returns the new expression tree. The original clause structure is * not modified. - * */ List * join_references(List *clauses, List *outer_tlist, - List *inner_tlist) + List *inner_tlist, + Index acceptable_rel) { - return (List *) replace_joinvar_refs((Node *) clauses, - outer_tlist, - inner_tlist); -} - -/* - * replace_joinvar_refs - * - * Replaces all variables within a join clause with a new var node - * whose varno/varattno fields contain a reference to a target list - * element from either the outer or inner join relation. - * - * Returns a suitably modified copy of the join clause; - * the original is not modified (and must not be!) - * - * Side effect: also runs fix_opids on the modified join clause. - * Really ought to make that happen in a uniform, consistent place... - * - * 'clause' is the join clause - * 'outer_tlist' is the target list of the outer join relation - * 'inner_tlist' is the target list of the inner join relation - */ -static Node * -replace_joinvar_refs(Node *clause, - List *outer_tlist, - List *inner_tlist) -{ - replace_joinvar_refs_context context; + join_references_context context; context.outer_tlist = outer_tlist; context.inner_tlist = inner_tlist; - return (Node *) fix_opids((List *) - replace_joinvar_refs_mutator(clause, &context)); + context.acceptable_rel = acceptable_rel; + return (List *) join_references_mutator((Node *) clauses, &context); } static Node * -replace_joinvar_refs_mutator(Node *node, - replace_joinvar_refs_context *context) +join_references_mutator(Node *node, + join_references_context *context) { if (node == NULL) return NULL; if (IsA(node, Var)) { Var *var = (Var *) node; - Resdom *resdom = tlist_member(var, context->outer_tlist); - - if (resdom != NULL && IsA(resdom, Resdom)) - return (Node *) makeVar(OUTER, - resdom->resno, - var->vartype, - var->vartypmod, - 0, - var->varnoold, - var->varoattno); - resdom = tlist_member(var, context->inner_tlist); - if (resdom != NULL && IsA(resdom, Resdom)) - return (Node *) makeVar(INNER, - resdom->resno, - var->vartype, - var->vartypmod, - 0, - var->varnoold, - var->varoattno); - /* Var not in either tlist, return an unmodified copy. */ - return copyObject(node); - } - return expression_tree_mutator(node, - replace_joinvar_refs_mutator, - (void *) context); -} + Var *newvar = (Var *) copyObject(var); + Resdom *resdom; -/* - * tlist_noname_references - * Creates a new target list for a node that scans a noname relation, - * setting the varnos to the id of the noname relation and setting varids - * if necessary (varids are only needed if this is a targetlist internal - * to the tree, in which case the targetlist entry always contains a var - * node, so we can just copy it from the noname). - * - * 'nonameid' is the id of the noname relation - * 'tlist' is the target list to be modified - * - * Returns new target list - * - */ -static List * -tlist_noname_references(Oid nonameid, - List *tlist) -{ - List *t_list = NIL; - List *entry; - - foreach(entry, tlist) - { - TargetEntry *xtl = lfirst(entry); - AttrNumber oattno; - TargetEntry *noname; - - if (IsA(get_expr(xtl), Var)) - oattno = ((Var *) xtl->expr)->varoattno; - else - oattno = 0; - - noname = makeTargetEntry(xtl->resdom, - (Node *) makeVar(nonameid, - xtl->resdom->resno, - xtl->resdom->restype, - xtl->resdom->restypmod, - 0, - nonameid, - oattno)); - - t_list = lappend(t_list, noname); + resdom = tlist_member((Node *) var, context->outer_tlist); + if (resdom) + { + newvar->varno = OUTER; + newvar->varattno = resdom->resno; + return (Node *) newvar; + } + resdom = tlist_member((Node *) var, context->inner_tlist); + if (resdom) + { + newvar->varno = INNER; + newvar->varattno = resdom->resno; + return (Node *) newvar; + } + /* + * 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"); + return (Node *) newvar; /* copy is probably not necessary here... */ } - return t_list; -} - -/*--------------------------------------------------------- - * - * set_result_tlist_references - * - * Change the target list of a Result node, so that it correctly - * addresses the tuples returned by its left tree subplan. - * - * NOTE: - * 1) we ignore the right tree! (in the current implementation - * it is always nil) - * 2) this routine will probably *NOT* work with nested dot - * fields.... - */ -static void -set_result_tlist_references(Result *resultNode) -{ - Plan *subplan; - List *resultTargetList; - List *subplanTargetList; - - resultTargetList = ((Plan *) resultNode)->targetlist; - /* - * NOTE: we only consider the left tree subplan. This is usually a seq - * scan. + * expression_tree_mutator will copy SubPlan nodes if given a chance. + * We do not want to do that here, because subselect.c has already + * constructed the initPlan and subPlan lists of the current plan node + * and we mustn't leave those dangling (ie, pointing to different + * copies of the nodes than what's in the targetlist & quals...) + * Instead, alter the SubPlan in-place. Grotty --- is there a better way? */ - subplan = ((Plan *) resultNode)->lefttree; - if (subplan != NULL) - subplanTargetList = subplan->targetlist; - else - subplanTargetList = NIL; - - replace_tlist_with_subplan_refs(resultTargetList, - (Index) OUTER, - subplanTargetList); -} - -/*--------------------------------------------------------- - * - * replace_tlist_with_subplan_refs - * - * Applies replace_vars_with_subplan_refs() to each entry of a targetlist. - */ -void -replace_tlist_with_subplan_refs(List *tlist, - Index subvarno, - List *subplanTargetList) -{ - List *t; - - foreach(t, tlist) + if (is_subplan(node)) { - TargetEntry *entry = (TargetEntry *) lfirst(t); - - replace_vars_with_subplan_refs((Node *) get_expr(entry), - subvarno, subplanTargetList); + Expr *expr = (Expr *) node; + SubLink *sublink = ((SubPlan *) expr->oper)->sublink; + + /* transform args list (params to be passed to subplan) */ + expr->args = (List *) + join_references_mutator((Node *) expr->args, + context); + /* transform sublink's oper list as well */ + sublink->oper = (List *) + join_references_mutator((Node *) sublink->oper, + context); + + return (Node *) expr; } + return expression_tree_mutator(node, + join_references_mutator, + (void *) context); } -/*--------------------------------------------------------- - * +/* * replace_vars_with_subplan_refs + * This routine modifies an expression tree so that all Var nodes + * reference target nodes of a subplan. It is used to fix up + * target and qual expressions of non-join upper-level plan nodes. * - * This routine modifies (destructively!) an expression tree so that all - * Var nodes reference target nodes of a subplan. It is used to fix up - * target expressions of upper-level plan nodes. + * An error is raised if no matching var can be found in the subplan tlist + * --- so this routine should only be applied to nodes whose subplans' + * targetlists were generated via flatten_tlist() or some such method. * - * 'clause': the tree to be fixed + * 'node': the tree to be fixed (a targetlist or qual list) * 'subvarno': varno to be assigned to all Vars * 'subplanTargetList': target list for subplan * - * Afterwards, all Var nodes have varno = subvarno, varattno = resno - * of corresponding subplan target. + * The resulting tree is a copy of the original in which all Var nodes have + * varno = subvarno, varattno = resno of corresponding subplan target. + * The original tree is not modified. */ -static void -replace_vars_with_subplan_refs(Node *clause, +static Node * +replace_vars_with_subplan_refs(Node *node, Index subvarno, List *subplanTargetList) { @@ -404,182 +351,84 @@ replace_vars_with_subplan_refs(Node *clause, context.subvarno = subvarno; context.subplanTargetList = subplanTargetList; - replace_vars_with_subplan_refs_walker(clause, &context); + return replace_vars_with_subplan_refs_mutator(node, &context); } -static bool -replace_vars_with_subplan_refs_walker(Node *node, +static Node * +replace_vars_with_subplan_refs_mutator(Node *node, replace_vars_with_subplan_refs_context *context) { if (node == NULL) - return false; + return NULL; if (IsA(node, Var)) { Var *var = (Var *) node; - TargetEntry *subplanVar; + Var *newvar = (Var *) copyObject(var); + Resdom *resdom; - subplanVar = match_varid(var, context->subplanTargetList); - if (!subplanVar) - elog(ERROR, "replace_vars_with_subplan_refs: variable not in target list"); + resdom = tlist_member((Node *) var, context->subplanTargetList); + if (!resdom) + elog(ERROR, "replace_vars_with_subplan_refs: variable not in subplan target list"); - /* - * Change the varno & varattno fields of the var node. - */ - var->varno = context->subvarno; - var->varattno = subplanVar->resdom->resno; - return false; + newvar->varno = context->subvarno; + newvar->varattno = resdom->resno; + return (Node *) newvar; + } + /* + * expression_tree_mutator will copy SubPlan nodes if given a chance. + * We do not want to do that here, because subselect.c has already + * constructed the initPlan and subPlan lists of the current plan node + * and we mustn't leave those dangling (ie, pointing to different + * copies of the nodes than what's in the targetlist & quals...) + * Instead, alter the SubPlan in-place. Grotty --- is there a better way? + */ + if (is_subplan(node)) + { + Expr *expr = (Expr *) node; + SubLink *sublink = ((SubPlan *) expr->oper)->sublink; + + /* transform args list (params to be passed to subplan) */ + expr->args = (List *) + replace_vars_with_subplan_refs_mutator((Node *) expr->args, + context); + /* transform sublink's oper list as well */ + sublink->oper = (List *) + replace_vars_with_subplan_refs_mutator((Node *) sublink->oper, + context); + + return (Node *) expr; } - return expression_tree_walker(node, - replace_vars_with_subplan_refs_walker, - (void *) context); + return expression_tree_mutator(node, + replace_vars_with_subplan_refs_mutator, + (void *) context); } /***************************************************************************** - * + * OPERATOR REGPROC LOOKUP *****************************************************************************/ -/*--------------------------------------------------------- - * - * set_agg_tlist_references - - * This routine has several responsibilities: - * * Update the target list of an Agg node so that it points to - * the tuples returned by its left tree subplan. - * * If there is a qual list (from a HAVING clause), similarly update - * vars in it to point to the subplan target list. - * - * The return value is TRUE if all qual clauses include Aggrefs, or FALSE - * if any do not (caller may choose to raise an error condition). - */ -bool -set_agg_tlist_references(Agg *aggNode) -{ - List *subplanTargetList; - List *tl; - List *ql; - bool all_quals_ok; - - subplanTargetList = aggNode->plan.lefttree->targetlist; - - foreach(tl, aggNode->plan.targetlist) - { - TargetEntry *tle = lfirst(tl); - - replace_vars_with_subplan_refs(tle->expr, - (Index) 0, - subplanTargetList); - } - - all_quals_ok = true; - foreach(ql, aggNode->plan.qual) - { - Node *qual = lfirst(ql); - - replace_vars_with_subplan_refs(qual, - (Index) 0, - subplanTargetList); - if (pull_agg_clause(qual) == NIL) - all_quals_ok = false; /* this qual clause has no agg - * functions! */ - } - - return all_quals_ok; -} - /* - * pull_agg_clause - * Recursively pulls all Aggref nodes from an expression clause. + * fix_opids + * Calculate opid field from opno for each Oper node in given tree. + * The given tree can be anything expression_tree_walker handles. * - * Returns list of Aggref nodes found. Note the nodes themselves are not - * copied, only referenced. + * The argument is modified in-place. (This is OK since we'd want the + * same change for any node, even if it gets visited more than once due to + * shared structure.) */ -List * -pull_agg_clause(Node *clause) -{ - List *result = NIL; - - pull_agg_clause_walker(clause, &result); - return result; -} - -static bool -pull_agg_clause_walker(Node *node, List **listptr) -{ - if (node == NULL) - return false; - if (IsA(node, Aggref)) - { - *listptr = lappend(*listptr, node); - return false; - } - return expression_tree_walker(node, pull_agg_clause_walker, - (void *) listptr); -} - -/* - * check_having_for_ungrouped_vars takes the havingQual and the list of - * GROUP BY clauses and checks for subplans in the havingQual that are being - * passed ungrouped variables as parameters. In other contexts, ungrouped - * vars in the havingQual will be detected by the parser (see parse_agg.c, - * exprIsAggOrGroupCol()). But that routine currently does not check subplans, - * because the necessary info is not computed until the planner runs. - * This ought to be cleaned up someday. - */ - void -check_having_for_ungrouped_vars(Node *clause, List *groupClause, - List *targetList) +fix_opids(Node *node) { - check_having_for_ungrouped_vars_context context; - - context.groupClause = groupClause; - context.targetList = targetList; - check_having_for_ungrouped_vars_walker(clause, &context); + /* This tree walk requires no special setup, so away we go... */ + fix_opids_walker(node, NULL); } static bool -check_having_for_ungrouped_vars_walker(Node *node, - check_having_for_ungrouped_vars_context *context) +fix_opids_walker (Node *node, void *context) { if (node == NULL) return false; - /* - * 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; - - foreach(t, ((Expr *) node)->args) - { - Node *thisarg = lfirst(t); - bool contained_in_group_clause = false; - List *gl; - - foreach(gl, context->groupClause) - { - GroupClause *gcl = lfirst(gl); - Node *groupexpr; - - groupexpr = get_sortgroupclause_expr(gcl, - context->targetList); - /* XXX is var_equal correct, or should we use equal()? */ - if (var_equal((Var *) thisarg, (Var *) groupexpr)) - { - contained_in_group_clause = true; - break; - } - } - - if (!contained_in_group_clause) - elog(ERROR, "Sub-SELECT in HAVING clause must use only GROUPed attributes from outer SELECT"); - } - } - return expression_tree_walker(node, - check_having_for_ungrouped_vars_walker, - (void *) context); + if (is_opclause(node)) + replace_opid((Oper *) ((Expr *) node)->oper); + return expression_tree_walker(node, fix_opids_walker, context); } diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index 188379c9a2d..c275b7adc45 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -6,7 +6,7 @@ * Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.22 1999/08/21 03:49:03 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.23 1999/08/22 20:14:49 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -48,29 +48,13 @@ int PlannerPlanId; /* to assign unique ID to subquery plans */ static int _new_param(Var *var, int varlevel) { - List *last; - int i = 0; + Var *paramVar = (Var *) copyObject(var); - if (PlannerParamVar == NULL) - last = PlannerParamVar = makeNode(List); - else - { - for (last = PlannerParamVar;;) - { - i++; - if (lnext(last) == NULL) - break; - last = lnext(last); - } - lnext(last) = makeNode(List); - last = lnext(last); - } + paramVar->varlevelsup = varlevel; - lnext(last) = NULL; - lfirst(last) = makeVar(var->varno, var->varattno, var->vartype, - var->vartypmod, varlevel, var->varnoold, var->varoattno); + PlannerParamVar = lappend(PlannerParamVar, paramVar); - return i; + return length(PlannerParamVar) - 1; } /* @@ -193,8 +177,7 @@ _make_subplan(SubLink *slink) List *rside = lnext(((Expr *) lfirst(lst))->args); TargetEntry *te = nth(i, plan->targetlist); Var *var = makeVar(0, 0, te->resdom->restype, - te->resdom->restypmod, - 0, 0, 0); + te->resdom->restypmod, 0); Param *prm = makeNode(Param); prm->paramkind = PARAM_EXEC; @@ -214,7 +197,7 @@ _make_subplan(SubLink *slink) } else if (node->parParam == NULL && slink->subLinkType == EXISTS_SUBLINK) { - Var *var = makeVar(0, 0, BOOLOID, -1, 0, 0, 0); + Var *var = makeVar(0, 0, BOOLOID, -1, 0); Param *prm = makeNode(Param); prm->paramkind = PARAM_EXEC; diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c index 2a9ddfc716a..95e5ddbc9db 100644 --- a/src/backend/optimizer/prep/preptlist.c +++ b/src/backend/optimizer/prep/preptlist.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.30 1999/08/21 03:49:05 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.31 1999/08/22 20:14:51 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -59,8 +59,6 @@ preprocess_targetlist(List *tlist, */ expanded_tlist = expand_targetlist(tlist, relid, command_type, result_relation); - /* XXX should the fix-opids be this early?? */ - fix_opids(expanded_tlist); t_list = copyObject(expanded_tlist); /* ------------------ @@ -87,7 +85,7 @@ preprocess_targetlist(List *tlist, 0, true); - var = makeVar(result_relation, -1, TIDOID, -1, 0, result_relation, -1); + var = makeVar(result_relation, -1, TIDOID, -1, 0); ctid = makeTargetEntry(resdom, (Node *) var); t_list = lappend(t_list, ctid); @@ -340,8 +338,8 @@ new_relation_targetlist(Oid relid, Index rt_index, NodeTag node_type) Var *temp_var; TargetEntry *temp_tle; - temp_var = makeVar(rt_index, attno, atttype, atttypmod, - 0, rt_index, attno); + temp_var = makeVar(rt_index, attno, atttype, + atttypmod, 0); temp_tle = makeTargetEntry(makeResdom(attno, atttype, diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index ca4353f6085..fbb5a98e83d 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.47 1999/08/16 02:17:56 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.48 1999/08/22 20:14:53 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -24,11 +24,19 @@ #include "nodes/plannodes.h" #include "optimizer/clauses.h" #include "optimizer/internal.h" +#include "optimizer/tlist.h" #include "optimizer/var.h" #include "utils/lsyscache.h" -static bool fix_opids_walker(Node *node, void *context); +typedef struct { + List *groupClause; + List *targetList; +} check_subplans_for_ungrouped_vars_context; + +static bool pull_agg_clause_walker(Node *node, List **listptr); +static bool check_subplans_for_ungrouped_vars_walker(Node *node, + check_subplans_for_ungrouped_vars_context *context); static int is_single_func(Node *node); @@ -351,12 +359,118 @@ pull_constant_clauses(List *quals, List **constantQual) else restqual = lcons(lfirst(q), restqual); } - freeList(quals); /* XXX seems a tad risky? */ *constantQual = constqual; return restqual; } /* + * pull_agg_clause + * Recursively pulls all Aggref nodes from an expression tree. + * + * Returns list of Aggref nodes found. Note the nodes themselves are not + * copied, only referenced. + */ +List * +pull_agg_clause(Node *clause) +{ + List *result = NIL; + + pull_agg_clause_walker(clause, &result); + return result; +} + +static bool +pull_agg_clause_walker(Node *node, List **listptr) +{ + if (node == NULL) + return false; + if (IsA(node, Aggref)) + { + *listptr = lappend(*listptr, node); + /* continue, to iterate over agg's arg as well (do nested aggregates + * actually work?) + */ + } + return expression_tree_walker(node, pull_agg_clause_walker, + (void *) listptr); +} + +/* + * check_subplans_for_ungrouped_vars + * Check for subplans that are being passed ungrouped variables as + * parameters; return TRUE if any are found. + * + * In most contexts, ungrouped variables will be detected by the parser (see + * parse_agg.c, exprIsAggOrGroupCol()). But that routine currently does not + * check subplans, because the necessary info is not computed until the + * planner runs. So we do it here, after we have processed the subplan. + * This ought to be cleaned up someday. + * + * 'clause' is the expression tree to be searched for subplans. + * 'groupClause' is the GROUP BY list (a list of GroupClause nodes). + * 'targetList' is the target list that the group clauses refer to. + */ +bool +check_subplans_for_ungrouped_vars(Node *clause, + List *groupClause, + List *targetList) +{ + check_subplans_for_ungrouped_vars_context context; + + context.groupClause = groupClause; + context.targetList = targetList; + return check_subplans_for_ungrouped_vars_walker(clause, &context); +} + +static bool +check_subplans_for_ungrouped_vars_walker(Node *node, + check_subplans_for_ungrouped_vars_context *context) +{ + if (node == NULL) + return false; + /* + * 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; + + foreach(t, ((Expr *) node)->args) + { + Node *thisarg = lfirst(t); + bool contained_in_group_clause = false; + List *gl; + + foreach(gl, context->groupClause) + { + GroupClause *gcl = lfirst(gl); + Node *groupexpr; + + groupexpr = get_sortgroupclause_expr(gcl, + context->targetList); + if (equal(thisarg, groupexpr)) + { + contained_in_group_clause = true; + break; + } + } + + if (!contained_in_group_clause) + return true; /* found an ungrouped argument */ + } + } + return expression_tree_walker(node, + check_subplans_for_ungrouped_vars_walker, + (void *) context); +} + + +/* * clause_relids_vars * Retrieves distinct relids and vars appearing within a clause. * @@ -417,31 +531,6 @@ NumRelids(Node *clause) } /* - * fix_opids - * Calculate opid field from opno for each Oper node in given tree. - * (The given tree can be anything expression_tree_walker handles.) - * - * Returns its argument, which has been modified in-place. - */ -List * -fix_opids(List *clauses) -{ - /* This tree walk requires no special setup, so away we go... */ - fix_opids_walker((Node *) clauses, NULL); - return clauses; -} - -static bool -fix_opids_walker (Node *node, void *context) -{ - if (node == NULL) - return false; - if (is_opclause(node)) - replace_opid((Oper *) ((Expr *) node)->oper); - return expression_tree_walker(node, fix_opids_walker, context); -} - -/* * get_relattval * Extract information from a restriction or join clause for * selectivity estimation. The inputs are an expression diff --git a/src/backend/optimizer/util/tlist.c b/src/backend/optimizer/util/tlist.c index 37a790cc3dd..dfe2963581f 100644 --- a/src/backend/optimizer/util/tlist.c +++ b/src/backend/optimizer/util/tlist.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/tlist.c,v 1.39 1999/08/21 03:49:07 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/tlist.c,v 1.40 1999/08/22 20:14:54 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -19,8 +19,6 @@ #include "optimizer/tlist.h" #include "optimizer/var.h" -static Node *unflatten_tlist_mutator(Node *node, List *flat_tlist); - /***************************************************************************** * ---------- RELATION node target list routines ---------- *****************************************************************************/ @@ -28,39 +26,38 @@ static Node *unflatten_tlist_mutator(Node *node, List *flat_tlist); /* * tlistentry_member * Finds the (first) member of the given tlist whose expression is - * var_equal() to the given var. Result is NULL if no such member. + * equal() to the given expression. Result is NULL if no such member. */ TargetEntry * -tlistentry_member(Var *var, List *targetlist) +tlistentry_member(Node *node, List *targetlist) { - if (var && IsA(var, Var)) + List *temp; + + foreach(temp, targetlist) { - List *temp; + TargetEntry *tlentry = (TargetEntry *) lfirst(temp); - foreach(temp, targetlist) - { - if (var_equal(var, get_expr(lfirst(temp)))) - return (TargetEntry *) lfirst(temp); - } + if (equal(node, tlentry->expr)) + return tlentry; } return NULL; } /* - * matching_tlist_var + * matching_tlist_expr * Same as tlistentry_member(), except returns the tlist expression * rather than its parent TargetEntry node. */ -Expr * -matching_tlist_var(Var *var, List *targetlist) +Node * +matching_tlist_expr(Node *node, List *targetlist) { TargetEntry *tlentry; - tlentry = tlistentry_member(var, targetlist); + tlentry = tlistentry_member(node, targetlist); if (tlentry) - return (Expr *) get_expr(tlentry); + return tlentry->expr; - return (Expr *) NULL; + return (Node *) NULL; } /* @@ -69,11 +66,11 @@ matching_tlist_var(Var *var, List *targetlist) * rather than its parent TargetEntry node. */ Resdom * -tlist_member(Var *var, List *tlist) +tlist_member(Node *node, List *targetlist) { TargetEntry *tlentry; - tlentry = tlistentry_member(var, tlist); + tlentry = tlistentry_member(node, targetlist); if (tlentry) return tlentry->resdom; @@ -89,7 +86,7 @@ tlist_member(Var *var, List *tlist) void add_var_to_tlist(RelOptInfo *rel, Var *var) { - if (! tlistentry_member(var, rel->targetlist)) + if (! tlistentry_member((Node *) var, rel->targetlist)) { /* XXX is copyObject necessary here? */ rel->targetlist = lappend(rel->targetlist, @@ -116,85 +113,11 @@ create_tl_element(Var *var, int resdomno) (Node *) var); } -/* - * get_actual_tlist - * Returns the targetlist elements from a relation tlist. - * - */ -List * -get_actual_tlist(List *tlist) -{ - - /* - * this function is not making sense. - ay 10/94 - */ -#ifdef NOT_USED - List *element = NIL; - List *result = NIL; - - if (tlist == NULL) - { - elog(DEBUG, "calling get_actual_tlist with empty tlist"); - return NIL; - } - - /* - * XXX - it is unclear to me what exactly get_entry should be doing, - * as it is unclear to me the exact relationship between "TL" "TLE" - * and joinlists - */ - - foreach(element, tlist) - result = lappend(result, lfirst((List *) lfirst(element))); - - return result; -#endif - return tlist; -} - /***************************************************************************** * ---------- GENERAL target list routines ---------- *****************************************************************************/ /* - * match_varid - * Searches a target list for an entry matching a given var. - * - * Returns the target list entry (resdom var) of the matching var, - * or NULL if no match. - */ -TargetEntry * -match_varid(Var *test_var, List *tlist) -{ - List *tl; - - Assert(test_var->varlevelsup == 0); /* XXX why? */ - - foreach(tl, tlist) - { - TargetEntry *entry = lfirst(tl); - Var *tlvar = get_expr(entry); - - if (!IsA(tlvar, Var)) - continue; - - /* - * we test the original varno, instead of varno which might be - * changed to INNER/OUTER. XXX is test on vartype necessary? - */ - Assert(tlvar->varlevelsup == 0); - - if (tlvar->varnoold == test_var->varnoold && - tlvar->varoattno == test_var->varoattno && - tlvar->vartype == test_var->vartype) - return entry; - } - - return NULL; -} - - -/* * new_unsorted_tlist * Creates a copy of a target list by creating new resdom nodes * without sort information. @@ -221,37 +144,6 @@ new_unsorted_tlist(List *targetlist) } /* - * copy_vars - * Replaces the var nodes in the first target list with those from - * the second target list. The two target lists are assumed to be - * identical except their actual resdoms and vars are different. - * - * 'target' is the target list to be replaced - * 'source' is the target list to be copied - * - * Returns a new target list. - * - */ -List * -copy_vars(List *target, List *source) -{ - List *result = NIL; - List *src; - List *dest; - - for (src = source, dest = target; - src != NIL && dest != NIL; - src = lnext(src), dest = lnext(dest)) - { - TargetEntry *temp = makeTargetEntry(((TargetEntry *) lfirst(dest))->resdom, - (Node *) get_expr(lfirst(src))); - - result = lappend(result, temp); - } - return result; -} - -/* * flatten_tlist * Create a target list that only contains unique variables. * @@ -292,7 +184,7 @@ add_to_flat_tlist(List *tlist, List *vars) { Var *var = lfirst(v); - if (! tlistentry_member(var, tlist)) + if (! tlistentry_member((Node *) var, tlist)) { Resdom *r; @@ -310,39 +202,6 @@ add_to_flat_tlist(List *tlist, List *vars) return tlist; } -/* - * unflatten_tlist - * Reconstructs the target list of a query by replacing vars within - * target expressions with vars from the 'flattened' target list. - * - * XXX is this really necessary? Why can't we just use the tlist as is? - * - * 'full_tlist' is the original target list - * 'flat_tlist' is the flattened (var-only) target list - * - * Returns the rebuilt target list. The original is not modified. - * - */ -List * -unflatten_tlist(List *full_tlist, List *flat_tlist) -{ - return (List *) unflatten_tlist_mutator((Node *) full_tlist, - flat_tlist); -} - -static Node * -unflatten_tlist_mutator(Node *node, List *flat_tlist) -{ - if (node == NULL) - return NULL; - if (IsA(node, Var)) - return (Node *) get_expr(match_varid((Var *) node, - flat_tlist)); - return expression_tree_mutator(node, unflatten_tlist_mutator, - (void *) flat_tlist); -} - - Var * get_expr(TargetEntry *tle) { diff --git a/src/backend/optimizer/util/var.c b/src/backend/optimizer/util/var.c index be181ea626f..a544041122b 100644 --- a/src/backend/optimizer/util/var.c +++ b/src/backend/optimizer/util/var.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.22 1999/08/10 03:00:15 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.23 1999/08/22 20:14:54 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -105,29 +105,3 @@ pull_var_clause_walker(Node *node, List **listptr) return expression_tree_walker(node, pull_var_clause_walker, (void *) listptr); } - -/* - * var_equal - * - * This is like equal() except that it does NOT test varnoold and - * varoattno. Also, it will not compare non-Var nodes. - * - * Returns t iff two var nodes correspond to the same attribute. - */ -bool -var_equal(Var *var1, Var *var2) -{ - if (var1 != NULL && IsA(var1, Var) && - var2 != NULL && IsA(var2, Var) && - var1->varno == var2->varno && - var1->varattno == var2->varattno && - var1->vartype == var2->vartype && - var1->vartypmod == var2->vartypmod && - var1->varlevelsup == var2->varlevelsup) - { - Assert(var1->varlevelsup == 0); /* XXX why do this here??? */ - return true; - } - else - return false; -} diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index 4a227de2c5b..19a287d99e8 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.53 1999/08/21 03:48:55 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.54 1999/08/22 20:15:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -444,7 +444,7 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs, */ toid = typeTypeId(typenameType(relname)); /* replace it in the arg list */ - lfirst(fargs) = makeVar(vnum, 0, toid, -1, 0, vnum, 0); + lfirst(fargs) = makeVar(vnum, 0, toid, -1, 0); } else if (!attisset) { /* set functions don't have parameters */ @@ -1300,7 +1300,7 @@ setup_tlist(char *attname, Oid relid) 0, InvalidOid, false); - varnode = makeVar(-1, attno, typeid, type_mod, 0, -1, attno); + varnode = makeVar(-1, attno, typeid, type_mod, 0); tle = makeTargetEntry(resnode, (Node *) varnode); return lcons(tle, NIL); @@ -1325,7 +1325,7 @@ setup_base_tlist(Oid typeid) 0, InvalidOid, false); - varnode = makeVar(-1, 1, typeid, -1, 0, -1, 1); + varnode = makeVar(-1, 1, typeid, -1, 0); tle = makeTargetEntry(resnode, (Node *) varnode); return lcons(tle, NIL); diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c index 80a8543d5a5..48da11d8d23 100644 --- a/src/backend/parser/parse_node.c +++ b/src/backend/parser/parse_node.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.29 1999/07/19 00:26:19 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.30 1999/08/22 20:15:04 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -216,8 +216,7 @@ make_var(ParseState *pstate, Oid relid, char *refname, vartypeid = get_atttype(relid, attid); type_mod = get_atttypmod(relid, attid); - varnode = makeVar(vnum, attid, vartypeid, type_mod, - sublevels_up, vnum, attid); + varnode = makeVar(vnum, attid, vartypeid, type_mod, sublevels_up); return varnode; } diff --git a/src/include/nodes/makefuncs.h b/src/include/nodes/makefuncs.h index 6a387fd5c5c..1aaead722d8 100644 --- a/src/include/nodes/makefuncs.h +++ b/src/include/nodes/makefuncs.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: makefuncs.h,v 1.20 1999/07/15 23:03:52 momjian Exp $ + * $Id: makefuncs.h,v 1.21 1999/08/22 20:15:00 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -25,9 +25,7 @@ extern Var *makeVar(Index varno, AttrNumber varattno, Oid vartype, int32 vartypmod, - Index varlevelsup, - Index varnoold, - AttrNumber varoattno); + Index varlevelsup); extern TargetEntry *makeTargetEntry(Resdom *resdom, Node *expr); diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index 4eea81446b2..10e51e40268 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: primnodes.h,v 1.34 1999/08/21 03:49:09 tgl Exp $ + * $Id: primnodes.h,v 1.35 1999/08/22 20:15:00 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -120,15 +120,23 @@ typedef struct Expr /* ---------------- * Var * varno - index of this var's relation in the range table - * (could be INNER or OUTER) + * (could also be INNER or OUTER) * varattno - attribute number of this var, or zero for all - * vartype - pg_type tuple oid for the type of this var + * vartype - pg_type tuple OID for the type of this var * vartypmod - pg_attribute typmod value - * varlevelsup - for subquery variables referencing outer relations - * varnoold - keep varno around in case it got changed to INNER/ - * OUTER (see match_varid) - * varoattno - attribute number of this var - * [ '(varnoold varoattno) was varid -ay 2/95] + * varlevelsup - for subquery variables referencing outer relations; + * 0 in a normal var, >0 means N levels up + * varnoold - original value of varno + * varoattno - original value of varattno + * + * Note: during parsing/planning, varnoold/varoattno are always just copies + * of varno/varattno. At the tail end of planning, Var nodes appearing in + * upper-level plan nodes are reassigned to point to the outputs of their + * subplans; for example, in a join node varno becomes INNER or OUTER and + * varattno becomes the index of the proper element of that subplan's target + * list. But varnoold/varoattno continue to hold the original values. + * The code doesn't really need varnoold/varoattno, but they are very useful + * for debugging and interpreting completed plans, so we keep them around. * ---------------- */ #define INNER 65000 @@ -145,8 +153,8 @@ typedef struct Var Oid vartype; int32 vartypmod; Index varlevelsup; /* erased by upper optimizer */ - Index varnoold; /* only used by optimizer */ - AttrNumber varoattno; /* only used by optimizer */ + Index varnoold; /* mainly for debugging --- see above */ + AttrNumber varoattno; } Var; /* ---------------- diff --git a/src/include/optimizer/clauses.h b/src/include/optimizer/clauses.h index ec2dce883fc..6ea6b4f97ea 100644 --- a/src/include/optimizer/clauses.h +++ b/src/include/optimizer/clauses.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: clauses.h,v 1.28 1999/08/16 02:17:44 tgl Exp $ + * $Id: clauses.h,v 1.29 1999/08/22 20:14:56 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -38,9 +38,13 @@ extern Expr *make_ands_explicit(List *andclauses); extern List *make_ands_implicit(Expr *clause); extern List *pull_constant_clauses(List *quals, List **constantQual); +extern List *pull_agg_clause(Node *clause); +extern bool check_subplans_for_ungrouped_vars(Node *clause, + List *groupClause, + List *targetList); + extern void clause_get_relids_vars(Node *clause, Relids *relids, List **vars); extern int NumRelids(Node *clause); -extern List *fix_opids(List *clauses); extern void get_relattval(Node *clause, int targetrelid, int *relid, AttrNumber *attno, Datum *constval, int *flag); @@ -53,8 +57,8 @@ extern bool expression_tree_walker(Node *node, bool (*walker) (), extern Node *expression_tree_mutator(Node *node, Node * (*mutator) (), void *context); -#define is_subplan(clause) ((Node*) (clause) != NULL && \ - nodeTag((Node*) (clause)) == T_Expr && \ - ((Expr *) (clause))->opType == SUBPLAN_EXPR) +#define is_subplan(clause) ((clause) != NULL && \ + IsA(clause, Expr) && \ + ((Expr *) (clause))->opType == SUBPLAN_EXPR) #endif /* CLAUSES_H */ diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h index 38ff367384b..3abda02d932 100644 --- a/src/include/optimizer/planmain.h +++ b/src/include/optimizer/planmain.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: planmain.h,v 1.31 1999/08/21 03:49:15 tgl Exp $ + * $Id: planmain.h,v 1.32 1999/08/22 20:14:56 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -27,14 +27,14 @@ extern Plan *query_planner(Query *root, * prototypes for plan/createplan.c */ extern Plan *create_plan(Path *best_path); -extern SeqScan *make_seqscan(List *qptlist, List *qpqual, Index scanrelid, - Plan *lefttree); +extern SeqScan *make_seqscan(List *qptlist, List *qpqual, Index scanrelid); extern Sort *make_sort(List *tlist, Oid nonameid, Plan *lefttree, int keycount); extern Agg *make_agg(List *tlist, Plan *lefttree); extern Group *make_group(List *tlist, bool tuplePerGroup, int ngrp, AttrNumber *grpColIdx, Plan *lefttree); extern Unique *make_unique(List *tlist, Plan *lefttree, char *uniqueAttr); +extern Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan); /* * prototypes for plan/initsplan.c @@ -47,17 +47,10 @@ extern void add_missing_vars_to_tlist(Query *root, List *tlist); /* * prototypes for plan/setrefs.c */ -extern void set_tlist_references(Plan *plan); +extern void set_plan_references(Plan *plan); extern List *join_references(List *clauses, List *outer_tlist, - List *inner_tlist); -extern void replace_tlist_with_subplan_refs(List *tlist, - Index subvarno, - List *subplanTargetList); -extern bool set_agg_tlist_references(Agg *aggNode); -extern List *pull_agg_clause(Node *clause); -extern void check_having_for_ungrouped_vars(Node *clause, - List *groupClause, - List *targetList); + List *inner_tlist, Index acceptable_rel); +extern void fix_opids(Node *node); /* * prep/prepkeyset.c diff --git a/src/include/optimizer/tlist.h b/src/include/optimizer/tlist.h index f0ddb9ac9dc..58b1b7b2e99 100644 --- a/src/include/optimizer/tlist.h +++ b/src/include/optimizer/tlist.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: tlist.h,v 1.21 1999/08/21 03:49:15 tgl Exp $ + * $Id: tlist.h,v 1.22 1999/08/22 20:14:57 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -15,20 +15,16 @@ #include "nodes/relation.h" -extern TargetEntry *tlistentry_member(Var *var, List *targetlist); -extern Expr *matching_tlist_var(Var *var, List *targetlist); +extern TargetEntry *tlistentry_member(Node *node, List *targetlist); +extern Node *matching_tlist_expr(Node *node, List *targetlist); +extern Resdom *tlist_member(Node *node, List *targetlist); + extern void add_var_to_tlist(RelOptInfo *rel, Var *var); extern TargetEntry *create_tl_element(Var *var, int resdomno); -extern List *get_actual_tlist(List *tlist); -extern Resdom *tlist_member(Var *var, List *tlist); -extern TargetEntry *match_varid(Var *test_var, List *tlist); extern List *new_unsorted_tlist(List *targetlist); -extern List *copy_vars(List *target, List *source); extern List *flatten_tlist(List *tlist); extern List *add_to_flat_tlist(List *tlist, List *vars); -extern List *unflatten_tlist(List *full_tlist, - List *flat_tlist); extern Var *get_expr(TargetEntry *tle); extern Node *get_sortgroupclause_expr(SortClause *sortClause, diff --git a/src/include/optimizer/var.h b/src/include/optimizer/var.h index 16f9f4f6634..440b62f49ad 100644 --- a/src/include/optimizer/var.h +++ b/src/include/optimizer/var.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: var.h,v 1.8 1999/07/15 15:21:23 momjian Exp $ + * $Id: var.h,v 1.9 1999/08/22 20:14:57 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -18,6 +18,5 @@ extern List *pull_varnos(Node *me); extern bool contain_var_clause(Node *clause); extern List *pull_var_clause(Node *clause); -extern bool var_equal(Var *var1, Var *var2); #endif /* VAR_H */ |