Reimplement CASE val WHEN compval1 THEN ... WHEN compval2 THEN ... END

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.
This commit is contained in:
Tom Lane 2004-03-17 20:48:43 +00:00
parent 8c702ea7ac
commit 55f7c3300d
16 changed files with 248 additions and 55 deletions

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.155 2004/03/17 01:02:23 tgl Exp $
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.156 2004/03/17 20:48:42 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -81,6 +81,9 @@ static Datum ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
static Datum ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
static Datum ExecEvalCaseTestExpr(ExprState *exprstate,
ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
static Datum ExecEvalArray(ArrayExprState *astate,
ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
@ -1809,10 +1812,29 @@ ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
{
List *clauses = caseExpr->args;
List *clause;
Datum save_datum;
bool save_isNull;
if (isDone)
*isDone = ExprSingleResult;
/*
* If there's a test expression, we have to evaluate it and save
* the value where the CaseTestExpr placeholders can find it.
* We must save and restore prior setting of econtext's caseValue fields,
* in case this node is itself within a larger CASE.
*/
save_datum = econtext->caseValue_datum;
save_isNull = econtext->caseValue_isNull;
if (caseExpr->arg)
{
econtext->caseValue_datum = ExecEvalExpr(caseExpr->arg,
econtext,
&econtext->caseValue_isNull,
NULL);
}
/*
* we evaluate each of the WHEN clauses in turn, as soon as one is
* true we return the corresponding result. If none are true then we
@ -1835,6 +1857,8 @@ ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
*/
if (DatumGetBool(clause_value) && !*isNull)
{
econtext->caseValue_datum = save_datum;
econtext->caseValue_isNull = save_isNull;
return ExecEvalExpr(wclause->result,
econtext,
isNull,
@ -1842,6 +1866,9 @@ ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
}
}
econtext->caseValue_datum = save_datum;
econtext->caseValue_isNull = save_isNull;
if (caseExpr->defresult)
{
return ExecEvalExpr(caseExpr->defresult,
@ -1854,6 +1881,22 @@ ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
return (Datum) 0;
}
/*
* ExecEvalCaseTestExpr
*
* Return the value stored by CASE.
*/
static Datum
ExecEvalCaseTestExpr(ExprState *exprstate,
ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone)
{
if (isDone)
*isDone = ExprSingleResult;
*isNull = econtext->caseValue_isNull;
return econtext->caseValue_datum;
}
/* ----------------------------------------------------------------
* ExecEvalArray - ARRAY[] expressions
*
@ -2478,6 +2521,10 @@ ExecInitExpr(Expr *node, PlanState *parent)
state = (ExprState *) makeNode(ExprState);
state->evalfunc = ExecEvalCoerceToDomainValue;
break;
case T_CaseTestExpr:
state = (ExprState *) makeNode(ExprState);
state->evalfunc = ExecEvalCaseTestExpr;
break;
case T_Aggref:
{
Aggref *aggref = (Aggref *) node;
@ -2666,6 +2713,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
List *inlist;
cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCase;
cstate->arg = ExecInitExpr(caseexpr->arg, parent);
FastListInit(&outlist);
foreach(inlist, caseexpr->args)
{
@ -2680,8 +2728,6 @@ ExecInitExpr(Expr *node, PlanState *parent)
FastAppend(&outlist, wstate);
}
cstate->args = FastListValue(&outlist);
/* caseexpr->arg should be null by now */
Assert(caseexpr->arg == NULL);
cstate->defresult = ExecInitExpr(caseexpr->defresult, parent);
state = (ExprState *) cstate;
}

View 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;

View File

@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.278 2004/03/11 01:47:35 ishii Exp $
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.279 2004/03/17 20:48:42 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -968,6 +968,20 @@ _copyCaseWhen(CaseWhen *from)
return newnode;
}
/*
* _copyCaseTestExpr
*/
static CaseTestExpr *
_copyCaseTestExpr(CaseTestExpr *from)
{
CaseTestExpr *newnode = makeNode(CaseTestExpr);
COPY_SCALAR_FIELD(typeId);
COPY_SCALAR_FIELD(typeMod);
return newnode;
}
/*
* _copyArrayExpr
*/
@ -2643,6 +2657,9 @@ copyObject(void *from)
case T_CaseWhen:
retval = _copyCaseWhen(from);
break;
case T_CaseTestExpr:
retval = _copyCaseTestExpr(from);
break;
case T_ArrayExpr:
retval = _copyArrayExpr(from);
break;

View File

@ -18,7 +18,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.217 2004/03/14 23:41:26 tgl Exp $
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.218 2004/03/17 20:48:42 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -403,6 +403,15 @@ _equalCaseWhen(CaseWhen *a, CaseWhen *b)
return true;
}
static bool
_equalCaseTestExpr(CaseTestExpr *a, CaseTestExpr *b)
{
COMPARE_SCALAR_FIELD(typeId);
COMPARE_SCALAR_FIELD(typeMod);
return true;
}
static bool
_equalArrayExpr(ArrayExpr *a, ArrayExpr *b)
{
@ -1724,6 +1733,9 @@ equal(void *a, void *b)
case T_CaseWhen:
retval = _equalCaseWhen(a, b);
break;
case T_CaseTestExpr:
retval = _equalCaseTestExpr(a, b);
break;
case T_ArrayExpr:
retval = _equalArrayExpr(a, b);
break;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.232 2004/01/31 05:09:40 neilc Exp $
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.233 2004/03/17 20:48:42 tgl Exp $
*
* NOTES
* Every node type that can appear in stored rules' parsetrees *must*
@ -805,6 +805,15 @@ _outCaseWhen(StringInfo str, CaseWhen *node)
WRITE_NODE_FIELD(result);
}
static void
_outCaseTestExpr(StringInfo str, CaseTestExpr *node)
{
WRITE_NODE_TYPE("CASETESTEXPR");
WRITE_OID_FIELD(typeId);
WRITE_INT_FIELD(typeMod);
}
static void
_outArrayExpr(StringInfo str, ArrayExpr *node)
{
@ -1701,6 +1710,9 @@ _outNode(StringInfo str, void *obj)
case T_CaseWhen:
_outCaseWhen(str, obj);
break;
case T_CaseTestExpr:
_outCaseTestExpr(str, obj);
break;
case T_ArrayExpr:
_outArrayExpr(str, obj);
break;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.165 2004/01/14 23:01:55 tgl Exp $
* $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.166 2004/03/17 20:48:42 tgl Exp $
*
* NOTES
* Path and Plan nodes do not have any readfuncs support, because we
@ -648,6 +648,20 @@ _readCaseWhen(void)
READ_DONE();
}
/*
* _readCaseTestExpr
*/
static CaseTestExpr *
_readCaseTestExpr(void)
{
READ_LOCALS(CaseTestExpr);
READ_OID_FIELD(typeId);
READ_INT_FIELD(typeMod);
READ_DONE();
}
/*
* _readArrayExpr
*/
@ -1010,6 +1024,8 @@ parseNodeString(void)
return_value = _readCaseExpr();
else if (MATCH("WHEN", 4))
return_value = _readCaseWhen();
else if (MATCH("CASETESTEXPR", 12))
return_value = _readCaseTestExpr();
else if (MATCH("ARRAY", 5))
return_value = _readArrayExpr();
else if (MATCH("COALESCE", 8))

View 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;
}

