* restrictinfo.c
* RestrictInfo node manipulation routines.
*
- * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
#include "postgres.h"
#include "optimizer/clauses.h"
-#include "optimizer/predtest.h"
#include "optimizer/restrictinfo.h"
#include "optimizer/var.h"
bool is_pushed_down,
bool outerjoin_delayed,
bool pseudoconstant,
+ Index security_level,
Relids required_relids,
Relids outer_relids,
Relids nullable_relids);
bool is_pushed_down,
bool outerjoin_delayed,
bool pseudoconstant,
+ Index security_level,
Relids required_relids,
Relids outer_relids,
Relids nullable_relids);
*
* The is_pushed_down, outerjoin_delayed, and pseudoconstant flags for the
* RestrictInfo must be supplied by the caller, as well as the correct values
- * for outer_relids and nullable_relids.
+ * for security_level, outer_relids, and nullable_relids.
* required_relids can be NULL, in which case it defaults to the actual clause
* contents (i.e., clause_relids).
*
bool is_pushed_down,
bool outerjoin_delayed,
bool pseudoconstant,
+ Index security_level,
Relids required_relids,
Relids outer_relids,
Relids nullable_relids)
is_pushed_down,
outerjoin_delayed,
pseudoconstant,
+ security_level,
required_relids,
outer_relids,
nullable_relids);
is_pushed_down,
outerjoin_delayed,
pseudoconstant,
+ security_level,
required_relids,
outer_relids,
nullable_relids);
}
-/*
- * make_restrictinfo_from_bitmapqual
- *
- * Given the bitmapqual Path structure for a bitmap indexscan, generate
- * RestrictInfo node(s) equivalent to the condition represented by the
- * indexclauses of the Path structure.
- *
- * The result is a List (effectively, implicit-AND representation) of
- * RestrictInfos.
- *
- * The caller must pass is_pushed_down, but we assume outerjoin_delayed
- * 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
- * that might be weaker than the actual scan represents.
- *
- * To do this through the normal make_restrictinfo() API, callers would have
- * to strip off the RestrictInfo nodes present in the indexclauses lists, and
- * then make_restrictinfo() would have to build new ones. It's better to have
- * a specialized routine to allow sharing of RestrictInfos.
- *
- * The qual manipulations here are much the same as in create_bitmap_subplan;
- * keep the two routines in sync!
- */
-List *
-make_restrictinfo_from_bitmapqual(Path *bitmapqual,
- bool is_pushed_down,
- bool include_predicates)
-{
- List *result;
- ListCell *l;
-
- if (IsA(bitmapqual, BitmapAndPath))
- {
- BitmapAndPath *apath = (BitmapAndPath *) bitmapqual;
-
- /*
- * There may well be redundant quals among the subplans, since a
- * top-level WHERE qual might have gotten used to form several
- * different index quals. We don't try exceedingly hard to eliminate
- * redundancies, but we do eliminate obvious duplicates by using
- * list_concat_unique.
- */
- result = NIL;
- foreach(l, apath->bitmapquals)
- {
- List *sublist;
-
- sublist = make_restrictinfo_from_bitmapqual((Path *) lfirst(l),
- is_pushed_down,
- include_predicates);
- result = list_concat_unique(result, sublist);
- }
- }
- else if (IsA(bitmapqual, BitmapOrPath))
- {
- BitmapOrPath *opath = (BitmapOrPath *) bitmapqual;
- List *withris = NIL;
- List *withoutris = NIL;
-
- /*
- * Here, we only detect qual-free subplans. A qual-free subplan would
- * cause us to generate "... OR true ..." which we may as well reduce
- * to just "true". We do not try to eliminate redundant subclauses
- * because (a) it's not as likely as in the AND case, and (b) we might
- * well be working with hundreds or even thousands of OR conditions,
- * perhaps from a long IN list. The performance of list_append_unique
- * would be unacceptable.
- */
- foreach(l, opath->bitmapquals)
- {
- List *sublist;
-
- sublist = make_restrictinfo_from_bitmapqual((Path *) lfirst(l),
- is_pushed_down,
- include_predicates);
- if (sublist == NIL)
- {
- /*
- * If we find a qual-less subscan, it represents a constant
- * TRUE, and hence the OR result is also constant TRUE, so we
- * can stop here.
- */
- return NIL;
- }
-
- /*
- * If the sublist contains multiple RestrictInfos, we create an
- * AND subclause. If there's just one, we have to check if it's
- * an OR clause, and if so flatten it to preserve AND/OR flatness
- * of our output.
- *
- * We construct lists with and without sub-RestrictInfos, so as
- * not to have to regenerate duplicate RestrictInfos below.
- */
- if (list_length(sublist) > 1)
- {
- withris = lappend(withris, make_andclause(sublist));
- sublist = get_actual_clauses(sublist);
- withoutris = lappend(withoutris, make_andclause(sublist));
- }
- else
- {
- RestrictInfo *subri = (RestrictInfo *) linitial(sublist);
-
- Assert(IsA(subri, RestrictInfo));
- if (restriction_is_or_clause(subri))
- {
- BoolExpr *subor = (BoolExpr *) subri->orclause;
-
- Assert(or_clause((Node *) subor));
- withris = list_concat(withris,
- list_copy(subor->args));
- subor = (BoolExpr *) subri->clause;
- Assert(or_clause((Node *) subor));
- withoutris = list_concat(withoutris,
- list_copy(subor->args));
- }
- else
- {
- withris = lappend(withris, subri);
- withoutris = lappend(withoutris, subri->clause);
- }
- }
- }
-
- /*
- * Avoid generating one-element ORs, which could happen due to
- * redundancy elimination or ScalarArrayOpExpr quals.
- */
- if (list_length(withris) <= 1)
- result = withris;
- else
- {
- /* Here's the magic part not available to outside callers */
- result =
- list_make1(make_restrictinfo_internal(make_orclause(withoutris),
- make_orclause(withris),
- is_pushed_down,
- false,
- false,
- NULL,
- NULL,
- NULL));
- }
- }
- else if (IsA(bitmapqual, IndexPath))
- {
- IndexPath *ipath = (IndexPath *) bitmapqual;
-
- result = list_copy(ipath->indexclauses);
- if (include_predicates && ipath->indexinfo->indpred != NIL)
- {
- foreach(l, ipath->indexinfo->indpred)
- {
- Expr *pred = (Expr *) lfirst(l);
-
- /*
- * We know that the index predicate must have been implied by
- * the query condition as a whole, but it may or may not be
- * implied by the conditions that got pushed into the
- * bitmapqual. Avoid generating redundant conditions.
- */
- if (!predicate_implied_by(list_make1(pred), result))
- result = lappend(result,
- make_restrictinfo(pred,
- is_pushed_down,
- false,
- false,
- NULL,
- NULL,
- NULL));
- }
- }
- }
- else
- {
- elog(ERROR, "unrecognized node type: %d", nodeTag(bitmapqual));
- result = NIL; /* keep compiler quiet */
- }
-
- return result;
-}
-
-/*
- * make_restrictinfos_from_actual_clauses
- *
- * Given a list of implicitly-ANDed restriction clauses, produce a list
- * of RestrictInfo nodes. This is used to reconstitute the RestrictInfo
- * 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,
- * 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 *
-make_restrictinfos_from_actual_clauses(PlannerInfo *root,
- List *clause_list)
-{
- List *result = NIL;
- ListCell *l;
-
- foreach(l, clause_list)
- {
- Expr *clause = (Expr *) lfirst(l);
- bool pseudoconstant;
- RestrictInfo *rinfo;
-
- /*
- * It's pseudoconstant if it contains no Vars and no volatile
- * functions. We probably can't see any sublinks here, so
- * contain_var_clause() would likely be enough, but for safety use
- * contain_vars_of_level() instead.
- */
- pseudoconstant =
- !contain_vars_of_level((Node *) clause, 0) &&
- !contain_volatile_functions((Node *) clause);
- if (pseudoconstant)
- {
- /* tell createplan.c to check for gating quals */
- root->hasPseudoConstantQuals = true;
- }
-
- rinfo = make_restrictinfo(clause,
- true,
- false,
- pseudoconstant,
- NULL,
- NULL,
- NULL);
- result = lappend(result, rinfo);
- }
- return result;
-}
-
/*
* make_restrictinfo_internal
*
bool is_pushed_down,
bool outerjoin_delayed,
bool pseudoconstant,
+ Index security_level,
Relids required_relids,
Relids outer_relids,
Relids nullable_relids)
restrictinfo->is_pushed_down = is_pushed_down;
restrictinfo->outerjoin_delayed = outerjoin_delayed;
restrictinfo->pseudoconstant = pseudoconstant;
- restrictinfo->can_join = false; /* may get set below */
+ restrictinfo->can_join = false; /* may get set below */
+ restrictinfo->security_level = security_level;
restrictinfo->outer_relids = outer_relids;
restrictinfo->nullable_relids = nullable_relids;
+ /*
+ * If it's potentially delayable by lower-level security quals, figure out
+ * whether it's leakproof. We can skip testing this for level-zero quals,
+ * since they would never get delayed on security grounds anyway.
+ */
+ if (security_level > 0)
+ restrictinfo->leakproof = !contain_leaked_vars((Node *) clause);
+ else
+ restrictinfo->leakproof = false; /* really, "don't know" */
+
/*
* If it's a binary opclause, set up left/right relids info. In any case
* set up the total clause relids info.
/*
* Fill in all the cacheable fields with "not yet set" markers. None of
- * these will be computed until/unless needed. Note in particular that we
+ * these will be computed until/unless needed. Note in particular that we
* don't mark a binary opclause as mergejoinable or hashjoinable here;
* that happens only if it appears in the right context (top level of a
* joinclause list).
restrictinfo->left_bucketsize = -1;
restrictinfo->right_bucketsize = -1;
+ restrictinfo->left_mcvfreq = -1;
+ restrictinfo->right_mcvfreq = -1;
return restrictinfo;
}
*
* The same is_pushed_down, outerjoin_delayed, and pseudoconstant flag
* values can be applied to all RestrictInfo nodes in the result. Likewise
- * for outer_relids and nullable_relids.
+ * for security_level, 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
bool is_pushed_down,
bool outerjoin_delayed,
bool pseudoconstant,
+ Index security_level,
Relids required_relids,
Relids outer_relids,
Relids nullable_relids)
is_pushed_down,
outerjoin_delayed,
pseudoconstant,
+ security_level,
NULL,
outer_relids,
nullable_relids));
is_pushed_down,
outerjoin_delayed,
pseudoconstant,
+ security_level,
required_relids,
outer_relids,
nullable_relids);
is_pushed_down,
outerjoin_delayed,
pseudoconstant,
+ security_level,
required_relids,
outer_relids,
nullable_relids));
is_pushed_down,
outerjoin_delayed,
pseudoconstant,
+ security_level,
required_relids,
outer_relids,
nullable_relids);
}
/*
- * get_actual_clauses
- *
- * Returns a list containing the bare clauses from 'restrictinfo_list'.
+ * restriction_is_securely_promotable
*
- * This is only to be used in cases where none of the RestrictInfos can
- * be pseudoconstant clauses (for instance, it's OK on indexqual lists).
+ * Returns true if it's okay to evaluate this clause "early", that is before
+ * other restriction clauses attached to the specified relation.
*/
-List *
-get_actual_clauses(List *restrictinfo_list)
+bool
+restriction_is_securely_promotable(RestrictInfo *restrictinfo,
+ RelOptInfo *rel)
{
- List *result = NIL;
- ListCell *l;
-
- foreach(l, restrictinfo_list)
- {
- RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
-
- Assert(IsA(rinfo, RestrictInfo));
-
- Assert(!rinfo->pseudoconstant);
-
- result = lappend(result, rinfo->clause);
- }
- return result;
+ /*
+ * It's okay if there are no baserestrictinfo clauses for the rel that
+ * would need to go before this one, *or* if this one is leakproof.
+ */
+ if (restrictinfo->security_level <= rel->baserestrict_min_security ||
+ restrictinfo->leakproof)
+ return true;
+ else
+ return false;
}
/*
- * get_all_actual_clauses
+ * get_actual_clauses
*
* Returns a list containing the bare clauses from 'restrictinfo_list'.
*
- * This loses the distinction between regular and pseudoconstant clauses,
- * so be careful what you use it for.
+ * This is only to be used in cases where none of the RestrictInfos can
+ * be pseudoconstant clauses (for instance, it's OK on indexqual lists).
*/
List *
-get_all_actual_clauses(List *restrictinfo_list)
+get_actual_clauses(List *restrictinfo_list)
{
List *result = NIL;
ListCell *l;
foreach(l, restrictinfo_list)
{
- RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
+ RestrictInfo *rinfo = lfirst_node(RestrictInfo, l);
- Assert(IsA(rinfo, RestrictInfo));
+ Assert(!rinfo->pseudoconstant);
result = lappend(result, rinfo->clause);
}
foreach(l, restrictinfo_list)
{
- RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
-
- Assert(IsA(rinfo, RestrictInfo));
+ RestrictInfo *rinfo = lfirst_node(RestrictInfo, l);
if (rinfo->pseudoconstant == pseudoconstant)
result = lappend(result, rinfo->clause);
foreach(l, restrictinfo_list)
{
- RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
-
- Assert(IsA(rinfo, RestrictInfo));
+ RestrictInfo *rinfo = lfirst_node(RestrictInfo, l);
if (rinfo->is_pushed_down)
{
* outer join, as that would change the results (rows would be suppressed
* rather than being null-extended).
*
- * Also 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.
+ * Also 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.
*
* Also, the join clause must not use any relations that have LATERAL
* references to the target relation, since we could not put such rels on
* not pushing the clause into its outer-join outer side, nor down into
* a lower outer join's inner side.
*
+ * The check about pushing a clause down into a lower outer join's inner side
+ * is only approximate; it sometimes returns "false" when actually it would
+ * be safe to use the clause here because we're still above the outer join
+ * in question. This is okay as long as the answers at different join levels
+ * are consistent: it just means we might sometimes fail to push a clause as
+ * far down as it could safely be pushed. It's unclear whether it would be
+ * worthwhile to do this more precisely. (But if it's ever fixed to be
+ * exactly accurate, there's an Assert in get_joinrel_parampathinfo() that
+ * should be re-enabled.)
+ *
* There's no check here equivalent to join_clause_is_movable_to's test on
- * lateral_relids. We assume the caller wouldn't be inquiring unless it'd
- * verified that the proposed outer rels don't have lateral references to
- * the current rel(s).
+ * lateral_referencers. We assume the caller wouldn't be inquiring unless
+ * it'd verified that the proposed outer rels don't have lateral references
+ * to the current rel(s). (If we are considering join paths with the outer
+ * rels on the outside and the current rels on the inside, then this should
+ * have been checked at the outset of such consideration; see join_is_legal
+ * and the path parameterization checks in joinpath.c.) On the other hand,
+ * in join_clause_is_movable_to we are asking whether the clause could be
+ * moved for some valid set of outer rels, so we don't have the benefit of
+ * relying on prior checks for lateral-reference validity.
+ *
+ * Note: if this returns true, it means that the clause could be moved to
+ * this join relation, but that doesn't mean that this is the lowest join
+ * it could be moved to. Caller may need to make additional calls to verify
+ * that this doesn't succeed on either of the inputs of a proposed join.
*
* Note: get_joinrel_parampathinfo depends on the fact that if
* current_and_outer is NULL, this function will always return false
Relids currentrelids,
Relids current_and_outer)
{
- /* Clause must be evaluatable given available context */
+ /* Clause must be evaluable given available context */
if (!bms_is_subset(rinfo->clause_relids, current_and_outer))
return false;
- /* Clause must physically reference target rel(s) */
+ /* Clause must physically reference at least one target rel */
if (!bms_overlap(currentrelids, rinfo->clause_relids))
return false;
if (bms_overlap(currentrelids, rinfo->outer_relids))
return false;
- /* Target rel(s) must not be nullable below the clause */
+ /*
+ * Target rel(s) must not be nullable below the clause. This is
+ * approximate, in the safe direction, because the current join might be
+ * above the join where the nulling would happen, in which case the clause
+ * would work correctly here. But we don't have enough info to be sure.
+ */
if (bms_overlap(currentrelids, rinfo->nullable_relids))
return false;