Skip to content

Commit 51972a9

Browse files
committed
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.
1 parent de25638 commit 51972a9

File tree

22 files changed

+645
-108
lines changed

22 files changed

+645
-108
lines changed

doc/src/sgml/func.sgml

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!--
2-
$Header: /cvsroot/pgsql/doc/src/sgml/func.sgml,v 1.139 2003/02/13 05:24:01 momjian Exp $
2+
$Header: /cvsroot/pgsql/doc/src/sgml/func.sgml,v 1.140 2003/02/16 02:30:36 tgl Exp $
33
PostgreSQL documentation
44
-->
55

@@ -6295,17 +6295,6 @@ SELECT NULLIF(value, '(none)') ...
62956295
</programlisting>
62966296
</para>
62976297

6298-
<tip>
6299-
<para>
6300-
<function>COALESCE</function> and <function>NULLIF</function> are
6301-
just shorthand for <token>CASE</token> expressions. They are actually
6302-
converted into <token>CASE</token> expressions at a very early stage
6303-
of processing, and subsequent processing thinks it is dealing with
6304-
<token>CASE</token>. Thus an incorrect <function>COALESCE</function> or
6305-
<function>NULLIF</function> usage may draw an error message that
6306-
refers to <token>CASE</token>.
6307-
</para>
6308-
</tip>
63096298
</sect2>
63106299

63116300
</sect1>

src/backend/catalog/dependency.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
* Portions Copyright (c) 1994, Regents of the University of California
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/catalog/dependency.c,v 1.21 2003/02/09 06:56:26 tgl Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/catalog/dependency.c,v 1.22 2003/02/16 02:30:37 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -933,6 +933,14 @@ find_expr_references_walker(Node *node,
933933
&context->addrs);
934934
/* fall through to examine arguments */
935935
}
936+
if (IsA(node, NullIfExpr))
937+
{
938+
NullIfExpr *nullifexpr = (NullIfExpr *) node;
939+
940+
add_object_address(OCLASS_OPERATOR, nullifexpr->opno, 0,
941+
&context->addrs);
942+
/* fall through to examine arguments */
943+
}
936944
if (IsA(node, Aggref))
937945
{
938946
Aggref *aggref = (Aggref *) node;

src/backend/executor/execQual.c

Lines changed: 135 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.124 2003/02/03 21:15:43 tgl Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.125 2003/02/16 02:30:37 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -64,7 +64,7 @@ static Datum ExecEvalFunc(FuncExprState *fcache, ExprContext *econtext,
6464
static Datum ExecEvalOper(FuncExprState *fcache, ExprContext *econtext,
6565
bool *isNull, ExprDoneCond *isDone);
6666
static Datum ExecEvalDistinct(FuncExprState *fcache, ExprContext *econtext,
67-
bool *isNull, ExprDoneCond *isDone);
67+
bool *isNull);
6868
static ExprDoneCond ExecEvalFuncArgs(FunctionCallInfo fcinfo,
6969
List *argList, ExprContext *econtext);
7070
static Datum ExecEvalNot(BoolExprState *notclause, ExprContext *econtext,
@@ -75,6 +75,11 @@ static Datum ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext,
7575
bool *isNull);
7676
static Datum ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
7777
bool *isNull, ExprDoneCond *isDone);
78+
static Datum ExecEvalCoalesce(CoalesceExprState *coalesceExpr,
79+
ExprContext *econtext,
80+
bool *isNull);
81+
static Datum ExecEvalNullIf(FuncExprState *nullIfExpr, ExprContext *econtext,
82+
bool *isNull);
7883
static Datum ExecEvalNullTest(GenericExprState *nstate,
7984
ExprContext *econtext,
8085
bool *isNull, ExprDoneCond *isDone);
@@ -1187,8 +1192,7 @@ ExecEvalOper(FuncExprState *fcache,
11871192
static Datum
11881193
ExecEvalDistinct(FuncExprState *fcache,
11891194
ExprContext *econtext,
1190-
bool *isNull,
1191-
ExprDoneCond *isDone)
1195+
bool *isNull)
11921196
{
11931197
Datum result;
11941198
FunctionCallInfoData fcinfo;
@@ -1370,6 +1374,7 @@ ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext, bool *isNull)
13701374
return BoolGetDatum(!AnyNull);
13711375
}
13721376

