OSDN Git Service

COALESCE() and NULLIF() are now first-class expressions, not macros
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 16 Feb 2003 02:30:39 +0000 (02:30 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 16 Feb 2003 02:30:39 +0000 (02:30 +0000)
that turn into CASE expressions.  They evaluate their arguments at most
once.  Patch by Kris Jurka, review and (very light) editorializing by me.

22 files changed:
doc/src/sgml/func.sgml
src/backend/catalog/dependency.c
src/backend/executor/execQual.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/nodes/outfuncs.c
src/backend/nodes/readfuncs.c
src/backend/optimizer/path/costsize.c
src/backend/optimizer/plan/setrefs.c
src/backend/optimizer/util/clauses.c
src/backend/parser/gram.y
src/backend/parser/parse_clause.c
src/backend/parser/parse_expr.c
src/backend/parser/parse_target.c
src/backend/utils/adt/ruleutils.c
src/include/catalog/catversion.h
src/include/nodes/execnodes.h
src/include/nodes/nodes.h
src/include/nodes/parsenodes.h
src/include/nodes/primnodes.h
src/pl/plpgsql/src/pl_exec.c
src/test/regress/expected/case.out

index f2d84a9..fd247be 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/func.sgml,v 1.139 2003/02/13 05:24:01 momjian Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/func.sgml,v 1.140 2003/02/16 02:30:36 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -6295,17 +6295,6 @@ SELECT NULLIF(value, '(none)') ...
 </programlisting>
   </para>
 
-  <tip>
-   <para>
-    <function>COALESCE</function> and <function>NULLIF</function> are
-    just shorthand for <token>CASE</token> expressions.  They are actually
-    converted into <token>CASE</token> expressions at a very early stage
-    of processing, and subsequent processing thinks it is dealing with
-    <token>CASE</token>.  Thus an incorrect <function>COALESCE</function> or
-    <function>NULLIF</function> usage may draw an error message that
-    refers to <token>CASE</token>.
-   </para>
-  </tip>
   </sect2>
 
  </sect1>
index f406a57..35df2ea 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/catalog/dependency.c,v 1.21 2003/02/09 06:56:26 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/catalog/dependency.c,v 1.22 2003/02/16 02:30:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -933,6 +933,14 @@ find_expr_references_walker(Node *node,
                                                   &context->addrs);
                /* fall through to examine arguments */
        }
+       if (IsA(node, NullIfExpr))
+       {
+               NullIfExpr *nullifexpr = (NullIfExpr *) node;
+
+               add_object_address(OCLASS_OPERATOR, nullifexpr->opno, 0,
+                                                  &context->addrs);
+               /* fall through to examine arguments */
+       }
        if (IsA(node, Aggref))
        {
                Aggref     *aggref = (Aggref *) node;
index a2583fc..968617c 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.124 2003/02/03 21:15:43 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.125 2003/02/16 02:30:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -64,7 +64,7 @@ static Datum ExecEvalFunc(FuncExprState *fcache, ExprContext *econtext,
 static Datum ExecEvalOper(FuncExprState *fcache, ExprContext *econtext,
                         bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalDistinct(FuncExprState *fcache, ExprContext *econtext,
-                                bool *isNull, ExprDoneCond *isDone);
+                                bool *isNull);
 static ExprDoneCond ExecEvalFuncArgs(FunctionCallInfo fcinfo,
                                 List *argList, ExprContext *econtext);
 static Datum ExecEvalNot(BoolExprState *notclause, ExprContext *econtext,
@@ -75,6 +75,11 @@ static Datum ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext,
                                                 bool *isNull);
 static Datum ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
                         bool *isNull, ExprDoneCond *isDone);
+static Datum ExecEvalCoalesce(CoalesceExprState *coalesceExpr,
+                                                         ExprContext *econtext,
+                                                         bool *isNull);
+static Datum ExecEvalNullIf(FuncExprState *nullIfExpr, ExprContext *econtext,
+                                                       bool *isNull);
 static Datum ExecEvalNullTest(GenericExprState *nstate,
                                                          ExprContext *econtext,
                                                          bool *isNull, ExprDoneCond *isDone);
@@ -1187,8 +1192,7 @@ ExecEvalOper(FuncExprState *fcache,
 static Datum
 ExecEvalDistinct(FuncExprState *fcache,
                                 ExprContext *econtext,
-                                bool *isNull,
-                                ExprDoneCond *isDone)
+                                bool *isNull)
 {
        Datum           result;
        FunctionCallInfoData fcinfo;
@@ -1370,6 +1374,7 @@ ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext, bool *isNull)
        return BoolGetDatum(!AnyNull);
 }
 
