COALESCE() and NULLIF() are now first-class expressions, not macros
authorTom Lane
Sun, 16 Feb 2003 02:30:39 +0000 (02:30 +0000)
committerTom Lane
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 f2d84a969aa7f4a6634e75241aedbc48ee4296da..fd247be28c2a905e6526e742b78f943dd2f4d10d 100644 (file)
@@ -1,5 +1,5 @@
 
 
@@ -6295,17 +6295,6 @@ SELECT NULLIF(value, '(none)') ...
 
   
 
-  
-   
-    COALESCE and NULLIF are
-    just shorthand for CASE expressions.  They are actually
-    converted into CASE expressions at a very early stage
-    of processing, and subsequent processing thinks it is dealing with
-    CASE.  Thus an incorrect COALESCE or
-    NULLIF usage may draw an error message that
-    refers to CASE.
-   
-  
   
 
  
index f406a574c928b7f8c38c274512081507da3898f1..35df2eae264169437b44264ac0ea817c90c4353d 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 a2583fcc4c9569f08192abce50e443fe4729b556..968617c39a9494f0ac03db8f840e793af44dfb81 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
  *
@@ -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;
index aa7a7efcc8b8e92d3b1808f2343f954a9e8e633a..2698f084787f758ddd6d1c5a68becb19b4c0d80b 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)
@@ -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;
index c0bd77756a4929717e0c0f8388f2d7ccec625367..378d8e44031895ae7357cddb4e4245684eeace53 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 $
  *
  *-------------------------------------------------------------------------
  */
@@ -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;
index 134ee4328e77536ff1391f0dfe79ef4e1071f708..8485244492cab297276ac95276985fda06be313c 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*
@@ -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;
index f37856728b1f07a779ec8674da8371058ac3d90d..410d092c916404f0ab59cb232de9c31c8035898b 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
@@ -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))
index 54e47e242431891dee536e5fe4696ff163ce2260..21bc152ce6e7ec19d239937661e51b9bdc4f3903 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 123b96f18802728ad71177236c2a141e939dfd83..4e17a85eb4b2cfd7d1b4e22d591d46ea3089df74 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 71b5909d20718092be2c5f0f21af09b70414c004..40e440a3754593fc9613dd1adbe475cfe53fbbd4 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 5a7dff919d89c254b4e2479548c4872f53c7c224..c593196dfc869179e570ac07071fed0a71a0a573 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 d65df553acf2606a8643e4afe46ff14b1eb7d65c..33e7cce420390774765d89403e75935c6ed0222c 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 f059a1db2c098cfa29e38f9994218b90728846b0..2ec65b52c215061eb578af63f09356ebbff4e618 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 70aaf18ef5884fc0ce1831aba3df432d43fa8924..4108e7557dab7e040046dcb77c01d72e5a64f36e 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 205ffd7540b123e27fbe46e7435336bac8f621ec..dfed27f89dab7a8ab2d061d02ce35924258237a3 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 230f1e277acf08b5f7bc98e5d7f293e524baf172..03e452121f053f2b099c32190989debb278c1578 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 8d2b13059840ad6511bfdeb7fc544d570a94551b..591870be5151c33b85414fcb338edc5242263125 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
@@ -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
  * ----------------
index a218790194e0034f8eb41f5c65a4e8d1f51fcd8c..356f4b60fa3726a7701a7bc669c3df35336aea47 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 9474bc6490b3a69d9db8f48b7d3c26360a8a7d42..381c11c3893b72e0fa710150fdce4dccc5700f6a 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 be917c4f2606e36311f23e2596e075ad23f4c665..b8a358d6147efcefc53da404ace367553ffd174a 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 7d4785ef7d51bf9cc35bf73c1310f521b2f2ab89..3486f1ccd237aab9f8e69d8a6930258c831a1310 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 2b53bc699dc8a03b70fed206c45ffbe082b71f8b..409a5a536aa0feabf8d85990b911073cea0ea8b7 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 *