1377+
13731378
/* ----------------------------------------------------------------
13741379
* ExecEvalCase
13751380
*
@@ -1429,6 +1434,91 @@ ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
14291434
return (Datum) 0;
14301435
}
14311436

1437+
/* ----------------------------------------------------------------
1438+
* ExecEvalCoalesce
1439+
* ----------------------------------------------------------------
1440+
*/
1441+
static Datum
1442+
ExecEvalCoalesce(CoalesceExprState *coalesceExpr, ExprContext *econtext,
1443+
bool *isNull)
1444+
{
1445+
List *arg;
1446+
1447+
/* Simply loop through until something NOT NULL is found */
1448+
foreach(arg, coalesceExpr->args)
1449+
{
1450+
ExprState *e = (ExprState *) lfirst(arg);
1451+
Datum value;
1452+
1453+
value = ExecEvalExpr(e, econtext, isNull, NULL);
1454+
if (!*isNull)
1455+
return value;
1456+
}
1457+
1458+
/* Else return NULL */
1459+
*isNull = true;
1460+
return (Datum) 0;
1461+
}
1462+
1463+
/* ----------------------------------------------------------------
1464+
* ExecEvalNullIf
1465+
*
1466+
* Note that this is *always* derived from the equals operator,
1467+
* but since we need special processing of the arguments
1468+
* we can not simply reuse ExecEvalOper() or ExecEvalFunc().
1469+
* ----------------------------------------------------------------
1470+
*/
1471+
static Datum
1472+
ExecEvalNullIf(FuncExprState *fcache, ExprContext *econtext,
1473+
bool *isNull)
1474+
{
1475+
Datum result;
1476+
FunctionCallInfoData fcinfo;
1477+
ExprDoneCond argDone;
1478+
List *argList;
1479+
1480+
/*
1481+
* Initialize function cache if first time through
1482+
*/
1483+
if (fcache->func.fn_oid == InvalidOid)
1484+
{
1485+
NullIfExpr *op = (NullIfExpr *) fcache->xprstate.expr;
1486+
1487+
init_fcache(op->opfuncid, fcache, econtext->ecxt_per_query_memory);
1488+
Assert(!fcache->func.fn_retset);
1489+
}
1490+
1491+
/*
1492+
* extract info from fcache
1493+
*/
1494+
argList = fcache->args;
1495+
1496+
/* Need to prep callinfo structure */
1497+
MemSet(&fcinfo, 0, sizeof(fcinfo));
1498+
fcinfo.flinfo = &(fcache->func);
1499+
argDone = ExecEvalFuncArgs(&fcinfo, argList, econtext);
1500+
if (argDone != ExprSingleResult)
1501+
elog(ERROR, "NULLIF does not support set arguments");
1502+
Assert(fcinfo.nargs == 2);
1503+
1504+
/* if either argument is NULL they can't be equal */
1505+
if (!fcinfo.argnull[0] && !fcinfo.argnull[1])
1506+
{
1507+
fcinfo.isnull = false;
1508+
result = FunctionCallInvoke(&fcinfo);
1509+
/* if the arguments are equal return null */
1510+
if (!fcinfo.isnull && DatumGetBool(result))
1511+
{
1512+
*isNull = true;
1513+
return (Datum) 0;
1514+
}
1515+
}
1516+
1517+
/* else return first argument */
1518+
*isNull = fcinfo.argnull[0];
1519+
return fcinfo.arg[0];
1520+
}
1521+
14321522
/* ----------------------------------------------------------------
14331523
* ExecEvalNullTest
14341524
*
@@ -1778,7 +1868,7 @@ ExecEvalExpr(ExprState *expression,
17781868
break;
17791869
case T_DistinctExpr:
17801870
retDatum = ExecEvalDistinct((FuncExprState *) expression, econtext,
1781-
isNull, isDone);
1871+
isNull);
17821872
break;
17831873
case T_BoolExpr:
17841874
{
@@ -1826,6 +1916,16 @@ ExecEvalExpr(ExprState *expression,
18261916
isNull,
18271917
isDone);
18281918
break;
1919+
case T_CoalesceExpr:
1920+
retDatum = ExecEvalCoalesce((CoalesceExprState *) expression,
1921+
econtext,
1922+
isNull);
1923+
break;
1924+
case T_NullIfExpr:
1925+
retDatum = ExecEvalNullIf((FuncExprState *) expression,
1926+
econtext,
1927+
isNull);
1928+
break;
18291929
case T_NullTest:
18301930
retDatum = ExecEvalNullTest((GenericExprState *) expression,
18311931
econtext,
@@ -2082,6 +2182,36 @@ ExecInitExpr(Expr *node, PlanState *parent)
20822182
state = (ExprState *) cstate;
20832183
}
20842184
break;
2185+
case T_CoalesceExpr:
2186+
{
2187+
CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
2188+
CoalesceExprState *cstate = makeNode(CoalesceExprState);
2189+
List *outlist = NIL;
2190+
List *inlist;
2191+
2192+
foreach(inlist, coalesceexpr->args)
2193+
{
2194+
Expr *e = (Expr *) lfirst(inlist);
2195+
ExprState *estate;
2196+
2197+
estate = ExecInitExpr(e, parent);
2198+
outlist = lappend(outlist, estate);
2199+
}
2200+
cstate->args = outlist;
2201+
state = (ExprState *) cstate;
2202+
}
2203+
break;
2204+
case T_NullIfExpr:
2205+
{
2206+
NullIfExpr *nullifexpr = (NullIfExpr *) node;
2207+
FuncExprState *fstate = makeNode(FuncExprState);
2208+
2209+
fstate->args = (List *)
2210+
ExecInitExpr((Expr *) nullifexpr->args, parent);
2211+
fstate->func.fn_oid = InvalidOid; /* not initialized */
2212+
state = (ExprState *) fstate;
2213+
}
2214+
break;
20852215
case T_NullTest:
20862216
{
20872217
NullTest *ntest = (NullTest *) node;

src/backend/nodes/copyfuncs.c

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
* Portions Copyright (c) 1994, Regents of the University of California
1616
*
1717
* IDENTIFICATION
18-
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.243 2003/02/10 04:44:44 tgl Exp $
18+
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.244 2003/02/16 02:30:37 tgl Exp $
1919
*
2020
*-------------------------------------------------------------------------
2121
*/
@@ -785,7 +785,7 @@ _copyOpExpr(OpExpr *from)
785785
}
786786

