1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-16 06:01:02 +03:00

COALESCE() and NULLIF() are now first-class expressions, not macros

that turn into CASE expressions.  They evaluate their arguments at most
once.  Patch by Kris Jurka, review and (very light) editorializing by me.
This commit is contained in:
Tom Lane
2003-02-16 02:30:39 +00:00
parent de25638d2f
commit 51972a9d5d
22 changed files with 644 additions and 107 deletions

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.124 2003/02/03 21:15:43 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.125 2003/02/16 02:30:37 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -64,7 +64,7 @@ static Datum ExecEvalFunc(FuncExprState *fcache, ExprContext *econtext,
static Datum ExecEvalOper(FuncExprState *fcache, ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
static Datum ExecEvalDistinct(FuncExprState *fcache, ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
bool *isNull);
static ExprDoneCond ExecEvalFuncArgs(FunctionCallInfo fcinfo,
List *argList, ExprContext *econtext);
static Datum ExecEvalNot(BoolExprState *notclause, ExprContext *econtext,
@ -75,6 +75,11 @@ static Datum ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext,
bool *isNull);
static Datum ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
static Datum ExecEvalCoalesce(CoalesceExprState *coalesceExpr,
ExprContext *econtext,
bool *isNull);
static Datum ExecEvalNullIf(FuncExprState *nullIfExpr, ExprContext *econtext,
bool *isNull);
static Datum ExecEvalNullTest(GenericExprState *nstate,
ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
@ -1187,8 +1192,7 @@ ExecEvalOper(FuncExprState *fcache,
static Datum
ExecEvalDistinct(FuncExprState *fcache,
ExprContext *econtext,
bool *isNull,
ExprDoneCond *isDone)
bool *isNull)
{
Datum result;
FunctionCallInfoData fcinfo;
@ -1370,6 +1374,7 @@ ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext, bool *isNull)
return BoolGetDatum(!AnyNull);
}
/* ----------------------------------------------------------------
* ExecEvalCase
*
@ -1429,6 +1434,91 @@ ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
return (Datum) 0;
}
/* ----------------------------------------------------------------
* ExecEvalCoalesce
* ----------------------------------------------------------------
*/
static Datum
ExecEvalCoalesce(CoalesceExprState *coalesceExpr, ExprContext *econtext,
bool *isNull)
{
List *arg;
/* Simply loop through until something NOT NULL is found */
foreach(arg, coalesceExpr->args)
{
ExprState *e = (ExprState *) lfirst(arg);
Datum value;
value = ExecEvalExpr(e, econtext, isNull, NULL);
if (!*isNull)
return value;
}
/* Else return NULL */
*isNull = true;
return (Datum) 0;
}
/* ----------------------------------------------------------------
* ExecEvalNullIf
*
* Note that this is *always* derived from the equals operator,
* but since we need special processing of the arguments
* we can not simply reuse ExecEvalOper() or ExecEvalFunc().
* ----------------------------------------------------------------
*/
static Datum
ExecEvalNullIf(FuncExprState *fcache, ExprContext *econtext,
bool *isNull)
{
Datum result;
FunctionCallInfoData fcinfo;
ExprDoneCond argDone;
List *argList;
/*
* Initialize function cache if first time through
*/
if (fcache->func.fn_oid == InvalidOid)
{
NullIfExpr *op = (NullIfExpr *) fcache->xprstate.expr;
init_fcache(op->opfuncid, fcache, econtext->ecxt_per_query_memory);
Assert(!fcache->func.fn_retset);
}
/*
* extract info from fcache
*/
argList = fcache->args;
/* Need to prep callinfo structure */
MemSet(&fcinfo, 0, sizeof(fcinfo));
fcinfo.flinfo = &(fcache->func);
argDone = ExecEvalFuncArgs(&fcinfo, argList, econtext);
if (argDone != ExprSingleResult)
elog(ERROR, "NULLIF does not support set arguments");
Assert(fcinfo.nargs == 2);
/* if either argument is NULL they can't be equal */
if (!fcinfo.argnull[0] && !fcinfo.argnull[1])
{
fcinfo.isnull = false;
result = FunctionCallInvoke(&fcinfo);
/* if the arguments are equal return null */
if (!fcinfo.isnull && DatumGetBool(result))
{
*isNull = true;
return (Datum) 0;
}
}
/* else return first argument */
*isNull = fcinfo.argnull[0];
return fcinfo.arg[0];
}
/* ----------------------------------------------------------------
* ExecEvalNullTest
*
@ -1778,7 +1868,7 @@ ExecEvalExpr(ExprState *expression,
break;
case T_DistinctExpr:
retDatum = ExecEvalDistinct((FuncExprState *) expression, econtext,
isNull, isDone);
isNull);
break;
case T_BoolExpr:
{
@ -1826,6 +1916,16 @@ ExecEvalExpr(ExprState *expression,
isNull,
isDone);
break;
case T_CoalesceExpr:
retDatum = ExecEvalCoalesce((CoalesceExprState *) expression,
econtext,
isNull);
break;
case T_NullIfExpr:
retDatum = ExecEvalNullIf((FuncExprState *) expression,
econtext,
isNull);
break;
case T_NullTest:
retDatum = ExecEvalNullTest((GenericExprState *) expression,
econtext,
@ -2082,6 +2182,36 @@ ExecInitExpr(Expr *node, PlanState *parent)
state = (ExprState *) cstate;
}
break;
case T_CoalesceExpr:
{
CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
CoalesceExprState *cstate = makeNode(CoalesceExprState);
List *outlist = NIL;
List *inlist;
foreach(inlist, coalesceexpr->args)
{
Expr *e = (Expr *) lfirst(inlist);
ExprState *estate;
estate = ExecInitExpr(e, parent);
outlist = lappend(outlist, estate);
}
cstate->args = outlist;
state = (ExprState *) cstate;
}
break;
case T_NullIfExpr:
{
NullIfExpr *nullifexpr = (NullIfExpr *) node;
FuncExprState *fstate = makeNode(FuncExprState);
fstate->args = (List *)
ExecInitExpr((Expr *) nullifexpr->args, parent);
fstate->func.fn_oid = InvalidOid; /* not initialized */
state = (ExprState *) fstate;
}
break;
case T_NullTest:
{
NullTest *ntest = (NullTest *) node;