Skip to content

Commit

Permalink
Don't include CaseTestExpr in JsonValueExpr.formatted_expr
Browse files Browse the repository at this point in the history
A CaseTestExpr is currently being put into
JsonValueExpr.formatted_expr as placeholder for the result of
evaluating JsonValueExpr.raw_expr, which in turn is evaluated
separately.  Though, there's no need for this indirection if
raw_expr itself can be embedded into formatted_expr and evaluated
as part of evaluating the latter, especially as there is no
special reason to evaluate it separately.  So this commit makes it
so.  As a result, JsonValueExpr.raw_expr no longer needs to be
evaluated in ExecInterpExpr(), eval_const_exprs_mutator() etc. and
is now only used for displaying the original "unformatted"
expression in ruleutils.c.

While at it, this also removes the function makeCaseTestExpr(),
because the code in makeJsonConstructorExpr() looks more readable
without it IMO and isn't used by anyone else either.

Finally, a note is added in the comment above CaseTestExpr's
definition that JsonConstructorExpr is also using it.

Reviewed-by: Álvaro Herrera <[email protected]>
Discussion: https://postgr.es/m/CA+HiwqE4XTdfb1nW=Ojoy_tQSRhYt-q_kb6i5d4xcKyrLC1Nbg@mail.gmail.com
  • Loading branch information
