diff options
Diffstat (limited to 'src/backend/optimizer/util/restrictinfo.c')
-rw-r--r-- | src/backend/optimizer/util/restrictinfo.c | 161 |
1 files changed, 97 insertions, 64 deletions
diff --git a/src/backend/optimizer/util/restrictinfo.c b/src/backend/optimizer/util/restrictinfo.c index 7d03d91f5d3..48e96ab5d7e 100644 --- a/src/backend/optimizer/util/restrictinfo.c +++ b/src/backend/optimizer/util/restrictinfo.c @@ -26,15 +26,15 @@ static RestrictInfo *make_restrictinfo_internal(Expr *clause, bool outerjoin_delayed, bool pseudoconstant, Relids required_relids, + Relids outer_relids, Relids nullable_relids); static Expr *make_sub_restrictinfos(Expr *clause, bool is_pushed_down, bool outerjoin_delayed, bool pseudoconstant, Relids required_relids, + Relids outer_relids, Relids nullable_relids); -static bool join_clause_is_redundant(RestrictInfo *rinfo, - List *reference_list); /* @@ -43,9 +43,10 @@ static bool join_clause_is_redundant(RestrictInfo *rinfo, * Build a RestrictInfo node containing the given subexpression. * * The is_pushed_down, outerjoin_delayed, and pseudoconstant flags for the - * RestrictInfo must be supplied by the caller, as well as the correct value - * for nullable_relids. required_relids can be NULL, in which case it - * defaults to the actual clause contents (i.e., clause_relids). + * RestrictInfo must be supplied by the caller, as well as the correct values + * for outer_relids and nullable_relids. + * required_relids can be NULL, in which case it defaults to the actual clause + * contents (i.e., clause_relids). * * We initialize fields that depend only on the given subexpression, leaving * others that depend on context (or may never be needed at all) to be filled @@ -57,6 +58,7 @@ make_restrictinfo(Expr *clause, bool outerjoin_delayed, bool pseudoconstant, Relids required_relids, + Relids outer_relids, Relids nullable_relids) { /* @@ -69,6 +71,7 @@ make_restrictinfo(Expr *clause, outerjoin_delayed, pseudoconstant, required_relids, + outer_relids, nullable_relids); /* Shouldn't be an AND clause, else AND/OR flattening messed up */ @@ -80,6 +83,7 @@ make_restrictinfo(Expr *clause, outerjoin_delayed, pseudoconstant, required_relids, + outer_relids, nullable_relids); } @@ -94,8 +98,8 @@ make_restrictinfo(Expr *clause, * RestrictInfos. * * The caller must pass is_pushed_down, but we assume outerjoin_delayed - * and pseudoconstant are false and nullable_relids is NULL (no other - * kind of qual should ever get into a bitmapqual). + * and pseudoconstant are false while outer_relids and nullable_relids + * are NULL (no other kind of qual should ever get into a bitmapqual). * * If include_predicates is true, we add any partial index predicates to * the explicit index quals. When this is not true, we return a condition @@ -227,6 +231,7 @@ make_restrictinfo_from_bitmapqual(Path *bitmapqual, false, false, NULL, + NULL, NULL)); } } @@ -254,6 +259,7 @@ make_restrictinfo_from_bitmapqual(Path *bitmapqual, false, false, NULL, + NULL, NULL)); } } @@ -275,8 +281,9 @@ make_restrictinfo_from_bitmapqual(Path *bitmapqual, * representation after doing transformations of a list of clauses. * * We assume that the clauses are relation-level restrictions and therefore - * we don't have to worry about is_pushed_down, outerjoin_delayed, or - * nullable_relids (these can be assumed true, false, and NULL, respectively). + * we don't have to worry about is_pushed_down, outerjoin_delayed, + * outer_relids, and nullable_relids (these can be assumed true, false, + * NULL, and NULL, respectively). * We do take care to recognize pseudoconstant clauses properly. */ List * @@ -312,6 +319,7 @@ make_restrictinfos_from_actual_clauses(PlannerInfo *root, false, pseudoconstant, NULL, + NULL, NULL); result = lappend(result, rinfo); } @@ -330,6 +338,7 @@ make_restrictinfo_internal(Expr *clause, bool outerjoin_delayed, bool pseudoconstant, Relids required_relids, + Relids outer_relids, Relids nullable_relids) { RestrictInfo *restrictinfo = makeNode(RestrictInfo); @@ -340,6 +349,7 @@ make_restrictinfo_internal(Expr *clause, restrictinfo->outerjoin_delayed = outerjoin_delayed; restrictinfo->pseudoconstant = pseudoconstant; restrictinfo->can_join = false; /* may get set below */ + restrictinfo->outer_relids = outer_relids; restrictinfo->nullable_relids = nullable_relids; /* @@ -427,7 +437,7 @@ make_restrictinfo_internal(Expr *clause, * * The same is_pushed_down, outerjoin_delayed, and pseudoconstant flag * values can be applied to all RestrictInfo nodes in the result. Likewise - * for nullable_relids. + * for outer_relids and nullable_relids. * * The given required_relids are attached to our top-level output, * but any OR-clause constituents are allowed to default to just the @@ -439,6 +449,7 @@ make_sub_restrictinfos(Expr *clause, bool outerjoin_delayed, bool pseudoconstant, Relids required_relids, + Relids outer_relids, Relids nullable_relids) { if (or_clause((Node *) clause)) @@ -453,6 +464,7 @@ make_sub_restrictinfos(Expr *clause, outerjoin_delayed, pseudoconstant, NULL, + outer_relids, nullable_relids)); return (Expr *) make_restrictinfo_internal(clause, make_orclause(orlist), @@ -460,6 +472,7 @@ make_sub_restrictinfos(Expr *clause, outerjoin_delayed, pseudoconstant, required_relids, + outer_relids, nullable_relids); } else if (and_clause((Node *) clause)) @@ -474,6 +487,7 @@ make_sub_restrictinfos(Expr *clause, outerjoin_delayed, pseudoconstant, required_relids, + outer_relids, nullable_relids)); return make_andclause(andlist); } @@ -484,6 +498,7 @@ make_sub_restrictinfos(Expr *clause, outerjoin_delayed, pseudoconstant, required_relids, + outer_relids, nullable_relids); } @@ -620,72 +635,90 @@ extract_actual_join_clauses(List *restrictinfo_list, /* - * select_nonredundant_join_clauses - * Select the members of restrictinfo_list that are not redundant with - * any member of reference_list. - * - * Given a list of RestrictInfo clauses that are to be applied in a join, - * select the ones that are not redundant with any clause that's listed in - * reference_list. This is used, for example, to avoid checking joinclauses - * again at a nestloop join when they've already been enforced by a - * parameterized inner path. - * - * "Redundant" means either equal() or derived from the same EquivalenceClass. - * We have to check the latter because indxpath.c may select different derived - * clauses than were selected by generate_join_implied_equalities(). - * - * Note that we are *not* checking for local redundancies within the given - * restrictinfo_list; that should have been handled elsewhere. + * join_clause_is_movable_to + * Test whether a join clause is a safe candidate for parameterization + * of a scan on the specified base relation. + * + * A movable join clause is one that can safely be evaluated at a rel below + * its normal semantic level (ie, its required_relids), if the values of + * variables that it would need from other rels are provided. + * + * We insist that the clause actually reference the target relation; this + * prevents undesirable movement of degenerate join clauses, and ensures + * that there is a unique place that a clause can be moved down to. + * + * We cannot move an outer-join clause into the non-nullable side of its + * outer join, as that would change the results (rows would be suppressed + * rather than being null-extended). + * + * And the target relation must not be in the clause's nullable_relids, i.e., + * there must not be an outer join below the clause that would null the Vars + * coming from the target relation. Otherwise the clause might give results + * different from what it would give at its normal semantic level. */ -List * -select_nonredundant_join_clauses(List *restrictinfo_list, - List *reference_list) +bool +join_clause_is_movable_to(RestrictInfo *rinfo, Index baserelid) { - List *result = NIL; - ListCell *item; - - /* Quick out if nothing could be removed */ - if (reference_list == NIL) - return restrictinfo_list; - - foreach(item, restrictinfo_list) - { - RestrictInfo *rinfo = (RestrictInfo *) lfirst(item); + /* Clause must physically reference target rel */ + if (!bms_is_member(baserelid, rinfo->clause_relids)) + return false; - /* drop it if redundant with any reference clause */ - if (join_clause_is_redundant(rinfo, reference_list)) - continue; + /* Cannot move an outer-join clause into the join's outer side */ + if (bms_is_member(baserelid, rinfo->outer_relids)) + return false; - /* otherwise, add it to result list */ - result = lappend(result, rinfo); - } + /* Target rel must not be nullable below the clause */ + if (bms_is_member(baserelid, rinfo->nullable_relids)) + return false; - return result; + return true; } /* - * join_clause_is_redundant - * Test whether rinfo is redundant with any clause in reference_list. + * join_clause_is_movable_into + * Test whether a join clause is movable and can be evaluated within + * the current join context. + * + * currentrelids: the relids of the proposed evaluation location + * current_and_outer: the union of currentrelids and the required_outer + * relids (parameterization's outer relations) + * + * The API would be a bit clearer if we passed the current relids and the + * outer relids separately and did bms_union internally; but since most + * callers need to apply this function to multiple clauses, we make the + * caller perform the union. + * + * Obviously, the clause must only refer to Vars available from the current + * relation plus the outer rels. We also check that it does reference at + * least one current Var, ensuring that the clause will be pushed down to + * a unique place in a parameterized join tree. And we check that we're + * not pushing the clause into its outer-join outer side, nor down into + * a lower outer join's inner side. + * + * Note: get_joinrel_parampathinfo depends on the fact that if + * current_and_outer is NULL, this function will always return false + * (since one or the other of the first two tests must fail). */ -static bool -join_clause_is_redundant(RestrictInfo *rinfo, - List *reference_list) +bool +join_clause_is_movable_into(RestrictInfo *rinfo, + Relids currentrelids, + Relids current_and_outer) { - ListCell *refitem; + /* Clause must be evaluatable given available context */ + if (!bms_is_subset(rinfo->clause_relids, current_and_outer)) + return false; - foreach(refitem, reference_list) - { - RestrictInfo *refrinfo = (RestrictInfo *) lfirst(refitem); + /* Clause must physically reference target rel(s) */ + if (!bms_overlap(currentrelids, rinfo->clause_relids)) + return false; - /* always consider exact duplicates redundant */ - if (equal(rinfo, refrinfo)) - return true; + /* Cannot move an outer-join clause into the join's outer side */ + if (bms_overlap(currentrelids, rinfo->outer_relids)) + return false; - /* check if derived from same EquivalenceClass */ - if (rinfo->parent_ec != NULL && - rinfo->parent_ec == refrinfo->parent_ec) - return true; - } + /* Target rel(s) must not be nullable below the clause */ + if (bms_overlap(currentrelids, rinfo->nullable_relids)) + return false; - return false; + return true; } |