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

@ -1,5 +1,5 @@
<!--
$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
-->
@ -6295,17 +6295,6 @@ SELECT NULLIF(value, '(none)') ...
</programlisting>
</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>
</sect1>

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* 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,
&context->addrs);
/* 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))
{
Aggref *aggref = (Aggref *) node;

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;

View File

@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* 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)
}
/*
* _copyDistinctExpr
* _copyDistinctExpr (same as OpExpr)
*/
static DistinctExpr *
_copyDistinctExpr(DistinctExpr *from)
@ -919,6 +919,37 @@ _copyCaseWhen(CaseWhen *from)
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
*/
@ -2484,6 +2515,12 @@ copyObject(void *from)
case T_CaseWhen:
retval = _copyCaseWhen(from);
break;
case T_CoalesceExpr:
retval = _copyCoalesceExpr(from);
break;
case T_NullIfExpr:
retval = _copyNullIfExpr(from);
break;
case T_NullTest:
retval = _copyNullTest(from);
break;

View File

@ -18,7 +18,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* 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)
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
_equalNullTest(NullTest *a, NullTest *b)
{
@ -1613,6 +1644,12 @@ equal(void *a, void *b)
case T_CaseWhen:
retval = _equalCaseWhen(a, b);
break;
case T_CoalesceExpr:
retval = _equalCoalesceExpr(a, b);
break;
case T_NullIfExpr:
retval = _equalNullIfExpr(a, b);
break;
case T_NullTest:
retval = _equalNullTest(a, b);
break;

View File

@ -8,7 +8,7 @@
*
*
* 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
* Every node type that can appear in stored rules' parsetrees *must*
@ -753,6 +753,27 @@ _outCaseWhen(StringInfo str, CaseWhen *node)
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
_outNullTest(StringInfo str, NullTest *node)
{
@ -1277,6 +1298,10 @@ _outAExpr(StringInfo str, A_Expr *node)
appendStringInfo(str, " DISTINCT ");
WRITE_NODE_FIELD(name);
break;
case AEXPR_NULLIF:
appendStringInfo(str, " NULLIF ");
WRITE_NODE_FIELD(name);
break;
case AEXPR_OF:
appendStringInfo(str, " OF ");
WRITE_NODE_FIELD(name);
@ -1576,6 +1601,12 @@ _outNode(StringInfo str, void *obj)
case T_CaseWhen:
_outCaseWhen(str, obj);
break;
case T_CoalesceExpr:
_outCoalesceExpr(str, obj);
break;
case T_NullIfExpr:
_outNullIfExpr(str, obj);
break;
case T_NullTest:
_outNullTest(str, obj);
break;

View File

@ -8,7 +8,7 @@
*
*
* 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
* Path and Plan nodes do not have any readfuncs support, because we
@ -606,6 +606,47 @@ _readCaseWhen(void)
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
*/
@ -895,6 +936,10 @@ parseNodeString(void)
return_value = _readCaseExpr();
else if (MATCH("WHEN", 4))
return_value = _readCaseWhen();
else if (MATCH("COALESCE", 8))
return_value = _readCoalesceExpr();
else if (MATCH("NULLIFEXPR", 10))
return_value = _readNullIfExpr();
else if (MATCH("NULLTEST", 8))
return_value = _readNullTest();
else if (MATCH("BOOLEANTEST", 11))

View File

@ -49,7 +49,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* 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)
*/
if (IsA(node, FuncExpr) ||
IsA(node, OpExpr) ||
IsA(node, DistinctExpr))
IsA(node, DistinctExpr) ||
IsA(node, NullIfExpr))
{
total->per_tuple += cpu_operator_cost;
}

View File

@ -9,7 +9,7 @@
*
*
* 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)
set_opfuncid((OpExpr *) node);
else if (IsA(node, DistinctExpr))
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))
{
SubPlan *sp = (SubPlan *) node;
@ -736,5 +738,7 @@ fix_opfuncids_walker(Node *node, void *context)
set_opfuncid((OpExpr *) node);
else if (IsA(node, DistinctExpr))
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);
}

View File

