Commit 3987e9e6 authored by Tom Lane's avatar Tom Lane

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.
parent 44404f39
...@@ -5187,50 +5187,36 @@ get_rule_expr(Node *node, deparse_context *context, ...@@ -5187,50 +5187,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);
} }
...@@ -5246,6 +5232,19 @@ get_rule_expr(Node *node, deparse_context *context, ...@@ -5246,6 +5232,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;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment