1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-11 20:28:21 +03:00

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.
This commit is contained in:
Tom Lane
2006-09-28 20:51:43 +00:00
parent d3aa4a8e33
commit f213131f20
10 changed files with 253 additions and 130 deletions

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.193 2006/07/27 19:52:05 tgl Exp $
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.194 2006/09/28 20:51:41 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -119,7 +119,7 @@ static Datum ExecEvalMinMax(MinMaxExprState *minmaxExpr,
static Datum ExecEvalNullIf(FuncExprState *nullIfExpr,
ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
static Datum ExecEvalNullTest(GenericExprState *nstate,
static Datum ExecEvalNullTest(NullTestState *nstate,
ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
static Datum ExecEvalBooleanTest(GenericExprState *bstate,
@ -1247,8 +1247,7 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
funcrettype = exprType((Node *) funcexpr->expr);
returnsTuple = (funcrettype == RECORDOID ||
get_typtype(funcrettype) == 'c');
returnsTuple = type_is_rowtype(funcrettype);
/*
* Prepare a resultinfo node for communication. We always do this even if
@ -2683,7 +2682,7 @@ ExecEvalNullIf(FuncExprState *nullIfExpr,
* ----------------------------------------------------------------
*/
static Datum
ExecEvalNullTest(GenericExprState *nstate,
ExecEvalNullTest(NullTestState *nstate,
ExprContext *econtext,
bool *isNull,
ExprDoneCond *isDone)
@ -2696,28 +2695,77 @@ ExecEvalNullTest(GenericExprState *nstate,
if (isDone && *isDone == ExprEndResult)
return result; /* nothing to check */
switch (ntest->nulltesttype)
if (nstate->argisrow && !(*isNull))
{
case IS_NULL:
if (*isNull)
HeapTupleHeader tuple;
Oid tupType;
int32 tupTypmod;
TupleDesc tupDesc;
HeapTupleData tmptup;
int att;
tuple = DatumGetHeapTupleHeader(result);
tupType = HeapTupleHeaderGetTypeId(tuple);
tupTypmod = HeapTupleHeaderGetTypMod(tuple);
/* Lookup tupdesc if first time through or if type changes */
tupDesc = get_cached_rowtype(tupType, tupTypmod,
&nstate->argdesc, econtext);
/*
* heap_attisnull needs a HeapTuple not a bare HeapTupleHeader.
*/
tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
tmptup.t_data = tuple;
for (att = 1; att <= tupDesc->natts; att++)
{
/* ignore dropped columns */
if (tupDesc->attrs[att-1]->attisdropped)
continue;
if (heap_attisnull(&tmptup, att))
{
*isNull = false;
return BoolGetDatum(true);
/* null field disproves IS NOT NULL */
if (ntest->nulltesttype == IS_NOT_NULL)
return BoolGetDatum(false);
}
else
return BoolGetDatum(false);
case IS_NOT_NULL:
if (*isNull)
{
*isNull = false;
return BoolGetDatum(false);
/* non-null field disproves IS NULL */
if (ntest->nulltesttype == IS_NULL)
return BoolGetDatum(false);
}
else
return BoolGetDatum(true);
default:
elog(ERROR, "unrecognized nulltesttype: %d",
(int) ntest->nulltesttype);
return (Datum) 0; /* keep compiler quiet */
}
return BoolGetDatum(true);
}
else
{
/* Simple scalar-argument case, or a null rowtype datum */
switch (ntest->nulltesttype)
{
case IS_NULL:
if (*isNull)
{
*isNull = false;
return BoolGetDatum(true);
}
else
return BoolGetDatum(false);
case IS_NOT_NULL:
if (*isNull)
{
*isNull = false;
return BoolGetDatum(false);
}
else
return BoolGetDatum(true);
default:
elog(ERROR, "unrecognized nulltesttype: %d",
(int) ntest->nulltesttype);
return (Datum) 0; /* keep compiler quiet */
}
}
}
@ -3609,11 +3657,13 @@ ExecInitExpr(Expr *node, PlanState *parent)
case T_NullTest:
{
NullTest *ntest = (NullTest *) node;
GenericExprState *gstate = makeNode(GenericExprState);
NullTestState *nstate = makeNode(NullTestState);
gstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalNullTest;
gstate->arg = ExecInitExpr(ntest->arg, parent);
state = (ExprState *) gstate;
nstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalNullTest;
nstate->arg = ExecInitExpr(ntest->arg, parent);
nstate->argisrow = type_is_rowtype(exprType((Node *) ntest->arg));
nstate->argdesc = NULL;
state = (ExprState *) nstate;
}
break;
case T_BooleanTest: