diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 836687240d..41918cda14 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -8,15 +8,15 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.110 2000/03/22 22:08:32 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.111 2000/04/04 01:21:48 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" +#include "optimizer/clauses.h" #include "optimizer/planmain.h" -#include "optimizer/subselect.h" /* @@ -88,9 +88,10 @@ CopyPlanFields(Plan *from, Plan *newnode) newnode->locParam = listCopy(from->locParam); newnode->chgParam = listCopy(from->chgParam); Node_Copy(from, newnode, initPlan); + /* subPlan list must point to subplans in the new subtree, not the old */ if (from->subPlan != NIL) - newnode->subPlan = nconc(SS_pull_subplan((Node *) newnode->targetlist), - SS_pull_subplan((Node *) newnode->qual)); + newnode->subPlan = nconc(pull_subplans((Node *) newnode->targetlist), + pull_subplans((Node *) newnode->qual)); else newnode->subPlan = NIL; newnode->nParamExec = from->nParamExec; @@ -142,7 +143,7 @@ _copyResult(Result *from) */ if (from->plan.subPlan != NIL) newnode->plan.subPlan = nconc(newnode->plan.subPlan, - SS_pull_subplan(newnode->resconstantqual)); + pull_subplans(newnode->resconstantqual)); return newnode; } @@ -252,6 +253,17 @@ _copyIndexScan(IndexScan *from) Node_Copy(from, newnode, indxqualorig); newnode->indxorderdir = from->indxorderdir; + /* + * We must add subplans in index quals to the new plan's subPlan list + */ + if (from->scan.plan.subPlan != NIL) + { + newnode->scan.plan.subPlan = nconc(newnode->scan.plan.subPlan, + pull_subplans((Node *) newnode->indxqual)); + newnode->scan.plan.subPlan = nconc(newnode->scan.plan.subPlan, + pull_subplans((Node *) newnode->indxqualorig)); + } + return newnode; } @@ -358,6 +370,13 @@ _copyMergeJoin(MergeJoin *from) */ Node_Copy(from, newnode, mergeclauses); + /* + * We must add subplans in mergeclauses to the new plan's subPlan list + */ + if (from->join.subPlan != NIL) + newnode->join.subPlan = nconc(newnode->join.subPlan, + pull_subplans((Node *) newnode->mergeclauses)); + return newnode; } @@ -382,9 +401,15 @@ _copyHashJoin(HashJoin *from) * ---------------- */ Node_Copy(from, newnode, hashclauses); - newnode->hashjoinop = from->hashjoinop; + /* + * We must add subplans in hashclauses to the new plan's subPlan list + */ + if (from->join.subPlan != NIL) + newnode->join.subPlan = nconc(newnode->join.subPlan, + pull_subplans((Node *) newnode->hashclauses)); + return newnode; } diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 8e20ae302e..3fab7f08b8 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.87 2000/03/22 22:08:34 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.88 2000/04/04 01:21:47 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -724,7 +724,9 @@ create_hashjoin_node(HashPath *best_path, * track of which index applies to each subgroup of index qual clauses... * * Returns a modified copy of the indexqual list --- the original is not - * changed. + * changed. Note also that the copy shares no substructure with the + * original; this is needed in case there is a subplan in it (we need + * two separate copies of the subplan tree, or things will go awry). */ static List * @@ -808,11 +810,13 @@ fix_indxqual_sublist(List *indexqual, int baserelid, Oid relam, get_relattval((Node *) clause, baserelid, &relid, &attno, &constval, &flag); - /* Copy enough structure to allow commuting and replacing an operand - * without changing original 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 + * full copy. */ - newclause = make_clause(clause->opType, clause->oper, - listCopy(clause->args)); + newclause = (Expr *) copyObject((Node *) clause); /* If the indexkey is on the right, commute the clause. */ if ((flag & SEL_RIGHT) == 0) @@ -834,11 +838,7 @@ fix_indxqual_sublist(List *indexqual, int baserelid, Oid relam, newopno = indexable_operator(newclause, opclass, relam, true); if (newopno == InvalidOid) elog(ERROR, "fix_indxqual_sublist: failed to find substitute op"); - if (newopno != ((Oper *) newclause->oper)->opno) - { - newclause->oper = (Node *) copyObject(newclause->oper); - ((Oper *) newclause->oper)->opno = newopno; - } + ((Oper *) newclause->oper)->opno = newopno; fixed_qual = lappend(fixed_qual, newclause); } diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index b44aa6408b..756333e005 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.60 2000/01/26 05:56:38 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.61 2000/04/04 01:21:47 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -59,6 +59,8 @@ static bool fix_opids_walker(Node *node, void *context); * 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 + * plan node's expression trees. * * set_plan_references recursively traverses the whole plan tree. * @@ -72,6 +74,11 @@ set_plan_references(Plan *plan) if (plan == NULL) return; + /* We must rebuild the plan's list of subplan nodes, since we are + * copying/mutating its expression trees. + */ + plan->subPlan = NIL; + /* * Plan-type-specific fixes */ @@ -83,6 +90,12 @@ set_plan_references(Plan *plan) case T_IndexScan: fix_opids((Node *) ((IndexScan *) plan)->indxqual); fix_opids((Node *) ((IndexScan *) plan)->indxqualorig); + plan->subPlan = + nconc(plan->subPlan, + pull_subplans((Node *) ((IndexScan *) plan)->indxqual)); + plan->subPlan = + nconc(plan->subPlan, + pull_subplans((Node *) ((IndexScan *) plan)->indxqualorig)); break; case T_NestLoop: set_join_references((Join *) plan); @@ -90,10 +103,16 @@ set_plan_references(Plan *plan) case T_MergeJoin: set_join_references((Join *) plan); fix_opids((Node *) ((MergeJoin *) plan)->mergeclauses); + plan->subPlan = + nconc(plan->subPlan, + 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)); break; case T_Material: case T_Sort: @@ -119,6 +138,9 @@ set_plan_references(Plan *plan) if (plan->lefttree != NULL) set_uppernode_references(plan, (Index) OUTER); fix_opids(((Result *) plan)->resconstantqual); + plan->subPlan = + nconc(plan->subPlan, + pull_subplans(((Result *) plan)->resconstantqual)); break; case T_Append: foreach(pl, ((Append *) plan)->appendplans) @@ -136,10 +158,17 @@ set_plan_references(Plan *plan) } /* - * For all plan types, fix operators in targetlist and qual expressions + * 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); + plan->subPlan = + nconc(plan->subPlan, + pull_subplans((Node *) plan->targetlist)); + plan->subPlan = + nconc(plan->subPlan, + pull_subplans((Node *) plan->qual)); /* * Now recurse into subplans, if any @@ -302,31 +331,7 @@ join_references_mutator(Node *node, */ 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... */ - } - /* - * 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 *) - 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 (Node *) newvar; } return expression_tree_mutator(node, join_references_mutator, @@ -383,30 +388,6 @@ replace_vars_with_subplan_refs_mutator(Node *node, 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_mutator(node, replace_vars_with_subplan_refs_mutator, (void *) context); diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index 3928f57836..b77d8b586f 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.33 2000/03/21 05:12:02 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.34 2000/04/04 01:21:47 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -442,6 +442,12 @@ set_unioni(List *l1, List *l2) * finalize_primnode: build lists of subplans and params appearing * in the given expression tree. NOTE: items are added to lists passed in, * so caller must initialize lists to NIL before first call! + * + * Note: the subplan list that is constructed here and assigned to the + * plan's subPlan field will be replaced with an up-to-date list in + * set_plan_references(). We could almost dispense with building this + * subplan list at all; I believe the only place that uses it is the + * check in make_subplan to see whether a subselect has any subselects. */ typedef struct finalize_primnode_results { @@ -604,6 +610,10 @@ SS_finalize_plan(Plan *plan) case T_IndexScan: finalize_primnode((Node *) ((IndexScan *) plan)->indxqual, &results); + /* 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. + */ break; case T_MergeJoin: @@ -670,32 +680,3 @@ SS_finalize_plan(Plan *plan) return results.paramids; } - -/* - * Construct a list of all subplans found within the given node tree. - */ - -static bool SS_pull_subplan_walker(Node *node, List **listptr); - -List * -SS_pull_subplan(Node *expr) -{ - List *result = NIL; - - SS_pull_subplan_walker(expr, &result); - return result; -} - -static bool -SS_pull_subplan_walker(Node *node, List **listptr) -{ - if (node == NULL) - return false; - if (is_subplan(node)) - { - *listptr = lappend(*listptr, ((Expr *) node)->oper); - /* fall through to check args to subplan */ - } - return expression_tree_walker(node, SS_pull_subplan_walker, - (void *) listptr); -} diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 8d4dae486a..d429db93a0 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.63 2000/03/21 05:12:12 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.64 2000/04/04 01:21:46 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -43,6 +43,8 @@ static bool contain_agg_clause_walker(Node *node, void *context); static bool pull_agg_clause_walker(Node *node, List **listptr); +static bool contain_subplans_walker(Node *node, void *context); +static bool pull_subplans_walker(Node *node, List **listptr); static bool check_subplans_for_ungrouped_vars_walker(Node *node, Query *context); static int is_single_func(Node *node); @@ -358,39 +360,9 @@ make_ands_implicit(Expr *clause) /***************************************************************************** - * * - * General clause-manipulating routines * - * * + * Aggregate-function clause manipulation *****************************************************************************/ - -/* - * pull_constant_clauses - * Scans through a list of qualifications and find those that - * contain no variables (of the current query level). - * - * Returns a list of the constant clauses in constantQual and the remaining - * quals as the return value. - * - */ -List * -pull_constant_clauses(List *quals, List **constantQual) -{ - List *q; - List *constqual = NIL; - List *restqual = NIL; - - foreach(q, quals) - { - if (!contain_var_clause(lfirst(q))) - constqual = lcons(lfirst(q), constqual); - else - restqual = lcons(lfirst(q), restqual); - } - *constantQual = constqual; - return restqual; -} - /* * contain_agg_clause * Recursively search for Aggref nodes within a clause. @@ -454,6 +426,68 @@ pull_agg_clause_walker(Node *node, List **listptr) (void *) listptr); } + +/***************************************************************************** + * Subplan clause manipulation + *****************************************************************************/ + +/* + * contain_subplans + * Recursively search for subplan nodes within a clause. + * + * If we see a SubLink node, we will return TRUE. This is only possible if + * the expression tree hasn't yet been transformed by subselect.c. We do not + * know whether the node will produce a true subplan or just an initplan, + * but we make the conservative assumption that it will be a subplan. + * + * Returns true if any subplan found. + */ +bool +contain_subplans(Node *clause) +{ + return contain_subplans_walker(clause, NULL); +} + +static bool +contain_subplans_walker(Node *node, void *context) +{ + if (node == NULL) + return false; + if (is_subplan(node) || IsA(node, SubLink)) + return true; /* abort the tree traversal and return true */ + return expression_tree_walker(node, contain_subplans_walker, context); +} + +/* + * pull_subplans + * Recursively pulls all subplans from an expression tree. + * + * Returns list of subplan nodes found. Note the nodes themselves are not + * copied, only referenced. + */ +List * +pull_subplans(Node *clause) +{ + List *result = NIL; + + pull_subplans_walker(clause, &result); + return result; +} + +static bool +pull_subplans_walker(Node *node, List **listptr) +{ + if (node == NULL) + return false; + if (is_subplan(node)) + { + *listptr = lappend(*listptr, ((Expr *) node)->oper); + /* fall through to check args to subplan */ + } + return expression_tree_walker(node, pull_subplans_walker, + (void *) listptr); +} + /* * check_subplans_for_ungrouped_vars * Check for subplans that are being passed ungrouped variables as @@ -556,6 +590,41 @@ check_subplans_for_ungrouped_vars_walker(Node *node, } +/***************************************************************************** + * * + * General clause-manipulating routines * + * * + *****************************************************************************/ + + +/* + * pull_constant_clauses + * Scans through a list of qualifications and find those that + * contain no variables (of the current query level). + * + * Returns a list of the constant clauses in constantQual and the remaining + * quals as the return value. + * + */ +List * +pull_constant_clauses(List *quals, List **constantQual) +{ + List *q; + List *constqual = NIL; + List *restqual = NIL; + + foreach(q, quals) + { + if (!contain_var_clause(lfirst(q))) + constqual = lcons(lfirst(q), constqual); + else + restqual = lcons(lfirst(q), restqual); + } + *constantQual = constqual; + return restqual; +} + + /* * clause_relids_vars * Retrieves distinct relids and vars appearing within a clause. @@ -726,7 +795,8 @@ default_results: * If the given expression is a function of a single relation, * return the relation number; else return 0 */ -static int is_single_func(Node *node) +static int +is_single_func(Node *node) { if (is_funcclause(node)) { diff --git a/src/include/optimizer/clauses.h b/src/include/optimizer/clauses.h index ef2bfeb63b..a6f6ea2e5f 100644 --- a/src/include/optimizer/clauses.h +++ b/src/include/optimizer/clauses.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: clauses.h,v 1.34 2000/03/21 05:11:51 tgl Exp $ + * $Id: clauses.h,v 1.35 2000/04/04 01:21:45 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -38,11 +38,15 @@ extern Expr *make_andclause(List *andclauses); extern Expr *make_ands_explicit(List *andclauses); extern List *make_ands_implicit(Expr *clause); -extern List *pull_constant_clauses(List *quals, List **constantQual); extern bool contain_agg_clause(Node *clause); extern List *pull_agg_clause(Node *clause); + +extern bool contain_subplans(Node *clause); +extern List *pull_subplans(Node *clause); extern void check_subplans_for_ungrouped_vars(Node *clause, Query *query); +extern List *pull_constant_clauses(List *quals, List **constantQual); + extern void clause_get_relids_vars(Node *clause, Relids *relids, List **vars); extern int NumRelids(Node *clause); extern void get_relattval(Node *clause, int targetrelid, diff --git a/src/include/optimizer/subselect.h b/src/include/optimizer/subselect.h index a00158deb9..f4e5112161 100644 --- a/src/include/optimizer/subselect.h +++ b/src/include/optimizer/subselect.h @@ -17,6 +17,5 @@ extern int PlannerPlanId; /* to assign unique ID to subquery plans */ extern List *SS_finalize_plan(Plan *plan); extern Node *SS_replace_correlation_vars(Node *expr); extern Node *SS_process_sublinks(Node *expr); -extern List *SS_pull_subplan(Node *expr); #endif /* SUBSELECT_H */