Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Postgres/src/bin/psql/   (Postgres Database Version 18.4©)  Datei vom 11.4.2026 mit Größe 12 kB image not shown  

Quelle  orclauses.c

  Sprache: C
 

/*-------------------------------------------------------------------------
 *
 * orclauses.c
 *   Routines to extract restriction OR clauses from join OR clauses
 *
 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *   src/backend/optimizer/util/orclauses.c
 *
 *-------------------------------------------------------------------------
 */


#include "postgres.h"

#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/optimizer.h"
#include "optimizer/orclauses.h"
#include "optimizer/paths.h"
#include "optimizer/restrictinfo.h"


static bool is_safe_restriction_clause_for(RestrictInfo *rinfo, RelOptInfo *rel);
static Expr *extract_or_clause(RestrictInfo *or_rinfo, RelOptInfo *rel);
static void consider_new_or_clause(PlannerInfo *root, RelOptInfo *rel,
           Expr *orclause, RestrictInfo *join_or_rinfo);


/*
 * extract_restriction_or_clauses
 *   Examine join OR-of-AND clauses to see if any useful restriction OR
 *   clauses can be extracted.  If so, add them to the query.
 *
 * Although a join clause must reference multiple relations overall,
 * an OR of ANDs clause might contain sub-clauses that reference just one
 * relation and can be used to build a restriction clause for that rel.
 * For example consider
 *  WHERE ((a.x = 42 AND b.y = 43) OR (a.x = 44 AND b.z = 45));
 * We can transform this into
 *  WHERE ((a.x = 42 AND b.y = 43) OR (a.x = 44 AND b.z = 45))
 *   AND (a.x = 42 OR a.x = 44)
 *   AND (b.y = 43 OR b.z = 45);
 * which allows the latter clauses to be applied during the scans of a and b,
 * perhaps as index qualifications, and in any case reducing the number of
 * rows arriving at the join.  In essence this is a partial transformation to
 * CNF (AND of ORs format).  It is not complete, however, because we do not
 * unravel the original OR --- doing so would usually bloat the qualification
 * expression to little gain.
 *
 * The added quals are partially redundant with the original OR, and therefore
 * would cause the size of the joinrel to be underestimated when it is finally
 * formed.  (This would be true of a full transformation to CNF as well; the
 * fault is not really in the transformation, but in clauselist_selectivity's
 * inability to recognize redundant conditions.)  We can compensate for this
 * redundancy by changing the cached selectivity of the original OR clause,
 * canceling out the (valid) reduction in the estimated sizes of the base
 * relations so that the estimated joinrel size remains the same.  This is
 * a MAJOR HACK: it depends on the fact that clause selectivities are cached
 * and on the fact that the same RestrictInfo node will appear in every
 * joininfo list that might be used when the joinrel is formed.
 * And it doesn't work in cases where the size estimation is nonlinear
 * (i.e., outer and IN joins).  But it beats not doing anything.
 *
 * We examine each base relation to see if join clauses associated with it
 * contain extractable restriction conditions.  If so, add those conditions
 * to the rel's baserestrictinfo and update the cached selectivities of the
 * join clauses.  Note that the same join clause will be examined afresh
 * from the point of view of each baserel that participates in it, so its
 * cached selectivity may get updated multiple times.
 */

void
extract_restriction_or_clauses(PlannerInfo *root)
{
 Index  rti;

 /* Examine each baserel for potential join OR clauses */
 for (rti = 1; rti < root->simple_rel_array_size; rti++)
 {
  RelOptInfo *rel = root->simple_rel_array[rti];
  ListCell   *lc;

  /* there may be empty slots corresponding to non-baserel RTEs */
  if (rel == NULL)
   continue;

  Assert(rel->relid == rti); /* sanity check on array */

  /* ignore RTEs that are "other rels" */
  if (rel->reloptkind != RELOPT_BASEREL)
   continue;

  /*
   * Find potentially interesting OR joinclauses.  We can use any
   * joinclause that is considered safe to move to this rel by the
   * parameterized-path machinery, even though what we are going to do
   * with it is not exactly a parameterized path.
 */

  foreach(lc, rel->joininfo)
  {
   RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);

   if (restriction_is_or_clause(rinfo) &&
    join_clause_is_movable_to(rinfo, rel))
   {
    /* Try to extract a qual for this rel only */
    Expr    *orclause = extract_or_clause(rinfo, rel);

    /*
     * If successful, decide whether we want to use the clause,
     * and insert it into the rel's restrictinfo list if so.
 */

    if (orclause)
     consider_new_or_clause(root, rel, orclause, rinfo);
   }
  }
 }
}