+
 /* ----------------------------------------------------------------
  *             ExecEvalCase
  *
@@ -1430,6 +1435,91 @@ ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
 }
 
 /* ----------------------------------------------------------------
+ *             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
  *
  *             Evaluate a NullTest node.
@@ -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;
index aa7a7ef..2698f08 100644 (file)
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.243 2003/02/10 04:44:44 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.244 2003/02/16 02:30:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -785,7 +785,7 @@ _copyOpExpr(OpExpr *from)
 }
 
 /*
- * _copyDistinctExpr
+ * _copyDistinctExpr (same as OpExpr)
  */
 static DistinctExpr *
 _copyDistinctExpr(DistinctExpr *from)
@@ -920,6 +920,37 @@ _copyCaseWhen(CaseWhen *from)
 }
 
 /*
+ * _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
  */
 static NullTest *
@@ -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;
index c0bd777..378d8e4 100644 (file)
@@ -18,7 +18,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.186 2003/02/10 04:44:45 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.187 2003/02/16 02:30:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -379,6 +379,37 @@ _equalCaseWhen(CaseWhen *a, CaseWhen *b)
 }
 
 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)
 {
        COMPARE_NODE_FIELD(arg);
@@ -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;
index 134ee43..8485244 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.199 2003/02/10 04:44:45 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.200 2003/02/16 02:30:37 tgl Exp $
  *
  * NOTES
  *       Every node type that can appear in stored rules' parsetrees *must*
@@ -754,6 +754,27 @@ _outCaseWhen(StringInfo str, CaseWhen *node)
 }
 
 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)
 {
        WRITE_NODE_TYPE("NULLTEST");
@@ -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;
index f378567..410d092 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.148 2003/02/09 06:56:27 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.149 2003/02/16 02:30:37 tgl Exp $
  *
  * NOTES
  *       Path and Plan nodes do not have any readfuncs support, because we
@@ -607,6 +607,47 @@ _readCaseWhen(void)
 }
 
 /*
+ * _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
  */
 static NullTest *
@@ -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))
index 54e47e2..21bc152 100644 (file)
@@ -49,7 +49,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.106 2003/02/15 21:39:58 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.107 2003/02/16 02:30:38 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1468,7 +1468,8 @@ cost_qual_eval_walker(Node *node, QualCost *total)
         */
        if (IsA(node, FuncExpr) ||
                IsA(node, OpExpr) ||
-               IsA(node, DistinctExpr))
+               IsA(node, DistinctExpr) ||
+               IsA(node, NullIfExpr))
        {
                total->per_tuple += cpu_operator_cost;
        }
index 123b96f..4e17a85 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.91 2003/01/20 18:54:52 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.92 2003/02/16 02:30:38 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -284,6 +284,8 @@ fix_expr_references_walker(Node *node, void *context)
                set_opfuncid((OpExpr *) node);
        else if (IsA(node, DistinctExpr))
                set_opfuncid((OpExpr *) node); /* rely on struct equivalence */
+       else if (IsA(node, NullIfExpr))
+               set_opfuncid((OpExpr *) node); /* rely on struct equivalence */
        else if (IsA(node, SubPlan))
        {
                SubPlan *sp = (SubPlan *) node;
@@ -736,5 +738,7 @@ fix_opfuncids_walker(Node *node, void *context)
                set_opfuncid((OpExpr *) node);
        else if (IsA(node, DistinctExpr))
                set_opfuncid((OpExpr *) node); /* rely on struct equivalence */
+       else if (IsA(node, NullIfExpr))
+               set_opfuncid((OpExpr *) node); /* rely on struct equivalence */
        return expression_tree_walker(node, fix_opfuncids_walker, context);
 }
