summaryrefslogtreecommitdiffstats
path: root/src/backend/optimizer/util/pathnode.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/backend/optimizer/util/pathnode.c175
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;
+}