/*
 * Is the given primitive (non-OR) RestrictInfo safe to move to the rel?
 */

static bool
is_safe_restriction_clause_for(RestrictInfo *rinfo, RelOptInfo *rel)
{
 /*
  * We want clauses that mention the rel, and only the rel.  So in
  * particular pseudoconstant clauses can be rejected quickly.  Then check
  * the clause's Var membership.
 */

 if (rinfo->pseudoconstant)
  return false;
 if (!bms_equal(rinfo->clause_relids, rel->relids))
  return false;

 /* We don't want extra evaluations of any volatile functions */
 if (contain_volatile_functions((Node *) rinfo->clause))
  return false;

 return true;
}

/*
 * Try to extract a restriction clause mentioning only "rel" from the given
 * join OR-clause.
 *
 * We must be able to extract at least one qual for this rel from each of
 * the arms of the OR, else we can't use it.
 *
 * Returns an OR clause (not a RestrictInfo!) pertaining to rel, or NULL
 * if no OR clause could be extracted.
 */

static Expr *
extract_or_clause(RestrictInfo *or_rinfo, RelOptInfo *rel)
{
 List    *clauselist = NIL;
 ListCell   *lc;

 /*
  * Scan each arm of the input OR clause.  Notice we descend into
  * or_rinfo->orclause, which has RestrictInfo nodes embedded below the
  * toplevel OR/AND structure.  This is useful because we can use the info
  * in those nodes to make is_safe_restriction_clause_for()'s checks
  * cheaper.  We'll strip those nodes from the returned tree, though,
  * meaning that fresh ones will be built if the clause is accepted as a
  * restriction clause.  This might seem wasteful --- couldn't we re-use
  * the existing RestrictInfos? But that'd require assuming that
  * selectivity and other cached data is computed exactly the same way for
  * a restriction clause as for a join clause, which seems undesirable.
 */

 Assert(is_orclause(or_rinfo->orclause));
 foreach(lc, ((BoolExpr *) or_rinfo->orclause)->args)
 {
  Node    *orarg = (Node *) lfirst(lc);
  List    *subclauses = NIL;
  Node    *subclause;

  /* OR arguments should be ANDs or sub-RestrictInfos */
  if (is_andclause(orarg))
  {
   List    *andargs = ((BoolExpr *) orarg)->args;
   ListCell   *lc2;

   foreach(lc2, andargs)
   {
    RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc2);

    if (restriction_is_or_clause(rinfo))
    {
     /*
      * Recurse to deal with nested OR.  Note we *must* recurse
      * here, this isn't just overly-tense optimization: we
      * have to descend far enough to find and strip all
      * RestrictInfos in the expression.
 */

     Expr    *suborclause;

     suborclause = extract_or_clause(rinfo, rel);
     if (suborclause)
      subclauses = lappend(subclauses, suborclause);
    }
    else if (is_safe_restriction_clause_for(rinfo, rel))
     subclauses = lappend(subclauses, rinfo->clause);
   }
  }
  else
  {
   RestrictInfo *rinfo = castNode(RestrictInfo, orarg);

   Assert(!restriction_is_or_clause(rinfo));
   if (is_safe_restriction_clause_for(rinfo, rel))
    subclauses = lappend(subclauses, rinfo->clause);
  }

  /*
   * If nothing could be extracted from this arm, we can't do anything
   * with this OR clause.
 */

  if (subclauses == NIL)
   return NULL;

  /*
   * OK, add subclause(s) to the result OR.  If we found more than one,
   * we need an AND node.  But if we found only one, and it is itself an
   * OR node, add its subclauses to the result instead; this is needed
   * to preserve AND/OR flatness (ie, no OR directly underneath OR).
 */

  subclause = (Node *) make_ands_explicit(subclauses);
  if (is_orclause(subclause))
   clauselist = list_concat(clauselist,
          ((BoolExpr *) subclause)->args);
  else
   clauselist = lappend(clauselist, subclause);
 }

 /*
  * If we got a restriction clause from every arm, wrap them up in an OR
  * node.  (In theory the OR node might be unnecessary, if there was only
  * one arm --- but then the input OR node was also redundant.)
 */

 if (clauselist != NIL)
  return make_orclause(clauselist);
 return NULL;
}

