From 0bd7305ffb16b238dea79d5f98f88c745fe21db8 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Thu, 26 May 2011 19:25:19 -0400
Subject: [PATCH] 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.
---
 src/backend/utils/adt/ruleutils.c | 67 +++++++++++++++----------------
 1 file changed, 33 insertions(+), 34 deletions(-)

diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 72316425d2..4b5f978110 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -4893,50 +4893,36 @@ get_rule_expr(Node *node, deparse_context *context,
 					CaseWhen   *when = (CaseWhen *) lfirst(temp);
 					Node	   *w = (Node *) when->expr;
 
-					if (!PRETTY_INDENT(context))
-						appendStringInfoChar(buf, ' ');
-					appendContextKeyword(context, "WHEN ",
-										 0, 0, 0);
 					if (caseexpr->arg)
 					{
 						/*
-						 * The parser should have produced WHEN clauses of the
-						 * form "CaseTestExpr = RHS"; we want to show just the
-						 * RHS.  If the user wrote something silly like "CASE
-						 * boolexpr WHEN TRUE THEN ...", then the optimizer's
-						 * simplify_boolean_equality() may have reduced this
-						 * to just "CaseTestExpr" or "NOT CaseTestExpr", for
-						 * which we have to show "TRUE" or "FALSE".  We have
-						 * also to consider the possibility that an implicit
-						 * coercion was inserted between the CaseTestExpr and
-						 * the operator.
+						 * The parser should have produced WHEN clauses of
+						 * the form "CaseTestExpr = RHS", possibly with an
+						 * implicit coercion inserted above the CaseTestExpr.
+						 * For accurate decompilation of rules it's essential
+						 * that we show just the RHS.  However in an
+						 * expression that's been through the optimizer, the
+						 * WHEN clause could be almost anything (since the
+						 * equality operator could have been expanded into an
+						 * inline function).  If we don't recognize the form
+						 * of the WHEN clause, just punt and display it as-is.
 						 */
 						if (IsA(w, OpExpr))
 						{
 							List	   *args = ((OpExpr *) w)->args;
-							Node	   *rhs;
 
-							Assert(list_length(args) == 2);
-							Assert(IsA(strip_implicit_coercions(linitial(args)),
-									   CaseTestExpr));
-							rhs = (Node *) lsecond(args);
-							get_rule_expr(rhs, context, false);
+							if (list_length(args) == 2 &&
+								IsA(strip_implicit_coercions(linitial(args)),
+									CaseTestExpr))
+								w = (Node *) lsecond(args);
 						}
-						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 ");
 					get_rule_expr((Node *) when->result, context, true);
 				}
@@ -4952,6 +4938,19 @@ get_rule_expr(Node *node, deparse_context *context,
 			}
 			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:
 			{
 				ArrayExpr  *arrayexpr = (ArrayExpr *) node;
-- 
2.40.0