Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit f213131

Browse files
committed
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.
1 parent d3aa4a8 commit f213131

File tree

10 files changed

+253
-130
lines changed

10 files changed

+253
-130
lines changed

doc/src/sgml/func.sgml

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.340 2006/09/22 16:20:00 tgl Exp $ -->
1+
<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.341 2006/09/28 20:51:41 tgl Exp $ -->
22

33
<chapter id="functions">
44
<title>Functions and Operators</title>
@@ -338,6 +338,19 @@
338338
</para>
339339
</tip>
340340

341+
<note>
342+
<para>
343+
If the <replaceable>expression</replaceable> is row-valued, then
344+
<literal>IS NULL</> is true when the row expression itself is null
345+
or when all the row's fields are null, while
346+
<literal>IS NOT NULL</> is true when the row expression itself is non-null
347+
and all the row's fields are non-null.
348+
This definition conforms to the SQL standard, and is a change from the
349+
inconsistent behavior exhibited by <productname>PostgreSQL</productname>
350+
versions prior to 8.2.
351+
</para>
352+
</note>
353+
341354
<para>
342355
<indexterm>
343356
<primary>IS DISTINCT FROM</primary>

src/backend/executor/execQual.c

Lines changed: 75 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.193 2006/07/27 19:52:05 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.194 2006/09/28 20:51:41 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -119,7 +119,7 @@ static Datum ExecEvalMinMax(MinMaxExprState *minmaxExpr,
119119
static Datum ExecEvalNullIf(FuncExprState *nullIfExpr,
120120
ExprContext *econtext,
121121
bool *isNull, ExprDoneCond *isDone);
122-
static Datum ExecEvalNullTest(GenericExprState *nstate,
122+
static Datum ExecEvalNullTest(NullTestState *nstate,
123123
ExprContext *econtext,
124124
bool *isNull, ExprDoneCond *isDone);
125125
static Datum ExecEvalBooleanTest(GenericExprState *bstate,
@@ -1247,8 +1247,7 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
12471247

12481248
funcrettype = exprType((Node *) funcexpr->expr);
12491249

1250-
returnsTuple = (funcrettype == RECORDOID ||
1251-
get_typtype(funcrettype) == 'c');
1250+
returnsTuple = type_is_rowtype(funcrettype);
12521251

12531252
/*
12541253
* Prepare a resultinfo node for communication. We always do this even if
@@ -2683,7 +2682,7 @@ ExecEvalNullIf(FuncExprState *nullIfExpr,
26832682
* ----------------------------------------------------------------
26842683
*/
26852684
static Datum
2686-
ExecEvalNullTest(GenericExprState *nstate,
2685+
ExecEvalNullTest(NullTestState *nstate,
26872686
ExprContext *econtext,
26882687
bool *isNull,
26892688
ExprDoneCond *isDone)
@@ -2696,28 +2695,77 @@ ExecEvalNullTest(GenericExprState *nstate,
26962695
if (isDone && *isDone == ExprEndResult)
26972696
return result; /* nothing to check */
26982697

2699-
switch (ntest->nulltesttype)
2698+
if (nstate->argisrow && !(*isNull))
27002699
{
2701-
case IS_NULL:
2702-
if (*isNull)
2700+
HeapTupleHeader tuple;
2701+
Oid tupType;
2702+
int32 tupTypmod;
2703+
TupleDesc tupDesc;
2704+
HeapTupleData tmptup;
2705+
int att;
2706+
2707+
tuple = DatumGetHeapTupleHeader(result);
2708+
2709+
tupType = HeapTupleHeaderGetTypeId(tuple);
2710+
tupTypmod = HeapTupleHeaderGetTypMod(tuple);
2711+
2712+
/* Lookup tupdesc if first time through or if type changes */
2713+
tupDesc = get_cached_rowtype(tupType, tupTypmod,
2714+
&nstate->argdesc, econtext);
2715+
2716+
/*
2717+
* heap_attisnull needs a HeapTuple not a bare HeapTupleHeader.
2718+
*/
2719+
tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
2720+
tmptup.t_data = tuple;
2721+
2722+
for (att = 1; att <= tupDesc->natts; att++)
2723+
{
2724+
/* ignore dropped columns */
2725+
if (tupDesc->attrs[att-1]->attisdropped)
2726+
continue;
2727+
if (heap_attisnull(&tmptup, att))
27032728
{
2704-
*isNull = false;
2705-
return BoolGetDatum(true);
2729+
/* null field disproves IS NOT NULL */
2730+
if (ntest->nulltesttype == IS_NOT_NULL)
2731+
return BoolGetDatum(false);
27062732
}
27072733
else
2708-
return BoolGetDatum(false);
2709-
case IS_NOT_NULL:
2710-
if (*isNull)
27112734
{
2712-
*isNull = false;
2713-
return BoolGetDatum(false);
2735+
/* non-null field disproves IS NULL */
2736+
if (ntest->nulltesttype == IS_NULL)
2737+
return BoolGetDatum(false);
27142738
}
2715-
else
2716-
return BoolGetDatum(true);
2717-
default:
2718-
elog(ERROR, "unrecognized nulltesttype: %d",
2719-
(int) ntest->nulltesttype);
2720-
return (Datum) 0; /* keep compiler quiet */
2739+
}
2740+
2741+
return BoolGetDatum(true);
2742+
}
2743+
else
2744+
{
2745+
/* Simple scalar-argument case, or a null rowtype datum */
2746+
switch (ntest->nulltesttype)
2747+
{
2748+
case IS_NULL:
2749+
if (*isNull)
2750+
{
2751+
*isNull = false;
2752+
return BoolGetDatum(true);
2753+
}
2754+
else
2755+
return BoolGetDatum(false);
2756+
case IS_NOT_NULL:
2757+
if (*isNull)
2758+
{
2759+
*isNull = false;
2760+
return BoolGetDatum(false);
2761+
}
2762+
else
2763+
return BoolGetDatum(true);
2764+
default:
2765+
elog(ERROR, "unrecognized nulltesttype: %d",
2766+
(int) ntest->nulltesttype);
2767+
return (Datum) 0; /* keep compiler quiet */
2768+
}
27212769
}
27222770
}
27232771

@@ -3609,11 +3657,13 @@ ExecInitExpr(Expr *node, PlanState *parent)
36093657
case T_NullTest:
36103658
{
36113659
NullTest *ntest = (NullTest *) node;
3612-
GenericExprState *gstate = makeNode(GenericExprState);
3660+
NullTestState *nstate = makeNode(NullTestState);
36133661

3614-
gstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalNullTest;
3615-
gstate->arg = ExecInitExpr(ntest->arg, parent);
3616-
state = (ExprState *) gstate;
3662+
nstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalNullTest;
3663+
nstate->arg = ExecInitExpr(ntest->arg, parent);
3664+
nstate->argisrow = type_is_rowtype(exprType((Node *) ntest->arg));
3665+
nstate->argdesc = NULL;
3666+
state = (ExprState *) nstate;
36173667
}
36183668
break;
36193669
case T_BooleanTest:

src/backend/optimizer/util/clauses.c

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.220 2006/09/06 20:40:47 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.221 2006/09/28 20:51:41 tgl Exp $
1212
*
1313
* HISTORY
1414
* AUTHOR DATE MAJOR EVENT
@@ -2099,6 +2099,85 @@ eval_const_expressions_mutator(Node *node,
20992099
newfselect->resulttypmod = fselect->resulttypmod;
21002100
return (Node *) newfselect;
21012101
}
2102+
if (IsA(node, NullTest))
2103+
{
2104+
NullTest *ntest = (NullTest *) node;
2105+
NullTest *newntest;
2106+
Node *arg;
2107+
2108+
arg = eval_const_expressions_mutator((Node *) ntest->arg,
2109+
context);
2110+
if (arg && IsA(arg, RowExpr))
2111+
{
2112+
RowExpr *rarg = (RowExpr *) arg;
2113+
List *newargs = NIL;
2114+
ListCell *l;
2115+
2116+
/*
2117+
* We break ROW(...) IS [NOT] NULL into separate tests on its
2118+
* component fields. This form is usually more efficient to
2119+
* evaluate, as well as being more amenable to optimization.
2120+
*/
2121+
foreach(l, rarg->args)
2122+
{
2123+
Node *relem = (Node *) lfirst(l);
2124+
2125+
/*
2126+
* A constant field refutes the whole NullTest if it's of
2127+
* the wrong nullness; else we can discard it.
2128+
*/
2129+
if (relem && IsA(relem, Const))
2130+
{
2131+
Const *carg = (Const *) relem;
2132+
2133+
if (carg->constisnull ?
2134+
(ntest->nulltesttype == IS_NOT_NULL) :
2135+
(ntest->nulltesttype == IS_NULL))
2136+
return makeBoolConst(false, false);
2137+
continue;
2138+
}
2139+
newntest = makeNode(NullTest);
2140+
newntest->arg = (Expr *) relem;
2141+
newntest->nulltesttype = ntest->nulltesttype;
2142+
newargs = lappend(newargs, newntest);
2143+
}
2144+
/* If all the inputs were constants, result is TRUE */
2145+
if (newargs == NIL)
2146+
return makeBoolConst(true, false);
2147+
/* If only one nonconst input, it's the result */
2148+
if (list_length(newargs) == 1)
2149+
return (Node *) linitial(newargs);
2150+
/* Else we need an AND node */
2151+
return (Node *) make_andclause(newargs);
2152+
}
2153+
if (arg && IsA(arg, Const))
2154+
{
2155+
Const *carg = (Const *) arg;
2156+
bool result;
2157+
2158+
switch (ntest->nulltesttype)
2159+
{
2160+
case IS_NULL:
2161+
result = carg->constisnull;
2162+
break;
2163+
case IS_NOT_NULL:
2164+
result = !carg->constisnull;
2165+
break;
2166+
default:
2167+
elog(ERROR, "unrecognized nulltesttype: %d",
2168+
(int) ntest->nulltesttype);
2169+
result = false; /* keep compiler quiet */
2170+
break;
2171+
}
2172+
2173+
return makeBoolConst(result, false);
2174+
}
2175+
2176+
newntest = makeNode(NullTest);
2177+
newntest->arg = (Expr *) arg;
2178+
newntest->nulltesttype = ntest->nulltesttype;
2179+
return (Node *) newntest;
2180+
}
21022181
if (IsA(node, BooleanTest))
21032182
{
21042183
BooleanTest *btest = (BooleanTest *) node;

src/backend/optimizer/util/predtest.c

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
*
1010
*
1111
* IDENTIFICATION
12-
* $PostgreSQL: pgsql/src/backend/optimizer/util/predtest.c,v 1.8 2006/08/05 00:21:14 tgl Exp $
12+
* $PostgreSQL: pgsql/src/backend/optimizer/util/predtest.c,v 1.9 2006/09/28 20:51:42 tgl Exp $
1313
*
1414
*-------------------------------------------------------------------------
1515
*/
@@ -21,6 +21,7 @@
2121
#include "executor/executor.h"
2222
#include "optimizer/clauses.h"
2323
#include "optimizer/predtest.h"
24+
#include "parser/parse_expr.h"
2425
#include "utils/array.h"
2526
#include "utils/lsyscache.h"
2627
#include "utils/syscache.h"
@@ -931,14 +932,18 @@ predicate_implied_by_simple_clause(Expr *predicate, Node *clause)
931932
{
932933
Expr *nonnullarg = ((NullTest *) predicate)->arg;
933934

934-
if (is_opclause(clause) &&
935-
list_member(((OpExpr *) clause)->args, nonnullarg) &&
936-
op_strict(((OpExpr *) clause)->opno))
937-
return true;
938-
if (is_funcclause(clause) &&
939-
list_member(((FuncExpr *) clause)->args, nonnullarg) &&
940-
func_strict(((FuncExpr *) clause)->funcid))
941-
return true;
935+
/* row IS NOT NULL does not act in the simple way we have in mind */
936+
if (!type_is_rowtype(exprType((Node *) nonnullarg)))
937+
{
938+
if (is_opclause(clause) &&
939+
list_member(((OpExpr *) clause)->args, nonnullarg) &&
940+
op_strict(((OpExpr *) clause)->opno))
941+
return true;
942+
if (is_funcclause(clause) &&
943+
list_member(((FuncExpr *) clause)->args, nonnullarg) &&
944+
func_strict(((FuncExpr *) clause)->funcid))
945+
return true;
946+
}
942947
return false; /* we can't succeed below... */
943948
}
944949

@@ -978,14 +983,18 @@ predicate_refuted_by_simple_clause(Expr *predicate, Node *clause)
978983
{
979984
Expr *isnullarg = ((NullTest *) predicate)->arg;
980985

981-
if (is_opclause(clause) &&
982-
list_member(((OpExpr *) clause)->args, isnullarg) &&
983-
op_strict(((OpExpr *) clause)->opno))
984-
return true;
985-
if (is_funcclause(clause) &&
986-
list_member(((FuncExpr *) clause)->args, isnullarg) &&
987-
func_strict(((FuncExpr *) clause)->funcid))
988-
return true;
986+
/* row IS NULL does not act in the simple way we have in mind */
987+
if (!type_is_rowtype(exprType((Node *) isnullarg)))
988+
{
989+
if (is_opclause(clause) &&
990+
list_member(((OpExpr *) clause)->args, isnullarg) &&
991+
op_strict(((OpExpr *) clause)->opno))
992+
return true;
993+
if (is_funcclause(clause) &&
994+
list_member(((FuncExpr *) clause)->args, isnullarg) &&
995+
func_strict(((FuncExpr *) clause)->funcid))
996+
return true;
997+
}
989998
return false; /* we can't succeed below... */
990999
}
9911000

0 commit comments

Comments
 (0)