amitlan committed Jul 13, 2023
1 parent 785480c commit b6e1157
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 55 deletions.
17 changes: 2 additions & 15 deletions src/backend/executor/execExpr.c
Original file line number Diff line number Diff line change
Expand Up @@ -2294,21 +2294,8 @@ ExecInitExprRec(Expr *node, ExprState *state,
{
JsonValueExpr *jve = (JsonValueExpr *) node;

ExecInitExprRec(jve->raw_expr, state, resv, resnull);

if (jve->formatted_expr)
{
Datum *innermost_caseval = state->innermost_caseval;
bool *innermost_isnull = state->innermost_casenull;

state->innermost_caseval = resv;
state->innermost_casenull = resnull;

ExecInitExprRec(jve->formatted_expr, state, resv, resnull);

state->innermost_caseval = innermost_caseval;
state->innermost_casenull = innermost_isnull;
}
Assert(jve->formatted_expr != NULL);
ExecInitExprRec(jve->formatted_expr, state, resv, resnull);
break;
}

Expand Down
4 changes: 4 additions & 0 deletions src/backend/nodes/makefuncs.c
Original file line number Diff line number Diff line change
Expand Up @@ -853,6 +853,10 @@ makeJsonValueExpr(Expr *expr, JsonFormat *format)
JsonValueExpr *jve = makeNode(JsonValueExpr);

jve->raw_expr = expr;

/*
* Set after checking the format, if needed, in transformJsonValueExpr().
*/
jve->formatted_expr = NULL;
jve->format = format;

Expand Down
23 changes: 5 additions & 18 deletions src/backend/optimizer/util/clauses.c
Original file line number Diff line number Diff line change
Expand Up @@ -2827,25 +2827,12 @@ eval_const_expressions_mutator(Node *node,
case T_JsonValueExpr:
{
JsonValueExpr *jve = (JsonValueExpr *) node;
Node *raw;
Node *formatted;

raw = eval_const_expressions_mutator((Node *) jve->raw_expr,
context);
if (raw && IsA(raw, Const))
{
Node *formatted;
Node *save_case_val = context->case_val;

context->case_val = raw;

formatted = eval_const_expressions_mutator((Node *) jve->formatted_expr,
context);

context->case_val = save_case_val;

if (formatted && IsA(formatted, Const))
return formatted;
}
formatted = eval_const_expressions_mutator((Node *) jve->formatted_expr,
context);
if (formatted && IsA(formatted, Const))
return formatted;
break;
}

Expand Down
43 changes: 22 additions & 21 deletions src/backend/parser/parse_expr.c
Original file line number Diff line number Diff line change
Expand Up @@ -3202,21 +3202,6 @@ makeJsonByteaToTextConversion(Node *expr, JsonFormat *format, int location)
return (Node *) fexpr;
}

/*
* Make a CaseTestExpr node.
*/
static Node *
makeCaseTestExpr(Node *expr)
{
CaseTestExpr *placeholder = makeNode(CaseTestExpr);

placeholder->typeId = exprType(expr);
placeholder->typeMod = exprTypmod(expr);
placeholder->collation = exprCollation(expr);

return (Node *) placeholder;
}

/*
* Transform JSON value expression using specified input JSON format or
* default format otherwise.
Expand Down Expand Up @@ -3268,11 +3253,8 @@ transformJsonValueExpr(ParseState *pstate, char *constructName,
if (format != JS_FORMAT_DEFAULT)
{
Oid targettype = format == JS_FORMAT_JSONB ? JSONBOID : JSONOID;
Node *orig = makeCaseTestExpr(expr);
Node *coerced;

expr = orig;

if (exprtype != BYTEAOID && typcategory != TYPCATEGORY_STRING)
ereport(ERROR,
errcode(ERRCODE_DATATYPE_MISMATCH),
Expand Down Expand Up @@ -3310,7 +3292,7 @@ transformJsonValueExpr(ParseState *pstate, char *constructName,
coerced = (Node *) fexpr;
}

if (coerced == orig)
if (coerced == expr)
expr = rawexpr;
else
{
Expand Down Expand Up @@ -3537,8 +3519,22 @@ makeJsonConstructorExpr(ParseState *pstate, JsonConstructorType type,
jsctor->absent_on_null = absent_on_null;
jsctor->location = location;

/*
* Coerce to the RETURNING type and format, if needed. We abuse
* CaseTestExpr here as placeholder to pass the result of either evaluating
* 'fexpr' or whatever is produced by ExecEvalJsonConstructor() that is of
* type JSON or JSONB to the coercion function.
*/
if (fexpr)
placeholder = makeCaseTestExpr((Node *) fexpr);
{
CaseTestExpr *cte = makeNode(CaseTestExpr);

cte->typeId = exprType((Node *) fexpr);
cte->typeMod = exprTypmod((Node *) fexpr);
cte->collation = exprCollation((Node *) fexpr);

placeholder = (Node *) cte;
}
else
{
CaseTestExpr *cte = makeNode(CaseTestExpr);
Expand Down Expand Up @@ -3636,6 +3632,11 @@ transformJsonArrayQueryConstructor(ParseState *pstate,
colref->location = ctor->location;

agg->arg = makeJsonValueExpr((Expr *) colref, ctor->format);
/*
* No formatting necessary, so set formatted_expr to be the same as
* raw_expr.
*/
agg->arg->formatted_expr = agg->arg->raw_expr;
agg->absent_on_null = ctor->absent_on_null;
agg->constructor = makeNode(JsonAggConstructor);
agg->constructor->agg_order = NIL;
Expand Down Expand Up @@ -3900,7 +3901,7 @@ transformJsonParseArg(ParseState *pstate, Node *jsexpr, JsonFormat *format,
{
JsonValueExpr *jve;

expr = makeCaseTestExpr(raw_expr);
expr = raw_expr;
expr = makeJsonByteaToTextConversion(expr, format, exprLocation(expr));
*exprtype = TEXTOID;

Expand Down
7 changes: 6 additions & 1 deletion src/include/nodes/primnodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -1264,6 +1264,8 @@ typedef struct CaseWhen
* see build_coercion_expression().
* * Nested FieldStore/SubscriptingRef assignment expressions in INSERT/UPDATE;
* see transformAssignmentIndirection().
* * 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
Expand Down Expand Up @@ -1593,12 +1595,15 @@ typedef struct JsonReturning
/*
* JsonValueExpr -
* representation of JSON value expression (expr [FORMAT JsonFormat])
*
* Note that raw_expr is only there for displaying and is not evaluated by
* ExecInterpExpr() and eval_const_exprs_mutator().
*/
typedef struct JsonValueExpr
{
NodeTag type;
Expr *raw_expr; /* raw expression */
Expr *formatted_expr; /* formatted expression or NULL */
Expr *formatted_expr; /* formatted expression */
JsonFormat *format; /* FORMAT clause, if specified */
} JsonValueExpr;

Expand Down

0 comments on commit b6e1157

Please sign in to comment.