index 71b5909..40e440a 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.129 2003/02/09 06:56:27 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.130 2003/02/16 02:30:38 tgl Exp $
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -451,24 +451,22 @@ expression_returns_set_walker(Node *node, void *context)
                        return true;
                /* else fall through to check args */
        }
-       if (IsA(node, DistinctExpr))
-       {
-               DistinctExpr   *expr = (DistinctExpr *) node;
-
-               if (expr->opretset)
-                       return true;
-               /* else fall through to check args */
-       }
 
        /* Avoid recursion for some cases that can't return a set */
-       if (IsA(node, BoolExpr))
-               return false;
        if (IsA(node, Aggref))
                return false;
+       if (IsA(node, DistinctExpr))
+               return false;
+       if (IsA(node, BoolExpr))
+               return false;
        if (IsA(node, SubLink))
                return false;
        if (IsA(node, SubPlan))
                return false;
+       if (IsA(node, CoalesceExpr))
+               return false;
+       if (IsA(node, NullIfExpr))
+               return false;
 
        return expression_tree_walker(node, expression_returns_set_walker,
                                                                  context);
@@ -559,6 +557,14 @@ contain_mutable_functions_walker(Node *node, void *context)
                        return true;
                /* else fall through to check args */
        }
+       if (IsA(node, NullIfExpr))
+       {
+               NullIfExpr   *expr = (NullIfExpr *) node;
+
+               if (op_volatile(expr->opno) != PROVOLATILE_IMMUTABLE)
+                       return true;
+               /* else fall through to check args */
+       }
        if (IsA(node, SubLink))
        {
                SubLink    *sublink = (SubLink *) node;
@@ -626,6 +632,14 @@ contain_volatile_functions_walker(Node *node, void *context)
                        return true;
                /* else fall through to check args */
        }
+       if (IsA(node, NullIfExpr))
+       {
+               NullIfExpr   *expr = (NullIfExpr *) node;
+
+               if (op_volatile(expr->opno) == PROVOLATILE_VOLATILE)
+                       return true;
+               /* else fall through to check args */
+       }
        if (IsA(node, SubLink))
        {
                SubLink    *sublink = (SubLink *) node;
@@ -707,6 +721,10 @@ contain_nonstrict_functions_walker(Node *node, void *context)
        }
        if (IsA(node, CaseExpr))
                return true;
+       if (IsA(node, CoalesceExpr))
+               return true;
+       if (IsA(node, NullIfExpr))
+               return true;
        if (IsA(node, NullTest))
                return true;
        if (IsA(node, BooleanTest))
@@ -1446,6 +1464,39 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
                newcase->defresult = (Expr *) defresult;
                return (Node *) newcase;
        }