/*
 * Consider whether a successfully-extracted restriction OR clause is
 * actually worth using.  If so, add it to the planner's data structures,
 * and adjust the original join clause (join_or_rinfo) to compensate.
 */

static void
consider_new_or_clause(PlannerInfo *root, RelOptInfo *rel,
        Expr *orclause, RestrictInfo *join_or_rinfo)
{
 RestrictInfo *or_rinfo;
 Selectivity or_selec,
    orig_selec;

 /*
  * Build a RestrictInfo from the new OR clause.  We can assume it's valid
  * as a base restriction clause.
 */

 or_rinfo = make_restrictinfo(root,
         orclause,
         true,
         false,
         false,
         false,
         join_or_rinfo->security_level,
         NULL,
         NULL,
         NULL);

 /*
  * Estimate its selectivity.  (We could have done this earlier, but doing
  * it on the RestrictInfo representation allows the result to get cached,
  * saving work later.)
 */

 or_selec = clause_selectivity(root, (Node *) or_rinfo,
          0, JOIN_INNER, NULL);

 /*
  * The clause is only worth adding to the query if it rejects a useful
  * fraction of the base relation's rows; otherwise, it's just going to
  * cause duplicate computation (since we will still have to check the
  * original OR clause when the join is formed).  Somewhat arbitrarily, we
  * set the selectivity threshold at 0.9.
 */

 if (or_selec > 0.9)
  return;     /* forget it */

 /*
  * OK, add it to the rel's restriction-clause list.
 */

 rel->baserestrictinfo = lappend(rel->baserestrictinfo, or_rinfo);
 rel->baserestrict_min_security = Min(rel->baserestrict_min_security,
           or_rinfo->security_level);

 /*
  * Adjust the original join OR clause's cached selectivity to compensate
  * for the selectivity of the added (but redundant) lower-level qual. This
  * should result in the join rel getting approximately the same rows
  * estimate as it would have gotten without all these shenanigans.
  *
  * XXX major hack alert: this depends on the assumption that the
  * selectivity will stay cached.
  *
  * XXX another major hack: we adjust only norm_selec, the cached
  * selectivity for JOIN_INNER semantics, even though the join clause
  * might've been an outer-join clause.  This is partly because we can't
  * easily identify the relevant SpecialJoinInfo here, and partly because
  * the linearity assumption we're making would fail anyway.  (If it is an
  * outer-join clause, "rel" must be on the nullable side, else we'd not
  * have gotten here.  So the computation of the join size is going to be
  * quite nonlinear with respect to the size of "rel", so it's not clear
  * how we ought to adjust outer_selec even if we could compute its
  * original value correctly.)
 */

 if (or_selec > 0)
 {
  SpecialJoinInfo sjinfo;

  /*
   * Make up a SpecialJoinInfo for JOIN_INNER semantics.  (Compare
   * approx_tuple_count() in costsize.c.)
 */

  init_dummy_sjinfo(&sjinfo,
        bms_difference(join_or_rinfo->clause_relids,
           rel->relids),
        rel->relids);

  /* Compute inner-join size */
  orig_selec = clause_selectivity(root, (Node *) join_or_rinfo,
          0, JOIN_INNER, &sjinfo);

  /* And hack cached selectivity so join size remains the same */
  join_or_rinfo->norm_selec = orig_selec / or_selec;
  /* ensure result stays in sane range */
  if (join_or_rinfo->norm_selec > 1)
   join_or_rinfo->norm_selec = 1;
  /* as explained above, we don't touch outer_selec */
 }
}

Messung V0.5 in Prozent
C=86 H=94 G=89

¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.14Angebot  (Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-08-08) ¤

*Eine klare Vorstellung vom Zielzustand






Versionsinformation zu Columbo

Bemerkung:

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Anfrage:

Dauer der Verarbeitung:

Sekunden

sprechenden Kalenders