Skip to content

Commit 554b00c

Browse files
committed
Fix up handling of simple-form CASE with constant test expression.
eval_const_expressions() can replace CaseTestExprs with constants when the surrounding CASE's test expression is a constant. This confuses ruleutils.c's heuristic for deparsing simple-form CASEs, leading to Assert failures or "unexpected CASE WHEN clause" errors. I had put in a hack solution for that years ago (see commit 514ce7a of 2006-10-01), but bug #5794 from Peter Speck shows that that solution failed to cover all cases. Fortunately, there's a much better way, which came to me upon reflecting that Peter's "CASE TRUE WHEN" seemed pretty redundant: we can "simplify" the simple-form CASE to the general form of CASE, by simply omitting the constant test expression from the rebuilt CASE construct. This is intuitively valid because there is no need for the executor to evaluate the test expression at runtime; it will never be referenced, because any CaseTestExprs that would have referenced it are now replaced by constants. This won't save a whole lot of cycles, since evaluating a Const is pretty cheap, but a cycle saved is a cycle earned. In any case it beats kluging ruleutils.c still further. So this patch improves const-simplification and reverts the previous change in ruleutils.c. Back-patch to all supported branches. The bug exists in 8.1 too, but it's out of warranty.
1 parent aebddf0 commit 554b00c

File tree

2 files changed

+21
-11
lines changed

2 files changed

+21
-11
lines changed

src/backend/optimizer/util/clauses.c

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2595,7 +2595,18 @@ eval_const_expressions_mutator(Node *node,
25952595
* placeholder nodes, so that we have the opportunity to reduce
25962596
* constant test conditions. For example this allows
25972597
* CASE 0 WHEN 0 THEN 1 ELSE 1/0 END
2598-
* to reduce to 1 rather than drawing a divide-by-0 error.
2598+
* to reduce to 1 rather than drawing a divide-by-0 error. Note
2599+
* that when the test expression is constant, we don't have to
2600+
* include it in the resulting CASE; for example
2601+
* CASE 0 WHEN x THEN y ELSE z END
2602+
* is transformed by the parser to
2603+
* CASE 0 WHEN CaseTestExpr = x THEN y ELSE z END
2604+
* which we can simplify to
2605+
* CASE WHEN 0 = x THEN y ELSE z END
2606+
* It is not necessary for the executor to evaluate the "arg"
2607+
* expression when executing the CASE, since any contained
2608+
* CaseTestExprs that might have referred to it will have been
2609+
* replaced by the constant.
25992610
*----------
26002611
*/
26012612
CaseExpr *caseexpr = (CaseExpr *) node;
@@ -2614,7 +2625,10 @@ eval_const_expressions_mutator(Node *node,
26142625
/* Set up for contained CaseTestExpr nodes */
26152626
save_case_val = context->case_val;
26162627
if (newarg && IsA(newarg, Const))
2628+
{
26172629
context->case_val = newarg;
2630+
newarg = NULL; /* not needed anymore, see comment above */
2631+
}
26182632
else
26192633
context->case_val = NULL;
26202634

src/backend/utils/adt/ruleutils.c

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4906,23 +4906,19 @@ get_rule_expr(Node *node, deparse_context *context,
49064906
* boolexpr WHEN TRUE THEN ...", then the optimizer's
49074907
* simplify_boolean_equality() may have reduced this
49084908
* to just "CaseTestExpr" or "NOT CaseTestExpr", for
4909-
* which we have to show "TRUE" or "FALSE". Also,
4910-
* depending on context the original CaseTestExpr
4911-
* might have been reduced to a Const (but we won't
4912-
* see "WHEN Const"). We have also to consider the
4913-
* possibility that an implicit coercion was inserted
4914-
* between the CaseTestExpr and the operator.
4909+
* which we have to show "TRUE" or "FALSE". We have
4910+
* also to consider the possibility that an implicit
4911+
* coercion was inserted between the CaseTestExpr and
4912+
* the operator.
49154913
*/
49164914
if (IsA(w, OpExpr))
49174915
{
49184916
List *args = ((OpExpr *) w)->args;
4919-
Node *lhs;
49204917
Node *rhs;
49214918

49224919
Assert(list_length(args) == 2);
4923-
lhs = strip_implicit_coercions(linitial(args));
4924-
Assert(IsA(lhs, CaseTestExpr) ||
4925-
IsA(lhs, Const));
4920+
Assert(IsA(strip_implicit_coercions(linitial(args)),
4921+
CaseTestExpr));
49264922
rhs = (Node *) lsecond(args);
49274923
get_rule_expr(rhs, context, false);
49284924
}

0 commit comments

Comments
 (0)