Commit 51972a9d authored by Tom Lane's avatar Tom Lane

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.
parent de25638d
<!-- <!--
$Header: /cvsroot/pgsql/doc/src/sgml/func.sgml,v 1.139 2003/02/13 05:24:01 momjian Exp $ $Header: /cvsroot/pgsql/doc/src/sgml/func.sgml,v 1.140 2003/02/16 02:30:36 tgl Exp $
PostgreSQL documentation PostgreSQL documentation
--> -->
...@@ -6295,17 +6295,6 @@ SELECT NULLIF(value, '(none)') ... ...@@ -6295,17 +6295,6 @@ SELECT NULLIF(value, '(none)') ...
</programlisting> </programlisting>
</para> </para>
<tip>
<para>
<function>COALESCE</function> and <function>NULLIF</function> are
just shorthand for <token>CASE</token> expressions. They are actually
converted into <token>CASE</token> expressions at a very early stage
of processing, and subsequent processing thinks it is dealing with
<token>CASE</token>. Thus an incorrect <function>COALESCE</function> or
<function>NULLIF</function> usage may draw an error message that
refers to <token>CASE</token>.
</para>
</tip>
</sect2> </sect2>
</sect1> </sect1>
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/dependency.c,v 1.21 2003/02/09 06:56:26 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/catalog/dependency.c,v 1.22 2003/02/16 02:30:37 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -933,6 +933,14 @@ find_expr_references_walker(Node *node, ...@@ -933,6 +933,14 @@ find_expr_references_walker(Node *node,
&context->addrs); &context->addrs);
/* fall through to examine arguments */ /* fall through to examine arguments */
} }
if (IsA(node, NullIfExpr))
{
NullIfExpr *nullifexpr = (NullIfExpr *) node;
add_object_address(OCLASS_OPERATOR, nullifexpr->opno, 0,
&context->addrs);
/* fall through to examine arguments */
}
if (IsA(node, Aggref)) if (IsA(node, Aggref))
{ {
Aggref *aggref = (Aggref *) node; Aggref *aggref = (Aggref *) node;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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, ...@@ -64,7 +64,7 @@ static Datum ExecEvalFunc(FuncExprState *fcache, ExprContext *econtext,
static Datum ExecEvalOper(FuncExprState *fcache, ExprContext *econtext, static Datum ExecEvalOper(FuncExprState *fcache, ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone); bool *isNull, ExprDoneCond *isDone);
static Datum ExecEvalDistinct(FuncExprState *fcache, ExprContext *econtext, static Datum ExecEvalDistinct(FuncExprState *fcache, ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone); bool *isNull);
static ExprDoneCond ExecEvalFuncArgs(FunctionCallInfo fcinfo, static ExprDoneCond ExecEvalFuncArgs(FunctionCallInfo fcinfo,
List *argList, ExprContext *econtext); List *argList, ExprContext *econtext);
static Datum ExecEvalNot(BoolExprState *notclause, ExprContext *econtext, static Datum ExecEvalNot(BoolExprState *notclause, ExprContext *econtext,
...@@ -75,6 +75,11 @@ static Datum ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext, ...@@ -75,6 +75,11 @@ static Datum ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext,
bool *isNull); bool *isNull);
static Datum ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext, static Datum ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone); 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, static Datum ExecEvalNullTest(GenericExprState *nstate,
ExprContext *econtext, ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone); bool *isNull, ExprDoneCond *isDone);
...@@ -1187,8 +1192,7 @@ ExecEvalOper(FuncExprState *fcache, ...@@ -1187,8 +1192,7 @@ ExecEvalOper(FuncExprState *fcache,
static Datum static Datum
ExecEvalDistinct(FuncExprState *fcache, ExecEvalDistinct(FuncExprState *fcache,
ExprContext *econtext, ExprContext *econtext,
bool *isNull, bool *isNull)
ExprDoneCond *isDone)
{ {
Datum result; Datum result;
FunctionCallInfoData fcinfo; FunctionCallInfoData fcinfo;
...@@ -1370,6 +1374,7 @@ ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext, bool *isNull) ...@@ -1370,6 +1374,7 @@ ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext, bool *isNull)
return BoolGetDatum(!AnyNull); return BoolGetDatum(!AnyNull);
} }
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* ExecEvalCase * ExecEvalCase
* *
...@@ -1429,6 +1434,91 @@ ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext, ...@@ -1429,6 +1434,91 @@ ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
return (Datum) 0; 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 * ExecEvalNullTest
* *
...@@ -1778,7 +1868,7 @@ ExecEvalExpr(ExprState *expression, ...@@ -1778,7 +1868,7 @@ ExecEvalExpr(ExprState *expression,
break; break;
case T_DistinctExpr: case T_DistinctExpr:
retDatum = ExecEvalDistinct((FuncExprState *) expression, econtext, retDatum = ExecEvalDistinct((FuncExprState *) expression, econtext,
isNull, isDone); isNull);
break; break;
case T_BoolExpr: case T_BoolExpr:
{ {
...@@ -1826,6 +1916,16 @@ ExecEvalExpr(ExprState *expression, ...@@ -1826,6 +1916,16 @@ ExecEvalExpr(ExprState *expression,
isNull, isNull,
isDone); isDone);
break; break;
case T_CoalesceExpr:
retDatum = ExecEvalCoalesce((CoalesceExprState *) expression,
econtext,
isNull);
break;
case T_NullIfExpr:
retDatum = ExecEvalNullIf((FuncExprState *) expression,
econtext,
isNull);
break;
case T_NullTest: case T_NullTest:
retDatum = ExecEvalNullTest((GenericExprState *) expression, retDatum = ExecEvalNullTest((GenericExprState *) expression,
econtext, econtext,
...@@ -2082,6 +2182,36 @@ ExecInitExpr(Expr *node, PlanState *parent) ...@@ -2082,6 +2182,36 @@ ExecInitExpr(Expr *node, PlanState *parent)
state = (ExprState *) cstate; state = (ExprState *) cstate;
} }
break; 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: case T_NullTest:
{ {
NullTest *ntest = (NullTest *) node; NullTest *ntest = (NullTest *) node;
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.243 2003/02/10 04:44:44 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.244 2003/02/16 02:30:37 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -785,7 +785,7 @@ _copyOpExpr(OpExpr *from) ...@@ -785,7 +785,7 @@ _copyOpExpr(OpExpr *from)
} }
/* /*
* _copyDistinctExpr * _copyDistinctExpr (same as OpExpr)
*/ */
static DistinctExpr * static DistinctExpr *
_copyDistinctExpr(DistinctExpr *from) _copyDistinctExpr(DistinctExpr *from)
...@@ -919,6 +919,37 @@ _copyCaseWhen(CaseWhen *from) ...@@ -919,6 +919,37 @@ _copyCaseWhen(CaseWhen *from)
return newnode; return newnode;
} }
/*
* _copyCoalesceExpr
*/
static CoalesceExpr *
_copyCoalesceExpr(CoalesceExpr *from)
{
CoalesceExpr *newnode = makeNode(CoalesceExpr);
COPY_SCALAR_FIELD(coalescetype);
COPY_NODE_FIELD(args);
return newnode;
}
/*
* _copyNullIfExpr (same as OpExpr)
*/
static NullIfExpr *
_copyNullIfExpr(NullIfExpr *from)
{
NullIfExpr *newnode = makeNode(NullIfExpr);
COPY_SCALAR_FIELD(opno);
COPY_SCALAR_FIELD(opfuncid);
COPY_SCALAR_FIELD(opresulttype);
COPY_SCALAR_FIELD(opretset);
COPY_NODE_FIELD(args);
return newnode;
}
/* /*
* _copyNullTest * _copyNullTest
*/ */
...@@ -2484,6 +2515,12 @@ copyObject(void *from) ...@@ -2484,6 +2515,12 @@ copyObject(void *from)
case T_CaseWhen: case T_CaseWhen:
retval = _copyCaseWhen(from); retval = _copyCaseWhen(from);
break; break;
case T_CoalesceExpr:
retval = _copyCoalesceExpr(from);
break;
case T_NullIfExpr:
retval = _copyNullIfExpr(from);
break;
case T_NullTest: case T_NullTest:
retval = _copyNullTest(from); retval = _copyNullTest(from);
break; break;
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.186 2003/02/10 04:44:45 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.187 2003/02/16 02:30:37 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -378,6 +378,37 @@ _equalCaseWhen(CaseWhen *a, CaseWhen *b) ...@@ -378,6 +378,37 @@ _equalCaseWhen(CaseWhen *a, CaseWhen *b)
return true; return true;
} }
static bool
_equalCoalesceExpr(CoalesceExpr *a, CoalesceExpr *b)
{
COMPARE_SCALAR_FIELD(coalescetype);
COMPARE_NODE_FIELD(args);
return true;
}
static bool
_equalNullIfExpr(NullIfExpr *a, NullIfExpr *b)
{
COMPARE_SCALAR_FIELD(opno);
/*
* Special-case opfuncid: it is allowable for it to differ if one
* node contains zero and the other doesn't. This just means that the
* one node isn't as far along in the parse/plan pipeline and hasn't
* had the opfuncid cache filled yet.
*/
if (a->opfuncid != b->opfuncid &&
a->opfuncid != 0 &&
b->opfuncid != 0)
return false;
COMPARE_SCALAR_FIELD(opresulttype);
COMPARE_SCALAR_FIELD(opretset);
COMPARE_NODE_FIELD(args);
return true;
}
static bool static bool
_equalNullTest(NullTest *a, NullTest *b) _equalNullTest(NullTest *a, NullTest *b)
{ {
...@@ -1613,6 +1644,12 @@ equal(void *a, void *b) ...@@ -1613,6 +1644,12 @@ equal(void *a, void *b)
case T_CaseWhen: case T_CaseWhen:
retval = _equalCaseWhen(a, b); retval = _equalCaseWhen(a, b);
break; break;
case T_CoalesceExpr:
retval = _equalCoalesceExpr(a, b);
break;
case T_NullIfExpr:
retval = _equalNullIfExpr(a, b);
break;
case T_NullTest: case T_NullTest:
retval = _equalNullTest(a, b); retval = _equalNullTest(a, b);
break; break;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.199 2003/02/10 04:44:45 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.200 2003/02/16 02:30:37 tgl Exp $
* *
* NOTES * NOTES
* Every node type that can appear in stored rules' parsetrees *must* * Every node type that can appear in stored rules' parsetrees *must*
...@@ -753,6 +753,27 @@ _outCaseWhen(StringInfo str, CaseWhen *node) ...@@ -753,6 +753,27 @@ _outCaseWhen(StringInfo str, CaseWhen *node)
WRITE_NODE_FIELD(result); WRITE_NODE_FIELD(result);
} }
static void
_outCoalesceExpr(StringInfo str, CoalesceExpr *node)
{
WRITE_NODE_TYPE("COALESCE");
WRITE_OID_FIELD(coalescetype);
WRITE_NODE_FIELD(args);
}
static void
_outNullIfExpr(StringInfo str, NullIfExpr *node)
{
WRITE_NODE_TYPE("NULLIFEXPR");
WRITE_OID_FIELD(opno);
WRITE_OID_FIELD(opfuncid);
WRITE_OID_FIELD(opresulttype);
WRITE_BOOL_FIELD(opretset);
WRITE_NODE_FIELD(args);
}
static void static void
_outNullTest(StringInfo str, NullTest *node) _outNullTest(StringInfo str, NullTest *node)
{ {
...@@ -1277,6 +1298,10 @@ _outAExpr(StringInfo str, A_Expr *node) ...@@ -1277,6 +1298,10 @@ _outAExpr(StringInfo str, A_Expr *node)
appendStringInfo(str, " DISTINCT "); appendStringInfo(str, " DISTINCT ");
WRITE_NODE_FIELD(name); WRITE_NODE_FIELD(name);
break; break;
case AEXPR_NULLIF:
appendStringInfo(str, " NULLIF ");
WRITE_NODE_FIELD(name);
break;
case AEXPR_OF: case AEXPR_OF:
appendStringInfo(str, " OF "); appendStringInfo(str, " OF ");
WRITE_NODE_FIELD(name); WRITE_NODE_FIELD(name);
...@@ -1576,6 +1601,12 @@ _outNode(StringInfo str, void *obj) ...@@ -1576,6 +1601,12 @@ _outNode(StringInfo str, void *obj)
case T_CaseWhen: case T_CaseWhen:
_outCaseWhen(str, obj); _outCaseWhen(str, obj);
break; break;
case T_CoalesceExpr:
_outCoalesceExpr(str, obj);
break;
case T_NullIfExpr:
_outNullIfExpr(str, obj);
break;
case T_NullTest: case T_NullTest:
_outNullTest(str, obj); _outNullTest(str, obj);
break; break;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.148 2003/02/09 06:56:27 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.149 2003/02/16 02:30:37 tgl Exp $
* *
* NOTES * NOTES
* Path and Plan nodes do not have any readfuncs support, because we * Path and Plan nodes do not have any readfuncs support, because we
...@@ -606,6 +606,47 @@ _readCaseWhen(void) ...@@ -606,6 +606,47 @@ _readCaseWhen(void)
READ_DONE(); READ_DONE();
} }
/*
* _readCoalesceExpr
*/
static CoalesceExpr *
_readCoalesceExpr(void)
{
READ_LOCALS(CoalesceExpr);
READ_OID_FIELD(coalescetype);
READ_NODE_FIELD(args);
READ_DONE();
}
/*
* _readNullIfExpr
*/
static NullIfExpr *
_readNullIfExpr(void)
{
READ_LOCALS(NullIfExpr);
READ_OID_FIELD(opno);
READ_OID_FIELD(opfuncid);
/*
* The opfuncid is stored in the textual format primarily for debugging
* and documentation reasons. We want to always read it as zero to force
* it to be re-looked-up in the pg_operator entry. This ensures that
* stored rules don't have hidden dependencies on operators' functions.
* (We don't currently support an ALTER OPERATOR command, but might
* someday.)
*/
local_node->opfuncid = InvalidOid;
READ_OID_FIELD(opresulttype);
READ_BOOL_FIELD(opretset);
READ_NODE_FIELD(args);
READ_DONE();
}
/* /*
* _readNullTest * _readNullTest
*/ */
...@@ -895,6 +936,10 @@ parseNodeString(void) ...@@ -895,6 +936,10 @@ parseNodeString(void)
return_value = _readCaseExpr(); return_value = _readCaseExpr();
else if (MATCH("WHEN", 4)) else if (MATCH("WHEN", 4))
return_value = _readCaseWhen(); return_value = _readCaseWhen();
else if (MATCH("COALESCE", 8))
return_value = _readCoalesceExpr();
else if (MATCH("NULLIFEXPR", 10))
return_value = _readNullIfExpr();
else if (MATCH("NULLTEST", 8)) else if (MATCH("NULLTEST", 8))
return_value = _readNullTest(); return_value = _readNullTest();
else if (MATCH("BOOLEANTEST", 11)) else if (MATCH("BOOLEANTEST", 11))
......
...@@ -49,7 +49,7 @@ ...@@ -49,7 +49,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.106 2003/02/15 21:39:58 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.107 2003/02/16 02:30:38 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1468,7 +1468,8 @@ cost_qual_eval_walker(Node *node, QualCost *total) ...@@ -1468,7 +1468,8 @@ cost_qual_eval_walker(Node *node, QualCost *total)
*/ */
if (IsA(node, FuncExpr) || if (IsA(node, FuncExpr) ||
IsA(node, OpExpr) || IsA(node, OpExpr) ||
IsA(node, DistinctExpr)) IsA(node, DistinctExpr) ||
IsA(node, NullIfExpr))
{ {
total->per_tuple += cpu_operator_cost; total->per_tuple += cpu_operator_cost;
} }
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.91 2003/01/20 18:54:52 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.92 2003/02/16 02:30:38 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -284,6 +284,8 @@ fix_expr_references_walker(Node *node, void *context) ...@@ -284,6 +284,8 @@ fix_expr_references_walker(Node *node, void *context)
set_opfuncid((OpExpr *) node); set_opfuncid((OpExpr *) node);
else if (IsA(node, DistinctExpr)) else if (IsA(node, DistinctExpr))
set_opfuncid((OpExpr *) node); /* rely on struct equivalence */ set_opfuncid((OpExpr *) node); /* rely on struct equivalence */
else if (IsA(node, NullIfExpr))
set_opfuncid((OpExpr *) node); /* rely on struct equivalence */
else if (IsA(node, SubPlan)) else if (IsA(node, SubPlan))
{ {
SubPlan *sp = (SubPlan *) node; SubPlan *sp = (SubPlan *) node;
...@@ -736,5 +738,7 @@ fix_opfuncids_walker(Node *node, void *context) ...@@ -736,5 +738,7 @@ fix_opfuncids_walker(Node *node, void *context)
set_opfuncid((OpExpr *) node); set_opfuncid((OpExpr *) node);
else if (IsA(node, DistinctExpr)) else if (IsA(node, DistinctExpr))
set_opfuncid((OpExpr *) node); /* rely on struct equivalence */ set_opfuncid((OpExpr *) node); /* rely on struct equivalence */
else if (IsA(node, NullIfExpr))
set_opfuncid((OpExpr *) node); /* rely on struct equivalence */
return expression_tree_walker(node, fix_opfuncids_walker, context); return expression_tree_walker(node, fix_opfuncids_walker, context);
} }
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.129 2003/02/09 06:56:27 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.130 2003/02/16 02:30:38 tgl Exp $
* *
* HISTORY * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -451,24 +451,22 @@ expression_returns_set_walker(Node *node, void *context) ...@@ -451,24 +451,22 @@ expression_returns_set_walker(Node *node, void *context)
return true; return true;
/* else fall through to check args */ /* else fall through to check args */
} }
if (IsA(node, DistinctExpr))
{
DistinctExpr *expr = (DistinctExpr *) node;
if (expr->opretset)
return true;
/* else fall through to check args */
}
/* Avoid recursion for some cases that can't return a set */ /* Avoid recursion for some cases that can't return a set */
if (IsA(node, BoolExpr))
return false;
if (IsA(node, Aggref)) if (IsA(node, Aggref))
return false; return false;
if (IsA(node, DistinctExpr))
return false;
if (IsA(node, BoolExpr))
return false;
if (IsA(node, SubLink)) if (IsA(node, SubLink))
return false; return false;
if (IsA(node, SubPlan)) if (IsA(node, SubPlan))
return false; return false;
if (IsA(node, CoalesceExpr))
return false;
if (IsA(node, NullIfExpr))
return false;
return expression_tree_walker(node, expression_returns_set_walker, return expression_tree_walker(node, expression_returns_set_walker,
context); context);
...@@ -559,6 +557,14 @@ contain_mutable_functions_walker(Node *node, void *context) ...@@ -559,6 +557,14 @@ contain_mutable_functions_walker(Node *node, void *context)
return true; return true;
/* else fall through to check args */ /* else fall through to check args */
} }
if (IsA(node, NullIfExpr))
{
NullIfExpr *expr = (NullIfExpr *) node;
if (op_volatile(expr->opno) != PROVOLATILE_IMMUTABLE)
return true;
/* else fall through to check args */
}
if (IsA(node, SubLink)) if (IsA(node, SubLink))
{ {
SubLink *sublink = (SubLink *) node; SubLink *sublink = (SubLink *) node;
...@@ -626,6 +632,14 @@ contain_volatile_functions_walker(Node *node, void *context) ...@@ -626,6 +632,14 @@ contain_volatile_functions_walker(Node *node, void *context)
return true; return true;
/* else fall through to check args */ /* else fall through to check args */
} }
if (IsA(node, NullIfExpr))
{
NullIfExpr *expr = (NullIfExpr *) node;
if (op_volatile(expr->opno) == PROVOLATILE_VOLATILE)
return true;
/* else fall through to check args */
}
if (IsA(node, SubLink)) if (IsA(node, SubLink))
{ {
SubLink *sublink = (SubLink *) node; SubLink *sublink = (SubLink *) node;
...@@ -707,6 +721,10 @@ contain_nonstrict_functions_walker(Node *node, void *context) ...@@ -707,6 +721,10 @@ contain_nonstrict_functions_walker(Node *node, void *context)
} }
if (IsA(node, CaseExpr)) if (IsA(node, CaseExpr))
return true; return true;
if (IsA(node, CoalesceExpr))
return true;
if (IsA(node, NullIfExpr))
return true;
if (IsA(node, NullTest)) if (IsA(node, NullTest))
return true; return true;
if (IsA(node, BooleanTest)) if (IsA(node, BooleanTest))
...@@ -1446,6 +1464,39 @@ eval_const_expressions_mutator(Node *node, List *active_fns) ...@@ -1446,6 +1464,39 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
newcase->defresult = (Expr *) defresult; newcase->defresult = (Expr *) defresult;
return (Node *) newcase; return (Node *) newcase;
} }
if (IsA(node, CoalesceExpr))
{
CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
CoalesceExpr *newcoalesce;
List *newargs = NIL;
List *arg;
foreach(arg, coalesceexpr->args)
{
Node *e;
e = eval_const_expressions_mutator((Node *) lfirst(arg),
active_fns);
/*
* We can remove null constants from the list.
* For a non-null constant, if it has not been preceded by any
* other non-null-constant expressions then that is the result.
*/
if (IsA(e, Const))
{
if (((Const *) e)->constisnull)
continue; /* drop null constant */
if (newargs == NIL)
return e; /* first expr */
}
newargs = lappend(newargs, e);
}
newcoalesce = makeNode(CoalesceExpr);
newcoalesce->coalescetype = coalesceexpr->coalescetype;
newcoalesce->args = newargs;
return (Node *) newcoalesce;
}
/* /*
* For any node type not handled above, we recurse using * For any node type not handled above, we recurse using
...@@ -2109,6 +2160,10 @@ expression_tree_walker(Node *node, ...@@ -2109,6 +2160,10 @@ expression_tree_walker(Node *node,
return true; return true;
} }
break; break;
case T_CoalesceExpr:
return walker(((CoalesceExpr *) node)->args, context);
case T_NullIfExpr:
return walker(((NullIfExpr *) node)->args, context);
case T_NullTest: case T_NullTest:
return walker(((NullTest *) node)->arg, context); return walker(((NullTest *) node)->arg, context);
case T_BooleanTest: case T_BooleanTest:
...@@ -2481,6 +2536,26 @@ expression_tree_mutator(Node *node, ...@@ -2481,6 +2536,26 @@ expression_tree_mutator(Node *node,
return (Node *) newnode; return (Node *) newnode;
} }
break; break;
case T_CoalesceExpr:
{
CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
CoalesceExpr *newnode;
FLATCOPY(newnode, coalesceexpr, CoalesceExpr);
MUTATE(newnode->args, coalesceexpr->args, List *);
return (Node *) newnode;
}
break;
case T_NullIfExpr:
{
NullIfExpr *expr = (NullIfExpr *) node;
NullIfExpr *newnode;
FLATCOPY(newnode, expr, NullIfExpr);
MUTATE(newnode->args, expr->args, List *);
return (Node *) newnode;
}
break;
case T_NullTest: case T_NullTest:
{ {
NullTest *ntest = (NullTest *) node; NullTest *ntest = (NullTest *) node;
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.403 2003/02/13 05:25:24 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.404 2003/02/16 02:30:38 tgl Exp $
* *
* HISTORY * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -6650,6 +6650,10 @@ in_expr: select_with_parens ...@@ -6650,6 +6650,10 @@ in_expr: select_with_parens
* COALESCE(a,b,...) * COALESCE(a,b,...)
* same as CASE WHEN a IS NOT NULL THEN a WHEN b IS NOT NULL THEN b ... END * same as CASE WHEN a IS NOT NULL THEN a WHEN b IS NOT NULL THEN b ... END
* - thomas 1998-11-09 * - thomas 1998-11-09
*
* NULLIF and COALESCE have become first class nodes to
* prevent double evaluation of arguments.
* - Kris Jurka 2003-02-11
*/ */
case_expr: CASE case_arg when_clause_list case_default END_P case_expr: CASE case_arg when_clause_list case_default END_P
{ {
...@@ -6661,29 +6665,12 @@ case_expr: CASE case_arg when_clause_list case_default END_P ...@@ -6661,29 +6665,12 @@ case_expr: CASE case_arg when_clause_list case_default END_P
} }
| NULLIF '(' a_expr ',' a_expr ')' | NULLIF '(' a_expr ',' a_expr ')'
{ {
CaseExpr *c = makeNode(CaseExpr); $$ = (Node *) makeSimpleA_Expr(AEXPR_NULLIF, "=", $3, $5);
CaseWhen *w = makeNode(CaseWhen);
w->expr = (Expr *) makeSimpleA_Expr(AEXPR_OP, "=", $3, $5);
/* w->result is left NULL */
c->args = makeList1(w);
c->defresult = (Expr *) $3;
$$ = (Node *)c;
} }
| COALESCE '(' expr_list ')' | COALESCE '(' expr_list ')'
{ {
CaseExpr *c = makeNode(CaseExpr); CoalesceExpr *c = makeNode(CoalesceExpr);
List *l; c->args = $3;
foreach (l,$3)
{
CaseWhen *w = makeNode(CaseWhen);
NullTest *n = makeNode(NullTest);
n->arg = lfirst(l);
n->nulltesttype = IS_NOT_NULL;
w->expr = (Expr *) n;
w->result = lfirst(l);
c->args = lappend(c->args, w);
}
$$ = (Node *)c; $$ = (Node *)c;
} }
; ;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.109 2003/02/13 20:45:21 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.110 2003/02/16 02:30:38 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -922,17 +922,10 @@ buildMergedJoinVar(JoinType jointype, Var *l_colvar, Var *r_colvar) ...@@ -922,17 +922,10 @@ buildMergedJoinVar(JoinType jointype, Var *l_colvar, Var *r_colvar)
* Here we must build a COALESCE expression to ensure that * Here we must build a COALESCE expression to ensure that
* the join output is non-null if either input is. * the join output is non-null if either input is.
*/ */
CaseExpr *c = makeNode(CaseExpr); CoalesceExpr *c = makeNode(CoalesceExpr);
CaseWhen *w = makeNode(CaseWhen);
NullTest *n = makeNode(NullTest); c->coalescetype = outcoltype;
c->args = makeList2(l_node, r_node);
n->arg = (Expr *) l_node;
n->nulltesttype = IS_NOT_NULL;
w->expr = (Expr *) n;
w->result = (Expr *) l_node;
c->casetype = outcoltype;
c->args = makeList1(w);
c->defresult = (Expr *) r_node;
res_node = (Node *) c; res_node = (Node *) c;
break; break;
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.145 2003/02/13 18:29:07 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.146 2003/02/16 02:30:38 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -277,6 +277,24 @@ transformExpr(ParseState *pstate, Node *expr) ...@@ -277,6 +277,24 @@ transformExpr(ParseState *pstate, Node *expr)
NodeSetTag(result, T_DistinctExpr); NodeSetTag(result, T_DistinctExpr);
} }
break; break;
case AEXPR_NULLIF:
{
Node *lexpr = transformExpr(pstate,
a->lexpr);
Node *rexpr = transformExpr(pstate,
a->rexpr);
result = (Node *) make_op(a->name,
lexpr,
rexpr);
if (((OpExpr *) result)->opresulttype != BOOLOID)
elog(ERROR, "NULLIF requires = operator to yield boolean");
/*
* We rely on NullIfExpr and OpExpr being same struct
*/
NodeSetTag(result, T_NullIfExpr);
}
break;
case AEXPR_OF: case AEXPR_OF:
{ {
/* /*
...@@ -615,6 +633,43 @@ transformExpr(ParseState *pstate, Node *expr) ...@@ -615,6 +633,43 @@ transformExpr(ParseState *pstate, Node *expr)
break; break;
} }
case T_CoalesceExpr:
{
CoalesceExpr *c = (CoalesceExpr *) expr;
CoalesceExpr *newc = makeNode(CoalesceExpr);
List *newargs = NIL;
List *newcoercedargs = NIL;
List *typeids = NIL;
List *args;
foreach(args, c->args)
{
Node *e = (Node *) lfirst(args);
Node *newe;
newe = transformExpr(pstate, e);
newargs = lappend(newargs, newe);
typeids = lappendo(typeids, exprType(newe));
}
newc->coalescetype = select_common_type(typeids, "COALESCE");
/* Convert arguments if necessary */
foreach(args, newargs)
{
Node *e = (Node *) lfirst(args);
Node *newe;
newe = coerce_to_common_type(e, newc->coalescetype,
"COALESCE");
newcoercedargs = lappend(newcoercedargs, newe);
}
newc->args = newcoercedargs;
result = (Node *) newc;
break;
}
case T_NullTest: case T_NullTest:
{ {
NullTest *n = (NullTest *) expr; NullTest *n = (NullTest *) expr;
...@@ -680,6 +735,7 @@ transformExpr(ParseState *pstate, Node *expr) ...@@ -680,6 +735,7 @@ transformExpr(ParseState *pstate, Node *expr)
case T_FuncExpr: case T_FuncExpr:
case T_OpExpr: case T_OpExpr:
case T_DistinctExpr: case T_DistinctExpr:
case T_NullIfExpr:
case T_BoolExpr: case T_BoolExpr:
case T_FieldSelect: case T_FieldSelect:
case T_RelabelType: case T_RelabelType:
...@@ -1020,6 +1076,12 @@ exprType(Node *expr) ...@@ -1020,6 +1076,12 @@ exprType(Node *expr)
case T_CaseWhen: case T_CaseWhen:
type = exprType((Node *) ((CaseWhen *) expr)->result); type = exprType((Node *) ((CaseWhen *) expr)->result);
break; break;
case T_CoalesceExpr:
type = ((CoalesceExpr *) expr)->coalescetype;
break;
case T_NullIfExpr:
type = exprType((Node *) lfirst(((NullIfExpr *) expr)->args));
break;
case T_NullTest: case T_NullTest:
type = BOOLOID; type = BOOLOID;
break; break;
...@@ -1126,6 +1188,37 @@ exprTypmod(Node *expr) ...@@ -1126,6 +1188,37 @@ exprTypmod(Node *expr)
return typmod; return typmod;
} }
break; break;
case T_CoalesceExpr:
{
/*
* If all the alternatives agree on type/typmod, return
* that typmod, else use -1
*/
CoalesceExpr *cexpr = (CoalesceExpr *) expr;
Oid coalescetype = cexpr->coalescetype;
int32 typmod;
List *arg;
typmod = exprTypmod((Node *) lfirst(cexpr->args));
foreach(arg, cexpr->args)
{
Node *e = (Node *) lfirst(arg);
if (exprType(e) != coalescetype)
return -1;
if (exprTypmod(e) != typmod)
return -1;
}
return typmod;
}
break;
case T_NullIfExpr:
{
NullIfExpr *nexpr = (NullIfExpr *) expr;
return exprTypmod((Node *) lfirst(nexpr->args));
}
break;
case T_CoerceToDomain: case T_CoerceToDomain:
return ((CoerceToDomain *) expr)->resulttypmod; return ((CoerceToDomain *) expr)->resulttypmod;
case T_CoerceToDomainValue: case T_CoerceToDomainValue:
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.97 2003/02/13 05:53:46 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.98 2003/02/16 02:30:38 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -482,6 +482,14 @@ FigureColnameInternal(Node *node, char **name) ...@@ -482,6 +482,14 @@ FigureColnameInternal(Node *node, char **name)
case T_FuncCall: case T_FuncCall:
*name = strVal(llast(((FuncCall *) node)->funcname)); *name = strVal(llast(((FuncCall *) node)->funcname));
return 2; return 2;
case T_A_Expr:
/* make nullif() act like a regular function */
if (((A_Expr *) node)->kind == AEXPR_NULLIF)
{
*name = "nullif";
return 2;
}
break;
case T_A_Const: case T_A_Const:
if (((A_Const *) node)->typename != NULL) if (((A_Const *) node)->typename != NULL)
{ {
...@@ -510,6 +518,10 @@ FigureColnameInternal(Node *node, char **name) ...@@ -510,6 +518,10 @@ FigureColnameInternal(Node *node, char **name)
return 1; return 1;
} }
break; break;
case T_CoalesceExpr:
/* make coalesce() act like a regular function */
*name = "coalesce";
return 2;
default: default:
break; break;
} }
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* back to source text * back to source text
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.135 2003/02/13 05:10:39 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.136 2003/02/16 02:30:39 tgl Exp $
* *
* This software is copyrighted by Jan Wieck - Hamburg. * This software is copyrighted by Jan Wieck - Hamburg.
* *
...@@ -2238,6 +2238,46 @@ get_rule_expr(Node *node, deparse_context *context, ...@@ -2238,6 +2238,46 @@ get_rule_expr(Node *node, deparse_context *context,
} }
break; break;
case T_CoalesceExpr:
{
CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
List *arg;
char *sep;
appendStringInfo(buf, "COALESCE(");
sep = "";
foreach(arg, coalesceexpr->args)
{
Node *e = (Node *) lfirst(arg);
appendStringInfo(buf, sep);
get_rule_expr(e, context, true);
sep = ", ";
}
appendStringInfo(buf, ")");
}
break;
case T_NullIfExpr:
{
NullIfExpr *nullifexpr = (NullIfExpr *) node;
List *arg;
char *sep;
appendStringInfo(buf, "NULLIF(");
sep = "";
foreach(arg, nullifexpr->args)
{
Node *e = (Node *) lfirst(arg);
appendStringInfo(buf, sep);
get_rule_expr(e, context, true);
sep = ", ";
}
appendStringInfo(buf, ")");
}
break;
case T_NullTest: case T_NullTest:
{ {
NullTest *ntest = (NullTest *) node; NullTest *ntest = (NullTest *) node;
......
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: catversion.h,v 1.176 2003/02/13 05:24:02 momjian Exp $ * $Id: catversion.h,v 1.177 2003/02/16 02:30:39 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -53,6 +53,6 @@ ...@@ -53,6 +53,6 @@
*/ */
/* yyyymmddN */ /* yyyymmddN */
#define CATALOG_VERSION_NO 200302131 #define CATALOG_VERSION_NO 200302151
#endif #endif
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: execnodes.h,v 1.94 2003/02/09 00:30:39 tgl Exp $ * $Id: execnodes.h,v 1.95 2003/02/16 02:30:39 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -441,8 +441,9 @@ typedef struct ArrayRefExprState ...@@ -441,8 +441,9 @@ typedef struct ArrayRefExprState
/* ---------------- /* ----------------
* FuncExprState node * FuncExprState node
* *
* Although named for FuncExpr, this is also used for OpExpr and DistinctExpr * Although named for FuncExpr, this is also used for OpExpr, DistinctExpr,
* nodes; be careful to check what xprstate.expr is actually pointing at! * and NullIf nodes; be careful to check what xprstate.expr is actually
* pointing at!
* ---------------- * ----------------
*/ */
typedef struct FuncExprState typedef struct FuncExprState
...@@ -539,6 +540,16 @@ typedef struct CaseWhenState ...@@ -539,6 +540,16 @@ typedef struct CaseWhenState
ExprState *result; /* substitution result */ ExprState *result; /* substitution result */
} CaseWhenState; } CaseWhenState;
/* ----------------
* CoalesceExprState node
* ----------------
*/
typedef struct CoalesceExprState
{
ExprState xprstate;
List *args; /* the arguments */
} CoalesceExprState;
/* ---------------- /* ----------------
* CoerceToDomainState node * CoerceToDomainState node
* ---------------- * ----------------
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: nodes.h,v 1.136 2003/02/03 21:15:44 tgl Exp $ * $Id: nodes.h,v 1.137 2003/02/16 02:30:39 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -112,6 +112,8 @@ typedef enum NodeTag ...@@ -112,6 +112,8 @@ typedef enum NodeTag
T_RelabelType, T_RelabelType,
T_CaseExpr, T_CaseExpr,
T_CaseWhen, T_CaseWhen,
T_CoalesceExpr,
T_NullIfExpr,
T_NullTest, T_NullTest,
T_BooleanTest, T_BooleanTest,
T_CoerceToDomain, T_CoerceToDomain,
...@@ -136,6 +138,7 @@ typedef enum NodeTag ...@@ -136,6 +138,7 @@ typedef enum NodeTag
T_SubPlanState, T_SubPlanState,
T_CaseExprState, T_CaseExprState,
T_CaseWhenState, T_CaseWhenState,
T_CoalesceExprState,
T_CoerceToDomainState, T_CoerceToDomainState,
T_DomainConstraintState, T_DomainConstraintState,
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: parsenodes.h,v 1.230 2003/02/13 05:20:03 momjian Exp $ * $Id: parsenodes.h,v 1.231 2003/02/16 02:30:39 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -174,6 +174,7 @@ typedef enum A_Expr_Kind ...@@ -174,6 +174,7 @@ typedef enum A_Expr_Kind
AEXPR_OR, AEXPR_OR,
AEXPR_NOT, AEXPR_NOT,
AEXPR_DISTINCT, /* IS DISTINCT FROM - name must be "=" */ AEXPR_DISTINCT, /* IS DISTINCT FROM - name must be "=" */
AEXPR_NULLIF, /* NULLIF - name must be "=" */
AEXPR_OF /* IS (not) OF - name must be "=" or "!=" */ AEXPR_OF /* IS (not) OF - name must be "=" or "!=" */
} A_Expr_Kind; } A_Expr_Kind;
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: primnodes.h,v 1.79 2003/02/09 00:30:40 tgl Exp $ * $Id: primnodes.h,v 1.80 2003/02/16 02:30:39 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -537,6 +537,24 @@ typedef struct CaseWhen ...@@ -537,6 +537,24 @@ typedef struct CaseWhen
Expr *result; /* substitution result */ Expr *result; /* substitution result */
} CaseWhen; } CaseWhen;
/*
* CoalesceExpr - a COALESCE expression
*/
typedef struct CoalesceExpr
{
Expr xpr;
Oid coalescetype; /* type of expression result */
List *args; /* the arguments */
} CoalesceExpr;
/*
* NullIfExpr - a NULLIF expression
*
* Like DistinctExpr, this is represented the same as an OpExpr referencing
* the "=" operator for x and y.
*/
typedef OpExpr NullIfExpr;
/* ---------------- /* ----------------
* NullTest * NullTest
* *
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* procedural language * procedural language
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.78 2003/02/03 21:15:45 tgl Exp $ * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.79 2003/02/16 02:30:39 tgl Exp $
* *
* This software is copyrighted by Jan Wieck - Hamburg. * This software is copyrighted by Jan Wieck - Hamburg.
* *
...@@ -3628,6 +3628,28 @@ exec_simple_check_node(Node *node) ...@@ -3628,6 +3628,28 @@ exec_simple_check_node(Node *node)
return TRUE; return TRUE;
} }
case T_CoalesceExpr:
{
CoalesceExpr *expr = (CoalesceExpr *) node;
if (!exec_simple_check_node((Node *) expr->args))
return FALSE;
return TRUE;
}
case T_NullIfExpr:
{
NullIfExpr *expr = (NullIfExpr *) node;
if (expr->opretset)
return FALSE;
if (!exec_simple_check_node((Node *) expr->args))
return FALSE;
return TRUE;
}
case T_NullTest: case T_NullTest:
return exec_simple_check_node((Node *) ((NullTest *) node)->arg); return exec_simple_check_node((Node *) ((NullTest *) node)->arg);
......
...@@ -154,32 +154,32 @@ SELECT * FROM CASE_TBL WHERE NULLIF(f,i) = 2; ...@@ -154,32 +154,32 @@ SELECT * FROM CASE_TBL WHERE NULLIF(f,i) = 2;
SELECT COALESCE(a.f, b.i, b.j) SELECT COALESCE(a.f, b.i, b.j)
FROM CASE_TBL a, CASE2_TBL b; FROM CASE_TBL a, CASE2_TBL b;
case coalesce
------- ----------
10.1 10.1
10.1 10.1
10.1 10.1
10.1 10.1
10.1 10.1
10.1 10.1
20.2 20.2
20.2 20.2
20.2 20.2
20.2 20.2
20.2 20.2
20.2 20.2
-30.3 -30.3
-30.3 -30.3
-30.3 -30.3
-30.3 -30.3
-30.3 -30.3
-30.3 -30.3
1 1
2 2
3 3
2 2
1 1
-6 -6
(24 rows) (24 rows)
SELECT * SELECT *
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment