aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer/util/placeholder.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/util/placeholder.c')
-rw-r--r--src/backend/optimizer/util/placeholder.c123
1 files changed, 119 insertions, 4 deletions
diff --git a/src/backend/optimizer/util/placeholder.c b/src/backend/optimizer/util/placeholder.c
index 72b9977022b..af10dbd124f 100644
--- a/src/backend/optimizer/util/placeholder.c
+++ b/src/backend/optimizer/util/placeholder.c
@@ -23,17 +23,32 @@
#include "optimizer/planmain.h"
#include "utils/lsyscache.h"
+
+typedef struct contain_placeholder_references_context
+{
+ int relid;
+ int sublevels_up;
+} contain_placeholder_references_context;
+
/* Local functions */
static void find_placeholders_recurse(PlannerInfo *root, Node *jtnode);
static void find_placeholders_in_expr(PlannerInfo *root, Node *expr);
+static bool contain_placeholder_references_walker(Node *node,
+ contain_placeholder_references_context *context);
/*
* make_placeholder_expr
* Make a PlaceHolderVar for the given expression.
*
- * phrels is the syntactic location (as a set of baserels) to attribute
+ * phrels is the syntactic location (as a set of relids) to attribute
* to the expression.
+ *
+ * The caller is responsible for adjusting phlevelsup and phnullingrels
+ * as needed. Because we do not know here which query level the PHV
+ * will be associated with, it's important that this function touches
+ * only root->glob; messing with other parts of PlannerInfo would be
+ * likely to do the wrong thing.
*/
PlaceHolderVar *
make_placeholder_expr(PlannerInfo *root, Expr *expr, Relids phrels)
@@ -42,8 +57,9 @@ make_placeholder_expr(PlannerInfo *root, Expr *expr, Relids phrels)
phv->phexpr = expr;
phv->phrels = phrels;
+ phv->phnullingrels = NULL; /* caller may change this later */
phv->phid = ++(root->glob->lastPHId);
- phv->phlevelsup = 0;
+ phv->phlevelsup = 0; /* caller may change this later */
return phv;
}
@@ -93,6 +109,15 @@ find_placeholder_info(PlannerInfo *root, PlaceHolderVar *phv)
phinfo->ph_var = copyObject(phv);
/*
+ * By convention, phinfo->ph_var->phnullingrels is always empty, since the
+ * PlaceHolderInfo represents the initially-calculated state of the
+ * PlaceHolderVar. PlaceHolderVars appearing in the query tree might have
+ * varying values of phnullingrels, reflecting outer joins applied above
+ * the calculation level.
+ */
+ phinfo->ph_var->phnullingrels = NULL;
+
+ /*
* Any referenced rels that are outside the PHV's syntactic scope are
* LATERAL references, which should be included in ph_lateral but not in
* ph_eval_at. If no referenced rels are within the syntactic scope,
@@ -339,6 +364,8 @@ update_placeholder_eval_levels(PlannerInfo *root, SpecialJoinInfo *new_sjinfo)
sjinfo->min_lefthand);
eval_at = bms_add_members(eval_at,
sjinfo->min_righthand);
+ if (sjinfo->ojrelid)
+ eval_at = bms_add_member(eval_at, sjinfo->ojrelid);
/* we'll need another iteration */
found_some = true;
}
@@ -413,6 +440,14 @@ add_placeholders_to_base_rels(PlannerInfo *root)
{
RelOptInfo *rel = find_base_rel(root, varno);
+ /*
+ * As in add_vars_to_targetlist(), a value computed at scan level
+ * has not yet been nulled by any outer join, so its phnullingrels
+ * should be empty.
+ */
+ Assert(phinfo->ph_var->phnullingrels == NULL);
+
+ /* Copying the PHV might be unnecessary here, but be safe */
rel->reltarget->exprs = lappend(rel->reltarget->exprs,
copyObject(phinfo->ph_var));
/* reltarget's cost and width fields will be updated later */
@@ -435,7 +470,8 @@ add_placeholders_to_base_rels(PlannerInfo *root)
*/
void
add_placeholders_to_joinrel(PlannerInfo *root, RelOptInfo *joinrel,
- RelOptInfo *outer_rel, RelOptInfo *inner_rel)
+ RelOptInfo *outer_rel, RelOptInfo *inner_rel,
+ SpecialJoinInfo *sjinfo)
{
Relids relids = joinrel->relids;
ListCell *lc;
@@ -466,9 +502,17 @@ add_placeholders_to_joinrel(PlannerInfo *root, RelOptInfo *joinrel,
if (!bms_is_subset(phinfo->ph_eval_at, outer_rel->relids) &&
!bms_is_subset(phinfo->ph_eval_at, inner_rel->relids))
{
- PlaceHolderVar *phv = phinfo->ph_var;
+ /* Copying might be unnecessary here, but be safe */
+ PlaceHolderVar *phv = copyObject(phinfo->ph_var);
QualCost cost;
+ /*
+ * It'll start out not nulled by anything. Joins above
+ * this one might add to its phnullingrels later, in much
+ * the same way as for Vars.
+ */
+ Assert(phv->phnullingrels == NULL);
+
joinrel->reltarget->exprs = lappend(joinrel->reltarget->exprs,
phv);
cost_qual_eval_node(&cost, (Node *) phv->phexpr, root);
@@ -499,3 +543,74 @@ add_placeholders_to_joinrel(PlannerInfo *root, RelOptInfo *joinrel,
}
}
}
+
+/*
+ * contain_placeholder_references_to
+ * Detect whether any PlaceHolderVars in the given clause contain
+ * references to the given relid (typically an OJ relid).
+ *
+ * "Contain" means that there's a use of the relid inside the PHV's
+ * contained expression, so that changing the nullability status of
+ * the rel might change what the PHV computes.
+ *
+ * The code here to cope with upper-level PHVs is likely dead, but keep it
+ * anyway just in case.
+ */
+bool
+contain_placeholder_references_to(PlannerInfo *root, Node *clause,
+ int relid)
+{
+ contain_placeholder_references_context context;
+
+ /* We can answer quickly in the common case that there's no PHVs at all */
+ if (root->glob->lastPHId == 0)
+ return false;
+ /* Else run the recursive search */
+ context.relid = relid;
+ context.sublevels_up = 0;
+ return contain_placeholder_references_walker(clause, &context);
+}
+
+static bool
+contain_placeholder_references_walker(Node *node,
+ contain_placeholder_references_context *context)
+{
+ if (node == NULL)
+ return false;
+ if (IsA(node, PlaceHolderVar))
+ {
+ PlaceHolderVar *phv = (PlaceHolderVar *) node;
+
+ /* We should just look through PHVs of other query levels */
+ if (phv->phlevelsup == context->sublevels_up)
+ {
+ /* If phrels matches, we found what we came for */
+ if (bms_is_member(context->relid, phv->phrels))
+ return true;
+
+ /*
+ * We should not examine phnullingrels: what we are looking for is
+ * references in the contained expression, not OJs that might null
+ * the result afterwards. Also, we don't need to recurse into the
+ * contained expression, because phrels should adequately
+ * summarize what's in there. So we're done here.
+ */
+ return false;
+ }
+ }
+ else if (IsA(node, Query))
+ {
+ /* Recurse into RTE subquery or not-yet-planned sublink subquery */
+ bool result;
+
+ context->sublevels_up++;
+ result = query_tree_walker((Query *) node,
+ contain_placeholder_references_walker,
+ context,
+ 0);
+ context->sublevels_up--;
+ return result;
+ }
+ return expression_tree_walker(node, contain_placeholder_references_walker,
+ context);
+}