diff options
Diffstat (limited to '')
-rw-r--r-- | src/backend/optimizer/util/pathnode.c | 175 |
1 files changed, 175 insertions, 0 deletions
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index 33affaf..46fd29b 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -26,6 +26,7 @@ #include "optimizer/optimizer.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" +#include "optimizer/placeholder.h" #include "optimizer/planmain.h" #include "optimizer/prep.h" #include "optimizer/restrictinfo.h" @@ -56,6 +57,10 @@ static int append_startup_cost_compare(const ListCell *a, const ListCell *b); static List *reparameterize_pathlist_by_child(PlannerInfo *root, List *pathlist, RelOptInfo *child_rel); +static bool contain_references_to(PlannerInfo *root, Node *clause, + Relids relids); +static bool ris_contain_references_to(PlannerInfo *root, List *rinfos, + Relids relids); /***************************************************************************** @@ -4052,6 +4057,40 @@ do { \ switch (nodeTag(path)) { case T_Path: + + /* + * If the path's restriction clauses contain lateral references to + * the other relation, we can't reparameterize, because we must + * not change the RelOptInfo's contents here. (Doing so would + * break things if we end up using a non-partitionwise join.) + */ + if (ris_contain_references_to(root, + path->parent->baserestrictinfo, + child_rel->top_parent_relids)) + return NULL; + + /* + * If it's a SampleScan with tablesample parameters referencing + * the other relation, we can't reparameterize, because we must + * not change the RTE's contents here. (Doing so would break + * things if we end up using a non-partitionwise join.) + */ + if (path->pathtype == T_SampleScan) + { + Index scan_relid = path->parent->relid; + RangeTblEntry *rte; + + /* it should be a base rel with a tablesample clause... */ + Assert(scan_relid > 0); + rte = planner_rt_fetch(scan_relid, root); + Assert(rte->rtekind == RTE_RELATION); + Assert(rte->tablesample != NULL); + + if (contain_references_to(root, (Node *) rte->tablesample, + child_rel->top_parent_relids)) + return NULL; + } + FLAT_COPY_PATH(new_path, path, Path); break; @@ -4059,6 +4098,18 @@ do { \ { IndexPath *ipath; + /* + * If the path's restriction clauses contain lateral + * references to the other relation, we can't reparameterize, + * because we must not change the IndexOptInfo's contents + * here. (Doing so would break things if we end up using a + * non-partitionwise join.) + */ + if (ris_contain_references_to(root, + path->parent->baserestrictinfo, + child_rel->top_parent_relids)) + return NULL; + FLAT_COPY_PATH(ipath, path, IndexPath); ADJUST_CHILD_ATTRS(ipath->indexclauses); new_path = (Path *) ipath; @@ -4069,6 +4120,18 @@ do { \ { BitmapHeapPath *bhpath; + /* + * If the path's restriction clauses contain lateral + * references to the other relation, we can't reparameterize, + * because we must not change the RelOptInfo's contents here. + * (Doing so would break things if we end up using a + * non-partitionwise join.) + */ + if (ris_contain_references_to(root, + path->parent->baserestrictinfo, + child_rel->top_parent_relids)) + return NULL; + FLAT_COPY_PATH(bhpath, path, BitmapHeapPath); REPARAMETERIZE_CHILD_PATH(bhpath->bitmapqual); new_path = (Path *) bhpath; @@ -4100,6 +4163,18 @@ do { \ ForeignPath *fpath; ReparameterizeForeignPathByChild_function rfpc_func; + /* + * If the path's restriction clauses contain lateral + * references to the other relation, we can't reparameterize, + * because we must not change the RelOptInfo's contents here. + * (Doing so would break things if we end up using a + * non-partitionwise join.) + */ + if (ris_contain_references_to(root, + path->parent->baserestrictinfo, + child_rel->top_parent_relids)) + return NULL; + FLAT_COPY_PATH(fpath, path, ForeignPath); if (fpath->fdw_outerpath) REPARAMETERIZE_CHILD_PATH(fpath->fdw_outerpath); @@ -4118,6 +4193,18 @@ do { \ { CustomPath *cpath; + /* + * If the path's restriction clauses contain lateral + * references to the other relation, we can't reparameterize, + * because we must not change the RelOptInfo's contents here. + * (Doing so would break things if we end up using a + * non-partitionwise join.) + */ + if (ris_contain_references_to(root, + path->parent->baserestrictinfo, + child_rel->top_parent_relids)) + return NULL; + FLAT_COPY_PATH(cpath, path, CustomPath); REPARAMETERIZE_CHILD_PATH_LIST(cpath->custom_paths); if (cpath->methods && @@ -4296,3 +4383,91 @@ reparameterize_pathlist_by_child(PlannerInfo *root, return result; } + +/* + * contain_references_to + * Detect whether any Vars or PlaceHolderVars in the given clause contain + * lateral references to the given 'relids'. + */ +static bool +contain_references_to(PlannerInfo *root, Node *clause, Relids relids) +{ + bool ret = false; + List *vars; + ListCell *lc; + + /* + * Examine all Vars and PlaceHolderVars used in the clause. + * + * By omitting the relevant flags, this also gives us a cheap sanity check + * that no aggregates or window functions appear in the clause. We don't + * expect any of those in scan-level restrictions or tablesamples. + */ + vars = pull_var_clause(clause, PVC_INCLUDE_PLACEHOLDERS); + foreach(lc, vars) + { + Node *node = (Node *) lfirst(lc); + + if (IsA(node, Var)) + { + Var *var = (Var *) node; + + if (bms_is_member(var->varno, relids)) + { + ret = true; + break; + } + } + else if (IsA(node, PlaceHolderVar)) + { + PlaceHolderVar *phv = (PlaceHolderVar *) node; + PlaceHolderInfo *phinfo = find_placeholder_info(root, phv, false); + + /* + * We should check both ph_eval_at (in case the PHV is to be + * computed at the other relation and then laterally referenced + * here) and ph_lateral (in case the PHV is to be evaluated here + * but contains lateral references to the other relation). The + * former case should not occur in baserestrictinfo clauses, but + * it can occur in tablesample clauses. + */ + if (bms_overlap(phinfo->ph_eval_at, relids) || + bms_overlap(phinfo->ph_lateral, relids)) + { + ret = true; + break; + } + } + else + Assert(false); + } + + list_free(vars); + + return ret; +} + +/* + * ris_contain_references_to + * Apply contain_references_to() to a list of RestrictInfos. + * + * We need extra code for this because pull_var_clause() can't descend + * through RestrictInfos. + */ +static bool +ris_contain_references_to(PlannerInfo *root, List *rinfos, Relids relids) +{ + ListCell *lc; + + foreach(lc, rinfos) + { + RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc); + + /* Pseudoconstant clauses can't contain any Vars or PHVs */ + if (rinfo->pseudoconstant) + continue; + if (contain_references_to(root, (Node *) rinfo->clause, relids)) + return true; + } + return false; +} |