]> granicus.if.org Git - postgresql/blobdiff - src/backend/optimizer/util/restrictinfo.c
Update copyright for 2018
[postgresql] / src / backend / optimizer / util / restrictinfo.c
index 33b029b0e4ea9f10f0367ebe1d7b3218ab009039..1075dde40c897835782ce7cad4ec4ac6c65c0bb1 100644 (file)
@@ -3,7 +3,7 @@
  * 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
  *
  *
@@ -15,7 +15,6 @@
 #include "postgres.h"
 
 #include "optimizer/clauses.h"
-#include "optimizer/predtest.h"
 #include "optimizer/restrictinfo.h"
 #include "optimizer/var.h"
 
@@ -25,6 +24,7 @@ static RestrictInfo *make_restrictinfo_internal(Expr *clause,
                                                   bool is_pushed_down,
                                                   bool outerjoin_delayed,
                                                   bool pseudoconstant,
+                                                  Index security_level,
                                                   Relids required_relids,
                                                   Relids outer_relids,
                                                   Relids nullable_relids);
@@ -32,6 +32,7 @@ static Expr *make_sub_restrictinfos(Expr *clause,
                                           bool is_pushed_down,
                                           bool outerjoin_delayed,
                                           bool pseudoconstant,
+                                          Index security_level,
                                           Relids required_relids,
                                           Relids outer_relids,
                                           Relids nullable_relids);
@@ -44,7 +45,7 @@ static Expr *make_sub_restrictinfos(Expr *clause,
  *
  * 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).
  *
@@ -57,6 +58,7 @@ make_restrictinfo(Expr *clause,
                                  bool is_pushed_down,
                                  bool outerjoin_delayed,
                                  bool pseudoconstant,
+                                 Index security_level,
                                  Relids required_relids,
                                  Relids outer_relids,
                                  Relids nullable_relids)
@@ -70,6 +72,7 @@ make_restrictinfo(Expr *clause,
                                                                                                           is_pushed_down,
                                                                                                           outerjoin_delayed,
                                                                                                           pseudoconstant,
+                                                                                                          security_level,
                                                                                                           required_relids,
                                                                                                           outer_relids,
                                                                                                           nullable_relids);
@@ -82,250 +85,12 @@ make_restrictinfo(Expr *clause,
                                                                          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
  *
@@ -337,6 +102,7 @@ make_restrictinfo_internal(Expr *clause,
                                                   bool is_pushed_down,
                                                   bool outerjoin_delayed,
                                                   bool pseudoconstant,
+                                                  Index security_level,
                                                   Relids required_relids,
                                                   Relids outer_relids,
                                                   Relids nullable_relids)
@@ -348,10 +114,21 @@ make_restrictinfo_internal(Expr *clause,
        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.
@@ -397,7 +174,7 @@ make_restrictinfo_internal(Expr *clause,
 
        /*
         * 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).
@@ -422,6 +199,8 @@ make_restrictinfo_internal(Expr *clause,
 
        restrictinfo->left_bucketsize = -1;
        restrictinfo->right_bucketsize = -1;
+       restrictinfo->left_mcvfreq = -1;
+       restrictinfo->right_mcvfreq = -1;
 
        return restrictinfo;
 }
@@ -437,7 +216,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 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
@@ -448,6 +227,7 @@ make_sub_restrictinfos(Expr *clause,
                                           bool is_pushed_down,
                                           bool outerjoin_delayed,
                                           bool pseudoconstant,
+                                          Index security_level,
                                           Relids required_relids,
                                           Relids outer_relids,
                                           Relids nullable_relids)
@@ -463,6 +243,7 @@ make_sub_restrictinfos(Expr *clause,
                                                                                                        is_pushed_down,
                                                                                                        outerjoin_delayed,
                                                                                                        pseudoconstant,
+                                                                                                       security_level,
                                                                                                        NULL,
                                                                                                        outer_relids,
                                                                                                        nullable_relids));
@@ -471,6 +252,7 @@ make_sub_restrictinfos(Expr *clause,
                                                                                                   is_pushed_down,
                                                                                                   outerjoin_delayed,
                                                                                                   pseudoconstant,
+                                                                                                  security_level,
                                                                                                   required_relids,
                                                                                                   outer_relids,
                                                                                                   nullable_relids);
@@ -486,6 +268,7 @@ make_sub_restrictinfos(Expr *clause,
                                                                                                         is_pushed_down,
                                                                                                         outerjoin_delayed,
                                                                                                         pseudoconstant,
+                                                                                                        security_level,
                                                                                                         required_relids,
                                                                                                         outer_relids,
                                                                                                         nullable_relids));
@@ -497,6 +280,7 @@ make_sub_restrictinfos(Expr *clause,
                                                                                                   is_pushed_down,
                                                                                                   outerjoin_delayed,
                                                                                                   pseudoconstant,
+                                                                                                  security_level,
                                                                                                   required_relids,
                                                                                                   outer_relids,
                                                                                                   nullable_relids);
@@ -517,51 +301,45 @@ restriction_is_or_clause(RestrictInfo *restrictinfo)
 }
 
 /*
- * 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);
        }
@@ -583,9 +361,7 @@ extract_actual_clauses(List *restrictinfo_list,
 
        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);
@@ -615,9 +391,7 @@ extract_actual_join_clauses(List *restrictinfo_list,
 
        foreach(l, restrictinfo_list)
        {
-               RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
-
-               Assert(IsA(rinfo, RestrictInfo));
+               RestrictInfo *rinfo = lfirst_node(RestrictInfo, l);
 
                if (rinfo->is_pushed_down)
                {
@@ -651,10 +425,9 @@ extract_actual_join_clauses(List *restrictinfo_list,
  * 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
@@ -703,10 +476,31 @@ join_clause_is_movable_to(RestrictInfo *rinfo, RelOptInfo *baserel)
  * 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
@@ -717,11 +511,11 @@ join_clause_is_movable_into(RestrictInfo *rinfo,
                                                        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;
 
@@ -729,7 +523,12 @@ join_clause_is_movable_into(RestrictInfo *rinfo,
        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;