+       if (IsA(node, CoalesceExpr))
+       {
+               CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
+               CoalesceExpr *newcoalesce;
+               List *newargs = NIL;
+               List *arg;
+
+               foreach(arg, coalesceexpr->args)
+               {
+                       Node *e;
+
+                       e = eval_const_expressions_mutator((Node *) lfirst(arg),
+                                                                                          active_fns);
+                       /* 
+                        * We can remove null constants from the list.
+                        * For a non-null constant, if it has not been preceded by any
+                        * other non-null-constant expressions then that is the result.
+                        */
+                       if (IsA(e, Const))
+                       {
+                               if (((Const *) e)->constisnull)
+                                       continue;       /* drop null constant */
+                               if (newargs == NIL)
+                                       return e;       /* first expr */
+                       }
+                       newargs = lappend(newargs, e);
+               }
+
+               newcoalesce = makeNode(CoalesceExpr);
+               newcoalesce->coalescetype = coalesceexpr->coalescetype;
+               newcoalesce->args = newargs;
+               return (Node *) newcoalesce;
+       }
 
        /*
         * For any node type not handled above, we recurse using
@@ -2109,6 +2160,10 @@ expression_tree_walker(Node *node,
                                        return true;
                        }
                        break;
+               case T_CoalesceExpr:
+                       return walker(((CoalesceExpr *) node)->args, context);
+               case T_NullIfExpr:
+                       return walker(((NullIfExpr *) node)->args, context);
                case T_NullTest:
                        return walker(((NullTest *) node)->arg, context);
                case T_BooleanTest:
@@ -2481,6 +2536,26 @@ expression_tree_mutator(Node *node,
                                return (Node *) newnode;
                        }
                        break;
+               case T_CoalesceExpr:
+                       {
+                               CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
+                               CoalesceExpr *newnode;
+
+                               FLATCOPY(newnode, coalesceexpr, CoalesceExpr);
+                               MUTATE(newnode->args, coalesceexpr->args, List *);
+                               return (Node *) newnode;
+                       }
+                       break;
+               case T_NullIfExpr:
+                       {
+                               NullIfExpr   *expr = (NullIfExpr *) node;
+                               NullIfExpr   *newnode;
+
+                               FLATCOPY(newnode, expr, NullIfExpr);
+                               MUTATE(newnode->args, expr->args, List *);
+                               return (Node *) newnode;
+                       }
+                       break;
                case T_NullTest:
                        {
                                NullTest   *ntest = (NullTest *) node;
index 5a7dff9..c593196 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.403 2003/02/13 05:25:24 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.404 2003/02/16 02:30:38 tgl Exp $
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -6650,6 +6650,10 @@ in_expr: select_with_parens
  *     COALESCE(a,b,...)
  * same as CASE WHEN a IS NOT NULL THEN a WHEN b IS NOT NULL THEN b ... END
  * - thomas 1998-11-09
+ * 
+ * NULLIF and COALESCE have become first class nodes to
+ * prevent double evaluation of arguments.
+ * - Kris Jurka 2003-02-11
  */
 case_expr:     CASE case_arg when_clause_list case_default END_P
                                {
@@ -6661,29 +6665,12 @@ case_expr:      CASE case_arg when_clause_list case_default END_P
                                }
                        | NULLIF '(' a_expr ',' a_expr ')'
                                {
-                                       CaseExpr *c = makeNode(CaseExpr);
-                                       CaseWhen *w = makeNode(CaseWhen);
-
-                                       w->expr = (Expr *) makeSimpleA_Expr(AEXPR_OP, "=", $3, $5);
-                                       /* w->result is left NULL */
-                                       c->args = makeList1(w);
-                                       c->defresult = (Expr *) $3;
-                                       $$ = (Node *)c;
+                                       $$ = (Node *) makeSimpleA_Expr(AEXPR_NULLIF, "=", $3, $5);
                                }
                        | COALESCE '(' expr_list ')'
                                {
-                                       CaseExpr *c = makeNode(CaseExpr);
-                                       List *l;
-                                       foreach (l,$3)
-                                       {
-                                               CaseWhen *w = makeNode(CaseWhen);
-                                               NullTest *n = makeNode(NullTest);
-                                               n->arg = lfirst(l);
-                                               n->nulltesttype = IS_NOT_NULL;
-                                               w->expr = (Expr *) n;
-                                               w->result = lfirst(l);
-                                               c->args = lappend(c->args, w);
-                                       }
+                                       CoalesceExpr *c = makeNode(CoalesceExpr);
+                                       c->args = $3;
                                        $$ = (Node *)c;
                                }
                ;
index d65df55..33e7cce 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.109 2003/02/13 20:45:21 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.110 2003/02/16 02:30:38 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -922,17 +922,10 @@ buildMergedJoinVar(JoinType jointype, Var *l_colvar, Var *r_colvar)
                                 * Here we must build a COALESCE expression to ensure that
                                 * the join output is non-null if either input is.
                                 */
-                               CaseExpr   *c = makeNode(CaseExpr);
-                               CaseWhen   *w = makeNode(CaseWhen);
-                               NullTest   *n = makeNode(NullTest);
-
-                               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;
                        }
index f059a1d..2ec65b5 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.145 2003/02/13 18:29:07 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.146 2003/02/16 02:30:38 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -277,6 +277,24 @@ transformExpr(ParseState *pstate, Node *expr)
                                                        NodeSetTag(result, T_DistinctExpr);
                                                }
                                                break;