@ -8,7 +8,7 @@
*
*
* 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
* AUTHOR DATE MAJOR EVENT
@ -451,24 +451,22 @@ expression_returns_set_walker(Node *node, void *context)
return true;
/* 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 */
if (IsA(node, BoolExpr))
return false;
if (IsA(node, Aggref))
return false;
if (IsA(node, DistinctExpr))
return false;
if (IsA(node, BoolExpr))
return false;
if (IsA(node, SubLink))
return false;
if (IsA(node, SubPlan))
return false;
if (IsA(node, CoalesceExpr))
return false;
if (IsA(node, NullIfExpr))
return false;
return expression_tree_walker(node, expression_returns_set_walker,
context);
@ -559,6 +557,14 @@ contain_mutable_functions_walker(Node *node, void *context)
return true;
/* 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))
{
SubLink *sublink = (SubLink *) node;
@ -626,6 +632,14 @@ contain_volatile_functions_walker(Node *node, void *context)
return true;
/* 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))
{
SubLink *sublink = (SubLink *) node;
@ -707,6 +721,10 @@ contain_nonstrict_functions_walker(Node *node, void *context)
}
if (IsA(node, CaseExpr))
return true;
if (IsA(node, CoalesceExpr))
return true;
if (IsA(node, NullIfExpr))
return true;
if (IsA(node, NullTest))
return true;
if (IsA(node, BooleanTest))
@ -1446,6 +1464,39 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
newcase->defresult = (Expr *) defresult;
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
@ -2109,6 +2160,10 @@ expression_tree_walker(Node *node,
return true;
}
break;
case T_CoalesceExpr:
return walker(((CoalesceExpr *) node)->args, context);
case T_NullIfExpr:
return walker(((NullIfExpr *) node)->args, context);
case T_NullTest:
return walker(((NullTest *) node)->arg, context);
case T_BooleanTest:
@ -2481,6 +2536,26 @@ expression_tree_mutator(Node *node,
return (Node *) newnode;
}
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:
{
NullTest *ntest = (NullTest *) node;

View File

@ -11,7 +11,7 @@
*
*
* 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
* AUTHOR DATE MAJOR EVENT
@ -6650,6 +6650,10 @@ in_expr: select_with_parens
* COALESCE(a,b,...)
* same as CASE WHEN a IS NOT NULL THEN a WHEN b IS NOT NULL THEN b ... END
* - 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
{
@ -6661,29 +6665,12 @@ case_expr: CASE case_arg when_clause_list case_default END_P
}
| NULLIF '(' a_expr ',' a_expr ')'
{
CaseExpr *c = makeNode(CaseExpr);
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;
$$ = (Node *) makeSimpleA_Expr(AEXPR_NULLIF, "=", $3, $5);
}
| COALESCE '(' expr_list ')'
{
CaseExpr *c = makeNode(CaseExpr);
List *l;
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);
}
CoalesceExpr *c = makeNode(CoalesceExpr);
c->args = $3;
$$ = (Node *)c;
}
;

View File

@ -8,7 +8,7 @@
*
*
* 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)
* Here we must build a COALESCE expression to ensure that
* the join output is non-null if either input is.
*/
CaseExpr *c = makeNode(CaseExpr);
CaseWhen *w = makeNode(CaseWhen);
NullTest *n = makeNode(NullTest);
CoalesceExpr *c = makeNode(CoalesceExpr);
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;
c->coalescetype = outcoltype;
c->args = makeList2(l_node, r_node);
res_node = (Node *) c;
break;
}

View File

