Make decompilation of optimized CASE constructs more robust.

We had some hacks in ruleutils.c to cope with various odd transformations
that the optimizer could do on a CASE foo WHEN "CaseTestExpr = RHS" clause.
However, the fundamental impossibility of covering all cases was exposed
by Heikki, who pointed out that the "=" operator could get replaced by an
inlined SQL function, which could contain nearly anything at all.  So give
up on the hacks and just print the expression as-is if we fail to recognize
it as "CaseTestExpr = RHS".  (We must cover that case so that decompiled
rules print correctly; but we are not under any obligation to make EXPLAIN
output be 100% valid SQL in all cases, and already could not do so in some
other cases.)  This approach requires that we have some printable
representation of the CaseTestExpr node type; I used "CASE_TEST_EXPR".

Back-patch to all supported branches, since the problem case fails in all.
This commit is contained in:
Tom Lane 2011-05-26 19:25:19 -04:00
parent 9a57eafe9d
commit f014211849
1 changed files with 33 additions and 34 deletions

View File

@ -3721,50 +3721,36 @@ get_rule_expr(Node *node, deparse_context *context,
CaseWhen *when = (CaseWhen *) lfirst(temp); CaseWhen *when = (CaseWhen *) lfirst(temp);
Node *w = (Node *) when->expr; Node *w = (Node *) when->expr;
if (!PRETTY_INDENT(context))
appendStringInfoChar(buf, ' ');
appendContextKeyword(context, "WHEN ",
0, 0, 0);
if (caseexpr->arg) if (caseexpr->arg)
{ {
/* /*
* The parser should have produced WHEN clauses of the * The parser should have produced WHEN clauses of
* form "CaseTestExpr = RHS"; we want to show just the * the form "CaseTestExpr = RHS", possibly with an
* RHS. If the user wrote something silly like "CASE * implicit coercion inserted above the CaseTestExpr.
* boolexpr WHEN TRUE THEN ...", then the optimizer's * For accurate decompilation of rules it's essential
* simplify_boolean_equality() may have reduced this * that we show just the RHS. However in an
* to just "CaseTestExpr" or "NOT CaseTestExpr", for * expression that's been through the optimizer, the
* which we have to show "TRUE" or "FALSE". We have * WHEN clause could be almost anything (since the
* also to consider the possibility that an implicit * equality operator could have been expanded into an
* coercion was inserted between the CaseTestExpr and * inline function). If we don't recognize the form
* the operator. * of the WHEN clause, just punt and display it as-is.
*/ */
if (IsA(w, OpExpr)) if (IsA(w, OpExpr))
{ {
List *args = ((OpExpr *) w)->args; List *args = ((OpExpr *) w)->args;
Node *rhs;
Assert(list_length(args) == 2); if (list_length(args) == 2 &&
Assert(IsA(strip_implicit_coercions(linitial(args)), IsA(strip_implicit_coercions(linitial(args)),
CaseTestExpr)); CaseTestExpr))
rhs = (Node *) lsecond(args); w = (Node *) lsecond(args);
get_rule_expr(rhs, context, false);
} }
else if (IsA(strip_implicit_coercions(w),
CaseTestExpr))
appendStringInfo(buf, "TRUE");
else if (not_clause(w))
{
Assert(IsA(strip_implicit_coercions((Node *) get_notclausearg((Expr *) w)),
CaseTestExpr));
appendStringInfo(buf, "FALSE");
}
else
elog(ERROR, "unexpected CASE WHEN clause: %d",
(int) nodeTag(w));
} }
else
get_rule_expr(w, context, false); if (!PRETTY_INDENT(context))
appendStringInfoChar(buf, ' ');
appendContextKeyword(context, "WHEN ",
0, 0, 0);
get_rule_expr(w, context, false);
appendStringInfo(buf, " THEN "); appendStringInfo(buf, " THEN ");
get_rule_expr((Node *) when->result, context, true); get_rule_expr((Node *) when->result, context, true);
} }
@ -3780,6 +3766,19 @@ get_rule_expr(Node *node, deparse_context *context,
} }
break; break;
case T_CaseTestExpr:
{
/*
* Normally we should never get here, since for expressions
* that can contain this node type we attempt to avoid
* recursing to it. But in an optimized expression we might
* be unable to avoid that (see comments for CaseExpr). If we
* do see one, print it as CASE_TEST_EXPR.
*/
appendStringInfo(buf, "CASE_TEST_EXPR");
}
break;
case T_ArrayExpr: case T_ArrayExpr:
{ {
ArrayExpr *arrayexpr = (ArrayExpr *) node; ArrayExpr *arrayexpr = (ArrayExpr *) node;