<!--
-$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
-->
</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>
* 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 $
*
*-------------------------------------------------------------------------
*/
&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;
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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,
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);
static Datum
ExecEvalDistinct(FuncExprState *fcache,
ExprContext *econtext,
- bool *isNull,
- ExprDoneCond *isDone)
+ bool *isNull)
{
Datum result;
FunctionCallInfoData fcinfo;
return BoolGetDatum(!AnyNull);
}
+
/* ----------------------------------------------------------------
* ExecEvalCase
*
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
*
break;
case T_DistinctExpr:
retDatum = ExecEvalDistinct((FuncExprState *) expression, econtext,
- isNull, isDone);
+ isNull);
break;
case T_BoolExpr:
{
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,
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;
* 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 $
*
*-------------------------------------------------------------------------
*/
}
/*
- * _copyDistinctExpr
+ * _copyDistinctExpr (same as OpExpr)
*/
static DistinctExpr *
_copyDistinctExpr(DistinctExpr *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
*/
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;
* 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 $
*
*-------------------------------------------------------------------------
*/
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)
{
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;
*
*
* 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*
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)
{
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);
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;
*
*
* 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
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
*/
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))
* 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 $
*
*-------------------------------------------------------------------------
*/
*/
if (IsA(node, FuncExpr) ||
IsA(node, OpExpr) ||
- IsA(node, DistinctExpr))
+ IsA(node, DistinctExpr) ||
+ IsA(node, NullIfExpr))
{
total->per_tuple += cpu_operator_cost;
}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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;
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);
}
*
*
* 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
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);
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;
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;
}
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))
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
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:
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;
*
*
* 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
* 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
{
}
| 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;
}
;
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
* 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;
}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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:
{
/*
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;
case T_FuncExpr:
case T_OpExpr:
case T_DistinctExpr:
+ case T_NullIfExpr:
case T_BoolExpr:
case T_FieldSelect:
case T_RelabelType:
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;
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:
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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)
{
return 1;
}
break;
+ case T_CoalesceExpr:
+ /* make coalesce() act like a regular function */
+ *name = "coalesce";
+ return 2;
default:
break;
}
* 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.
*
}
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;
* 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 $
*
*-------------------------------------------------------------------------
*/
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 200302131
+#define CATALOG_VERSION_NO 200302151
#endif
* 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 $
*
*-------------------------------------------------------------------------
*/
/* ----------------
* 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
ExprState *result; /* substitution result */
} CaseWhenState;
+/* ----------------
+ * CoalesceExprState node
+ * ----------------
+ */
+typedef struct CoalesceExprState
+{
+ ExprState xprstate;
+ List *args; /* the arguments */
+} CoalesceExprState;
+
/* ----------------
* CoerceToDomainState node
* ----------------
* 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 $
*
*-------------------------------------------------------------------------
*/
T_RelabelType,
T_CaseExpr,
T_CaseWhen,
+ T_CoalesceExpr,
+ T_NullIfExpr,
T_NullTest,
T_BooleanTest,
T_CoerceToDomain,
T_SubPlanState,
T_CaseExprState,
T_CaseWhenState,
+ T_CoalesceExprState,
T_CoerceToDomainState,
T_DomainConstraintState,
* 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 $
*
*-------------------------------------------------------------------------
*/
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;
* 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 $
*
*-------------------------------------------------------------------------
*/
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
*
* 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.
*
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);
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 *