Reimplement CASE val WHEN compval1 THEN ... WHEN compval2 THEN ... END
authorTom Lane
Wed, 17 Mar 2004 20:48:43 +0000 (20:48 +0000)
committerTom Lane
Wed, 17 Mar 2004 20:48:43 +0000 (20:48 +0000)
so that the 'val' is computed only once, per recent discussion.  The
speedup is not much when 'val' is just a simple variable, but could be
significant for larger expressions.  More importantly this avoids issues
with multiple evaluations of a volatile 'val', and it allows the CASE
expression to be reverse-listed in its original form by ruleutils.c.

16 files changed:
src/backend/executor/execQual.c
src/backend/executor/execUtils.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/nodes/outfuncs.c
src/backend/nodes/readfuncs.c
src/backend/optimizer/util/clauses.c
src/backend/parser/gram.y
src/backend/parser/parse_expr.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/primnodes.h
src/pl/plpgsql/src/pl_exec.c
src/test/regress/expected/rules.out

index 94a5c110d410e56a5c7cf8aeb7a85609c350db97..fc16cccdda9fec82b2e871f084d99808e6fcbc65 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.155 2004/03/17 01:02:23 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.156 2004/03/17 20:48:42 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -81,6 +81,9 @@ static Datum ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext,
                         bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
             bool *isNull, ExprDoneCond *isDone);
+static Datum ExecEvalCaseTestExpr(ExprState *exprstate,
+                                 ExprContext *econtext,
+                                 bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalArray(ArrayExprState *astate,
                           ExprContext *econtext,
                           bool *isNull, ExprDoneCond *isDone);
@@ -1809,10 +1812,29 @@ ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
 {
    List       *clauses = caseExpr->args;
    List       *clause;
+   Datum       save_datum;
+   bool        save_isNull;
 
    if (isDone)
        *isDone = ExprSingleResult;
 
+   /*
+    * If there's a test expression, we have to evaluate it and save
+    * the value where the CaseTestExpr placeholders can find it.
+    * We must save and restore prior setting of econtext's caseValue fields,
+    * in case this node is itself within a larger CASE.
+    */
+   save_datum = econtext->caseValue_datum;
+   save_isNull = econtext->caseValue_isNull;
+
+   if (caseExpr->arg)
+   {
+       econtext->caseValue_datum = ExecEvalExpr(caseExpr->arg,
+                                                econtext,
+                                                &econtext->caseValue_isNull,
+                                                NULL);
+   }
+
    /*
     * we evaluate each of the WHEN clauses in turn, as soon as one is
     * true we return the corresponding result. If none are true then we
@@ -1835,6 +1857,8 @@ ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
         */
        if (DatumGetBool(clause_value) && !*isNull)
        {
+           econtext->caseValue_datum = save_datum;
+           econtext->caseValue_isNull = save_isNull;
            return ExecEvalExpr(wclause->result,
                                econtext,
                                isNull,
@@ -1842,6 +1866,9 @@ ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
        }
    }
 
+   econtext->caseValue_datum = save_datum;
+   econtext->caseValue_isNull = save_isNull;
+
    if (caseExpr->defresult)
    {
        return ExecEvalExpr(caseExpr->defresult,
@@ -1854,6 +1881,22 @@ ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
    return (Datum) 0;
 }
 
+/*
+ * ExecEvalCaseTestExpr
+ *
+ * Return the value stored by CASE.
+ */
+static Datum
+ExecEvalCaseTestExpr(ExprState *exprstate,
+                    ExprContext *econtext,
+                    bool *isNull, ExprDoneCond *isDone)
+{
+   if (isDone)
+       *isDone = ExprSingleResult;
+   *isNull = econtext->caseValue_isNull;
+   return econtext->caseValue_datum;
+}
+
 /* ----------------------------------------------------------------
  *     ExecEvalArray - ARRAY[] expressions
  *
@@ -2478,6 +2521,10 @@ ExecInitExpr(Expr *node, PlanState *parent)
            state = (ExprState *) makeNode(ExprState);
            state->evalfunc = ExecEvalCoerceToDomainValue;
            break;
+       case T_CaseTestExpr:
+           state = (ExprState *) makeNode(ExprState);
+           state->evalfunc = ExecEvalCaseTestExpr;
+           break;
        case T_Aggref:
            {
                Aggref     *aggref = (Aggref *) node;
@@ -2666,6 +2713,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
                List       *inlist;
 
                cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCase;
+               cstate->arg = ExecInitExpr(caseexpr->arg, parent);
                FastListInit(&outlist);
                foreach(inlist, caseexpr->args)
                {
@@ -2680,8 +2728,6 @@ ExecInitExpr(Expr *node, PlanState *parent)
                    FastAppend(&outlist, wstate);
                }
                cstate->args = FastListValue(&outlist);
-               /* caseexpr->arg should be null by now */
-               Assert(caseexpr->arg == NULL);
                cstate->defresult = ExecInitExpr(caseexpr->defresult, parent);
                state = (ExprState *) cstate;
            }
index b89f4015970f6a43cd6bd81ff3816a41b563f54b..9702b1cf0aa7dc2cca0d4bdc984a974207ef74fc 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.109 2004/01/22 02:23:21 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.110 2004/03/17 20:48:42 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -306,6 +306,9 @@ CreateExprContext(EState *estate)
    econtext->ecxt_aggvalues = NULL;
    econtext->ecxt_aggnulls = NULL;
 
+   econtext->caseValue_datum = (Datum) 0;
+   econtext->caseValue_isNull = true;
+
    econtext->domainValue_datum = (Datum) 0;
    econtext->domainValue_isNull = true;
 
index c1c4ddfed8b0fa919d3aebd312c76e31c1c1ce6b..c7d6193280ed58ca17d02dcc853b5bbf0da0affb 100644 (file)
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.278 2004/03/11 01:47:35 ishii Exp $
+ *   $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.279 2004/03/17 20:48:42 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -968,6 +968,20 @@ _copyCaseWhen(CaseWhen *from)
    return newnode;
 }
 
+/*
+ * _copyCaseTestExpr
+ */
+static CaseTestExpr *
+_copyCaseTestExpr(CaseTestExpr *from)
+{
+   CaseTestExpr *newnode = makeNode(CaseTestExpr);
+
+   COPY_SCALAR_FIELD(typeId);
+   COPY_SCALAR_FIELD(typeMod);
+
+   return newnode;
+}
+
 /*
  * _copyArrayExpr
  */
@@ -2643,6 +2657,9 @@ copyObject(void *from)
        case T_CaseWhen:
            retval = _copyCaseWhen(from);
            break;
+       case T_CaseTestExpr:
+           retval = _copyCaseTestExpr(from);
+           break;
        case T_ArrayExpr:
            retval = _copyArrayExpr(from);
            break;
index fadd02c935708768a0f6142b6a0cee24d98051b0..900d98dc8c0f8f88d4ff52b7e6029784a54a00d0 100644 (file)
@@ -18,7 +18,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.217 2004/03/14 23:41:26 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.218 2004/03/17 20:48:42 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -403,6 +403,15 @@ _equalCaseWhen(CaseWhen *a, CaseWhen *b)
    return true;
 }
 