+                                       case AEXPR_NULLIF:
+                                               {
+                                                       Node       *lexpr = transformExpr(pstate,
+                                                                                                                         a->lexpr);
+                                                       Node       *rexpr = transformExpr(pstate,
+                                                                                                                         a->rexpr);
+
+                                                       result = (Node *) make_op(a->name,
+                                                                                                         lexpr,
+                                                                                                         rexpr);
+                                                       if (((OpExpr *) result)->opresulttype != BOOLOID)
+                                                               elog(ERROR, "NULLIF requires = operator to yield boolean");
+                                                       /*
+                                                        * We rely on NullIfExpr and OpExpr being same struct
+                                                        */
+                                                       NodeSetTag(result, T_NullIfExpr);
+                                               }
+                                               break;
                                        case AEXPR_OF:
                                                {
                                                        /*
@@ -615,6 +633,43 @@ transformExpr(ParseState *pstate, Node *expr)
                                break;
                        }
 
+               case T_CoalesceExpr:
+                       {
+                               CoalesceExpr *c = (CoalesceExpr *) expr;
+                               CoalesceExpr *newc = makeNode(CoalesceExpr);
+                               List *newargs = NIL;
+                               List *newcoercedargs = NIL;
+                               List *typeids = NIL;
+                               List *args;
+
+                               foreach(args, c->args)
+                               {
+                                       Node *e = (Node *) lfirst(args);
+                                       Node *newe;
+
+                                       newe = transformExpr(pstate, e);
+                                       newargs = lappend(newargs, newe);
+                                       typeids = lappendo(typeids, exprType(newe));
+                               }
+
+                               newc->coalescetype = select_common_type(typeids, "COALESCE");
+
+                               /* Convert arguments if necessary */
+                               foreach(args, newargs)
+                               {
+                                       Node *e = (Node *) lfirst(args);
+                                       Node *newe;
+
+                                       newe = coerce_to_common_type(e, newc->coalescetype,
+                                                                                                "COALESCE");
+                                       newcoercedargs = lappend(newcoercedargs, newe);
+                               }
+
+                               newc->args = newcoercedargs;
+                               result = (Node *) newc;
+                               break;
+                       }
+
                case T_NullTest:
                        {
                                NullTest   *n = (NullTest *) expr;
@@ -680,6 +735,7 @@ transformExpr(ParseState *pstate, Node *expr)
                case T_FuncExpr:
                case T_OpExpr:
                case T_DistinctExpr:
+               case T_NullIfExpr:
                case T_BoolExpr:
                case T_FieldSelect:
                case T_RelabelType:
@@ -1020,6 +1076,12 @@ exprType(Node *expr)
                case T_CaseWhen:
                        type = exprType((Node *) ((CaseWhen *) expr)->result);
                        break;
+               case T_CoalesceExpr:
+                       type = ((CoalesceExpr *) expr)->coalescetype;
+                       break;
+               case T_NullIfExpr:
+                       type = exprType((Node *) lfirst(((NullIfExpr *) expr)->args));
+                       break;
                case T_NullTest:
                        type = BOOLOID;
                        break;
@@ -1126,6 +1188,37 @@ exprTypmod(Node *expr)
                                return typmod;
                        }
                        break;
+               case T_CoalesceExpr:
+                       {
+                               /*
+                                * If all the alternatives agree on type/typmod, return
+                                * that typmod, else use -1
+                                */
+                               CoalesceExpr *cexpr = (CoalesceExpr *) expr;
+                               Oid coalescetype = cexpr->coalescetype;
+                               int32 typmod;
+                               List *arg;
+
+                               typmod = exprTypmod((Node *) lfirst(cexpr->args));
+                               foreach(arg, cexpr->args)
+                               {
+                                       Node *e = (Node *) lfirst(arg);
+
+                                       if (exprType(e) != coalescetype)
+                                               return -1;
+                                       if (exprTypmod(e) != typmod)
+                                               return -1;
+                               }
+                               return typmod;
+                       }
+                       break;
+               case T_NullIfExpr:
+                       {
+                               NullIfExpr *nexpr = (NullIfExpr *) expr;
+
+                               return exprTypmod((Node *) lfirst(nexpr->args));
+                       }
+                       break;
                case T_CoerceToDomain:
                        return ((CoerceToDomain *) expr)->resulttypmod;
                case T_CoerceToDomainValue:
