aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/backend/executor/execQual.c52
-rw-r--r--src/backend/executor/execUtils.c5
-rw-r--r--src/backend/nodes/copyfuncs.c19
-rw-r--r--src/backend/nodes/equalfuncs.c14
-rw-r--r--src/backend/nodes/outfuncs.c14
-rw-r--r--src/backend/nodes/readfuncs.c18
-rw-r--r--src/backend/optimizer/util/clauses.c28
-rw-r--r--src/backend/parser/gram.y3
-rw-r--r--src/backend/parser/parse_expr.c62
-rw-r--r--src/backend/utils/adt/ruleutils.c23
-rw-r--r--src/include/catalog/catversion.h4
-rw-r--r--src/include/nodes/execnodes.h7
-rw-r--r--src/include/nodes/nodes.h3
-rw-r--r--src/include/nodes/primnodes.h37
-rw-r--r--src/pl/plpgsql/src/pl_exec.c8
-rw-r--r--src/test/regress/expected/rules.out6
16 files changed, 248 insertions, 55 deletions
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index 94a5c110d41..fc16cccdda9 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -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;
}
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index b89f4015970..9702b1cf0aa 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -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;
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index c1c4ddfed8b..c7d6193280e 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -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;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index fadd02c9357..900d98dc8c0 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -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;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 93afd868f87..4db6517cd76 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -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;
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 93c71fd2247..116345686bf 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -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))
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 1f3a8afc7f4..1487aec453d 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -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;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 9fd2e5b5795..e8abfe039f0 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -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;
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index c23e39a25d0..3881cc39538 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -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:
{
/*
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 7e1513796db..3960152f387 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -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);
}
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 8f30e0bf594..2d91191c49b 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -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
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index afdd0669d40..440fcc4d576 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -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;
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index ced6f6cb432..5c34b8d8559 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -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,
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 065ca656a7f..d94196d7400 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -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
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 0c409c0e64e..b984f0932bb 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -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;
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 902ff4472f6..cf0f6383314 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -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");