mirror of
https://github.com/postgres/postgres.git
synced 2025-07-17 06:41:09 +03:00
Adjust constant-folding of CASE expressions so that the simple comparison
form of CASE (eg, CASE 0 WHEN 1 THEN ...) can be constant-folded as it was in 7.4. Also, avoid constant-folding result expressions that are certainly unreachable --- the former coding was a bit cavalier about this and could generate unexpected results for all-constant CASE expressions. Add regression test cases. Per report from Vlad Marchenko.
This commit is contained in:
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.187 2005/01/28 19:34:07 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.188 2005/02/02 21:49:07 tgl Exp $
|
||||||
*
|
*
|
||||||
* HISTORY
|
* HISTORY
|
||||||
* AUTHOR DATE MAJOR EVENT
|
* AUTHOR DATE MAJOR EVENT
|
||||||
@ -49,6 +49,7 @@
|
|||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
List *active_fns;
|
List *active_fns;
|
||||||
|
Node *case_val;
|
||||||
bool estimate;
|
bool estimate;
|
||||||
} eval_const_expressions_context;
|
} eval_const_expressions_context;
|
||||||
|
|
||||||
@ -1195,6 +1196,7 @@ eval_const_expressions(Node *node)
|
|||||||
eval_const_expressions_context context;
|
eval_const_expressions_context context;
|
||||||
|
|
||||||
context.active_fns = NIL; /* nothing being recursively simplified */
|
context.active_fns = NIL; /* nothing being recursively simplified */
|
||||||
|
context.case_val = NULL; /* no CASE being examined */
|
||||||
context.estimate = false; /* safe transformations only */
|
context.estimate = false; /* safe transformations only */
|
||||||
return eval_const_expressions_mutator(node, &context);
|
return eval_const_expressions_mutator(node, &context);
|
||||||
}
|
}
|
||||||
@ -1219,6 +1221,7 @@ estimate_expression_value(Node *node)
|
|||||||
eval_const_expressions_context context;
|
eval_const_expressions_context context;
|
||||||
|
|
||||||
context.active_fns = NIL; /* nothing being recursively simplified */
|
context.active_fns = NIL; /* nothing being recursively simplified */
|
||||||
|
context.case_val = NULL; /* no CASE being examined */
|
||||||
context.estimate = true; /* unsafe transformations OK */
|
context.estimate = true; /* unsafe transformations OK */
|
||||||
return eval_const_expressions_mutator(node, &context);
|
return eval_const_expressions_mutator(node, &context);
|
||||||
}
|
}
|
||||||
@ -1592,71 +1595,98 @@ eval_const_expressions_mutator(Node *node,
|
|||||||
* If there are no non-FALSE alternatives, we simplify the entire
|
* If there are no non-FALSE alternatives, we simplify the entire
|
||||||
* CASE to the default result (ELSE result).
|
* CASE to the default result (ELSE result).
|
||||||
*
|
*
|
||||||
* If we have a simple-form CASE with constant test expression and
|
* If we have a simple-form CASE with constant test expression,
|
||||||
* one or more constant comparison expressions, we could run the
|
* we substitute the constant value for contained CaseTestExpr
|
||||||
* implied comparisons and potentially reduce those arms to constants.
|
* placeholder nodes, so that we have the opportunity to reduce
|
||||||
* This is not yet implemented, however. At present, the
|
* constant test conditions. For example this allows
|
||||||
* CaseTestExpr placeholder will always act as a non-constant node
|
* CASE 0 WHEN 0 THEN 1 ELSE 1/0 END
|
||||||
* and prevent the comparison boolean expressions from being reduced
|
* to reduce to 1 rather than drawing a divide-by-0 error.
|
||||||
* to Const nodes.
|
|
||||||
*----------
|
*----------
|
||||||
*/
|
*/
|
||||||
CaseExpr *caseexpr = (CaseExpr *) node;
|
CaseExpr *caseexpr = (CaseExpr *) node;
|
||||||
CaseExpr *newcase;
|
CaseExpr *newcase;
|
||||||
|
Node *save_case_val;
|
||||||
Node *newarg;
|
Node *newarg;
|
||||||
List *newargs;
|
List *newargs;
|
||||||
Node *defresult;
|
bool const_true_cond;
|
||||||
Const *const_input;
|
Node *defresult = NULL;
|
||||||
ListCell *arg;
|
ListCell *arg;
|
||||||
|
|
||||||
/* Simplify the test expression, if any */
|
/* Simplify the test expression, if any */
|
||||||
newarg = eval_const_expressions_mutator((Node *) caseexpr->arg,
|
newarg = eval_const_expressions_mutator((Node *) caseexpr->arg,
|
||||||
context);
|
context);
|
||||||
|
|
||||||
|
/* Set up for contained CaseTestExpr nodes */
|
||||||
|
save_case_val = context->case_val;
|
||||||
|
if (newarg && IsA(newarg, Const))
|
||||||
|
context->case_val = newarg;
|
||||||
|
else
|
||||||
|
context->case_val = NULL;
|
||||||
|
|
||||||
/* Simplify the WHEN clauses */
|
/* Simplify the WHEN clauses */
|
||||||
newargs = NIL;
|
newargs = NIL;
|
||||||
|
const_true_cond = false;
|
||||||
foreach(arg, caseexpr->args)
|
foreach(arg, caseexpr->args)
|
||||||
{
|
{
|
||||||
/* Simplify this alternative's condition and result */
|
CaseWhen *oldcasewhen = (CaseWhen *) lfirst(arg);
|
||||||
CaseWhen *casewhen = (CaseWhen *)
|
Node *casecond;
|
||||||
expression_tree_mutator((Node *) lfirst(arg),
|
Node *caseresult;
|
||||||
eval_const_expressions_mutator,
|
|
||||||
(void *) context);
|
|
||||||
|
|
||||||
Assert(IsA(casewhen, CaseWhen));
|
Assert(IsA(oldcasewhen, CaseWhen));
|
||||||
if (casewhen->expr == NULL ||
|
|
||||||
!IsA(casewhen->expr, Const))
|
/* Simplify this alternative's test condition */
|
||||||
|
casecond =
|
||||||
|
eval_const_expressions_mutator((Node *) oldcasewhen->expr,
|
||||||
|
context);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the test condition is constant FALSE (or NULL), then drop
|
||||||
|
* this WHEN clause completely, without processing the result.
|
||||||
|
*/
|
||||||
|
if (casecond && IsA(casecond, Const))
|
||||||
{
|
{
|
||||||
newargs = lappend(newargs, casewhen);
|
Const *const_input = (Const *) casecond;
|
||||||
|
|
||||||
|
if (const_input->constisnull ||
|
||||||
|
!DatumGetBool(const_input->constvalue))
|
||||||
|
continue; /* drop alternative with FALSE condition */
|
||||||
|
/* Else it's constant TRUE */
|
||||||
|
const_true_cond = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Simplify this alternative's result value */
|
||||||
|
caseresult =
|
||||||
|
eval_const_expressions_mutator((Node *) oldcasewhen->result,
|
||||||
|
context);
|
||||||
|
|
||||||
|
/* If non-constant test condition, emit a new WHEN node */
|
||||||
|
if (!const_true_cond)
|
||||||
|
{
|
||||||
|
CaseWhen *newcasewhen = makeNode(CaseWhen);
|
||||||
|
|
||||||
|
newcasewhen->expr = (Expr *) casecond;
|
||||||
|
newcasewhen->result = (Expr *) caseresult;
|
||||||
|
newargs = lappend(newargs, newcasewhen);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const_input = (Const *) casewhen->expr;
|
|
||||||
if (const_input->constisnull ||
|
|
||||||
!DatumGetBool(const_input->constvalue))
|
|
||||||
continue; /* drop alternative with FALSE condition */
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Found a TRUE condition. If it's the first (un-dropped)
|
* Found a TRUE condition, so none of the remaining alternatives
|
||||||
* alternative, the CASE reduces to just this alternative.
|
* can be reached. We treat the result as the default result.
|
||||||
*/
|
*/
|
||||||
if (newargs == NIL)
|
defresult = caseresult;
|
||||||
return (Node *) casewhen->result;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Otherwise, add it to the list, and drop all the rest.
|
|
||||||
*/
|
|
||||||
newargs = lappend(newargs, casewhen);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Simplify the default result */
|
/* Simplify the default result, unless we replaced it above */
|
||||||
defresult = eval_const_expressions_mutator((Node *) caseexpr->defresult,
|
if (!const_true_cond)
|
||||||
context);
|
defresult =
|
||||||
|
eval_const_expressions_mutator((Node *) caseexpr->defresult,
|
||||||
|
context);
|
||||||
|
|
||||||
/*
|
context->case_val = save_case_val;
|
||||||
* If no non-FALSE alternatives, CASE reduces to the default
|
|
||||||
* result
|
/* If no non-FALSE alternatives, CASE reduces to the default result */
|
||||||
*/
|
|
||||||
if (newargs == NIL)
|
if (newargs == NIL)
|
||||||
return defresult;
|
return defresult;
|
||||||
/* Otherwise we need a new CASE node */
|
/* Otherwise we need a new CASE node */
|
||||||
@ -1667,6 +1697,18 @@ eval_const_expressions_mutator(Node *node,
|
|||||||
newcase->defresult = (Expr *) defresult;
|
newcase->defresult = (Expr *) defresult;
|
||||||
return (Node *) newcase;
|
return (Node *) newcase;
|
||||||
}
|
}
|
||||||
|
if (IsA(node, CaseTestExpr))
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* If we know a constant test value for the current CASE
|
||||||
|
* construct, substitute it for the placeholder. Else just
|
||||||
|
* return the placeholder as-is.
|
||||||
|
*/
|
||||||
|
if (context->case_val)
|
||||||
|
return copyObject(context->case_val);
|
||||||
|
else
|
||||||
|
return copyObject(node);
|
||||||
|
}
|
||||||
if (IsA(node, ArrayExpr))
|
if (IsA(node, ArrayExpr))
|
||||||
{
|
{
|
||||||
ArrayExpr *arrayexpr = (ArrayExpr *) node;
|
ArrayExpr *arrayexpr = (ArrayExpr *) node;
|
||||||
|
@ -72,6 +72,23 @@ SELECT '6' AS "One",
|
|||||||
6 | 6
|
6 | 6
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
|
-- Constant-expression folding shouldn't evaluate unreachable subexpressions
|
||||||
|
SELECT CASE WHEN 1=0 THEN 1/0 WHEN 1=1 THEN 1 ELSE 2/0 END;
|
||||||
|
case
|
||||||
|
------
|
||||||
|
1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT CASE 1 WHEN 0 THEN 1/0 WHEN 1 THEN 1 ELSE 2/0 END;
|
||||||
|
case
|
||||||
|
------
|
||||||
|
1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- However we do not currently suppress folding of potentially
|
||||||
|
-- reachable subexpressions
|
||||||
|
SELECT CASE WHEN i > 100 THEN 1/0 ELSE 0 END FROM case_tbl;
|
||||||
|
ERROR: division by zero
|
||||||
-- Test for cases involving untyped literals in test expression
|
-- Test for cases involving untyped literals in test expression
|
||||||
SELECT CASE 'a' WHEN 'a' THEN 1 ELSE 2 END;
|
SELECT CASE 'a' WHEN 'a' THEN 1 ELSE 2 END;
|
||||||
case
|
case
|
||||||
|
@ -58,6 +58,14 @@ SELECT '6' AS "One",
|
|||||||
ELSE 7
|
ELSE 7
|
||||||
END AS "Two WHEN with default";
|
END AS "Two WHEN with default";
|
||||||
|
|
||||||
|
-- Constant-expression folding shouldn't evaluate unreachable subexpressions
|
||||||
|
SELECT CASE WHEN 1=0 THEN 1/0 WHEN 1=1 THEN 1 ELSE 2/0 END;
|
||||||
|
SELECT CASE 1 WHEN 0 THEN 1/0 WHEN 1 THEN 1 ELSE 2/0 END;
|
||||||
|
|
||||||
|
-- However we do not currently suppress folding of potentially
|
||||||
|
-- reachable subexpressions
|
||||||
|
SELECT CASE WHEN i > 100 THEN 1/0 ELSE 0 END FROM case_tbl;
|
||||||
|
|
||||||
-- Test for cases involving untyped literals in test expression
|
-- Test for cases involving untyped literals in test expression
|
||||||
SELECT CASE 'a' WHEN 'a' THEN 1 ELSE 2 END;
|
SELECT CASE 'a' WHEN 'a' THEN 1 ELSE 2 END;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user