index 70aaf18..4108e75 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.97 2003/02/13 05:53:46 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.98 2003/02/16 02:30:38 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -482,6 +482,14 @@ FigureColnameInternal(Node *node, char **name)
                case T_FuncCall:
                        *name = strVal(llast(((FuncCall *) node)->funcname));
                        return 2;
+               case T_A_Expr:
+                       /* make nullif() act like a regular function */
+                       if (((A_Expr *) node)->kind == AEXPR_NULLIF)
+                       {
+                               *name = "nullif";
+                               return 2;
+                       }
+                       break;
                case T_A_Const:
                        if (((A_Const *) node)->typename != NULL)
                        {
@@ -510,6 +518,10 @@ FigureColnameInternal(Node *node, char **name)
                                return 1;
                        }
                        break;
+               case T_CoalesceExpr:
+                       /* make coalesce() act like a regular function */
+                       *name = "coalesce";
+                       return 2;
                default:
                        break;
        }
index 205ffd7..dfed27f 100644 (file)
@@ -3,7 +3,7 @@
  *                             back to source text
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.135 2003/02/13 05:10:39 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.136 2003/02/16 02:30:39 tgl Exp $
  *
  *       This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -2238,6 +2238,46 @@ get_rule_expr(Node *node, deparse_context *context,
                        }
                        break;
 
+               case T_CoalesceExpr:
+                       {
+                               CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
+                               List *arg;
+                               char *sep;
+
+                               appendStringInfo(buf, "COALESCE(");
+                               sep = "";
+                               foreach(arg, coalesceexpr->args)
+                               {
+                                       Node *e = (Node *) lfirst(arg);
+
+                                       appendStringInfo(buf, sep);
+                                       get_rule_expr(e, context, true);
+                                       sep = ", ";
+                               }
+                               appendStringInfo(buf, ")");
+                       }
+                       break;
+                       
+               case T_NullIfExpr:
+                       {
+                               NullIfExpr *nullifexpr = (NullIfExpr *) node;
+                               List *arg;
+                               char *sep;
+
+                               appendStringInfo(buf, "NULLIF(");
+                               sep = "";
+                               foreach(arg, nullifexpr->args)
+                               {
+                                       Node *e = (Node *) lfirst(arg);
+
+                                       appendStringInfo(buf, sep);
+                                       get_rule_expr(e, context, true);
+                                       sep = ", ";
+                               }
+                               appendStringInfo(buf, ")");
+                       }
+                       break;
+
                case T_NullTest:
                        {
                                NullTest   *ntest = (NullTest *) node;
index 230f1e2..03e4521 100644 (file)
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: catversion.h,v 1.176 2003/02/13 05:24:02 momjian Exp $
+ * $Id: catversion.h,v 1.177 2003/02/16 02:30:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     200302131
+#define CATALOG_VERSION_NO     200302151
 
 #endif
index 8d2b130..591870b 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: execnodes.h,v 1.94 2003/02/09 00:30:39 tgl Exp $
+ * $Id: execnodes.h,v 1.95 2003/02/16 02:30:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -441,8 +441,9 @@ typedef struct ArrayRefExprState
 /* ----------------
  *             FuncExprState node
  *
- * Although named for FuncExpr, this is also used for OpExpr and DistinctExpr
- * nodes; be careful to check what xprstate.expr is actually pointing at!
+ * Although named for FuncExpr, this is also used for OpExpr, DistinctExpr,
+ * and NullIf nodes; be careful to check what xprstate.expr is actually
+ * pointing at!
  * ----------------
  */
 typedef struct FuncExprState
@@ -540,6 +541,16 @@ typedef struct CaseWhenState
 } CaseWhenState;
 
 /* ----------------
+ *             CoalesceExprState node
+ * ----------------
+ */
+typedef struct CoalesceExprState
+{
+       ExprState       xprstate;
+       List    *args;                          /* the arguments */
+} CoalesceExprState;
+
+/* ----------------
  *             CoerceToDomainState node
  * ----------------
  */
index a218790..356f4b6 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: nodes.h,v 1.136 2003/02/03 21:15:44 tgl Exp $
+ * $Id: nodes.h,v 1.137 2003/02/16 02:30:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -112,6 +112,8 @@ typedef enum NodeTag
        T_RelabelType,
        T_CaseExpr,
        T_CaseWhen,
