mirror of
https://git.postgresql.org/git/postgresql.git
synced 2025-03-13 19:57:53 +08:00
COALESCE() and NULLIF() are now first-class expressions, not macros
that turn into CASE expressions. They evaluate their arguments at most once. Patch by Kris Jurka, review and (very light) editorializing by me.
This commit is contained in:
parent
de25638d2f
commit
51972a9d5d
@ -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>
|
||||
|
@ -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);
|
||||
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;
|
||||
}
|
||||
|
@ -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,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 *
|
||||
|
Loading…
x
Reference in New Issue
Block a user