aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/optimizer/util/plancat.c42
-rw-r--r--src/backend/optimizer/util/predtest.c100
2 files changed, 94 insertions, 48 deletions
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index d29f9ff0929..5f927095edc 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/util/plancat.c,v 1.139 2008/01/01 19:45:50 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/util/plancat.c,v 1.140 2008/01/12 00:11:39 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -47,7 +47,8 @@ get_relation_info_hook_type get_relation_info_hook = NULL;
static void estimate_rel_size(Relation rel, int32 *attr_widths,
BlockNumber *pages, double *tuples);
-static List *get_relation_constraints(Oid relationObjectId, RelOptInfo *rel);
+static List *get_relation_constraints(Oid relationObjectId, RelOptInfo *rel,
+ bool include_notnull);
/*
@@ -453,12 +454,16 @@ estimate_rel_size(Relation rel, int32 *attr_widths,
* indicated by rel->relid. This allows the expressions to be easily
* compared to expressions taken from WHERE.
*
+ * If include_notnull is true, "col IS NOT NULL" expressions are generated
+ * and added to the result for each column that's marked attnotnull.
+ *
* Note: at present this is invoked at most once per relation per planner
* run, and in many cases it won't be invoked at all, so there seems no
* point in caching the data in RelOptInfo.
*/
static List *
-get_relation_constraints(Oid relationObjectId, RelOptInfo *rel)
+get_relation_constraints(Oid relationObjectId, RelOptInfo *rel,
+ bool include_notnull)
{
List *result = NIL;
Index varno = rel->relid;
@@ -513,6 +518,30 @@ get_relation_constraints(Oid relationObjectId, RelOptInfo *rel)
result = list_concat(result,
make_ands_implicit((Expr *) cexpr));
}
+
+ /* Add NOT NULL constraints in expression form, if requested */
+ if (include_notnull && constr->has_not_null)
+ {
+ int natts = relation->rd_att->natts;
+
+ for (i = 1; i <= natts; i++)
+ {
+ Form_pg_attribute att = relation->rd_att->attrs[i - 1];
+
+ if (att->attnotnull && !att->attisdropped)
+ {
+ NullTest *ntest = makeNode(NullTest);
+
+ ntest->arg = (Expr *) makeVar(varno,
+ i,
+ att->atttypid,
+ att->atttypmod,
+ 0);
+ ntest->nulltesttype = IS_NOT_NULL;
+ result = lappend(result, ntest);
+ }
+ }
+ }
}
heap_close(relation, NoLock);
@@ -567,8 +596,11 @@ relation_excluded_by_constraints(RelOptInfo *rel, RangeTblEntry *rte)
if (rte->rtekind != RTE_RELATION || rte->inh)
return false;
- /* OK to fetch the constraint expressions */
- constraint_pred = get_relation_constraints(rte->relid, rel);
+ /*
+ * OK to fetch the constraint expressions. Include "col IS NOT NULL"
+ * expressions for attnotnull columns, in case we can refute those.
+ */
+ constraint_pred = get_relation_constraints(rte->relid, rel, true);
/*
* We do not currently enforce that CHECK constraints contain only
diff --git a/src/backend/optimizer/util/predtest.c b/src/backend/optimizer/util/predtest.c
index a6a28f10e7f..4a1a6056e3b 100644
--- a/src/backend/optimizer/util/predtest.c
+++ b/src/backend/optimizer/util/predtest.c
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/util/predtest.c,v 1.18 2008/01/01 19:45:50 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/util/predtest.c,v 1.19 2008/01/12 00:11:39 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -82,7 +82,6 @@ static Node *arrayexpr_next_fn(PredIterInfo info);
static void arrayexpr_cleanup_fn(PredIterInfo info);
static bool predicate_implied_by_simple_clause(Expr *predicate, Node *clause);
static bool predicate_refuted_by_simple_clause(Expr *predicate, Node *clause);
-static bool is_null_contradicts(NullTest *ntest, Node *clause);
static Node *extract_not_arg(Node *clause);
static bool list_member_strip(List *list, Expr *datum);
static bool btree_predicate_proof(Expr *predicate, Node *clause,
@@ -129,6 +128,14 @@ predicate_implied_by(List *predicate_list, List *restrictinfo_list)
* This is NOT the same as !(predicate_implied_by), though it is similar
* in the technique and structure of the code.
*
+ * An important fine point is that truth of the clauses must imply that
+ * the predicate returns FALSE, not that it does not return TRUE. This
+ * is normally used to try to refute CHECK constraints, and the only
+ * thing we can assume about a CHECK constraint is that it didn't return
+ * FALSE --- a NULL result isn't a violation per the SQL spec. (Someday
+ * perhaps this code should be extended to support both "strong" and
+ * "weak" refutation, but for now we only need "strong".)
+ *
* The top-level List structure of each list corresponds to an AND list.
* We assume that eval_const_expressions() has been applied and so there
* are no un-flattened ANDs or ORs (e.g., no AND immediately within an AND,
@@ -408,10 +415,11 @@ predicate_implied_by_recurse(Node *clause, Node *predicate)
*
* In addition, if the predicate is a NOT-clause then we can use
* A R=> NOT B if: A => B
- * while if the restriction clause is a NOT-clause then we can use
- * NOT A R=> B if: B => A
* This works for several different SQL constructs that assert the non-truth
* of their argument, ie NOT, IS FALSE, IS NOT TRUE, IS UNKNOWN.
+ * Unfortunately we *cannot* use
+ * NOT A R=> B if: B => A
+ * because this type of reasoning fails to prove that B doesn't yield NULL.
*
* Other comments are as for predicate_implied_by_recurse().
*----------
@@ -595,13 +603,21 @@ predicate_refuted_by_recurse(Node *clause, Node *predicate)
case CLASS_ATOM:
+#ifdef NOT_USED
/*
* If A is a NOT-clause, A R=> B if B => A's arg
+ *
+ * Unfortunately not: this would only prove that B is not-TRUE,
+ * not that it's not NULL either. Keep this code as a comment
+ * because it would be useful if we ever had a need for the
+ * weak form of refutation.
*/
not_arg = extract_not_arg(clause);
if (not_arg &&
predicate_implied_by_recurse(predicate, not_arg))
return true;
+#endif
+
switch (pclass)
{
case CLASS_AND:
@@ -990,9 +1006,11 @@ predicate_implied_by_simple_clause(Expr *predicate, Node *clause)
* When the predicate is of the form "foo IS NULL", we can conclude that
* the predicate is refuted if the clause is a strict operator or function
* that has "foo" as an input (see notes for implication case), or if the
- * clause is "foo IS NOT NULL". Conversely a clause "foo IS NULL" refutes
- * predicates of those types. (The motivation for covering these cases is
- * to support using IS NULL/IS NOT NULL as partition-defining constraints.)
+ * clause is "foo IS NOT NULL". A clause "foo IS NULL" refutes a predicate
+ * "foo IS NOT NULL", but unfortunately does not refute strict predicates,
+ * because we are looking for strong refutation. (The motivation for covering
+ * these cases is to support using IS NULL/IS NOT NULL as partition-defining
+ * constraints.)
*
* Finally, we may be able to deduce something using knowledge about btree
* operator families; this is encapsulated in btree_predicate_proof().
@@ -1010,8 +1028,28 @@ predicate_refuted_by_simple_clause(Expr *predicate, Node *clause)
if (predicate && IsA(predicate, NullTest) &&
((NullTest *) predicate)->nulltesttype == IS_NULL)
{
- if (is_null_contradicts((NullTest *) predicate, clause))
+ Expr *isnullarg = ((NullTest *) predicate)->arg;
+
+ /* row IS NULL does not act in the simple way we have in mind */
+ if (type_is_rowtype(exprType((Node *) isnullarg)))
+ return false;
+
+ /* Any strict op/func on foo refutes foo IS NULL */
+ if (is_opclause(clause) &&
+ list_member_strip(((OpExpr *) clause)->args, isnullarg) &&
+ op_strict(((OpExpr *) clause)->opno))
+ return true;
+ if (is_funcclause(clause) &&
+ list_member_strip(((FuncExpr *) clause)->args, isnullarg) &&
+ func_strict(((FuncExpr *) clause)->funcid))
return true;
+
+ /* foo IS NOT NULL refutes foo IS NULL */
+ if (clause && IsA(clause, NullTest) &&
+ ((NullTest *) clause)->nulltesttype == IS_NOT_NULL &&
+ equal(((NullTest *) clause)->arg, isnullarg))
+ return true;
+
return false; /* we can't succeed below... */
}
@@ -1019,8 +1057,18 @@ predicate_refuted_by_simple_clause(Expr *predicate, Node *clause)
if (clause && IsA(clause, NullTest) &&
((NullTest *) clause)->nulltesttype == IS_NULL)
{
- if (is_null_contradicts((NullTest *) clause, (Node *) predicate))
+ Expr *isnullarg = ((NullTest *) clause)->arg;
+
+ /* row IS NULL does not act in the simple way we have in mind */
+ if (type_is_rowtype(exprType((Node *) isnullarg)))
+ return false;
+
+ /* foo IS NULL refutes foo IS NOT NULL */
+ if (predicate && IsA(predicate, NullTest) &&
+ ((NullTest *) predicate)->nulltesttype == IS_NOT_NULL &&
+ equal(((NullTest *) predicate)->arg, isnullarg))
return true;
+
return false; /* we can't succeed below... */
}
@@ -1030,40 +1078,6 @@ predicate_refuted_by_simple_clause(Expr *predicate, Node *clause)
/*
- * Check whether a "foo IS NULL" test contradicts clause. (We say
- * "contradicts" rather than "refutes" because the refutation goes
- * both ways.)
- */
-static bool
-is_null_contradicts(NullTest *ntest, Node *clause)
-{
- Expr *isnullarg = ntest->arg;
-
- /* row IS NULL does not act in the simple way we have in mind */
- if (type_is_rowtype(exprType((Node *) isnullarg)))
- return false;
-
- /* foo IS NULL contradicts any strict op/func on foo */
- if (is_opclause(clause) &&
- list_member_strip(((OpExpr *) clause)->args, isnullarg) &&
- op_strict(((OpExpr *) clause)->opno))
- return true;
- if (is_funcclause(clause) &&
- list_member_strip(((FuncExpr *) clause)->args, isnullarg) &&
- func_strict(((FuncExpr *) clause)->funcid))
- return true;
-
- /* foo IS NULL contradicts foo IS NOT NULL */
- if (clause && IsA(clause, NullTest) &&
- ((NullTest *) clause)->nulltesttype == IS_NOT_NULL &&
- equal(((NullTest *) clause)->arg, isnullarg))
- return true;
-
- return false;
-}
-
-
-/*
* If clause asserts the non-truth of a subclause, return that subclause;
* otherwise return NULL.
*/