+static bool
+_equalCaseTestExpr(CaseTestExpr *a, CaseTestExpr *b)
+{
+   COMPARE_SCALAR_FIELD(typeId);
+   COMPARE_SCALAR_FIELD(typeMod);
+
+   return true;
+}
+
 static bool
 _equalArrayExpr(ArrayExpr *a, ArrayExpr *b)
 {
@@ -1724,6 +1733,9 @@ equal(void *a, void *b)
        case T_CaseWhen:
            retval = _equalCaseWhen(a, b);
            break;
+       case T_CaseTestExpr:
+           retval = _equalCaseTestExpr(a, b);
+           break;
        case T_ArrayExpr:
            retval = _equalArrayExpr(a, b);
            break;
index 93afd868f8751a72ea5b89bf07f9515f9bffe58f..4db6517cd766c7b1b5f83c5e4ef5f0f05a1087a4 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.232 2004/01/31 05:09:40 neilc Exp $
+ *   $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.233 2004/03/17 20:48:42 tgl Exp $
  *
  * NOTES
  *   Every node type that can appear in stored rules' parsetrees *must*
@@ -805,6 +805,15 @@ _outCaseWhen(StringInfo str, CaseWhen *node)
    WRITE_NODE_FIELD(result);
 }
 
+static void
+_outCaseTestExpr(StringInfo str, CaseTestExpr *node)
+{
+   WRITE_NODE_TYPE("CASETESTEXPR");
+
+   WRITE_OID_FIELD(typeId);
+   WRITE_INT_FIELD(typeMod);
+}
+
 static void
 _outArrayExpr(StringInfo str, ArrayExpr *node)
 {
@@ -1701,6 +1710,9 @@ _outNode(StringInfo str, void *obj)
            case T_CaseWhen:
                _outCaseWhen(str, obj);
                break;
+           case T_CaseTestExpr:
+               _outCaseTestExpr(str, obj);
+               break;
            case T_ArrayExpr:
                _outArrayExpr(str, obj);
                break;
index 93c71fd224775e28ee8b51616bfba178d65b410b..116345686bfa5b6fea581f94b83ea85587a9f82f 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.165 2004/01/14 23:01:55 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.166 2004/03/17 20:48:42 tgl Exp $
  *
  * NOTES
  *   Path and Plan nodes do not have any readfuncs support, because we
@@ -648,6 +648,20 @@ _readCaseWhen(void)
    READ_DONE();
 }
 