@ -8,7 +8,7 @@
*
*
* 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)
NodeSetTag(result, T_DistinctExpr);
}
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:
{
/*
@ -615,6 +633,43 @@ transformExpr(ParseState *pstate, Node *expr)
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:
{
NullTest *n = (NullTest *) expr;
@ -680,6 +735,7 @@ transformExpr(ParseState *pstate, Node *expr)
case T_FuncExpr:
case T_OpExpr:
case T_DistinctExpr:
case T_NullIfExpr:
case T_BoolExpr:
case T_FieldSelect:
case T_RelabelType:
@ -1020,6 +1076,12 @@ exprType(Node *expr)
case T_CaseWhen:
type = exprType((Node *) ((CaseWhen *) expr)->result);
break;
case T_CoalesceExpr:
type = ((CoalesceExpr *) expr)->coalescetype;
break;
case T_NullIfExpr:
type = exprType((Node *) lfirst(((NullIfExpr *) expr)->args));
break;
case T_NullTest:
type = BOOLOID;
break;
@ -1126,6 +1188,37 @@ exprTypmod(Node *expr)
return typmod;
}
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:
return ((CoerceToDomain *) expr)->resulttypmod;
case T_CoerceToDomainValue:

View File

@ -8,7 +8,7 @@
*
*
* 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)
case T_FuncCall:
*name = strVal(llast(((FuncCall *) node)->funcname));
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:
if (((A_Const *) node)->typename != NULL)
{
@ -510,6 +518,10 @@ FigureColnameInternal(Node *node, char **name)
return 1;
}
break;
case T_CoalesceExpr:
/* make coalesce() act like a regular function */
*name = "coalesce";
return 2;
default:
break;
}

View File

@ -3,7 +3,7 @@
* back to source text
*
* 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.
*
@ -2238,6 +2238,46 @@ get_rule_expr(Node *node, deparse_context *context,
}
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:
{
NullTest *ntest = (NullTest *) node;

View File

@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* 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 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 200302131
#define CATALOG_VERSION_NO 200302151
#endif

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* 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
/* ----------------
* FuncExprState node
*
* Although named for FuncExpr, this is also used for OpExpr and DistinctExpr
* nodes; be careful to check what xprstate.expr is actually pointing at!
* Although named for FuncExpr, this is also used for OpExpr, DistinctExpr,
* and NullIf nodes; be careful to check what xprstate.expr is actually
* pointing at!
* ----------------
*/
typedef struct FuncExprState
@ -539,6 +540,16 @@ typedef struct CaseWhenState
ExprState *result; /* substitution result */
} CaseWhenState;
/* ----------------
* CoalesceExprState node
* ----------------
*/
typedef struct CoalesceExprState
{
ExprState xprstate;
List *args; /* the arguments */
} CoalesceExprState;
/* ----------------
* CoerceToDomainState node
* ----------------

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* 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
T_RelabelType,
T_CaseExpr,
T_CaseWhen,
T_CoalesceExpr,
T_NullIfExpr,
T_NullTest,
T_BooleanTest,
T_CoerceToDomain,
@ -136,6 +138,7 @@ typedef enum NodeTag
T_SubPlanState,
T_CaseExprState,
T_CaseWhenState,
T_CoalesceExprState,
T_CoerceToDomainState,
T_DomainConstraintState,

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* 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
AEXPR_OR,
AEXPR_NOT,
AEXPR_DISTINCT, /* IS DISTINCT FROM - name must be "=" */
AEXPR_NULLIF, /* NULLIF - name must be "=" */
AEXPR_OF /* IS (not) OF - name must be "=" or "!=" */
} A_Expr_Kind;

View File

@ -10,7 +10,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* 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
Expr *result; /* substitution result */
} 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
*

View File

@ -3,7 +3,7 @@
* procedural language
*
* 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.
*
@ -3628,6 +3628,28 @@ exec_simple_check_node(Node *node)
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:
return exec_simple_check_node((Node *) ((NullTest *) node)->arg);

View File

@ -154,32 +154,32 @@ SELECT * FROM CASE_TBL WHERE NULLIF(f,i) = 2;
SELECT COALESCE(a.f, b.i, b.j)
FROM CASE_TBL a, CASE2_TBL b;
case
-------
10.1
10.1
10.1
10.1
10.1
10.1
20.2
20.2
20.2
20.2
20.2
20.2
-30.3
-30.3
-30.3
-30.3
-30.3
-30.3
1
2
3
2
1
-6
coalesce
----------
10.1
10.1
10.1
10.1
10.1
10.1
20.2
20.2
20.2
20.2
20.2
20.2
-30.3
-30.3
-30.3
-30.3
-30.3
-30.3
1
2
3
2
1
-6
(24 rows)
SELECT *