* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.429 2009/04/05 19:59:39 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.430 2009/04/16 20:42:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
COPY_SCALAR_FIELD(pseudoconstant);
COPY_BITMAPSET_FIELD(clause_relids);
COPY_BITMAPSET_FIELD(required_relids);
+ COPY_BITMAPSET_FIELD(nullable_relids);
COPY_BITMAPSET_FIELD(left_relids);
COPY_BITMAPSET_FIELD(right_relids);
COPY_NODE_FIELD(orclause);
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.352 2009/04/05 19:59:40 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.353 2009/04/16 20:42:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
COMPARE_SCALAR_FIELD(is_pushed_down);
COMPARE_SCALAR_FIELD(outerjoin_delayed);
COMPARE_BITMAPSET_FIELD(required_relids);
+ COMPARE_BITMAPSET_FIELD(nullable_relids);
/*
* We ignore all the remaining fields, since they may not be set yet, and
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.358 2009/04/05 19:59:40 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.359 2009/04/16 20:42:16 tgl Exp $
*
* NOTES
* Every node type that can appear in stored rules' parsetrees *must*
WRITE_BOOL_FIELD(pseudoconstant);
WRITE_BITMAPSET_FIELD(clause_relids);
WRITE_BITMAPSET_FIELD(required_relids);
+ WRITE_BITMAPSET_FIELD(nullable_relids);
WRITE_BITMAPSET_FIELD(left_relids);
WRITE_BITMAPSET_FIELD(right_relids);
WRITE_NODE_FIELD(orclause);
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.238 2009/03/11 03:32:22 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.239 2009/04/16 20:42:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
if (boolqual)
{
resultquals = lappend(resultquals,
- make_restrictinfo(boolqual,
- true,
- false,
- false,
- NULL));
+ make_simple_restrictinfo(boolqual));
continue;
}
}
{
Assert(index->amsearchnulls);
resultquals = lappend(resultquals,
- make_restrictinfo(clause,
- true,
- false,
- false,
- NULL));
+ make_simple_restrictinfo(clause));
}
else
elog(ERROR, "unsupported indexqual type: %d",
matching_cols);
rc->rargs = list_truncate((List *) copyObject(clause->rargs),
matching_cols);
- return make_restrictinfo((Expr *) rc, true, false, false, NULL);
+ return make_simple_restrictinfo((Expr *) rc);
}
else
{
opexpr = make_opclause(linitial_oid(new_ops), BOOLOID, false,
copyObject(linitial(clause->largs)),
copyObject(linitial(clause->rargs)));
- return make_restrictinfo(opexpr, true, false, false, NULL);
+ return make_simple_restrictinfo(opexpr);
}
}
elog(ERROR, "no = operator for opfamily %u", opfamily);
expr = make_opclause(oproid, BOOLOID, false,
(Expr *) leftop, (Expr *) prefix_const);
- result = list_make1(make_restrictinfo(expr, true, false, false, NULL));
+ result = list_make1(make_simple_restrictinfo(expr));
return result;
}
elog(ERROR, "no >= operator for opfamily %u", opfamily);
expr = make_opclause(oproid, BOOLOID, false,
(Expr *) leftop, (Expr *) prefix_const);
- result = list_make1(make_restrictinfo(expr, true, false, false, NULL));
+ result = list_make1(make_simple_restrictinfo(expr));
/*-------
* If we can create a string larger than the prefix, we can say
{
expr = make_opclause(oproid, BOOLOID, false,
(Expr *) leftop, (Expr *) greaterstr);
- result = lappend(result,
- make_restrictinfo(expr, true, false, false, NULL));
+ result = lappend(result, make_simple_restrictinfo(expr));
}
return result;
(Expr *) leftop,
(Expr *) makeConst(datatype, -1, -1, opr1right,
false, false));
- result = list_make1(make_restrictinfo(expr, true, false, false, NULL));
+ result = list_make1(make_simple_restrictinfo(expr));
/* create clause "key <= network_scan_last( rightop )" */
(Expr *) leftop,
(Expr *) makeConst(datatype, -1, -1, opr2right,
false, false));
- result = lappend(result,
- make_restrictinfo(expr, true, false, false, NULL));
+ result = lappend(result, make_simple_restrictinfo(expr));
return result;
}
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/path/orindxpath.c,v 1.88 2009/02/15 20:16:21 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/path/orindxpath.c,v 1.89 2009/04/16 20:42:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
ListCell *i;
/*
- * Find potentially interesting OR joinclauses. Note we must ignore any
- * joinclauses that are marked outerjoin_delayed or !is_pushed_down,
- * because they cannot be pushed down to the per-relation level due to
- * outer-join rules. (XXX in some cases it might be possible to allow
- * this, but it would require substantially more bookkeeping about where
- * the clause came from.)
+ * Find potentially interesting OR joinclauses.
+ *
+ * We must ignore clauses for which the target rel is in nullable_relids;
+ * that means there's an outer join below the clause and so it can't be
+ * enforced at the relation scan level.
+ *
+ * We must also ignore clauses that are marked !is_pushed_down (ie they
+ * are themselves outer-join clauses). It would be safe to extract an
+ * index condition from such a clause if we are within the nullable rather
+ * than the non-nullable side of its join, but we haven't got enough
+ * context here to tell which applies. OR clauses in outer-join quals
+ * aren't exactly common, so we'll let that case go unoptimized for now.
*/
foreach(i, rel->joininfo)
{
if (restriction_is_or_clause(rinfo) &&
rinfo->is_pushed_down &&
- !rinfo->outerjoin_delayed)
+ !bms_is_member(rel->relid, rinfo->nullable_relids))
{
/*
* Use the generate_bitmap_or_paths() machinery to estimate the
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.149 2009/02/27 22:41:38 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.150 2009/04/16 20:42:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
Relids ojscope,
Relids outerjoin_nonnullable);
static bool check_outerjoin_delay(PlannerInfo *root, Relids *relids_p,
- bool is_pushed_down);
+ Relids *nullable_relids_p, bool is_pushed_down);
static bool check_redundant_nullability_qual(PlannerInfo *root, Node *clause);
static void check_mergejoinable(RestrictInfo *restrictinfo);
static void check_hashjoinable(RestrictInfo *restrictinfo);
bool pseudoconstant = false;
bool maybe_equivalence;
bool maybe_outer_join;
+ Relids nullable_relids;
RestrictInfo *restrictinfo;
/*
Assert(!ojscope);
is_pushed_down = true;
outerjoin_delayed = false;
+ nullable_relids = NULL;
/* Don't feed it back for more deductions */
maybe_equivalence = false;
maybe_outer_join = false;
maybe_outer_join = true;
/* Check to see if must be delayed by lower outer join */
- outerjoin_delayed = check_outerjoin_delay(root, &relids, false);
+ outerjoin_delayed = check_outerjoin_delay(root,
+ &relids,
+ &nullable_relids,
+ false);
/*
* Now force the qual to be evaluated exactly at the level of joining
is_pushed_down = true;
/* Check to see if must be delayed by lower outer join */
- outerjoin_delayed = check_outerjoin_delay(root, &relids, true);
+ outerjoin_delayed = check_outerjoin_delay(root,
+ &relids,
+ &nullable_relids,
+ true);
if (outerjoin_delayed)
{
is_pushed_down,
outerjoin_delayed,
pseudoconstant,
- relids);
+ relids,
+ nullable_relids);
/*
* If it's a join clause (either naturally, or because delayed by
* If the qual must be delayed, add relids to *relids_p to reflect the lowest
* safe level for evaluating the qual, and return TRUE. Any extra delay for
* higher-level joins is reflected by setting delay_upper_joins to TRUE in
- * SpecialJoinInfo structs.
+ * SpecialJoinInfo structs. We also compute nullable_relids, the set of
+ * referenced relids that are nullable by lower outer joins (note that this
+ * can be nonempty even for a non-delayed qual).
*
* For an is_pushed_down qual, we can evaluate the qual as soon as (1) we have
* all the rels it mentions, and (2) we are at or above any outer joins that
* mentioning only C cannot be applied below the join to A.
*
* For a non-pushed-down qual, this isn't going to determine where we place the
- * qual, but we need to determine outerjoin_delayed anyway for possible use
- * in reconsider_outer_join_clauses().
+ * qual, but we need to determine outerjoin_delayed and nullable_relids anyway
+ * for use later in the planning process.
*
* Lastly, a pushed-down qual that references the nullable side of any current
* join_info_list member and has to be evaluated above that OJ (because its
* two OJs to commute.)
*/
static bool
-check_outerjoin_delay(PlannerInfo *root, Relids *relids_p,
+check_outerjoin_delay(PlannerInfo *root,
+ Relids *relids_p, /* in/out parameter */
+ Relids *nullable_relids_p, /* output parameter */
bool is_pushed_down)
{
- Relids relids = *relids_p;
+ Relids relids;
+ Relids nullable_relids;
bool outerjoin_delayed;
bool found_some;
+ /* fast path if no special joins */
+ if (root->join_info_list == NIL)
+ {
+ *nullable_relids_p = NULL;
+ return false;
+ }
+
+ /* must copy relids because we need the original value at the end */
+ relids = bms_copy(*relids_p);
+ nullable_relids = NULL;
outerjoin_delayed = false;
do
{
(sjinfo->jointype == JOIN_FULL &&
bms_overlap(relids, sjinfo->min_lefthand)))
{
- /* yes, so set the result flag */
- outerjoin_delayed = true;
- /* have we included all its rels in relids? */
+ /* yes; have we included all its rels in relids? */
if (!bms_is_subset(sjinfo->min_lefthand, relids) ||
!bms_is_subset(sjinfo->min_righthand, relids))
{
/* no, so add them in */
relids = bms_add_members(relids, sjinfo->min_lefthand);
relids = bms_add_members(relids, sjinfo->min_righthand);
+ outerjoin_delayed = true;
/* we'll need another iteration */
found_some = true;
}
+ /* track all the nullable rels of relevant OJs */
+ nullable_relids = bms_add_members(nullable_relids,
+ sjinfo->min_righthand);
+ if (sjinfo->jointype == JOIN_FULL)
+ nullable_relids = bms_add_members(nullable_relids,
+ sjinfo->min_lefthand);
/* set delay_upper_joins if needed */
if (is_pushed_down && sjinfo->jointype != JOIN_FULL &&
bms_overlap(relids, sjinfo->min_lefthand))
}
} while (found_some);
+ /* identify just the actually-referenced nullable rels */
+ nullable_relids = bms_int_members(nullable_relids, *relids_p);
+
+ /* replace *relids_p, and return nullable_relids */
+ bms_free(*relids_p);
*relids_p = relids;
+ *nullable_relids_p = nullable_relids;
return outerjoin_delayed;
}
true, /* is_pushed_down */
false, /* outerjoin_delayed */
false, /* pseudoconstant */
- qualscope);
+ qualscope, /* required_relids */
+ NULL); /* nullable_relids */
/* Set mergejoinability info always, and hashjoinability if enabled */
check_mergejoinable(restrictinfo);
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.57 2009/02/06 23:43:23 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.58 2009/04/16 20:42:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
bool is_pushed_down,
bool outerjoin_delayed,
bool pseudoconstant,
- Relids required_relids);
+ Relids required_relids,
+ Relids nullable_relids);
static Expr *make_sub_restrictinfos(Expr *clause,
bool is_pushed_down,
bool outerjoin_delayed,
bool pseudoconstant,
- Relids required_relids);
+ Relids required_relids,
+ Relids nullable_relids);
static bool join_clause_is_redundant(PlannerInfo *root,
RestrictInfo *rinfo,
List *reference_list);
* 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. 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 value
+ * for 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
bool is_pushed_down,
bool outerjoin_delayed,
bool pseudoconstant,
- Relids required_relids)
+ Relids required_relids,
+ Relids nullable_relids)
{
/*
* If it's an OR clause, build a modified copy with RestrictInfos inserted
is_pushed_down,
outerjoin_delayed,
pseudoconstant,
- required_relids);
+ required_relids,
+ nullable_relids);
/* Shouldn't be an AND clause, else AND/OR flattening messed up */
Assert(!and_clause((Node *) clause));
is_pushed_down,
outerjoin_delayed,
pseudoconstant,
- required_relids);
+ required_relids,
+ nullable_relids);
}
/*
* RestrictInfos.
*
* The caller must pass is_pushed_down, but we assume outerjoin_delayed
- * and pseudoconstant are false (no such qual should ever get into a
- * bitmapqual).
+ * and pseudoconstant are false and nullable_relids is 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
is_pushed_down,
false,
false,
+ NULL,
NULL));
}
}
is_pushed_down,
false,
false,
+ NULL,
NULL));
}
}
bool is_pushed_down,
bool outerjoin_delayed,
bool pseudoconstant,
- Relids required_relids)
+ Relids required_relids,
+ Relids nullable_relids)
{
RestrictInfo *restrictinfo = makeNode(RestrictInfo);
restrictinfo->outerjoin_delayed = outerjoin_delayed;
restrictinfo->pseudoconstant = pseudoconstant;
restrictinfo->can_join = false; /* may get set below */
+ restrictinfo->nullable_relids = nullable_relids;
/*
* If it's a binary opclause, set up left/right relids info. In any case
* simple clauses are valid RestrictInfos.
*
* The same is_pushed_down, outerjoin_delayed, and pseudoconstant flag
- * values can be applied to all RestrictInfo nodes in the result.
+ * values can be applied to all RestrictInfo nodes in the result. Likewise
+ * for 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,
- Relids required_relids)
+ Relids required_relids,
+ Relids nullable_relids)
{
if (or_clause((Node *) clause))
{
is_pushed_down,
outerjoin_delayed,
pseudoconstant,
- NULL));
+ NULL,
+ nullable_relids));
return (Expr *) make_restrictinfo_internal(clause,
make_orclause(orlist),
is_pushed_down,
outerjoin_delayed,
pseudoconstant,
- required_relids);
+ required_relids,
+ nullable_relids);
}
else if (and_clause((Node *) clause))
{
is_pushed_down,
outerjoin_delayed,
pseudoconstant,
- required_relids));
+ required_relids,
+ nullable_relids));
return make_andclause(andlist);
}
else
is_pushed_down,
outerjoin_delayed,
pseudoconstant,
- required_relids);
+ required_relids,
+ nullable_relids);
}
/*
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.171 2009/03/26 17:15:35 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.172 2009/04/16 20:42:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
*
* RestrictInfo nodes also contain an outerjoin_delayed flag, which is true
* if the clause's applicability must be delayed due to any outer joins
- * appearing below its own syntactic level (ie, it references any Vars from
- * the nullable side of any lower outer join).
+ * appearing below it (ie, it has to be postponed to some join level higher
+ * than the set of relations it actually references). There is also a
+ * nullable_relids field, which is the set of rels it references that can be
+ * forced null by some outer join below the clause. outerjoin_delayed = true
+ * is subtly different from nullable_relids != NULL: a clause might reference
+ * some nullable rels and yet not be outerjoin_delayed because it also
+ * references all the other rels of the outer join(s). A clause that is not
+ * outerjoin_delayed can be enforced anywhere it is computable.
*
* In general, the referenced clause might be arbitrarily complex. The
* kinds of clauses we can handle as indexscan quals, mergejoin clauses,
/* The set of relids required to evaluate the clause: */
Relids required_relids;
+ /* The relids used in the clause that are nullable by lower outer joins: */
+ Relids nullable_relids;
+
/* These fields are set for any binary opclause: */
Relids left_relids; /* relids in left side of clause */
Relids right_relids; /* relids in right side of clause */
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/optimizer/restrictinfo.h,v 1.42 2009/01/01 17:24:00 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/restrictinfo.h,v 1.43 2009/04/16 20:42:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "nodes/relation.h"
+/* Convenience macro for the common case of a valid-everywhere qual */
+#define make_simple_restrictinfo(clause) \
+ make_restrictinfo(clause, true, false, false, NULL, NULL)
+
extern RestrictInfo *make_restrictinfo(Expr *clause,
bool is_pushed_down,
bool outerjoin_delayed,
bool pseudoconstant,
- Relids required_relids);
+ Relids required_relids,
+ Relids nullable_relids);
extern List *make_restrictinfo_from_bitmapqual(Path *bitmapqual,
bool is_pushed_down,
bool include_predicates);