+/*
+ * _readCaseTestExpr
+ */
+static CaseTestExpr *
+_readCaseTestExpr(void)
+{
+   READ_LOCALS(CaseTestExpr);
+
+   READ_OID_FIELD(typeId);
+   READ_INT_FIELD(typeMod);
+
+   READ_DONE();
+}
+
 /*
  * _readArrayExpr
  */
@@ -1010,6 +1024,8 @@ parseNodeString(void)
        return_value = _readCaseExpr();
    else if (MATCH("WHEN", 4))
        return_value = _readCaseWhen();
+   else if (MATCH("CASETESTEXPR", 12))
+       return_value = _readCaseTestExpr();
    else if (MATCH("ARRAY", 5))
        return_value = _readArrayExpr();
    else if (MATCH("COALESCE", 8))
index 1f3a8afc7f44fa46059e503490b43c6aabe18d57..1487aec453d76deca5227bcc8210904396b4a93b 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.164 2004/03/14 23:41:27 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.165 2004/03/17 20:48:42 tgl Exp $
  *
  * HISTORY
  *   AUTHOR            DATE            MAJOR EVENT
@@ -1397,15 +1397,29 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
         * simplify the entire CASE to that alternative's expression.
         * If there are no non-FALSE alternatives, we simplify the entire
         * CASE to the default result (ELSE result).
+        *
+        * If we have a simple-form CASE with constant test expression and
+        * one or more constant comparison expressions, we could run the
+        * implied comparisons and potentially reduce those arms to constants.
+        * This is not yet implemented, however.  At present, the
+        * CaseTestExpr placeholder will always act as a non-constant node
+        * and prevent the comparison boolean expressions from being reduced
+        * to Const nodes.
         *----------
         */
        CaseExpr   *caseexpr = (CaseExpr *) node;
        CaseExpr   *newcase;
+       Node       *newarg;
        FastList    newargs;
        Node       *defresult;
        Const      *const_input;
        List       *arg;
 