787787
/*
788-
* _copyDistinctExpr
788+
* _copyDistinctExpr (same as OpExpr)
789789
*/
790790
static DistinctExpr *
791791
_copyDistinctExpr(DistinctExpr *from)
@@ -919,6 +919,37 @@ _copyCaseWhen(CaseWhen *from)
919919
return newnode;
920920
}
921921

922+
/*
923+
* _copyCoalesceExpr
924+
*/
925+
static CoalesceExpr *
926+
_copyCoalesceExpr(CoalesceExpr *from)
927+
{
928+
CoalesceExpr *newnode = makeNode(CoalesceExpr);
929+
930+
COPY_SCALAR_FIELD(coalescetype);
931+
COPY_NODE_FIELD(args);
932+
933+
return newnode;
934+
}
935+
936+
/*
937+
* _copyNullIfExpr (same as OpExpr)
938+
*/
939+
static NullIfExpr *
940+
_copyNullIfExpr(NullIfExpr *from)
941+
{
942+
NullIfExpr *newnode = makeNode(NullIfExpr);
943+
944+
COPY_SCALAR_FIELD(opno);
945+
COPY_SCALAR_FIELD(opfuncid);
946+
COPY_SCALAR_FIELD(opresulttype);
947+
COPY_SCALAR_FIELD(opretset);
948+
COPY_NODE_FIELD(args);
949+
950+
return newnode;
951+
}
952+
922953
/*
923954
* _copyNullTest
924955
*/
@@ -2484,6 +2515,12 @@ copyObject(void *from)
24842515
case T_CaseWhen:
24852516
retval = _copyCaseWhen(from);
24862517
break;
2518+
case T_CoalesceExpr:
2519+
retval = _copyCoalesceExpr(from);
2520+
break;
2521+
case T_NullIfExpr:
2522+
retval = _copyNullIfExpr(from);
2523+
break;
24872524
case T_NullTest:
24882525
retval = _copyNullTest(from);
24892526
break;

src/backend/nodes/equalfuncs.c

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
* Portions Copyright (c) 1994, Regents of the University of California
1919
*
2020
* IDENTIFICATION
21-
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.186 2003/02/10 04:44:45 tgl Exp $
21+
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.187 2003/02/16 02:30:37 tgl Exp $
2222
*
2323
*-------------------------------------------------------------------------
2424
*/
@@ -378,6 +378,37 @@ _equalCaseWhen(CaseWhen *a, CaseWhen *b)
378378
return true;
379379
}
380380

381+
static bool
382+
_equalCoalesceExpr(CoalesceExpr *a, CoalesceExpr *b)
383+
{
384+
COMPARE_SCALAR_FIELD(coalescetype);
385+
COMPARE_NODE_FIELD(args);
386+
387+
return true;
388+
}
389+
390+
static bool
391+
_equalNullIfExpr(NullIfExpr *a, NullIfExpr *b)
392+
{
393+
COMPARE_SCALAR_FIELD(opno);
394+
/*
395+
* Special-case opfuncid: it is allowable for it to differ if one
396+
* node contains zero and the other doesn't. This just means that the
397+
* one node isn't as far along in the parse/plan pipeline and hasn't
398+
* had the opfuncid cache filled yet.
399+
*/
400+
if (a->opfuncid != b->opfuncid &&
401+
a->opfuncid != 0 &&
402+
b->opfuncid != 0)
403+
return false;
404+
405+
COMPARE_SCALAR_FIELD(opresulttype);
406+
COMPARE_SCALAR_FIELD(opretset);
407+
COMPARE_NODE_FIELD(args);
408+
409+
return true;
410+
}
411+
381412
static bool
382413
_equalNullTest(NullTest *a, NullTest *b)
383414
{
@@ -1613,6 +1644,12 @@ equal(void *a, void *b)
16131644
case T_CaseWhen:
16141645
retval = _equalCaseWhen(a, b);
16151646
break;
1647+
case T_CoalesceExpr:
1648+
retval = _equalCoalesceExpr(a, b);
1649+
break;
1650+
case T_NullIfExpr:
1651+
retval = _equalNullIfExpr(a, b);
1652+
break;
16161653
case T_NullTest:
16171654
retval = _equalNullTest(a, b);
16181655
break;

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy