diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index 94a5c110d410e56a5c7cf8aeb7a85609c350db97..fc16cccdda9fec82b2e871f084d99808e6fcbc65 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,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; } diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index b89f4015970f6a43cd6bd81ff3816a41b563f54b..9702b1cf0aa7dc2cca0d4bdc984a974207ef74fc 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 c1c4ddfed8b0fa919d3aebd312c76e31c1c1ce6b..c7d6193280ed58ca17d02dcc853b5bbf0da0affb 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 $ * *------------------------------------------------------------------------- */ @@ -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; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index fadd02c935708768a0f6142b6a0cee24d98051b0..900d98dc8c0f8f88d4ff52b7e6029784a54a00d0 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 $ * *------------------------------------------------------------------------- */ @@ -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; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 93afd868f8751a72ea5b89bf07f9515f9bffe58f..4db6517cd766c7b1b5f83c5e4ef5f0f05a1087a4 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* @@ -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; diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 93c71fd224775e28ee8b51616bfba178d65b410b..116345686bfa5b6fea581f94b83ea85587a9f82f 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 @@ -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)) diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 1f3a8afc7f44fa46059e503490b43c6aabe18d57..1487aec453d76deca5227bcc8210904396b4a93b 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 9fd2e5b5795e8d8726a01864658fd11e64def04a..e8abfe039f091ce53a3cd9e1759d86efd2d0d8a6 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 c23e39a25d0a1d0487ea9f7a55d2d91c8839c3f4..3881cc39538581bc530a011cb0d22124fe60ded4 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 7e1513796db032ba4c407e4ffebd406691226462..3960152f3876cdd97ea31eb62132ea04f30a692b 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 8f30e0bf5949400155d1af5de251b4c4d6a45e92..2d91191c49b9f322000dc6584cdeae3b2f6c6c6d 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 afdd0669d40de0e7918532478b16a928aba989c1..440fcc4d576d7035b982e3c9114edb84e3acf8b6 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 ced6f6cb432ee0dbe8f90118d944e7cc524dd66e..5c34b8d8559a3042790b3b3107bf9e141a99c4ea 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 065ca656a7f5ae91237786397512db7147abdd64..d94196d740069b2507e083147ec9f6eee58808d6 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 { @@ -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 * diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index 0c409c0e64e74e3b0c818b6c76453fda9b4b4520..b984f0932bbf55a5a459eaaf1e42d30d937651b7 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 902ff4472f6d8e4d9041cac3f9c391a44978f1b4..cf0f63833149b9e1069f0ada1416ce5860652259 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");