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
-->
......@@ -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>
......
......@@ -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;
......
......@@ -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;
......
......@@ -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;
......
......@@ -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;
......
......@@ -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;
......
......@@ -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))
......
......@@ -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;
}
......
......@@ -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);
}
......@@ -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;
......
......@@ -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;
}
;
......
......@@ -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);
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;
CoalesceExpr *c = makeNode(CoalesceExpr);
c->coalescetype = outcoltype;
c->args = makeList2(l_node, r_node);
res_node = (Node *) c;
break;
}
......
......@@ -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:
......
......@@ -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;
}
......
......@@ -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;
......
......@@ -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
......@@ -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
* ----------------
......
......@@ -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,
......
......@@ -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;
......
......@@ -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
*
......
......@@ -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);
......
......@@ -154,8 +154,8 @@ 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
-------
coalesce
----------
10.1
10.1
10.1
......
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