OSDN Git Service

Reimplement CASE val WHEN compval1 THEN ... WHEN compval2 THEN ... END
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 17 Mar 2004 20:48:43 +0000 (20:48 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
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 94a5c11..fc16ccc 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,11 +1812,30 @@ 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
         * return the value of the default clause, or NULL if there is none.
@@ -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 b89f401..9702b1c 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 c1c4ddf..c7d6193 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 $
  *
  *-------------------------------------------------------------------------
  */
@@ -969,6 +969,20 @@ _copyCaseWhen(CaseWhen *from)
 }
 
 /*
+ * _copyCaseTestExpr
+ */
+static CaseTestExpr *
+_copyCaseTestExpr(CaseTestExpr *from)
+{
+       CaseTestExpr *newnode = makeNode(CaseTestExpr);
+
+       COPY_SCALAR_FIELD(typeId);
+       COPY_SCALAR_FIELD(typeMod);
+
+       return newnode;
+}
+
+/*
  * _copyArrayExpr
  */
 static ArrayExpr *
@@ -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 fadd02c..900d98d 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 $
  *
  *-------------------------------------------------------------------------
  */
@@ -404,6 +404,15 @@ _equalCaseWhen(CaseWhen *a, CaseWhen *b)
 }
 
 static bool
+_equalCaseTestExpr(CaseTestExpr *a, CaseTestExpr *b)
+{
+       COMPARE_SCALAR_FIELD(typeId);
+       COMPARE_SCALAR_FIELD(typeMod);
+
+       return true;
+}
+
+static bool
 _equalArrayExpr(ArrayExpr *a, ArrayExpr *b)
 {
        COMPARE_SCALAR_FIELD(array_typeid);
@@ -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 93afd86..4db6517 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*
@@ -806,6 +806,15 @@ _outCaseWhen(StringInfo str, CaseWhen *node)
 }
 
 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)
 {
        WRITE_NODE_TYPE("ARRAY");
@@ -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 93c71fd..1163456 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
@@ -649,6 +649,20 @@ _readCaseWhen(void)
 }
 
 /*
+ * _readCaseTestExpr
+ */
+static CaseTestExpr *
+_readCaseTestExpr(void)
+{
+       READ_LOCALS(CaseTestExpr);
+
+       READ_OID_FIELD(typeId);
+       READ_INT_FIELD(typeMod);
+
+       READ_DONE();
+}
+
+/*
  * _readArrayExpr
  */
 static ArrayExpr *
@@ -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 1f3a8af..1487aec 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 9fd2e5b..e8abfe0 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 c23e39a..3881cc3 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 7e15137..3960152 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 8f30e0b..2d91191 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 afdd066..440fcc4 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 ced6f6c..5c34b8d 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 065ca65..d94196d 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
 {
@@ -586,6 +605,18 @@ typedef struct CaseWhen
 } 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
  *
  * Note: if multidims is false, the constituent expressions all yield the
index 0c409c0..b984f09 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 902ff44..cf0f638 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");