diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2012-08-07 19:02:54 -0400 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2012-08-07 19:02:54 -0400 |
commit | 5ebaaa49445eb1ba7b299bbea3a477d4e4c0430b (patch) | |
tree | 1a72c939a655e9acbba1c71a1831dd38ee41db95 /src/backend/optimizer/plan/createplan.c | |
parent | 5078be480412790e4f1b2aeda04f8c65fc7a3b93 (diff) | |
download | postgresql-5ebaaa49445eb1ba7b299bbea3a477d4e4c0430b.tar.gz postgresql-5ebaaa49445eb1ba7b299bbea3a477d4e4c0430b.zip |
Implement SQL-standard LATERAL subqueries.
This patch implements the standard syntax of LATERAL attached to a
sub-SELECT in FROM, and also allows LATERAL attached to a function in FROM,
since set-returning function calls are expected to be one of the principal
use-cases.
The main change here is a rewrite of the mechanism for keeping track of
which relations are visible for column references while the FROM clause is
being scanned. The parser "namespace" lists are no longer lists of bare
RTEs, but are lists of ParseNamespaceItem structs, which carry an RTE
pointer as well as some visibility-controlling flags. Aside from
supporting LATERAL correctly, this lets us get rid of the ancient hacks
that required rechecking subqueries and JOIN/ON and function-in-FROM
expressions for invalid references after they were initially parsed.
Invalid column references are now always correctly detected on sight.
In passing, remove assorted parser error checks that are now dead code by
virtue of our having gotten rid of add_missing_from, as well as some
comments that are obsolete for the same reason. (It was mainly
add_missing_from that caused so much fudging here in the first place.)
The planner support for this feature is very minimal, and will be improved
in future patches. It works well enough for testing purposes, though.
catversion bump forced due to new field in RangeTblEntry.
Diffstat (limited to 'src/backend/optimizer/plan/createplan.c')
-rw-r--r-- | src/backend/optimizer/plan/createplan.c | 111 |
1 files changed, 110 insertions, 1 deletions
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 414406bb8a1..6bb821fb385 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -84,6 +84,7 @@ static HashJoin *create_hashjoin_plan(PlannerInfo *root, HashPath *best_path, Plan *outer_plan, Plan *inner_plan); static Node *replace_nestloop_params(PlannerInfo *root, Node *expr); static Node *replace_nestloop_params_mutator(Node *node, PlannerInfo *root); +static void identify_nestloop_extparams(PlannerInfo *root, Plan *subplan); static List *fix_indexqual_references(PlannerInfo *root, IndexPath *index_path); static List *fix_indexorderby_references(PlannerInfo *root, IndexPath *index_path); static Node *fix_indexqual_operand(Node *node, IndexOptInfo *index, int indexcol); @@ -1640,6 +1641,7 @@ create_subqueryscan_plan(PlannerInfo *root, Path *best_path, { scan_clauses = (List *) replace_nestloop_params(root, (Node *) scan_clauses); + identify_nestloop_extparams(root, best_path->parent->subplan); } scan_plan = make_subqueryscan(tlist, @@ -1664,11 +1666,13 @@ create_functionscan_plan(PlannerInfo *root, Path *best_path, FunctionScan *scan_plan; Index scan_relid = best_path->parent->relid; RangeTblEntry *rte; + Node *funcexpr; /* it should be a function base rel... */ Assert(scan_relid > 0); rte = planner_rt_fetch(scan_relid, root); Assert(rte->rtekind == RTE_FUNCTION); + funcexpr = rte->funcexpr; /* Sort clauses into best execution order */ scan_clauses = order_qual_clauses(root, scan_clauses); @@ -1676,8 +1680,17 @@ create_functionscan_plan(PlannerInfo *root, Path *best_path, /* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */ scan_clauses = extract_actual_clauses(scan_clauses, false); + /* Replace any outer-relation variables with nestloop params */ + if (best_path->param_info) + { + scan_clauses = (List *) + replace_nestloop_params(root, (Node *) scan_clauses); + /* The func expression itself could contain nestloop params, too */ + funcexpr = replace_nestloop_params(root, funcexpr); + } + scan_plan = make_functionscan(tlist, scan_clauses, scan_relid, - rte->funcexpr, + funcexpr, rte->eref->colnames, rte->funccoltypes, rte->funccoltypmods, @@ -2560,6 +2573,102 @@ replace_nestloop_params_mutator(Node *node, PlannerInfo *root) } /* + * identify_nestloop_extparams + * Identify extParams of a parameterized subquery that need to be fed + * from an outer nestloop. + * + * The subplan's references to the outer variables are already represented + * as PARAM_EXEC Params, so we need not modify the subplan here. What we + * do need to do is add entries to root->curOuterParams to signal the parent + * nestloop plan node that it must provide these values. + */ +static void +identify_nestloop_extparams(PlannerInfo *root, Plan *subplan) +{ + Bitmapset *tmpset; + int paramid; + + /* Examine each extParam of the subquery's plan */ + tmpset = bms_copy(subplan->extParam); + while ((paramid = bms_first_member(tmpset)) >= 0) + { + PlannerParamItem *pitem = list_nth(root->glob->paramlist, paramid); + + /* Ignore anything coming from an upper query level */ + if (pitem->abslevel != root->query_level) + continue; + + if (IsA(pitem->item, Var)) + { + Var *var = (Var *) pitem->item; + NestLoopParam *nlp; + ListCell *lc; + + /* If not from a nestloop outer rel, nothing to do */ + if (!bms_is_member(var->varno, root->curOuterRels)) + continue; + /* Is this param already listed in root->curOuterParams? */ + foreach(lc, root->curOuterParams) + { + nlp = (NestLoopParam *) lfirst(lc); + if (nlp->paramno == paramid) + { + Assert(equal(var, nlp->paramval)); + /* Present, so nothing to do */ + break; + } + } + if (lc == NULL) + { + /* No, so add it */ + nlp = makeNode(NestLoopParam); + nlp->paramno = paramid; + nlp->paramval = copyObject(var); + root->curOuterParams = lappend(root->curOuterParams, nlp); + } + } + else if (IsA(pitem->item, PlaceHolderVar)) + { + PlaceHolderVar *phv = (PlaceHolderVar *) pitem->item; + NestLoopParam *nlp; + ListCell *lc; + + /* + * If not from a nestloop outer rel, nothing to do. We use + * bms_overlap as a cheap/quick test to see if the PHV might be + * evaluated in the outer rels, and then grab its PlaceHolderInfo + * to tell for sure. + */ + if (!bms_overlap(phv->phrels, root->curOuterRels)) + continue; + if (!bms_is_subset(find_placeholder_info(root, phv, false)->ph_eval_at, + root->curOuterRels)) + continue; + /* Is this param already listed in root->curOuterParams? */ + foreach(lc, root->curOuterParams) + { + nlp = (NestLoopParam *) lfirst(lc); + if (nlp->paramno == paramid) + { + Assert(equal(phv, nlp->paramval)); + /* Present, so nothing to do */ + break; + } + } + if (lc == NULL) + { + /* No, so add it */ + nlp = makeNode(NestLoopParam); + nlp->paramno = paramid; + nlp->paramval = copyObject(phv); + root->curOuterParams = lappend(root->curOuterParams, nlp); + } + } + } + bms_free(tmpset); +} + +/* * fix_indexqual_references * Adjust indexqual clauses to the form the executor's indexqual * machinery needs. |