diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2006-09-28 20:51:43 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2006-09-28 20:51:43 +0000 |
commit | f213131f2024bcb85aea7d2a7dfadef6a0ee1b31 (patch) | |
tree | 090a85694dfd64f735732bc49b8489835186d83b /src/backend/optimizer/util/clauses.c | |
parent | d3aa4a8e33bee5e5274615cfd461aac810d7bbc1 (diff) | |
download | postgresql-f213131f2024bcb85aea7d2a7dfadef6a0ee1b31.tar.gz postgresql-f213131f2024bcb85aea7d2a7dfadef6a0ee1b31.zip |
Fix IS NULL and IS NOT NULL tests on row-valued expressions to conform to
the SQL spec, viz IS NULL is true if all the row's fields are null, IS NOT
NULL is true if all the row's fields are not null. The former coding got
this right for a limited number of cases with IS NULL (ie, those where it
could disassemble a ROW constructor at parse time), but was entirely wrong
for IS NOT NULL. Per report from Teodor.
I desisted from changing the behavior for arrays, since on closer inspection
it's not clear that there's any support for that in the SQL spec. This
probably needs more consideration.
Diffstat (limited to 'src/backend/optimizer/util/clauses.c')
-rw-r--r-- | src/backend/optimizer/util/clauses.c | 81 |
1 files changed, 80 insertions, 1 deletions
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 48b125a774c..99d3147aeb5 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.220 2006/09/06 20:40:47 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.221 2006/09/28 20:51:41 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -2099,6 +2099,85 @@ eval_const_expressions_mutator(Node *node, newfselect->resulttypmod = fselect->resulttypmod; return (Node *) newfselect; } + if (IsA(node, NullTest)) + { + NullTest *ntest = (NullTest *) node; + NullTest *newntest; + Node *arg; + + arg = eval_const_expressions_mutator((Node *) ntest->arg, + context); + if (arg && IsA(arg, RowExpr)) + { + RowExpr *rarg = (RowExpr *) arg; + List *newargs = NIL; + ListCell *l; + + /* + * We break ROW(...) IS [NOT] NULL into separate tests on its + * component fields. This form is usually more efficient to + * evaluate, as well as being more amenable to optimization. + */ + foreach(l, rarg->args) + { + Node *relem = (Node *) lfirst(l); + + /* + * A constant field refutes the whole NullTest if it's of + * the wrong nullness; else we can discard it. + */ + if (relem && IsA(relem, Const)) + { + Const *carg = (Const *) relem; + + if (carg->constisnull ? + (ntest->nulltesttype == IS_NOT_NULL) : + (ntest->nulltesttype == IS_NULL)) + return makeBoolConst(false, false); + continue; + } + newntest = makeNode(NullTest); + newntest->arg = (Expr *) relem; + newntest->nulltesttype = ntest->nulltesttype; + newargs = lappend(newargs, newntest); + } + /* If all the inputs were constants, result is TRUE */ + if (newargs == NIL) + return makeBoolConst(true, false); + /* If only one nonconst input, it's the result */ + if (list_length(newargs) == 1) + return (Node *) linitial(newargs); + /* Else we need an AND node */ + return (Node *) make_andclause(newargs); + } + if (arg && IsA(arg, Const)) + { + Const *carg = (Const *) arg; + bool result; + + switch (ntest->nulltesttype) + { + case IS_NULL: + result = carg->constisnull; + break; + case IS_NOT_NULL: + result = !carg->constisnull; + break; + default: + elog(ERROR, "unrecognized nulltesttype: %d", + (int) ntest->nulltesttype); + result = false; /* keep compiler quiet */ + break; + } + + return makeBoolConst(result, false); + } + + newntest = makeNode(NullTest); + newntest->arg = (Expr *) arg; + newntest->nulltesttype = ntest->nulltesttype; + return (Node *) newntest; + } if (IsA(node, BooleanTest)) { BooleanTest *btest = (BooleanTest *) node; |