View 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;

View 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:
{
/*

View 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);
}

View 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

View 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;

View 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,

View File

@ -10,7 +10,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.95 2004/03/14 23:41:27 tgl Exp $
* $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.96 2004/03/17 20:48:43 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -563,8 +563,27 @@ typedef struct RelabelType
CoercionForm relabelformat; /* how to display this node */
} RelabelType;
/*
/*----------
* CaseExpr - a CASE expression
*
* We support two distinct forms of CASE expression:
* CASE WHEN boolexpr THEN expr [ WHEN boolexpr THEN expr ... ]
* CASE testexpr WHEN compexpr THEN expr [ WHEN compexpr THEN expr ... ]
* These are distinguishable by the "arg" field being NULL in the first case
* and the testexpr in the second case.
*
* In the raw grammar output for the second form, the condition expressions
* of the WHEN clauses are just the comparison values. Parse analysis
* converts these to valid boolean expressions of the form
* CaseTestExpr '=' compexpr
* where the CaseTestExpr node is a placeholder that emits the correct
* value at runtime. This structure is used so that the testexpr need be
* evaluated only once. Note that after parse analysis, the condition
* expressions always yield boolean.
*
* Note: we can test whether a CaseExpr has been through parse analysis
* yet by checking whether casetype is InvalidOid or not.
*----------
*/
typedef struct CaseExpr
{
@ -576,7 +595,7 @@ typedef struct CaseExpr
} CaseExpr;
/*
* CaseWhen - an argument to a CASE expression
* CaseWhen - one arm of a CASE expression
*/
typedef struct CaseWhen
{
@ -585,6 +604,18 @@ typedef struct CaseWhen
Expr *result; /* substitution result */
} CaseWhen;
/*
* Placeholder node for the test value to be processed by a CASE expression.
* This is effectively like a Param, but can be implemented more simply
* since we need only one replacement value at a time.
*/
typedef struct CaseTestExpr
{
Expr xpr;
Oid typeId; /* type for substituted value */
int32 typeMod; /* typemod for substituted value */
} CaseTestExpr;
/*
* ArrayExpr - an ARRAY[] expression
*

View 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;

View 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");