mirror of
https://github.com/postgres/postgres.git
synced 2026-10-23 13:29:09 +03:00
Fix const-folding of JSON constructors inside a simple CASE
A JSON constructor with a RETURNING clause uses a CaseTestExpr as the
placeholder for its result in the coercion expression. When such a
constructor appears in a WHEN clause of a simple CASE whose test
expression is a constant, eval_const_expressions substituted that
constant for the placeholder, so the coercion produced the CASE's test
value instead of the constructor's result. For instance,
CASE 'x' WHEN JSON_OBJECT('a': 'b' RETURNING text) THEN 1 ELSE 0 END
evaluated to 1.
To fix, keep case_val out of scope while simplifying the coercion, as
is already done for the elemexpr of an ArrayCoerceExpr.
Back-patch to v16, where the SQL/JSON constructor functions were
introduced.
Author: Richard Guo <guofenglinux@gmail.com>
Discussion: https://postgr.es/m/CAMbWs48A=VCFbteTkuCoknO1_0-Cu0aMBT0M07dm7vj1QyixDg@mail.gmail.com
Backpatch-through: 16
This commit is contained in:
@@ -2904,7 +2904,6 @@ eval_const_expressions_mutator(Node *node,
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case T_JsonValueExpr:
|
||||
{
|
||||
JsonValueExpr *jve = (JsonValueExpr *) node;
|
||||
@@ -2928,7 +2927,41 @@ eval_const_expressions_mutator(Node *node,
|
||||
(Expr *) formatted_expr,
|
||||
copyObject(jve->format));
|
||||
}
|
||||
case T_JsonConstructorExpr:
|
||||
{
|
||||
JsonConstructorExpr *jce = (JsonConstructorExpr *) node;
|
||||
JsonConstructorExpr *newjce;
|
||||
Node *save_case_val;
|
||||
|
||||
/*
|
||||
* Copy the node and const-simplify its arguments. We can't
|
||||
* use ece_generic_processing() here because we need to mess
|
||||
* with case_val only while processing the coercion.
|
||||
*/
|
||||
newjce = makeNode(JsonConstructorExpr);
|
||||
memcpy(newjce, jce, sizeof(JsonConstructorExpr));
|
||||
newjce->args = (List *)
|
||||
eval_const_expressions_mutator((Node *) jce->args,
|
||||
context);
|
||||
newjce->func = (Expr *)
|
||||
eval_const_expressions_mutator((Node *) jce->func,
|
||||
context);
|
||||
|
||||
/*
|
||||
* Set up for the CaseTestExpr node contained in the coercion.
|
||||
* We must prevent it from absorbing any outer CASE value.
|
||||
*/
|
||||
save_case_val = context->case_val;
|
||||
context->case_val = NULL;
|
||||
|
||||
newjce->coercion = (Expr *)
|
||||
eval_const_expressions_mutator((Node *) jce->coercion,
|
||||
context);
|
||||
|
||||
context->case_val = save_case_val;
|
||||
|
||||
return (Node *) newjce;
|
||||
}
|
||||
case T_SubPlan:
|
||||
case T_AlternativeSubPlan:
|
||||
|
||||
|
||||
@@ -1271,11 +1271,11 @@ typedef struct CaseWhen
|
||||
* * Placeholder for intermediate results in some SQL/JSON expression nodes,
|
||||
* such as JsonConstructorExpr.
|
||||
*
|
||||
* The uses in CaseExpr and ArrayCoerceExpr are safe only to the extent that
|
||||
* there is not any other CaseExpr or ArrayCoerceExpr between the value source
|
||||
* node and its child CaseTestExpr(s). This is true in the parse analysis
|
||||
* output, but the planner's function-inlining logic has to be careful not to
|
||||
* break it.
|
||||
* The uses in CaseExpr, ArrayCoerceExpr, and JsonConstructorExpr are safe
|
||||
* only to the extent that there is not any other such node between the
|
||||
* value source node and its child CaseTestExpr(s). This is true in the
|
||||
* parse analysis output, but the planner's constant-folding of simple CASE
|
||||
* and its function-inlining logic have to be careful not to break it.
|
||||
*
|
||||
* The nested-assignment-expression case is safe because the only node types
|
||||
* that can be above such CaseTestExprs are FieldStore and SubscriptingRef.
|
||||
|
||||
@@ -281,6 +281,13 @@ SELECT JSON_OBJECT(1: 1, '2': NULL, '3': 1, 4: NULL, '5': 'a' ABSENT ON NULL WIT
|
||||
{"1": 1, "3": 1, "5": "a"}
|
||||
(1 row)
|
||||
|
||||
-- the RETURNING coercion must not pick up the test value of an enclosing CASE
|
||||
SELECT CASE 'x' WHEN JSON_OBJECT('a': 'b' RETURNING text) THEN 1 ELSE 0 END;
|
||||
case
|
||||
------
|
||||
0
|
||||
(1 row)
|
||||
|
||||
-- JSON_ARRAY()
|
||||
SELECT JSON_ARRAY();
|
||||
json_array
|
||||
|
||||
@@ -88,6 +88,8 @@ SELECT JSON_OBJECT(1: 1, '2': NULL, '1': 1 ABSENT ON NULL WITH UNIQUE RETURNING
|
||||
SELECT JSON_OBJECT(1: 1, '2': NULL, '1': 1 ABSENT ON NULL WITHOUT UNIQUE RETURNING jsonb);
|
||||
SELECT JSON_OBJECT(1: 1, '2': NULL, '3': 1, 4: NULL, '5': 'a' ABSENT ON NULL WITH UNIQUE RETURNING jsonb);
|
||||
|
||||
-- the RETURNING coercion must not pick up the test value of an enclosing CASE
|
||||
SELECT CASE 'x' WHEN JSON_OBJECT('a': 'b' RETURNING text) THEN 1 ELSE 0 END;
|
||||
|
||||
-- JSON_ARRAY()
|
||||
SELECT JSON_ARRAY();
|
||||
|
||||
Reference in New Issue
Block a user