Improve predicate_refuted_by_simple_clause() to handle IS NULL and IS NOT NULL

more completely.  The motivation for having it understand IS NULL at all was
to allow use of "foo IS NULL" as one of the subsets of a partitioning on
"foo", but as reported by Aleksander Kmetec, it wasn't really getting the job
done.  Backpatch to 8.2 since this is arguably a performance bug.
This commit is contained in:
Tom Lane 2007-05-12 19:22:35 +00:00
parent 9aa3c782c9
commit 1856e609ec
1 changed files with 51 additions and 16 deletions

View File

@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/util/predtest.c,v 1.14 2007/03/17 00:11:04 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/util/predtest.c,v 1.15 2007/05/12 19:22:35 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -82,6 +82,7 @@ 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 btree_predicate_proof(Expr *predicate, Node *clause,
bool refute_it);
@ -987,7 +988,10 @@ 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.
* 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.)
*
* Finally, we may be able to deduce something using knowledge about btree
* operator families; this is encapsulated in btree_predicate_proof().
@ -1001,24 +1005,21 @@ predicate_refuted_by_simple_clause(Expr *predicate, Node *clause)
if ((Node *) predicate == clause)
return false;
/* Try the IS NULL case */
/* Try the predicate-IS-NULL case */
if (predicate && IsA(predicate, NullTest) &&
((NullTest *) predicate)->nulltesttype == IS_NULL)
{
Expr *isnullarg = ((NullTest *) predicate)->arg;
if (is_null_contradicts((NullTest *) predicate, clause))
return true;
return false; /* we can't succeed below... */
}
/* row IS NULL does not act in the simple way we have in mind */
if (!type_is_rowtype(exprType((Node *) isnullarg)))
{
if (is_opclause(clause) &&
list_member(((OpExpr *) clause)->args, isnullarg) &&
op_strict(((OpExpr *) clause)->opno))
return true;
if (is_funcclause(clause) &&
list_member(((FuncExpr *) clause)->args, isnullarg) &&
func_strict(((FuncExpr *) clause)->funcid))
return true;
}
/* Try the clause-IS-NULL case */
if (clause && IsA(clause, NullTest) &&
((NullTest *) clause)->nulltesttype == IS_NULL)
{
if (is_null_contradicts((NullTest *) clause, (Node *) predicate))
return true;
return false; /* we can't succeed below... */
}
@ -1027,6 +1028,40 @@ 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(((OpExpr *) clause)->args, isnullarg) &&
op_strict(((OpExpr *) clause)->opno))
return true;
if (is_funcclause(clause) &&
list_member(((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.