mirror of
https://github.com/postgres/postgres.git
synced 2025-07-05 07:21:24 +03:00
Make NestLoop plan nodes pass outer-relation variables into their inner
relation using the general PARAM_EXEC executor parameter mechanism, rather than the ad-hoc kluge of passing the outer tuple down through ExecReScan. The previous method was hard to understand and could never be extended to handle parameters coming from multiple join levels. This patch doesn't change the set of possible plans nor have any significant performance effect, but it's necessary infrastructure for future generalization of the concept of an inner indexscan plan. ExecReScan's second parameter is now unused, so it's removed.
This commit is contained in:
@ -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(root, best_path->outerjoinpath);
|
||||
inner_plan = create_plan(root, best_path->innerjoinpath);
|
||||
outer_plan = create_plan_recurse(root, best_path->outerjoinpath);
|
||||
|
||||
/* 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);
|
||||
@ -2015,13 +2109,73 @@ 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;
|
||||
}
|
||||
|
@ -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))
|
||||
{
|
||||
@ -996,193 +999,6 @@ set_join_references(PlannerGlobal *glob, Join *join, int rtoffset)
|
||||
pfree(inner_itlist);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
@ -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.
|
||||
|
@ -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));
|
||||
child_params = finalize_plan(root,
|
||||
plan->lefttree,
|
||||
valid_params,
|
||||
scan_params);
|
||||
context.paramids = bms_add_members(context.paramids, child_params);
|
||||
|
||||
context.paramids = bms_add_members(context.paramids,
|
||||
finalize_plan(root,
|
||||
plan->righttree,
|
||||
valid_params,
|
||||
scan_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
|
||||
|
Reference in New Issue
Block a user