+       /* Simplify the test expression, if any */
+       newarg = eval_const_expressions_mutator((Node *) caseexpr->arg,
+                                               active_fns);
+
+       /* Simplify the WHEN clauses */
        FastListInit(&newargs);
        foreach(arg, caseexpr->args)
        {
@@ -1454,7 +1468,7 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
        /* Otherwise we need a new CASE node */
        newcase = makeNode(CaseExpr);
        newcase->casetype = caseexpr->casetype;
-       newcase->arg = NULL;
+       newcase->arg = (Expr *) newarg;
        newcase->args = FastListValue(&newargs);
        newcase->defresult = (Expr *) defresult;
        return (Node *) newcase;
@@ -2319,6 +2333,7 @@ expression_tree_walker(Node *node,
        case T_Const:
        case T_Param:
        case T_CoerceToDomainValue:
+       case T_CaseTestExpr:
        case T_SetToDefault:
        case T_RangeTblRef:
            /* primitive node types with no subnodes */
@@ -2425,6 +2440,8 @@ expression_tree_walker(Node *node,
            {
                CaseExpr   *caseexpr = (CaseExpr *) node;
 
+               if (walker(caseexpr->arg, context))
+                   return true;
                /* we assume walker doesn't care about CaseWhens, either */
                foreach(temp, caseexpr->args)
                {
@@ -2436,9 +2453,6 @@ expression_tree_walker(Node *node,
                    if (walker(when->result, context))
                        return true;
                }
-               /* caseexpr->arg should be null, but we'll check it anyway */
-               if (walker(caseexpr->arg, context))
-                   return true;
                if (walker(caseexpr->defresult, context))
                    return true;
            }
@@ -2692,6 +2706,7 @@ expression_tree_mutator(Node *node,
        case T_Const:
        case T_Param:
        case T_CoerceToDomainValue:
+       case T_CaseTestExpr:
        case T_SetToDefault:
        case T_RangeTblRef:
            /* primitive node types with no subnodes */
@@ -2829,9 +2844,8 @@ expression_tree_mutator(Node *node,
                CaseExpr   *newnode;
 
                FLATCOPY(newnode, caseexpr, CaseExpr);
-               MUTATE(newnode->args, caseexpr->args, List *);
-               /* caseexpr->arg should be null, but we'll check it anyway */
                MUTATE(newnode->arg, caseexpr->arg, Expr *);
+               MUTATE(newnode->args, caseexpr->args, List *);
                MUTATE(newnode->defresult, caseexpr->defresult, Expr *);
                return (Node *) newnode;
            }
index 9fd2e5b5795e8d8726a01864658fd11e64def04a..e8abfe039f091ce53a3cd9e1759d86efd2d0d8a6 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.448 2004/03/11 01:47:37 ishii Exp $
+ *   $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.449 2004/03/17 20:48:42 tgl Exp $
  *
  * HISTORY
  *   AUTHOR            DATE            MAJOR EVENT
@@ -6965,6 +6965,7 @@ in_expr:  select_with_parens
 case_expr: CASE case_arg when_clause_list case_default END_P
                {
                    CaseExpr *c = makeNode(CaseExpr);
+                   c->casetype = InvalidOid; /* not analyzed yet */
                    c->arg = (Expr *) $2;
                    c->args = $3;
                    c->defresult = (Expr *) $4;
index c23e39a25d0a1d0487ea9f7a55d2d91c8839c3f4..3881cc39538581bc530a011cb0d22124fe60ded4 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.165 2004/02/13 01:08:20 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.166 2004/03/17 20:48:42 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -637,14 +637,39 @@ transformExpr(ParseState *pstate, Node *expr)
        case T_CaseExpr:
            {
                CaseExpr   *c = (CaseExpr *) expr;
-               CaseExpr   *newc = makeNode(CaseExpr);
-               List       *newargs = NIL;
-               List       *typeids = NIL;
+               CaseExpr   *newc;
+               Node       *arg;
+               CaseTestExpr *placeholder;
+               List       *newargs;
+               List       *typeids;
                List       *args;
                Node       *defresult;
                Oid         ptype;
 
+               /* If we already transformed this node, do nothing */
+               if (OidIsValid(c->casetype))
+               {
+                   result = expr;
+                   break;
+               }
+               newc = makeNode(CaseExpr);
+
+               /* transform the test expression, if any */
+               arg = transformExpr(pstate, (Node *) c->arg);
+               newc->arg = (Expr *) arg;
+               /* generate placeholder for test expression */
+               if (arg)
+               {
+                   placeholder = makeNode(CaseTestExpr);
+                   placeholder->typeId = exprType(arg);
+                   placeholder->typeMod = exprTypmod(arg);
+               }
+               else
+                   placeholder = NULL;
+
                /* transform the list of arguments */
+               newargs = NIL;
+               typeids = NIL;
                foreach(args, c->args)
                {
                    CaseWhen   *w = (CaseWhen *) lfirst(args);
@@ -654,11 +679,11 @@ transformExpr(ParseState *pstate, Node *expr)
                    Assert(IsA(w, CaseWhen));
 
                    warg = (Node *) w->expr;
-                   if (c->arg != NULL)
+                   if (placeholder)
                    {
                        /* shorthand form was specified, so expand... */
                        warg = (Node *) makeSimpleA_Expr(AEXPR_OP, "=",
-                                                        (Node *) c->arg,
+                                                        (Node *) placeholder,
                                                         warg);
                    }
                    neww->expr = (Expr *) transformExpr(pstate, warg);
@@ -667,18 +692,7 @@ transformExpr(ParseState *pstate, Node *expr)
                                                     (Node *) neww->expr,
                                                            "CASE/WHEN");
 
-                   /*
-                    * result is NULL for NULLIF() construct - thomas
-                    * 1998-11-11
-                    */
                    warg = (Node *) w->result;
-                   if (warg == NULL)
-                   {
-                       A_Const    *n = makeNode(A_Const);
-
-                       n->val.type = T_Null;
-                       warg = (Node *) n;
-                   }
                    neww->result = (Expr *) transformExpr(pstate, warg);
 
                    newargs = lappend(newargs, neww);
@@ -687,13 +701,6 @@ transformExpr(ParseState *pstate, Node *expr)
 
                newc->args = newargs;
 
-               /*
-                * It's not shorthand anymore, so drop the implicit
-                * argument. This is necessary to keep any re-application
-                * of transformExpr from doing the wrong thing.
-                */
-               newc->arg = NULL;
-
                /* transform the default clause */
                defresult = (Node *) c->defresult;
                if (defresult == NULL)
@@ -714,6 +721,7 @@ transformExpr(ParseState *pstate, Node *expr)
                typeids = lconso(exprType((Node *) newc->defresult), typeids);
 
                ptype = select_common_type(typeids, "CASE");
+               Assert(OidIsValid(ptype));
                newc->casetype = ptype;
 
                /* Convert default result clause, if necessary */
@@ -915,6 +923,7 @@ transformExpr(ParseState *pstate, Node *expr)
        case T_BoolExpr:
        case T_FieldSelect:
        case T_RelabelType:
+       case T_CaseTestExpr:
        case T_CoerceToDomain:
        case T_CoerceToDomainValue:
        case T_SetToDefault:
@@ -1288,6 +1297,9 @@ exprType(Node *expr)
        case T_CaseWhen:
            type = exprType((Node *) ((CaseWhen *) expr)->result);
            break;
+       case T_CaseTestExpr:
+           type = ((CaseTestExpr *) expr)->typeId;
+           break;
        case T_ArrayExpr:
            type = ((ArrayExpr *) expr)->array_typeid;
            break;
@@ -1408,6 +1420,8 @@ exprTypmod(Node *expr)
                return typmod;
            }
            break;
+       case T_CaseTestExpr:
+           return ((CaseTestExpr *) expr)->typeMod;
        case T_CoalesceExpr:
            {
                /*
index 7e1513796db032ba4c407e4ffebd406691226462..3960152f3876cdd97ea31eb62132ea04f30a692b 100644 (file)
@@ -3,7 +3,7 @@
  *             back to source text
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.162 2004/01/31 05:09:40 neilc Exp $
+ *   $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.163 2004/03/17 20:48:42 tgl Exp $
  *
  *   This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -2564,7 +2564,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
                case T_ArrayExpr:       /* other separators */
                case T_CoalesceExpr:    /* own parentheses */
                case T_NullIfExpr:      /* other separators */
-               case T_Aggref:  /* own parentheses */
+               case T_Aggref:          /* own parentheses */
                case T_CaseExpr:        /* other separators */
                    return true;
                default:
@@ -2610,7 +2610,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
                case T_ArrayExpr:       /* other separators */
                case T_CoalesceExpr:    /* own parentheses */
                case T_NullIfExpr:      /* other separators */
-               case T_Aggref:  /* own parentheses */
+               case T_Aggref:          /* own parentheses */
                case T_CaseExpr:        /* other separators */
                    return true;
                default:
@@ -3026,6 +3026,11 @@ get_rule_expr(Node *node, deparse_context *context,
 
                appendContextKeyword(context, "CASE",
                                     0, PRETTYINDENT_VAR, 0);
+               if (caseexpr->arg)
+               {
+                   appendStringInfoChar(buf, ' ');
+                   get_rule_expr((Node *) caseexpr->arg, context, true);
+               }
                foreach(temp, caseexpr->args)
                {
                    CaseWhen   *when = (CaseWhen *) lfirst(temp);
@@ -3034,7 +3039,17 @@ get_rule_expr(Node *node, deparse_context *context,
                        appendStringInfoChar(buf, ' ');
                    appendContextKeyword(context, "WHEN ",
                                         0, 0, 0);
-                   get_rule_expr((Node *) when->expr, context, false);
+                   if (caseexpr->arg)
+                   {
+                       /* Show only the RHS of "CaseTestExpr = RHS" */
+                       Node   *rhs;
+
+                       Assert(IsA(when->expr, OpExpr));
+                       rhs = (Node *) lsecond(((OpExpr *) when->expr)->args);
+                       get_rule_expr(rhs, context, false);
+                   }
+                   else
+                       get_rule_expr((Node *) when->expr, context, false);
                    appendStringInfo(buf, " THEN ");
                    get_rule_expr((Node *) when->result, context, true);
                }
index 8f30e0bf5949400155d1af5de251b4c4d6a45e92..2d91191c49b9f322000dc6584cdeae3b2f6c6c6d 100644 (file)
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.220 2004/03/15 01:13:41 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.221 2004/03/17 20:48:42 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 200403141
+#define CATALOG_VERSION_NO 200403171
 
 #endif
index afdd0669d40de0e7918532478b16a928aba989c1..440fcc4d576d7035b982e3c9114edb84e3acf8b6 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.113 2004/03/17 01:02:24 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.114 2004/03/17 20:48:42 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -113,6 +113,10 @@ typedef struct ExprContext
    Datum      *ecxt_aggvalues; /* precomputed values for Aggref nodes */
    bool       *ecxt_aggnulls;  /* null flags for Aggref nodes */
 
+   /* Value to substitute for CaseTestExpr nodes in expression */
+   Datum       caseValue_datum;
+   bool        caseValue_isNull;
+
    /* Value to substitute for CoerceToDomainValue nodes in expression */
    Datum       domainValue_datum;
    bool        domainValue_isNull;
@@ -566,6 +570,7 @@ typedef struct SubPlanState
 typedef struct CaseExprState
 {
    ExprState   xprstate;
+   ExprState  *arg;            /* implicit equality comparison argument */
    List       *args;           /* the arguments (list of WHEN clauses) */
    ExprState  *defresult;      /* the default result (ELSE clause) */
 } CaseExprState;
index ced6f6cb432ee0dbe8f90118d944e7cc524dd66e..5c34b8d8559a3042790b3b3107bf9e141a99c4ea 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.150 2004/01/07 18:43:36 neilc Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.151 2004/03/17 20:48:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -113,6 +113,7 @@ typedef enum NodeTag
    T_RelabelType,
    T_CaseExpr,
    T_CaseWhen,
+   T_CaseTestExpr,
    T_ArrayExpr,
    T_CoalesceExpr,
    T_NullIfExpr,
index 065ca656a7f5ae91237786397512db7147abdd64..d94196d740069b2507e083147ec9f6eee58808d6 100644 (file)
@@ -10,7 +10,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.95 2004/03/14 23:41:27 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.96 2004/03/17 20:48:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -563,8 +563,27 @@ typedef struct RelabelType
    CoercionForm relabelformat; /* how to display this node */
 } RelabelType;
 
-/*
+/*----------
  * CaseExpr - a CASE expression
+ *
+ * We support two distinct forms of CASE expression:
+ *     CASE WHEN boolexpr THEN expr [ WHEN boolexpr THEN expr ... ]
+ *     CASE testexpr WHEN compexpr THEN expr [ WHEN compexpr THEN expr ... ]
+ * These are distinguishable by the "arg" field being NULL in the first case
+ * and the testexpr in the second case.
+ *
+ * In the raw grammar output for the second form, the condition expressions
+ * of the WHEN clauses are just the comparison values.  Parse analysis
+ * converts these to valid boolean expressions of the form
+ *     CaseTestExpr '=' compexpr
+ * where the CaseTestExpr node is a placeholder that emits the correct
+ * value at runtime.  This structure is used so that the testexpr need be
+ * evaluated only once.  Note that after parse analysis, the condition
+ * expressions always yield boolean.
+ *
+ * Note: we can test whether a CaseExpr has been through parse analysis
+ * yet by checking whether casetype is InvalidOid or not.
+ *----------
  */
 typedef struct CaseExpr
 {
@@ -576,7 +595,7 @@ typedef struct CaseExpr
 } CaseExpr;
 
 /*
- * CaseWhen - an argument to a CASE expression
+ * CaseWhen - one arm of a CASE expression
  */
 typedef struct CaseWhen
 {
@@ -585,6 +604,18 @@ typedef struct CaseWhen
    Expr       *result;         /* substitution result */
 } CaseWhen;
 
+/*
+ * Placeholder node for the test value to be processed by a CASE expression.
+ * This is effectively like a Param, but can be implemented more simply
+ * since we need only one replacement value at a time.
+ */
+typedef struct CaseTestExpr
+{
+   Expr        xpr;
+   Oid         typeId;         /* type for substituted value */
+   int32       typeMod;        /* typemod for substituted value */
+} CaseTestExpr;
+
 /*
  * ArrayExpr - an ARRAY[] expression
  *
index 0c409c0e64e74e3b0c818b6c76453fda9b4b4520..b984f0932bbf55a5a459eaaf1e42d30d937651b7 100644 (file)
@@ -3,7 +3,7 @@
  *           procedural language
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.97 2004/02/25 18:10:51 tgl Exp $
+ *   $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.98 2004/03/17 20:48:43 tgl Exp $
  *
  *   This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -3729,6 +3729,9 @@ exec_simple_check_node(Node *node)
                return TRUE;
            }
 
+       case T_CaseTestExpr:
+           return TRUE;
+
        case T_ArrayExpr:
            {
                ArrayExpr  *expr = (ArrayExpr *) node;
@@ -3770,6 +3773,9 @@ exec_simple_check_node(Node *node)
        case T_CoerceToDomain:
            return exec_simple_check_node((Node *) ((CoerceToDomain *) node)->arg);
 
+       case T_CoerceToDomainValue:
+           return TRUE;
+
        case T_List:
            {
                List       *expr = (List *) node;
index 902ff4472f6d8e4d9041cac3f9c391a44978f1b4..cf0f63833149b9e1069f0ada1416ce5860652259 100644 (file)
@@ -1272,8 +1272,8 @@ drop table cchild;
 -- Check that ruleutils are working
 --
 SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schema' ORDER BY viewname;
-         viewname         |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      definition                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       
---------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+         viewname         |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          definition                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           
+--------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  iexit                    | SELECT ih.name, ih.thepath, interpt_pp(ih.thepath, r.thepath) AS exit FROM ihighway ih, ramp r WHERE (ih.thepath ## r.thepath);
  pg_indexes               | SELECT n.nspname AS schemaname, c.relname AS tablename, i.relname AS indexname, pg_get_indexdef(i.oid) AS indexdef FROM (((pg_index x JOIN pg_class c ON ((c.oid = x.indrelid))) JOIN pg_class i ON ((i.oid = x.indexrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE ((c.relkind = 'r'::"char") AND (i.relkind = 'i'::"char"));
  pg_locks                 | SELECT l.relation, l."database", l."transaction", l.pid, l."mode", l.granted FROM pg_lock_status() l(relation oid, "database" oid, "transaction" xid, pid integer, "mode" text, granted boolean);
@@ -1296,7 +1296,7 @@ SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schem
  pg_statio_user_indexes   | SELECT pg_statio_all_indexes.relid, pg_statio_all_indexes.indexrelid, pg_statio_all_indexes.schemaname, pg_statio_all_indexes.relname, pg_statio_all_indexes.indexrelname, pg_statio_all_indexes.idx_blks_read, pg_statio_all_indexes.idx_blks_hit FROM pg_statio_all_indexes WHERE ((pg_statio_all_indexes.schemaname <> 'pg_catalog'::name) AND (pg_statio_all_indexes.schemaname <> 'pg_toast'::name));
  pg_statio_user_sequences | SELECT pg_statio_all_sequences.relid, pg_statio_all_sequences.schemaname, pg_statio_all_sequences.relname, pg_statio_all_sequences.blks_read, pg_statio_all_sequences.blks_hit FROM pg_statio_all_sequences WHERE ((pg_statio_all_sequences.schemaname <> 'pg_catalog'::name) AND (pg_statio_all_sequences.schemaname <> 'pg_toast'::name));
  pg_statio_user_tables    | SELECT pg_statio_all_tables.relid, pg_statio_all_tables.schemaname, pg_statio_all_tables.relname, pg_statio_all_tables.heap_blks_read, pg_statio_all_tables.heap_blks_hit, pg_statio_all_tables.idx_blks_read, pg_statio_all_tables.idx_blks_hit, pg_statio_all_tables.toast_blks_read, pg_statio_all_tables.toast_blks_hit, pg_statio_all_tables.tidx_blks_read, pg_statio_all_tables.tidx_blks_hit FROM pg_statio_all_tables WHERE ((pg_statio_all_tables.schemaname <> 'pg_catalog'::name) AND (pg_statio_all_tables.schemaname <> 'pg_toast'::name));
- pg_stats                 | SELECT nspname AS schemaname, relname AS tablename, attname, stanullfrac AS null_frac, stawidth AS avg_width, stadistinct AS n_distinct, CASE WHEN (1 = stakind1) THEN stavalues1 WHEN (1 = stakind2) THEN stavalues2 WHEN (1 = stakind3) THEN stavalues3 WHEN (1 = stakind4) THEN stavalues4 ELSE NULL::"unknown" END AS most_common_vals, CASE WHEN (1 = stakind1) THEN stanumbers1 WHEN (1 = stakind2) THEN stanumbers2 WHEN (1 = stakind3) THEN stanumbers3 WHEN (1 = stakind4) THEN stanumbers4 ELSE NULL::real[] END AS most_common_freqs, CASE WHEN (2 = stakind1) THEN stavalues1 WHEN (2 = stakind2) THEN stavalues2 WHEN (2 = stakind3) THEN stavalues3 WHEN (2 = stakind4) THEN stavalues4 ELSE NULL::"unknown" END AS histogram_bounds, CASE WHEN (3 = stakind1) THEN stanumbers1[1] WHEN (3 = stakind2) THEN stanumbers2[1] WHEN (3 = stakind3) THEN stanumbers3[1] WHEN (3 = stakind4) THEN stanumbers4[1] ELSE NULL::real END AS correlation FROM (((pg_statistic s JOIN pg_class c ON ((c.oid = s.starelid))) JOIN pg_attribute a ON (((c.oid = a.attrelid) AND (a.attnum = s.staattnum)))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE has_table_privilege(c.oid, 'select'::text);
+ pg_stats                 | SELECT nspname AS schemaname, relname AS tablename, attname, stanullfrac AS null_frac, stawidth AS avg_width, stadistinct AS n_distinct, CASE 1 WHEN stakind1 THEN stavalues1 WHEN stakind2 THEN stavalues2 WHEN stakind3 THEN stavalues3 WHEN stakind4 THEN stavalues4 ELSE NULL::"unknown" END AS most_common_vals, CASE 1 WHEN stakind1 THEN stanumbers1 WHEN stakind2 THEN stanumbers2 WHEN stakind3 THEN stanumbers3 WHEN stakind4 THEN stanumbers4 ELSE NULL::real[] END AS most_common_freqs, CASE 2 WHEN stakind1 THEN stavalues1 WHEN stakind2 THEN stavalues2 WHEN stakind3 THEN stavalues3 WHEN stakind4 THEN stavalues4 ELSE NULL::"unknown" END AS histogram_bounds, CASE 3 WHEN stakind1 THEN stanumbers1[1] WHEN stakind2 THEN stanumbers2[1] WHEN stakind3 THEN stanumbers3[1] WHEN stakind4 THEN stanumbers4[1] ELSE NULL::real END AS correlation FROM (((pg_statistic s JOIN pg_class c ON ((c.oid = s.starelid))) JOIN pg_attribute a ON (((c.oid = a.attrelid) AND (a.attnum = s.staattnum)))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE has_table_privilege(c.oid, 'select'::text);
  pg_tables                | SELECT n.nspname AS schemaname, c.relname AS tablename, pg_get_userbyid(c.relowner) AS tableowner, c.relhasindex AS hasindexes, c.relhasrules AS hasrules, (c.reltriggers > 0) AS hastriggers FROM (pg_class c LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = 'r'::"char");
  pg_user                  | SELECT pg_shadow.usename, pg_shadow.usesysid, pg_shadow.usecreatedb, pg_shadow.usesuper, pg_shadow.usecatupd, '********'::text AS passwd, pg_shadow.valuntil, pg_shadow.useconfig FROM pg_shadow;
  pg_views                 | SELECT n.nspname AS schemaname, c.relname AS viewname, pg_get_userbyid(c.relowner) AS viewowner, pg_get_viewdef(c.oid) AS definition FROM (pg_class c LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = 'v'::"char");