+       T_CoalesceExpr,
+       T_NullIfExpr,
        T_NullTest,
        T_BooleanTest,
        T_CoerceToDomain,
@@ -136,6 +138,7 @@ typedef enum NodeTag
        T_SubPlanState,
        T_CaseExprState,
        T_CaseWhenState,
+       T_CoalesceExprState,
        T_CoerceToDomainState,
        T_DomainConstraintState,
 
index 9474bc6..381c11c 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parsenodes.h,v 1.230 2003/02/13 05:20:03 momjian Exp $
+ * $Id: parsenodes.h,v 1.231 2003/02/16 02:30:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -174,6 +174,7 @@ typedef enum A_Expr_Kind
        AEXPR_OR,
        AEXPR_NOT,
        AEXPR_DISTINCT,                         /* IS DISTINCT FROM - name must be "=" */
+       AEXPR_NULLIF,                           /* NULLIF - name must be "=" */
        AEXPR_OF                                        /* IS (not) OF - name must be "=" or "!=" */
 } A_Expr_Kind;
 
index be917c4..b8a358d 100644 (file)
@@ -10,7 +10,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: primnodes.h,v 1.79 2003/02/09 00:30:40 tgl Exp $
+ * $Id: primnodes.h,v 1.80 2003/02/16 02:30:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -537,6 +537,24 @@ typedef struct CaseWhen
        Expr       *result;                     /* substitution result */
 } CaseWhen;
 
+/*
+ * CoalesceExpr - a COALESCE expression
+ */
+typedef struct CoalesceExpr
+{
+       Expr    xpr;
+       Oid             coalescetype;           /* type of expression result */
+       List   *args;                           /* the arguments */
+} CoalesceExpr;
+
+/*
+ * NullIfExpr - a NULLIF expression
+ *
+ * Like DistinctExpr, this is represented the same as an OpExpr referencing
+ * the "=" operator for x and y.
+ */
+typedef OpExpr NullIfExpr;
+
 /* ----------------
  * NullTest
  *
index 7d4785e..3486f1c 100644 (file)
@@ -3,7 +3,7 @@
  *                       procedural language
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.78 2003/02/03 21:15:45 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.79 2003/02/16 02:30:39 tgl Exp $
  *
  *       This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -3628,6 +3628,28 @@ exec_simple_check_node(Node *node)
                                return TRUE;
                        }
 
+               case T_CoalesceExpr:
+                       {
+                               CoalesceExpr   *expr = (CoalesceExpr *) node;
+
+                               if (!exec_simple_check_node((Node *) expr->args))
+                                       return FALSE;
+
+                               return TRUE;
+                       }
+
+               case T_NullIfExpr:
+                       {
+                               NullIfExpr *expr = (NullIfExpr *) node;
+
+                               if (expr->opretset)
+                                       return FALSE;
+                               if (!exec_simple_check_node((Node *) expr->args))
+                                       return FALSE;
+
+                               return TRUE;
+                       }
+
                case T_NullTest:
                        return exec_simple_check_node((Node *) ((NullTest *) node)->arg);
 
index 2b53bc6..409a5a5 100644 (file)
@@ -154,32 +154,32 @@ SELECT * FROM CASE_TBL WHERE NULLIF(f,i) = 2;
 
 SELECT COALESCE(a.f, b.i, b.j)
   FROM CASE_TBL a, CASE2_TBL b;
- case  
--------
-  10.1
-  10.1
-  10.1
-  10.1
-  10.1
-  10.1
-  20.2
-  20.2
-  20.2
-  20.2
-  20.2
-  20.2
- -30.3
- -30.3
- -30.3
- -30.3
- -30.3
- -30.3
-     1
-     2
-     3
-     2
-     1
-    -6
+ coalesce 
+----------
+     10.1
+     10.1
+     10.1
+     10.1
+     10.1
+     10.1
+     20.2
+     20.2
+     20.2
+     20.2
+     20.2
+     20.2
   -30.3
   -30.3
   -30.3
   -30.3
   -30.3
   -30.3
+        1
+        2
+        3
+        2
+        1
+       -6
 (24 rows)
 
 SELECT *