diff options
Diffstat (limited to 'src/backend/optimizer/plan/setrefs.c')
-rw-r--r-- | src/backend/optimizer/plan/setrefs.c | 234 |
1 files changed, 183 insertions, 51 deletions
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 85ba9d1ca1e..4348bfef601 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -30,11 +30,21 @@ #include "utils/syscache.h" +typedef enum +{ + NRM_EQUAL, /* expect exact match of nullingrels */ + NRM_SUBSET, /* actual Var may have a subset of input */ + NRM_SUPERSET /* actual Var may have a superset of input */ +} NullingRelsMatch; + typedef struct { int varno; /* RT index of Var */ AttrNumber varattno; /* attr number of Var */ AttrNumber resno; /* TLE position of Var */ +#ifdef USE_ASSERT_CHECKING + Bitmapset *varnullingrels; /* Var's varnullingrels */ +#endif } tlist_vinfo; typedef struct @@ -60,6 +70,7 @@ typedef struct indexed_tlist *inner_itlist; Index acceptable_rel; int rtoffset; + NullingRelsMatch nrm_match; double num_exec; } fix_join_expr_context; @@ -69,6 +80,7 @@ typedef struct indexed_tlist *subplan_itlist; int newvarno; int rtoffset; + NullingRelsMatch nrm_match; double num_exec; } fix_upper_expr_context; @@ -159,7 +171,12 @@ static indexed_tlist *build_tlist_index(List *tlist); static Var *search_indexed_tlist_for_var(Var *var, indexed_tlist *itlist, int newvarno, - int rtoffset); + int rtoffset, + NullingRelsMatch nrm_match); +static Var *search_indexed_tlist_for_phv(PlaceHolderVar *phv, + indexed_tlist *itlist, + int newvarno, + NullingRelsMatch nrm_match); static Var *search_indexed_tlist_for_non_var(Expr *node, indexed_tlist *itlist, int newvarno); @@ -172,14 +189,18 @@ static List *fix_join_expr(PlannerInfo *root, indexed_tlist *outer_itlist, indexed_tlist *inner_itlist, Index acceptable_rel, - int rtoffset, double num_exec); + int rtoffset, + NullingRelsMatch nrm_match, + double num_exec); static Node *fix_join_expr_mutator(Node *node, fix_join_expr_context *context); static Node *fix_upper_expr(PlannerInfo *root, Node *node, indexed_tlist *subplan_itlist, int newvarno, - int rtoffset, double num_exec); + int rtoffset, + NullingRelsMatch nrm_match, + double num_exec); static Node *fix_upper_expr_mutator(Node *node, fix_upper_expr_context *context); static List *set_returning_clause_references(PlannerInfo *root, @@ -1118,13 +1139,13 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset) fix_join_expr(root, splan->onConflictSet, NULL, itlist, linitial_int(splan->resultRelations), - rtoffset, NUM_EXEC_QUAL(plan)); + rtoffset, NRM_EQUAL, NUM_EXEC_QUAL(plan)); splan->onConflictWhere = (Node *) fix_join_expr(root, (List *) splan->onConflictWhere, NULL, itlist, linitial_int(splan->resultRelations), - rtoffset, NUM_EXEC_QUAL(plan)); + rtoffset, NRM_EQUAL, NUM_EXEC_QUAL(plan)); pfree(itlist); @@ -1181,6 +1202,7 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset) NULL, itlist, resultrel, rtoffset, + NRM_EQUAL, NUM_EXEC_TLIST(plan)); /* Fix quals too. */ @@ -1189,6 +1211,7 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset) NULL, itlist, resultrel, rtoffset, + NRM_EQUAL, NUM_EXEC_QUAL(plan)); } } @@ -1334,6 +1357,7 @@ set_indexonlyscan_references(PlannerInfo *root, index_itlist, INDEX_VAR, rtoffset, + NRM_EQUAL, NUM_EXEC_TLIST((Plan *) plan)); plan->scan.plan.qual = (List *) fix_upper_expr(root, @@ -1341,6 +1365,7 @@ set_indexonlyscan_references(PlannerInfo *root, index_itlist, INDEX_VAR, rtoffset, + NRM_EQUAL, NUM_EXEC_QUAL((Plan *) plan)); plan->recheckqual = (List *) fix_upper_expr(root, @@ -1348,6 +1373,7 @@ set_indexonlyscan_references(PlannerInfo *root, index_itlist, INDEX_VAR, rtoffset, + NRM_EQUAL, NUM_EXEC_QUAL((Plan *) plan)); /* indexqual is already transformed to reference index columns */ plan->indexqual = fix_scan_list(root, plan->indexqual, @@ -1554,6 +1580,7 @@ set_foreignscan_references(PlannerInfo *root, itlist, INDEX_VAR, rtoffset, + NRM_EQUAL, NUM_EXEC_TLIST((Plan *) fscan)); fscan->scan.plan.qual = (List *) fix_upper_expr(root, @@ -1561,6 +1588,7 @@ set_foreignscan_references(PlannerInfo *root, itlist, INDEX_VAR, rtoffset, + NRM_EQUAL, NUM_EXEC_QUAL((Plan *) fscan)); fscan->fdw_exprs = (List *) fix_upper_expr(root, @@ -1568,6 +1596,7 @@ set_foreignscan_references(PlannerInfo *root, itlist, INDEX_VAR, rtoffset, + NRM_EQUAL, NUM_EXEC_QUAL((Plan *) fscan)); fscan->fdw_recheck_quals = (List *) fix_upper_expr(root, @@ -1575,6 +1604,7 @@ set_foreignscan_references(PlannerInfo *root, itlist, INDEX_VAR, rtoffset, + NRM_EQUAL, NUM_EXEC_QUAL((Plan *) fscan)); pfree(itlist); /* fdw_scan_tlist itself just needs fix_scan_list() adjustments */ @@ -1603,6 +1633,7 @@ set_foreignscan_references(PlannerInfo *root, } fscan->fs_relids = offset_relid_set(fscan->fs_relids, rtoffset); + fscan->fs_base_relids = offset_relid_set(fscan->fs_base_relids, rtoffset); /* Adjust resultRelation if it's valid */ if (fscan->resultRelation > 0) @@ -1635,6 +1666,7 @@ set_customscan_references(PlannerInfo *root, itlist, INDEX_VAR, rtoffset, + NRM_EQUAL, NUM_EXEC_TLIST((Plan *) cscan)); cscan->scan.plan.qual = (List *) fix_upper_expr(root, @@ -1642,6 +1674,7 @@ set_customscan_references(PlannerInfo *root, itlist, INDEX_VAR, rtoffset, + NRM_EQUAL, NUM_EXEC_QUAL((Plan *) cscan)); cscan->custom_exprs = (List *) fix_upper_expr(root, @@ -1649,6 +1682,7 @@ set_customscan_references(PlannerInfo *root, itlist, INDEX_VAR, rtoffset, + NRM_EQUAL, NUM_EXEC_QUAL((Plan *) cscan)); pfree(itlist); /* custom_scan_tlist itself just needs fix_scan_list() adjustments */ @@ -1835,6 +1869,7 @@ set_hash_references(PlannerInfo *root, Plan *plan, int rtoffset) outer_itlist, OUTER_VAR, rtoffset, + NRM_EQUAL, NUM_EXEC_QUAL(plan)); /* Hash doesn't project */ @@ -2170,6 +2205,7 @@ fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context) /* At scan level, we should always just evaluate the contained expr */ PlaceHolderVar *phv = (PlaceHolderVar *) node; + Assert(phv->phnullingrels == NULL); return fix_scan_expr_mutator((Node *) phv->phexpr, context); } if (IsA(node, AlternativeSubPlan)) @@ -2227,6 +2263,7 @@ set_join_references(PlannerInfo *root, Join *join, int rtoffset) inner_itlist, (Index) 0, rtoffset, + NRM_EQUAL, NUM_EXEC_QUAL((Plan *) join)); /* Now do join-type-specific stuff */ @@ -2239,11 +2276,21 @@ set_join_references(PlannerInfo *root, Join *join, int rtoffset) { NestLoopParam *nlp = (NestLoopParam *) lfirst(lc); + /* + * Because we don't reparameterize parameterized paths to match + * the outer-join level at which they are used, Vars seen in the + * NestLoopParam expression may have nullingrels that are just a + * subset of those in the Vars actually available from the outer + * side. Not checking this exactly is a bit grotty, but the work + * needed to make things match up perfectly seems well out of + * proportion to the value. + */ nlp->paramval = (Var *) fix_upper_expr(root, (Node *) nlp->paramval, outer_itlist, OUTER_VAR, rtoffset, + NRM_SUBSET, NUM_EXEC_TLIST(outer_plan)); /* Check we replaced any PlaceHolderVar with simple Var */ if (!(IsA(nlp->paramval, Var) && @@ -2261,6 +2308,7 @@ set_join_references(PlannerInfo *root, Join *join, int rtoffset) inner_itlist, (Index) 0, rtoffset, + NRM_EQUAL, NUM_EXEC_QUAL((Plan *) join)); } else if (IsA(join, HashJoin)) @@ -2273,6 +2321,7 @@ set_join_references(PlannerInfo *root, Join *join, int rtoffset) inner_itlist, (Index) 0, rtoffset, + NRM_EQUAL, NUM_EXEC_QUAL((Plan *) join)); /* @@ -2284,45 +2333,27 @@ set_join_references(PlannerInfo *root, Join *join, int rtoffset) outer_itlist, OUTER_VAR, rtoffset, + NRM_EQUAL, NUM_EXEC_QUAL((Plan *) join)); } /* * Now we need to fix up the targetlist and qpqual, which are logically - * above the join. This means they should not re-use any input expression - * that was computed in the nullable side of an outer join. Vars and - * PlaceHolderVars are fine, so we can implement this restriction just by - * clearing has_non_vars in the indexed_tlist structs. - * - * XXX This is a grotty workaround for the fact that we don't clearly - * distinguish between a Var appearing below an outer join and the "same" - * Var appearing above it. If we did, we'd not need to hack the matching - * rules this way. + * above the join. This means that, if it's not an inner join, any Vars + * and PHVs appearing here should have nullingrels that include the + * effects of the outer join, ie they will have nullingrels equal to the + * input Vars' nullingrels plus the bit added by the outer join. We don't + * currently have enough info available here to identify what that should + * be, so we just tell fix_join_expr to accept superset nullingrels + * matches instead of exact ones. */ - switch (join->jointype) - { - case JOIN_LEFT: - case JOIN_SEMI: - case JOIN_ANTI: - inner_itlist->has_non_vars = false; - break; - case JOIN_RIGHT: - outer_itlist->has_non_vars = false; - break; - case JOIN_FULL: - outer_itlist->has_non_vars = false; - inner_itlist->has_non_vars = false; - break; - default: - break; - } - join->plan.targetlist = fix_join_expr(root, join->plan.targetlist, outer_itlist, inner_itlist, (Index) 0, rtoffset, + (join->jointype == JOIN_INNER ? NRM_EQUAL : NRM_SUPERSET), NUM_EXEC_TLIST((Plan *) join)); join->plan.qual = fix_join_expr(root, join->plan.qual, @@ -2330,6 +2361,7 @@ set_join_references(PlannerInfo *root, Join *join, int rtoffset) inner_itlist, (Index) 0, rtoffset, + (join->jointype == JOIN_INNER ? NRM_EQUAL : NRM_SUPERSET), NUM_EXEC_QUAL((Plan *) join)); pfree(outer_itlist); @@ -2384,6 +2416,7 @@ set_upper_references(PlannerInfo *root, Plan *plan, int rtoffset) subplan_itlist, OUTER_VAR, rtoffset, + NRM_EQUAL, NUM_EXEC_TLIST(plan)); } else @@ -2392,6 +2425,7 @@ set_upper_references(PlannerInfo *root, Plan *plan, int rtoffset) subplan_itlist, OUTER_VAR, rtoffset, + NRM_EQUAL, NUM_EXEC_TLIST(plan)); tle = flatCopyTargetEntry(tle); tle->expr = (Expr *) newexpr; @@ -2405,6 +2439,7 @@ set_upper_references(PlannerInfo *root, Plan *plan, int rtoffset) subplan_itlist, OUTER_VAR, rtoffset, + NRM_EQUAL, NUM_EXEC_QUAL(plan)); pfree(subplan_itlist); @@ -2605,7 +2640,7 @@ set_dummy_tlist_references(Plan *plan, int rtoffset) * tlist_member() searches. * * The result of this function is an indexed_tlist struct to pass to - * search_indexed_tlist_for_var() or search_indexed_tlist_for_non_var(). + * search_indexed_tlist_for_var() and siblings. * When done, the indexed_tlist may be freed with a single pfree(). */ static indexed_tlist * @@ -2637,6 +2672,9 @@ build_tlist_index(List *tlist) vinfo->varno = var->varno; vinfo->varattno = var->varattno; vinfo->resno = tle->resno; +#ifdef USE_ASSERT_CHECKING + vinfo->varnullingrels = var->varnullingrels; +#endif vinfo++; } else if (tle->expr && IsA(tle->expr, PlaceHolderVar)) @@ -2689,6 +2727,9 @@ build_tlist_index_other_vars(List *tlist, int ignore_rel) vinfo->varno = var->varno; vinfo->varattno = var->varattno; vinfo->resno = tle->resno; +#ifdef USE_ASSERT_CHECKING + vinfo->varnullingrels = var->varnullingrels; +#endif vinfo++; } } @@ -2708,10 +2749,17 @@ build_tlist_index_other_vars(List *tlist, int ignore_rel) * modified varno/varattno (to wit, newvarno and the resno of the TLE entry). * Also ensure that varnosyn is incremented by rtoffset. * If no match, return NULL. + * + * In debugging builds, we cross-check the varnullingrels of the subplan + * output Var based on nrm_match. Most call sites should pass NRM_EQUAL + * indicating we expect an exact match. However, there are places where + * we haven't cleaned things up completely, and we have to settle for + * allowing subset or superset matches. */ static Var * search_indexed_tlist_for_var(Var *var, indexed_tlist *itlist, - int newvarno, int rtoffset) + int newvarno, int rtoffset, + NullingRelsMatch nrm_match) { int varno = var->varno; AttrNumber varattno = var->varattno; @@ -2727,6 +2775,27 @@ search_indexed_tlist_for_var(Var *var, indexed_tlist *itlist, /* Found a match */ Var *newvar = copyVar(var); + /* + * Assert that we kept all the nullingrels machinations straight. + * + * XXX we skip the check for system columns and whole-row Vars. + * That's because such Vars might be row identity Vars, which are + * generated without any varnullingrels. It'd be hard to do + * otherwise, since they're normally made very early in planning, + * when we haven't looked at the jointree yet and don't know which + * joins might null such Vars. Doesn't seem worth the expense to + * make them fully valid. (While it's slightly annoying that we + * thereby lose checking for user-written references to such + * columns, it seems unlikely that a bug in nullingrels logic + * would affect only system columns.) + */ + Assert(varattno <= 0 || + (nrm_match == NRM_SUBSET ? + bms_is_subset(var->varnullingrels, vinfo->varnullingrels) : + nrm_match == NRM_SUPERSET ? + bms_is_subset(vinfo->varnullingrels, var->varnullingrels) : + bms_equal(vinfo->varnullingrels, var->varnullingrels))); + newvar->varno = newvarno; newvar->varattno = vinfo->resno; if (newvar->varnosyn > 0) @@ -2739,15 +2808,63 @@ search_indexed_tlist_for_var(Var *var, indexed_tlist *itlist, } /* - * search_indexed_tlist_for_non_var --- find a non-Var in an indexed tlist + * search_indexed_tlist_for_phv --- find a PlaceHolderVar in an indexed tlist * * If a match is found, return a Var constructed to reference the tlist item. * If no match, return NULL. * - * NOTE: it is a waste of time to call this unless itlist->has_ph_vars or - * itlist->has_non_vars. Furthermore, set_join_references() relies on being - * able to prevent matching of non-Vars by clearing itlist->has_non_vars, - * so there's a correctness reason not to call it unless that's set. + * Cross-check phnullingrels as in search_indexed_tlist_for_var. + * + * NOTE: it is a waste of time to call this unless itlist->has_ph_vars. + */ +static Var * +search_indexed_tlist_for_phv(PlaceHolderVar *phv, + indexed_tlist *itlist, int newvarno, + NullingRelsMatch nrm_match) +{ + ListCell *lc; + + foreach(lc, itlist->tlist) + { + TargetEntry *tle = (TargetEntry *) lfirst(lc); + + if (tle->expr && IsA(tle->expr, PlaceHolderVar)) + { + PlaceHolderVar *subphv = (PlaceHolderVar *) tle->expr; + Var *newvar; + + /* + * Analogously to search_indexed_tlist_for_var, we match on phid + * only. We don't use equal(), partially for speed but mostly + * because phnullingrels might not be exactly equal. + */ + if (phv->phid != subphv->phid) + continue; + + /* Assert that we kept all the nullingrels machinations straight */ + Assert(nrm_match == NRM_SUBSET ? + bms_is_subset(phv->phnullingrels, subphv->phnullingrels) : + nrm_match == NRM_SUPERSET ? + bms_is_subset(subphv->phnullingrels, phv->phnullingrels) : + bms_equal(subphv->phnullingrels, phv->phnullingrels)); + + /* Found a matching subplan output expression */ + newvar = makeVarFromTargetEntry(newvarno, tle); + newvar->varnosyn = 0; /* wasn't ever a plain Var */ + newvar->varattnosyn = 0; + return newvar; + } + } + return NULL; /* no match */ +} + +/* + * search_indexed_tlist_for_non_var --- find a non-Var/PHV in an indexed tlist + * + * If a match is found, return a Var constructed to reference the tlist item. + * If no match, return NULL. + * + * NOTE: it is a waste of time to call this unless itlist->has_non_vars. */ static Var * search_indexed_tlist_for_non_var(Expr *node, @@ -2854,6 +2971,7 @@ search_indexed_tlist_for_sortgroupref(Expr *node, * 'acceptable_rel' is either zero or the rangetable index of a relation * whose Vars may appear in the clause without provoking an error * 'rtoffset': how much to increment varnos by + * 'nrm_match': as for search_indexed_tlist_for_var() * 'num_exec': estimated number of executions of expression * * Returns the new expression tree. The original clause structure is @@ -2866,6 +2984,7 @@ fix_join_expr(PlannerInfo *root, indexed_tlist *inner_itlist, Index acceptable_rel, int rtoffset, + NullingRelsMatch nrm_match, double num_exec) { fix_join_expr_context context; @@ -2875,6 +2994,7 @@ fix_join_expr(PlannerInfo *root, context.inner_itlist = inner_itlist; context.acceptable_rel = acceptable_rel; context.rtoffset = rtoffset; + context.nrm_match = nrm_match; context.num_exec = num_exec; return (List *) fix_join_expr_mutator((Node *) clauses, &context); } @@ -2896,7 +3016,8 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context) newvar = search_indexed_tlist_for_var(var, context->outer_itlist, OUTER_VAR, - context->rtoffset); + context->rtoffset, + context->nrm_match); if (newvar) return (Node *) newvar; } @@ -2907,7 +3028,8 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context) newvar = search_indexed_tlist_for_var(var, context->inner_itlist, INNER_VAR, - context->rtoffset); + context->rtoffset, + context->nrm_match); if (newvar) return (Node *) newvar; } @@ -2932,22 +3054,25 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context) /* See if the PlaceHolderVar has bubbled up from a lower plan node */ if (context->outer_itlist && context->outer_itlist->has_ph_vars) { - newvar = search_indexed_tlist_for_non_var((Expr *) phv, - context->outer_itlist, - OUTER_VAR); + newvar = search_indexed_tlist_for_phv(phv, + context->outer_itlist, + OUTER_VAR, + context->nrm_match); if (newvar) return (Node *) newvar; } if (context->inner_itlist && context->inner_itlist->has_ph_vars) { - newvar = search_indexed_tlist_for_non_var((Expr *) phv, - context->inner_itlist, - INNER_VAR); + newvar = search_indexed_tlist_for_phv(phv, + context->inner_itlist, + INNER_VAR, + context->nrm_match); if (newvar) return (Node *) newvar; } /* If not supplied by input plans, evaluate the contained expr */ + /* XXX can we assert something about phnullingrels? */ return fix_join_expr_mutator((Node *) phv->phexpr, context); } /* Try matching more complex expressions too, if tlists have any */ @@ -3006,6 +3131,7 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context) * 'subplan_itlist': indexed target list for subplan (or index) * 'newvarno': varno to use for Vars referencing tlist elements * 'rtoffset': how much to increment varnos by + * 'nrm_match': as for search_indexed_tlist_for_var() * 'num_exec': estimated number of executions of expression * * The resulting tree is a copy of the original in which all Var nodes have @@ -3018,6 +3144,7 @@ fix_upper_expr(PlannerInfo *root, indexed_tlist *subplan_itlist, int newvarno, int rtoffset, + NullingRelsMatch nrm_match, double num_exec) { fix_upper_expr_context context; @@ -3026,6 +3153,7 @@ fix_upper_expr(PlannerInfo *root, context.subplan_itlist = subplan_itlist; context.newvarno = newvarno; context.rtoffset = rtoffset; + context.nrm_match = nrm_match; context.num_exec = num_exec; return fix_upper_expr_mutator(node, &context); } @@ -3044,7 +3172,8 @@ fix_upper_expr_mutator(Node *node, fix_upper_expr_context *context) newvar = search_indexed_tlist_for_var(var, context->subplan_itlist, context->newvarno, - context->rtoffset); + context->rtoffset, + context->nrm_match); if (!newvar) elog(ERROR, "variable not found in subplan target list"); return (Node *) newvar; @@ -3056,13 +3185,15 @@ fix_upper_expr_mutator(Node *node, fix_upper_expr_context *context) /* See if the PlaceHolderVar has bubbled up from a lower plan node */ if (context->subplan_itlist->has_ph_vars) { - newvar = search_indexed_tlist_for_non_var((Expr *) phv, - context->subplan_itlist, - context->newvarno); + newvar = search_indexed_tlist_for_phv(phv, + context->subplan_itlist, + context->newvarno, + context->nrm_match); if (newvar) return (Node *) newvar; } /* If not supplied by input plan, evaluate the contained expr */ + /* XXX can we assert something about phnullingrels? */ return fix_upper_expr_mutator((Node *) phv->phexpr, context); } /* Try matching more complex expressions too, if tlist has any */ @@ -3169,6 +3300,7 @@ set_returning_clause_references(PlannerInfo *root, NULL, resultRelation, rtoffset, + NRM_EQUAL, NUM_EXEC_TLIST(topplan)); pfree(itlist); |