diff options
Diffstat (limited to 'src/backend/optimizer')
-rw-r--r-- | src/backend/optimizer/plan/createplan.c | 201 | ||||
-rw-r--r-- | src/backend/optimizer/plan/setrefs.c | 238 | ||||
-rw-r--r-- | src/backend/optimizer/plan/subselect.c | 159 |
3 files changed, 318 insertions, 280 deletions
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 3d6f9f94faa..434babc5d85 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.275 2010/05/25 17:44:41 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.276 2010/07/12 17:01:05 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -28,6 +28,7 @@ #include "optimizer/planmain.h" #include "optimizer/predtest.h" #include "optimizer/restrictinfo.h" +#include "optimizer/subselect.h" #include "optimizer/tlist.h" #include "optimizer/var.h" #include "parser/parse_clause.h" @@ -35,6 +36,7 @@ #include "utils/lsyscache.h" +static Plan *create_plan_recurse(PlannerInfo *root, Path *best_path); static Plan *create_scan_plan(PlannerInfo *root, Path *best_path); static List *build_relation_tlist(RelOptInfo *rel); static bool use_physical_tlist(PlannerInfo *root, RelOptInfo *rel); @@ -72,7 +74,10 @@ static MergeJoin *create_mergejoin_plan(PlannerInfo *root, MergePath *best_path, Plan *outer_plan, Plan *inner_plan); static HashJoin *create_hashjoin_plan(PlannerInfo *root, HashPath *best_path, Plan *outer_plan, Plan *inner_plan); -static List *fix_indexqual_references(List *indexquals, IndexPath *index_path); +static Node *replace_nestloop_params(PlannerInfo *root, Node *expr); +static Node *replace_nestloop_params_mutator(Node *node, PlannerInfo *root); +static List *fix_indexqual_references(PlannerInfo *root, IndexPath *index_path, + List *indexquals); static List *get_switched_clauses(List *clauses, Relids outerrelids); static List *order_qual_clauses(PlannerInfo *root, List *clauses); static void copy_path_costsize(Plan *dest, Path *src); @@ -103,7 +108,7 @@ static WorkTableScan *make_worktablescan(List *qptlist, List *qpqual, static BitmapAnd *make_bitmap_and(List *bitmapplans); static BitmapOr *make_bitmap_or(List *bitmapplans); static NestLoop *make_nestloop(List *tlist, - List *joinclauses, List *otherclauses, + List *joinclauses, List *otherclauses, List *nestParams, Plan *lefttree, Plan *righttree, JoinType jointype); static HashJoin *make_hashjoin(List *tlist, @@ -133,8 +138,8 @@ static Material *make_material(Plan *lefttree); /* * create_plan - * Creates the access plan for a query by tracing backwards through the - * desired chain of pathnodes, starting at the node 'best_path'. For + * Creates the access plan for a query by recursively processing the + * desired tree of pathnodes, starting at the node 'best_path'. For * every pathnode found, we create a corresponding plan node containing * appropriate id, target list, and qualification information. * @@ -151,6 +156,29 @@ create_plan(PlannerInfo *root, Path *best_path) { Plan *plan; + /* Initialize this module's private workspace in PlannerInfo */ + root->curOuterRels = NULL; + root->curOuterParams = NIL; + + /* Recursively process the path tree */ + plan = create_plan_recurse(root, best_path); + + /* Check we successfully assigned all NestLoopParams to plan nodes */ + if (root->curOuterParams != NIL) + elog(ERROR, "failed to assign all NestLoopParams to plan nodes"); + + return plan; +} + +/* + * create_plan_recurse + * Recursive guts of create_plan(). + */ +static Plan * +create_plan_recurse(PlannerInfo *root, Path *best_path) +{ + Plan *plan; + switch (best_path->pathtype) { case T_SeqScan: @@ -477,9 +505,16 @@ create_join_plan(PlannerInfo *root, JoinPath *best_path) Plan *outer_plan; Plan *inner_plan; Plan *plan; + Relids saveOuterRels = root->curOuterRels; + + outer_plan = create_plan_recurse(root, best_path->outerjoinpath); - outer_plan = create_plan(root, best_path->outerjoinpath); - inner_plan = create_plan(root, best_path->innerjoinpath); + /* For a nestloop, include outer relids in curOuterRels for inner side */ + if (best_path->path.pathtype == T_NestLoop) + root->curOuterRels = bms_union(root->curOuterRels, + best_path->outerjoinpath->parent->relids); + + inner_plan = create_plan_recurse(root, best_path->innerjoinpath); switch (best_path->path.pathtype) { @@ -496,6 +531,10 @@ create_join_plan(PlannerInfo *root, JoinPath *best_path) inner_plan); break; case T_NestLoop: + /* Restore curOuterRels */ + bms_free(root->curOuterRels); + root->curOuterRels = saveOuterRels; + plan = (Plan *) create_nestloop_plan(root, (NestPath *) best_path, outer_plan, @@ -571,7 +610,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path) { Path *subpath = (Path *) lfirst(subpaths); - subplans = lappend(subplans, create_plan(root, subpath)); + subplans = lappend(subplans, create_plan_recurse(root, subpath)); } plan = make_append(subplans, tlist); @@ -616,7 +655,7 @@ create_material_plan(PlannerInfo *root, MaterialPath *best_path) Material *plan; Plan *subplan; - subplan = create_plan(root, best_path->subpath); + subplan = create_plan_recurse(root, best_path->subpath); /* We don't want any excess columns in the materialized tuples */ disuse_physical_tlist(subplan, best_path->subpath); @@ -650,7 +689,7 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path) int groupColPos; ListCell *l; - subplan = create_plan(root, best_path->subpath); + subplan = create_plan_recurse(root, best_path->subpath); /* Done if we don't need to do any actual unique-ifying */ if (best_path->umethod == UNIQUE_PATH_NOOP) @@ -904,7 +943,7 @@ create_indexscan_plan(PlannerInfo *root, * The executor needs a copy with the indexkey on the left of each clause * and with index attr numbers substituted for table ones. */ - fixed_indexquals = fix_indexqual_references(indexquals, best_path); + fixed_indexquals = fix_indexqual_references(root, best_path, indexquals); /* * If this is an innerjoin scan, the indexclauses will contain join @@ -975,6 +1014,22 @@ create_indexscan_plan(PlannerInfo *root, /* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */ qpqual = extract_actual_clauses(qpqual, false); + /* + * We have to replace any outer-relation variables with nestloop params + * in the indexqualorig and qpqual expressions. A bit annoying to have to + * do this separately from the processing in fix_indexqual_references --- + * rethink this when generalizing the inner indexscan support. But note + * we can't really do this earlier because it'd break the comparisons to + * predicates above ... (or would it? Those wouldn't have outer refs) + */ + if (best_path->isjoininner) + { + stripped_indexquals = (List *) + replace_nestloop_params(root, (Node *) stripped_indexquals); + qpqual = (List *) + replace_nestloop_params(root, (Node *) qpqual); + } + /* Finally ready to build the plan node */ scan_plan = make_indexscan(tlist, qpqual, @@ -1267,6 +1322,17 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual, *indexqual = lappend(*indexqual, pred); } } + /* + * Replace outer-relation variables with nestloop params, but only + * after doing the above comparisons to index predicates. + */ + if (ipath->isjoininner) + { + *qual = (List *) + replace_nestloop_params(root, (Node *) *qual); + *indexqual = (List *) + replace_nestloop_params(root, (Node *) *indexqual); + } } else { @@ -1572,11 +1638,16 @@ create_nestloop_plan(PlannerInfo *root, Plan *outer_plan, Plan *inner_plan) { + NestLoop *join_plan; List *tlist = build_relation_tlist(best_path->path.parent); List *joinrestrictclauses = best_path->joinrestrictinfo; List *joinclauses; List *otherclauses; - NestLoop *join_plan; + Relids outerrelids; + List *nestParams; + ListCell *cell; + ListCell *prev; + ListCell *next; /* * If the inner path is a nestloop inner indexscan, it might be using some @@ -1605,9 +1676,32 @@ create_nestloop_plan(PlannerInfo *root, otherclauses = NIL; } + /* + * Identify any nestloop parameters that should be supplied by this join + * node, and move them from root->curOuterParams to the nestParams list. + */ + outerrelids = best_path->outerjoinpath->parent->relids; + nestParams = NIL; + prev = NULL; + for (cell = list_head(root->curOuterParams); cell; cell = next) + { + NestLoopParam *nlp = (NestLoopParam *) lfirst(cell); + + next = lnext(cell); + if (bms_is_member(nlp->paramval->varno, outerrelids)) + { + root->curOuterParams = list_delete_cell(root->curOuterParams, + cell, prev); + nestParams = lappend(nestParams, nlp); + } + else + prev = cell; + } + join_plan = make_nestloop(tlist, joinclauses, otherclauses, + nestParams, outer_plan, inner_plan, best_path->jointype); @@ -2016,12 +2110,72 @@ create_hashjoin_plan(PlannerInfo *root, *****************************************************************************/ /* + * replace_nestloop_params + * Replace outer-relation Vars in the given expression with nestloop Params + * + * All Vars belonging to the relation(s) identified by root->curOuterRels + * are replaced by Params, and entries are added to root->curOuterParams if + * not already present. + */ +static Node * +replace_nestloop_params(PlannerInfo *root, Node *expr) +{ + /* No setup needed for tree walk, so away we go */ + return replace_nestloop_params_mutator(expr, root); +} + +static Node * +replace_nestloop_params_mutator(Node *node, PlannerInfo *root) +{ + if (node == NULL) + return NULL; + if (IsA(node, Var)) + { + Var *var = (Var *) node; + Param *param; + NestLoopParam *nlp; + ListCell *lc; + + /* Upper-level Vars should be long gone at this point */ + Assert(var->varlevelsup == 0); + /* If not to be replaced, we can just return the Var unmodified */ + if (!bms_is_member(var->varno, root->curOuterRels)) + return node; + /* Create a Param representing the Var */ + param = assign_nestloop_param(root, var); + /* Is this param already listed in root->curOuterParams? */ + foreach(lc, root->curOuterParams) + { + nlp = (NestLoopParam *) lfirst(lc); + if (nlp->paramno == param->paramid) + { + Assert(equal(var, nlp->paramval)); + /* Present, so we can just return the Param */ + return (Node *) param; + } + } + /* No, so add it */ + nlp = makeNode(NestLoopParam); + nlp->paramno = param->paramid; + nlp->paramval = var; + root->curOuterParams = lappend(root->curOuterParams, nlp); + /* And return the replacement Param */ + return (Node *) param; + } + return expression_tree_mutator(node, + replace_nestloop_params_mutator, + (void *) root); +} + +/* * fix_indexqual_references * Adjust indexqual clauses to the form the executor's indexqual * machinery needs. * - * We have three tasks here: + * We have four tasks here: * * Remove RestrictInfo nodes from the input clauses. + * * Replace any outer-relation Var nodes with nestloop Params. + * (XXX eventually, that responsibility should go elsewhere?) * * Index keys must be represented by Var nodes with varattno set to the * index's attribute number, not the attribute number in the original rel. * * If the index key is on the right, commute the clause to put it on the @@ -2033,7 +2187,8 @@ create_hashjoin_plan(PlannerInfo *root, * two separate copies of the subplan tree, or things will go awry). */ static List * -fix_indexqual_references(List *indexquals, IndexPath *index_path) +fix_indexqual_references(PlannerInfo *root, IndexPath *index_path, + List *indexquals) { IndexOptInfo *index = index_path->indexinfo; List *fixed_indexquals; @@ -2041,26 +2196,20 @@ fix_indexqual_references(List *indexquals, IndexPath *index_path) fixed_indexquals = NIL; - /* - * For each qual clause, commute if needed to put the indexkey operand on - * the left, and then fix its varattno. (We do not need to change the - * other side of the clause.) - */ foreach(l, indexquals) { RestrictInfo *rinfo = (RestrictInfo *) lfirst(l); - Expr *clause; + Node *clause; Assert(IsA(rinfo, RestrictInfo)); /* - * Make a copy that will become the fixed clause. + * Replace any outer-relation variables with nestloop params. * - * 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. + * This also makes a copy of the clause, so it's safe to modify it + * in-place below. */ - clause = (Expr *) copyObject((Node *) rinfo->clause); + clause = replace_nestloop_params(root, (Node *) rinfo->clause); if (IsA(clause, OpExpr)) { @@ -2765,6 +2914,7 @@ static NestLoop * make_nestloop(List *tlist, List *joinclauses, List *otherclauses, + List *nestParams, Plan *lefttree, Plan *righttree, JoinType jointype) @@ -2779,6 +2929,7 @@ make_nestloop(List *tlist, plan->righttree = righttree; node->join.jointype = jointype; node->join.joinqual = joinclauses; + node->nestParams = nestParams; return node; } diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 70be2e66f2d..450970e5c37 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.160 2010/02/26 02:00:45 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.161 2010/07/12 17:01:06 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -89,8 +89,6 @@ static Node *fix_scan_expr(PlannerGlobal *glob, Node *node, int rtoffset); static Node *fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context); static bool fix_scan_expr_walker(Node *node, fix_scan_expr_context *context); static void set_join_references(PlannerGlobal *glob, Join *join, int rtoffset); -static void set_inner_join_references(PlannerGlobal *glob, Plan *inner_plan, - indexed_tlist *outer_itlist); static void set_upper_references(PlannerGlobal *glob, Plan *plan, int rtoffset); static void set_dummy_tlist_references(Plan *plan, int rtoffset); static indexed_tlist *build_tlist_index(List *tlist); @@ -878,12 +876,11 @@ fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context) Assert(var->varlevelsup == 0); /* - * We should not see any Vars marked INNER, but in a nestloop inner - * scan there could be OUTER Vars. Leave them alone. + * We should not see any Vars marked INNER or OUTER. */ Assert(var->varno != INNER); - if (var->varno > 0 && var->varno != OUTER) - var->varno += context->rtoffset; + Assert(var->varno != OUTER); + var->varno += context->rtoffset; if (var->varnoold > 0) var->varnoold += context->rtoffset; return (Node *) var; @@ -927,10 +924,6 @@ fix_scan_expr_walker(Node *node, fix_scan_expr_context *context) * values to the result domain number of either the corresponding outer * or inner join tuple item. Also perform opcode lookup for these * expressions. and add regclass OIDs to glob->relationOids. - * - * In the case of a nestloop with inner indexscan, we will also need to - * apply the same transformation to any outer vars appearing in the - * quals of the child indexscan. set_inner_join_references does that. */ static void set_join_references(PlannerGlobal *glob, Join *join, int rtoffset) @@ -966,8 +959,18 @@ set_join_references(PlannerGlobal *glob, Join *join, int rtoffset) /* Now do join-type-specific stuff */ if (IsA(join, NestLoop)) { - /* This processing is split out to handle possible recursion */ - set_inner_join_references(glob, inner_plan, outer_itlist); + NestLoop *nl = (NestLoop *) join; + ListCell *lc; + + foreach(lc, nl->nestParams) + { + NestLoopParam *nlp = (NestLoopParam *) lfirst(lc); + + nlp->paramval = (Var *) fix_upper_expr(glob, + (Node *) nlp->paramval, + outer_itlist, + rtoffset); + } } else if (IsA(join, MergeJoin)) { @@ -997,193 +1000,6 @@ set_join_references(PlannerGlobal *glob, Join *join, int rtoffset) } /* - * set_inner_join_references - * Handle join references appearing in an inner indexscan's quals - * - * To handle bitmap-scan plan trees, we have to be able to recurse down - * to the bottom BitmapIndexScan nodes; likewise, appendrel indexscans - * require recursing through Append nodes. This is split out as a separate - * function so that it can recurse. - * - * Note we do *not* apply any rtoffset for non-join Vars; this is because - * the quals will be processed again by fix_scan_expr when the set_plan_refs - * recursion reaches the inner indexscan, and so we'd have done it twice. - */ -static void -set_inner_join_references(PlannerGlobal *glob, Plan *inner_plan, - indexed_tlist *outer_itlist) -{ - if (IsA(inner_plan, IndexScan)) - { - /* - * 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 side of the join. - */ - IndexScan *innerscan = (IndexScan *) inner_plan; - List *indexqualorig = innerscan->indexqualorig; - - /* No work needed if indexqual refers only to its own rel... */ - if (NumRelids((Node *) indexqualorig) > 1) - { - Index innerrel = innerscan->scan.scanrelid; - - /* only refs to outer vars get changed in the inner qual */ - innerscan->indexqualorig = fix_join_expr(glob, - indexqualorig, - outer_itlist, - NULL, - innerrel, - 0); - innerscan->indexqual = fix_join_expr(glob, - innerscan->indexqual, - outer_itlist, - NULL, - innerrel, - 0); - - /* - * We must fix the inner qpqual too, if it has join clauses (this - * could happen if special operators are involved: some indexquals - * may get rechecked as qpquals). - */ - if (NumRelids((Node *) inner_plan->qual) > 1) - inner_plan->qual = fix_join_expr(glob, - inner_plan->qual, - outer_itlist, - NULL, - innerrel, - 0); - } - } - else if (IsA(inner_plan, BitmapIndexScan)) - { - /* - * Same, but index is being used within a bitmap plan. - */ - BitmapIndexScan *innerscan = (BitmapIndexScan *) inner_plan; - List *indexqualorig = innerscan->indexqualorig; - - /* No work needed if indexqual refers only to its own rel... */ - if (NumRelids((Node *) indexqualorig) > 1) - { - Index innerrel = innerscan->scan.scanrelid; - - /* only refs to outer vars get changed in the inner qual */ - innerscan->indexqualorig = fix_join_expr(glob, - indexqualorig, - outer_itlist, - NULL, - innerrel, - 0); - innerscan->indexqual = fix_join_expr(glob, - innerscan->indexqual, - outer_itlist, - NULL, - innerrel, - 0); - /* no need to fix inner qpqual */ - Assert(inner_plan->qual == NIL); - } - } - else if (IsA(inner_plan, BitmapHeapScan)) - { - /* - * The inner side is a bitmap scan plan. Fix the top node, and - * recurse to get the lower nodes. - * - * Note: create_bitmap_scan_plan removes clauses from bitmapqualorig - * if they are duplicated in qpqual, so must test these independently. - */ - BitmapHeapScan *innerscan = (BitmapHeapScan *) inner_plan; - Index innerrel = innerscan->scan.scanrelid; - List *bitmapqualorig = innerscan->bitmapqualorig; - - /* only refs to outer vars get changed in the inner qual */ - if (NumRelids((Node *) bitmapqualorig) > 1) - innerscan->bitmapqualorig = fix_join_expr(glob, - bitmapqualorig, - outer_itlist, - NULL, - innerrel, - 0); - - /* - * We must fix the inner qpqual too, if it has join clauses (this - * could happen if special operators are involved: some indexquals may - * get rechecked as qpquals). - */ - if (NumRelids((Node *) inner_plan->qual) > 1) - inner_plan->qual = fix_join_expr(glob, - inner_plan->qual, - outer_itlist, - NULL, - innerrel, - 0); - - /* Now recurse */ - set_inner_join_references(glob, inner_plan->lefttree, outer_itlist); - } - else if (IsA(inner_plan, BitmapAnd)) - { - /* All we need do here is recurse */ - BitmapAnd *innerscan = (BitmapAnd *) inner_plan; - ListCell *l; - - foreach(l, innerscan->bitmapplans) - { - set_inner_join_references(glob, (Plan *) lfirst(l), outer_itlist); - } - } - else if (IsA(inner_plan, BitmapOr)) - { - /* All we need do here is recurse */ - BitmapOr *innerscan = (BitmapOr *) inner_plan; - ListCell *l; - - foreach(l, innerscan->bitmapplans) - { - set_inner_join_references(glob, (Plan *) lfirst(l), outer_itlist); - } - } - else if (IsA(inner_plan, TidScan)) - { - TidScan *innerscan = (TidScan *) inner_plan; - Index innerrel = innerscan->scan.scanrelid; - - innerscan->tidquals = fix_join_expr(glob, - innerscan->tidquals, - outer_itlist, - NULL, - innerrel, - 0); - } - else if (IsA(inner_plan, Append)) - { - /* - * The inner side is an append plan. Recurse to see if it contains - * indexscans that need to be fixed. - */ - Append *appendplan = (Append *) inner_plan; - ListCell *l; - - foreach(l, appendplan->appendplans) - { - set_inner_join_references(glob, (Plan *) lfirst(l), outer_itlist); - } - } - else if (IsA(inner_plan, Result)) - { - /* Recurse through a gating Result node (similar to Append case) */ - Result *result = (Result *) inner_plan; - - if (result->plan.lefttree) - set_inner_join_references(glob, result->plan.lefttree, outer_itlist); - } -} - -/* * set_upper_references * Update the targetlist and quals of an upper-level plan node * to refer to the tuples returned by its lefttree subplan. @@ -1532,23 +1348,21 @@ search_indexed_tlist_for_sortgroupref(Node *node, * * 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, while Vars of the inner relation should be adjusted by rtoffset. - * (We also implement RETURNING clause fixup using this second scenario.) + * and a RETURNING clause, which may contain both Vars of the target relation + * and Vars of other relations. In the latter case we want to replace the + * other-relation Vars by OUTER references, while leaving target Vars alone. * * 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_itlist = NULL and acceptable_rel = the (not-offseted-yet) ID - * of the inner relation. + * match a Var will be reported as an error. For the RETURNING case, pass + * inner_itlist = NULL and acceptable_rel = the ID of the target relation. * * 'clauses' is the targetlist or list of join clauses * 'outer_itlist' is the indexed target list of the outer join relation * 'inner_itlist' is the indexed target list of the inner join relation, * or NULL * 'acceptable_rel' is either zero or the rangetable index of a relation - * whose Vars may appear in the clause without provoking an error. - * 'rtoffset' is what to add to varno for Vars of acceptable_rel. + * whose Vars may appear in the clause without provoking an error + * 'rtoffset': how much to increment varnoold by * * Returns the new expression tree. The original clause structure is * not modified. @@ -1603,8 +1417,8 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context) if (var->varno == context->acceptable_rel) { var = copyVar(var); - var->varno += context->rtoffset; - var->varnoold += context->rtoffset; + if (var->varnoold > 0) + var->varnoold += context->rtoffset; return (Node *) var; } @@ -1783,7 +1597,7 @@ set_returning_clause_references(PlannerGlobal *glob, /* * We can perform the desired Var fixup by abusing the fix_join_expr - * machinery that normally handles inner indexscan fixup. We search the + * machinery that formerly handled inner indexscan fixup. We search the * top plan's targetlist for Vars of non-result relations, and use * fix_join_expr to convert RETURNING Vars into references to those tlist * entries, while leaving result-rel Vars as-is. diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index cf503d51135..b94419a09dc 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 - * $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.162 2010/04/19 00:55:25 rhaas Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.163 2010/07/12 17:01:06 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -83,30 +83,20 @@ static bool finalize_primnode(Node *node, finalize_primnode_context *context); /* - * Generate a Param node to replace the given Var, - * which is expected to have varlevelsup > 0 (ie, it is not local). + * Select a PARAM_EXEC number to identify the given Var. + * If the Var already has a param slot, return that one. */ -static Param * -replace_outer_var(PlannerInfo *root, Var *var) +static int +assign_param_for_var(PlannerInfo *root, Var *var) { - Param *retval; ListCell *ppl; PlannerParamItem *pitem; Index abslevel; int i; - Assert(var->varlevelsup > 0 && var->varlevelsup < root->query_level); abslevel = root->query_level - var->varlevelsup; - /* - * If there's already a paramlist entry for this same Var, just use it. - * NOTE: in sufficiently complex querytrees, it is possible for the same - * varno/abslevel to refer to different RTEs in different parts of the - * parsetree, so that different fields might end up sharing the same Param - * number. As long as we check the vartype/typmod as well, I believe that - * this sort of aliasing will cause no trouble. The correct field should - * get stored into the Param slot at execution in each part of the tree. - */ + /* If there's already a paramlist entry for this same Var, just use it */ i = 0; foreach(ppl, root->glob->paramlist) { @@ -119,24 +109,76 @@ replace_outer_var(PlannerInfo *root, Var *var) pvar->varattno == var->varattno && pvar->vartype == var->vartype && pvar->vartypmod == var->vartypmod) - break; + return i; } i++; } - if (!ppl) - { - /* Nope, so make a new one */ - var = (Var *) copyObject(var); - var->varlevelsup = 0; + /* Nope, so make a new one */ + var = (Var *) copyObject(var); + var->varlevelsup = 0; - pitem = makeNode(PlannerParamItem); - pitem->item = (Node *) var; - pitem->abslevel = abslevel; + pitem = makeNode(PlannerParamItem); + pitem->item = (Node *) var; + pitem->abslevel = abslevel; - root->glob->paramlist = lappend(root->glob->paramlist, pitem); - /* i is already the correct index for the new item */ - } + root->glob->paramlist = lappend(root->glob->paramlist, pitem); + + /* i is already the correct list index for the new item */ + return i; +} + +/* + * Generate a Param node to replace the given Var, + * which is expected to have varlevelsup > 0 (ie, it is not local). + */ +static Param * +replace_outer_var(PlannerInfo *root, Var *var) +{ + Param *retval; + int i; + + Assert(var->varlevelsup > 0 && var->varlevelsup < root->query_level); + + /* + * Find the Var in root->glob->paramlist, or add it if not present. + * + * NOTE: in sufficiently complex querytrees, it is possible for the same + * varno/abslevel to refer to different RTEs in different parts of the + * parsetree, so that different fields might end up sharing the same Param + * number. As long as we check the vartype/typmod as well, I believe that + * this sort of aliasing will cause no trouble. The correct field should + * get stored into the Param slot at execution in each part of the tree. + */ + i = assign_param_for_var(root, var); + + retval = makeNode(Param); + retval->paramkind = PARAM_EXEC; + retval->paramid = i; + retval->paramtype = var->vartype; + retval->paramtypmod = var->vartypmod; + retval->location = -1; + + return retval; +} + +/* + * Generate a Param node to replace the given Var, which will be supplied + * from an upper NestLoop join node. + * + * Because we allow nestloop and subquery Params to alias each other, + * this is effectively the same as replace_outer_var, except that we expect + * the Var to be local to the current query level. + */ +Param * +assign_nestloop_param(PlannerInfo *root, Var *var) +{ + Param *retval; + int i; + + Assert(var->varlevelsup == 0); + + i = assign_param_for_var(root, var); retval = makeNode(Param); retval->paramkind = PARAM_EXEC; @@ -1773,8 +1815,9 @@ SS_finalize_plan(PlannerInfo *root, Plan *plan, bool attach_initplans) * * Note: this is a bit overly generous since some parameters of upper * query levels might belong to query subtrees that don't include this - * query. However, valid_params is only a debugging crosscheck, so it - * doesn't seem worth expending lots of cycles to try to be exact. + * query, or might be nestloop params that won't be passed down at all. + * However, valid_params is only a debugging crosscheck, so it doesn't + * seem worth expending lots of cycles to try to be exact. */ valid_params = bms_copy(initSetParam); paramid = 0; @@ -1849,6 +1892,8 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params, { finalize_primnode_context context; int locally_added_param; + Bitmapset *nestloop_params; + Bitmapset *child_params; if (plan == NULL) return NULL; @@ -1856,6 +1901,7 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params, context.root = root; context.paramids = NULL; /* initialize set to empty */ locally_added_param = -1; /* there isn't one */ + nestloop_params = NULL; /* there aren't any */ /* * When we call finalize_primnode, context.paramids sets are automatically @@ -2056,8 +2102,20 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params, break; case T_NestLoop: - finalize_primnode((Node *) ((Join *) plan)->joinqual, - &context); + { + ListCell *l; + + finalize_primnode((Node *) ((Join *) plan)->joinqual, + &context); + /* collect set of params that will be passed to right child */ + foreach(l, ((NestLoop *) plan)->nestParams) + { + NestLoopParam *nlp = (NestLoopParam *) lfirst(l); + + nestloop_params = bms_add_member(nestloop_params, + nlp->paramno); + } + } break; case T_MergeJoin: @@ -2120,17 +2178,32 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params, } /* Process left and right child plans, if any */ - context.paramids = bms_add_members(context.paramids, - finalize_plan(root, - plan->lefttree, - valid_params, - scan_params)); - - context.paramids = bms_add_members(context.paramids, - finalize_plan(root, - plan->righttree, - valid_params, - scan_params)); + child_params = finalize_plan(root, + plan->lefttree, + valid_params, + scan_params); + context.paramids = bms_add_members(context.paramids, child_params); + + if (nestloop_params) + { + /* right child can reference nestloop_params as well as valid_params */ + child_params = finalize_plan(root, + plan->righttree, + bms_union(nestloop_params, valid_params), + scan_params); + /* ... and they don't count as parameters used at my level */ + child_params = bms_difference(child_params, nestloop_params); + bms_free(nestloop_params); + } + else + { + /* easy case */ + child_params = finalize_plan(root, + plan->righttree, + valid_params, + scan_params); + } + context.paramids = bms_add_members(context.paramids, child_params); /* * Any locally generated parameter doesn't count towards its generating |