mirror of
https://github.com/postgres/postgres.git
synced 2025-07-21 16:02:15 +03:00
expression eval: Fix EEOP_JSON_CONSTRUCTOR and EEOP_JSONEXPR size.
The new expression step types increased the size of ExprEvalStep by ~4 for all types of expression steps, slowing down expression evaluation noticeably. Move them out of line. There's other issues with these expression steps, but addressing them is largely independent of this aspect. Author: Andres Freund <andres@anarazel.de> Reviewed-By: Andrew Dunstan <andrew@dunslane.net> Discussion: https://postgr.es/m/20220616233130.rparivafipt6doj3@alap3.anarazel.de Backpatch: 15-
This commit is contained in:
@ -2470,32 +2470,38 @@ ExecInitExprRec(Expr *node, ExprState *state,
|
||||
}
|
||||
else
|
||||
{
|
||||
JsonConstructorExprState *jcstate;
|
||||
|
||||
jcstate = palloc0(sizeof(JsonConstructorExprState));
|
||||
|
||||
scratch.opcode = EEOP_JSON_CONSTRUCTOR;
|
||||
scratch.d.json_constructor.constructor = ctor;
|
||||
scratch.d.json_constructor.arg_values = palloc(sizeof(Datum) * nargs);
|
||||
scratch.d.json_constructor.arg_nulls = palloc(sizeof(bool) * nargs);
|
||||
scratch.d.json_constructor.arg_types = palloc(sizeof(Oid) * nargs);
|
||||
scratch.d.json_constructor.nargs = nargs;
|
||||
scratch.d.json_constructor.jcstate = jcstate;
|
||||
|
||||
jcstate->constructor = ctor;
|
||||
jcstate->arg_values = palloc(sizeof(Datum) * nargs);
|
||||
jcstate->arg_nulls = palloc(sizeof(bool) * nargs);
|
||||
jcstate->arg_types = palloc(sizeof(Oid) * nargs);
|
||||
jcstate->nargs = nargs;
|
||||
|
||||
foreach(lc, args)
|
||||
{
|
||||
Expr *arg = (Expr *) lfirst(lc);
|
||||
|
||||
scratch.d.json_constructor.arg_types[argno] = exprType((Node *) arg);
|
||||
jcstate->arg_types[argno] = exprType((Node *) arg);
|
||||
|
||||
if (IsA(arg, Const))
|
||||
{
|
||||
/* Don't evaluate const arguments every round */
|
||||
Const *con = (Const *) arg;
|
||||
|
||||
scratch.d.json_constructor.arg_values[argno] = con->constvalue;
|
||||
scratch.d.json_constructor.arg_nulls[argno] = con->constisnull;
|
||||
jcstate->arg_values[argno] = con->constvalue;
|
||||
jcstate->arg_nulls[argno] = con->constisnull;
|
||||
}
|
||||
else
|
||||
{
|
||||
ExecInitExprRec(arg, state,
|
||||
&scratch.d.json_constructor.arg_values[argno],
|
||||
&scratch.d.json_constructor.arg_nulls[argno]);
|
||||
&jcstate->arg_values[argno],
|
||||
&jcstate->arg_nulls[argno]);
|
||||
}
|
||||
argno++;
|
||||
}
|
||||
@ -2506,14 +2512,14 @@ ExecInitExprRec(Expr *node, ExprState *state,
|
||||
bool is_jsonb =
|
||||
ctor->returning->format->format_type == JS_FORMAT_JSONB;
|
||||
|
||||
scratch.d.json_constructor.arg_type_cache =
|
||||
palloc(sizeof(*scratch.d.json_constructor.arg_type_cache) * nargs);
|
||||
jcstate->arg_type_cache =
|
||||
palloc(sizeof(*jcstate->arg_type_cache) * nargs);
|
||||
|
||||
for (int i = 0; i < nargs; i++)
|
||||
{
|
||||
int category;
|
||||
Oid outfuncid;
|
||||
Oid typid = scratch.d.json_constructor.arg_types[i];
|
||||
Oid typid = jcstate->arg_types[i];
|
||||
|
||||
if (is_jsonb)
|
||||
{
|
||||
@ -2532,8 +2538,8 @@ ExecInitExprRec(Expr *node, ExprState *state,
|
||||
category = (int) jscat;
|
||||
}
|
||||
|
||||
scratch.d.json_constructor.arg_type_cache[i].outfuncid = outfuncid;
|
||||
scratch.d.json_constructor.arg_type_cache[i].category = category;
|
||||
jcstate->arg_type_cache[i].outfuncid = outfuncid;
|
||||
jcstate->arg_type_cache[i].category = category;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2572,41 +2578,44 @@ ExecInitExprRec(Expr *node, ExprState *state,
|
||||
case T_JsonExpr:
|
||||
{
|
||||
JsonExpr *jexpr = castNode(JsonExpr, node);
|
||||
JsonExprState *jsestate = palloc0(sizeof(JsonExprState));
|
||||
ListCell *argexprlc;
|
||||
ListCell *argnamelc;
|
||||
|
||||
scratch.opcode = EEOP_JSONEXPR;
|
||||
scratch.d.jsonexpr.jsexpr = jexpr;
|
||||
scratch.d.jsonexpr.jsestate = jsestate;
|
||||
|
||||
scratch.d.jsonexpr.formatted_expr =
|
||||
palloc(sizeof(*scratch.d.jsonexpr.formatted_expr));
|
||||
jsestate->jsexpr = jexpr;
|
||||
|
||||
jsestate->formatted_expr =
|
||||
palloc(sizeof(*jsestate->formatted_expr));
|
||||
|
||||
ExecInitExprRec((Expr *) jexpr->formatted_expr, state,
|
||||
&scratch.d.jsonexpr.formatted_expr->value,
|
||||
&scratch.d.jsonexpr.formatted_expr->isnull);
|
||||
&jsestate->formatted_expr->value,
|
||||
&jsestate->formatted_expr->isnull);
|
||||
|
||||
scratch.d.jsonexpr.pathspec =
|
||||
palloc(sizeof(*scratch.d.jsonexpr.pathspec));
|
||||
jsestate->pathspec =
|
||||
palloc(sizeof(*jsestate->pathspec));
|
||||
|
||||
ExecInitExprRec((Expr *) jexpr->path_spec, state,
|
||||
&scratch.d.jsonexpr.pathspec->value,
|
||||
&scratch.d.jsonexpr.pathspec->isnull);
|
||||
&jsestate->pathspec->value,
|
||||
&jsestate->pathspec->isnull);
|
||||
|
||||
scratch.d.jsonexpr.res_expr =
|
||||
palloc(sizeof(*scratch.d.jsonexpr.res_expr));
|
||||
jsestate->res_expr =
|
||||
palloc(sizeof(*jsestate->res_expr));
|
||||
|
||||
scratch.d.jsonexpr.result_expr = jexpr->result_coercion
|
||||
jsestate->result_expr = jexpr->result_coercion
|
||||
? ExecInitExprWithCaseValue((Expr *) jexpr->result_coercion->expr,
|
||||
state->parent,
|
||||
&scratch.d.jsonexpr.res_expr->value,
|
||||
&scratch.d.jsonexpr.res_expr->isnull)
|
||||
&jsestate->res_expr->value,
|
||||
&jsestate->res_expr->isnull)
|
||||
: NULL;
|
||||
|
||||
scratch.d.jsonexpr.default_on_empty = !jexpr->on_empty ? NULL :
|
||||
jsestate->default_on_empty = !jexpr->on_empty ? NULL :
|
||||
ExecInitExpr((Expr *) jexpr->on_empty->default_expr,
|
||||
state->parent);
|
||||
|
||||
scratch.d.jsonexpr.default_on_error =
|
||||
jsestate->default_on_error =
|
||||
ExecInitExpr((Expr *) jexpr->on_error->default_expr,
|
||||
state->parent);
|
||||
|
||||
@ -2617,11 +2626,11 @@ ExecInitExprRec(Expr *node, ExprState *state,
|
||||
|
||||
/* lookup the result type's input function */
|
||||
getTypeInputInfo(jexpr->returning->typid, &typinput,
|
||||
&scratch.d.jsonexpr.input.typioparam);
|
||||
fmgr_info(typinput, &scratch.d.jsonexpr.input.func);
|
||||
&jsestate->input.typioparam);
|
||||
fmgr_info(typinput, &jsestate->input.func);
|
||||
}
|
||||
|
||||
scratch.d.jsonexpr.args = NIL;
|
||||
jsestate->args = NIL;
|
||||
|
||||
forboth(argexprlc, jexpr->passing_values,
|
||||
argnamelc, jexpr->passing_names)
|
||||
@ -2640,11 +2649,11 @@ ExecInitExprRec(Expr *node, ExprState *state,
|
||||
var->value = (Datum) 0;
|
||||
var->isnull = true;
|
||||
|
||||
scratch.d.jsonexpr.args =
|
||||
lappend(scratch.d.jsonexpr.args, var);
|
||||
jsestate->args =
|
||||
lappend(jsestate->args, var);
|
||||
}
|
||||
|
||||
scratch.d.jsonexpr.cache = NULL;
|
||||
jsestate->cache = NULL;
|
||||
|
||||
if (jexpr->coercions)
|
||||
{
|
||||
@ -2653,13 +2662,13 @@ ExecInitExprRec(Expr *node, ExprState *state,
|
||||
Datum *caseval;
|
||||
bool *casenull;
|
||||
|
||||
scratch.d.jsonexpr.coercion_expr =
|
||||
palloc(sizeof(*scratch.d.jsonexpr.coercion_expr));
|
||||
jsestate->coercion_expr =
|
||||
palloc(sizeof(*jsestate->coercion_expr));
|
||||
|
||||
caseval = &scratch.d.jsonexpr.coercion_expr->value;
|
||||
casenull = &scratch.d.jsonexpr.coercion_expr->isnull;
|
||||
caseval = &jsestate->coercion_expr->value;
|
||||
casenull = &jsestate->coercion_expr->isnull;
|
||||
|
||||
for (cstate = &scratch.d.jsonexpr.coercions.null,
|
||||
for (cstate = &jsestate->coercions.null,
|
||||
coercion = &jexpr->coercions->null;
|
||||
coercion <= &jexpr->coercions->composite;
|
||||
coercion++, cstate++)
|
||||
|
@ -4510,39 +4510,40 @@ ExecEvalJsonConstructor(ExprState *state, ExprEvalStep *op,
|
||||
ExprContext *econtext)
|
||||
{
|
||||
Datum res;
|
||||
JsonConstructorExpr *ctor = op->d.json_constructor.constructor;
|
||||
JsonConstructorExprState *jcstate = op->d.json_constructor.jcstate;
|
||||
JsonConstructorExpr *ctor = jcstate->constructor;
|
||||
bool is_jsonb = ctor->returning->format->format_type == JS_FORMAT_JSONB;
|
||||
bool isnull = false;
|
||||
|
||||
if (ctor->type == JSCTOR_JSON_ARRAY)
|
||||
res = (is_jsonb ?
|
||||
jsonb_build_array_worker :
|
||||
json_build_array_worker) (op->d.json_constructor.nargs,
|
||||
op->d.json_constructor.arg_values,
|
||||
op->d.json_constructor.arg_nulls,
|
||||
op->d.json_constructor.arg_types,
|
||||
op->d.json_constructor.constructor->absent_on_null);
|
||||
json_build_array_worker) (jcstate->nargs,
|
||||
jcstate->arg_values,
|
||||
jcstate->arg_nulls,
|
||||
jcstate->arg_types,
|
||||
ctor->absent_on_null);
|
||||
else if (ctor->type == JSCTOR_JSON_OBJECT)
|
||||
res = (is_jsonb ?
|
||||
jsonb_build_object_worker :
|
||||
json_build_object_worker) (op->d.json_constructor.nargs,
|
||||
op->d.json_constructor.arg_values,
|
||||
op->d.json_constructor.arg_nulls,
|
||||
op->d.json_constructor.arg_types,
|
||||
op->d.json_constructor.constructor->absent_on_null,
|
||||
op->d.json_constructor.constructor->unique);
|
||||
json_build_object_worker) (jcstate->nargs,
|
||||
jcstate->arg_values,
|
||||
jcstate->arg_nulls,
|
||||
jcstate->arg_types,
|
||||
ctor->absent_on_null,
|
||||
ctor->unique);
|
||||
else if (ctor->type == JSCTOR_JSON_SCALAR)
|
||||
{
|
||||
if (op->d.json_constructor.arg_nulls[0])
|
||||
if (jcstate->arg_nulls[0])
|
||||
{
|
||||
res = (Datum) 0;
|
||||
isnull = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Datum value = op->d.json_constructor.arg_values[0];
|
||||
int category = op->d.json_constructor.arg_type_cache[0].category;
|
||||
Oid outfuncid = op->d.json_constructor.arg_type_cache[0].outfuncid;
|
||||
Datum value = jcstate->arg_values[0];
|
||||
int category = jcstate->arg_type_cache[0].category;
|
||||
Oid outfuncid = jcstate->arg_type_cache[0].outfuncid;
|
||||
|
||||
if (is_jsonb)
|
||||
res = to_jsonb_worker(value, category, outfuncid);
|
||||
@ -4552,14 +4553,14 @@ ExecEvalJsonConstructor(ExprState *state, ExprEvalStep *op,
|
||||
}
|
||||
else if (ctor->type == JSCTOR_JSON_PARSE)
|
||||
{
|
||||
if (op->d.json_constructor.arg_nulls[0])
|
||||
if (jcstate->arg_nulls[0])
|
||||
{
|
||||
res = (Datum) 0;
|
||||
isnull = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Datum value = op->d.json_constructor.arg_values[0];
|
||||
Datum value = jcstate->arg_values[0];
|
||||
text *js = DatumGetTextP(value);
|
||||
|
||||
if (is_jsonb)
|
||||
@ -4627,14 +4628,17 @@ ExecEvalJsonExprCoercion(ExprEvalStep *op, ExprContext *econtext,
|
||||
Datum res, bool *isNull, void *p, bool *error)
|
||||
{
|
||||
ExprState *estate = p;
|
||||
JsonExprState *jsestate;
|
||||
|
||||
if (estate) /* coerce using specified expression */
|
||||
return ExecEvalExpr(estate, econtext, isNull);
|
||||
|
||||
if (op->d.jsonexpr.jsexpr->op != JSON_EXISTS_OP)
|
||||
jsestate = op->d.jsonexpr.jsestate;
|
||||
|
||||
if (jsestate->jsexpr->op != JSON_EXISTS_OP)
|
||||
{
|
||||
JsonCoercion *coercion = op->d.jsonexpr.jsexpr->result_coercion;
|
||||
JsonExpr *jexpr = op->d.jsonexpr.jsexpr;
|
||||
JsonCoercion *coercion = jsestate->jsexpr->result_coercion;
|
||||
JsonExpr *jexpr = jsestate->jsexpr;
|
||||
Jsonb *jb = *isNull ? NULL : DatumGetJsonbP(res);
|
||||
|
||||
if ((coercion && coercion->via_io) ||
|
||||
@ -4644,25 +4648,25 @@ ExecEvalJsonExprCoercion(ExprEvalStep *op, ExprContext *econtext,
|
||||
/* strip quotes and call typinput function */
|
||||
char *str = *isNull ? NULL : JsonbUnquote(jb);
|
||||
|
||||
return InputFunctionCall(&op->d.jsonexpr.input.func, str,
|
||||
op->d.jsonexpr.input.typioparam,
|
||||
return InputFunctionCall(&jsestate->input.func, str,
|
||||
jsestate->input.typioparam,
|
||||
jexpr->returning->typmod);
|
||||
}
|
||||
else if (coercion && coercion->via_populate)
|
||||
return json_populate_type(res, JSONBOID,
|
||||
jexpr->returning->typid,
|
||||
jexpr->returning->typmod,
|
||||
&op->d.jsonexpr.cache,
|
||||
&jsestate->cache,
|
||||
econtext->ecxt_per_query_memory,
|
||||
isNull);
|
||||
}
|
||||
|
||||
if (op->d.jsonexpr.result_expr)
|
||||
if (jsestate->result_expr)
|
||||
{
|
||||
op->d.jsonexpr.res_expr->value = res;
|
||||
op->d.jsonexpr.res_expr->isnull = *isNull;
|
||||
jsestate->res_expr->value = res;
|
||||
jsestate->res_expr->isnull = *isNull;
|
||||
|
||||
res = ExecEvalExpr(op->d.jsonexpr.result_expr, econtext, isNull);
|
||||
res = ExecEvalExpr(jsestate->result_expr, econtext, isNull);
|
||||
}
|
||||
|
||||
return res;
|
||||
@ -4895,7 +4899,8 @@ ExecEvalJsonExpr(ExprEvalStep *op, ExprContext *econtext,
|
||||
{
|
||||
ExecEvalJsonExprContext *cxt = pcxt;
|
||||
JsonPath *path = cxt->path;
|
||||
JsonExpr *jexpr = op->d.jsonexpr.jsexpr;
|
||||
JsonExprState *jsestate = op->d.jsonexpr.jsestate;
|
||||
JsonExpr *jexpr = jsestate->jsexpr;
|
||||
ExprState *estate = NULL;
|
||||
bool empty = false;
|
||||
Datum res = (Datum) 0;
|
||||
@ -4904,7 +4909,7 @@ ExecEvalJsonExpr(ExprEvalStep *op, ExprContext *econtext,
|
||||
{
|
||||
case JSON_QUERY_OP:
|
||||
res = JsonPathQuery(item, path, jexpr->wrapper, &empty, error,
|
||||
op->d.jsonexpr.args);
|
||||
jsestate->args);
|
||||
if (error && *error)
|
||||
{
|
||||
*resnull = true;
|
||||
@ -4917,7 +4922,7 @@ ExecEvalJsonExpr(ExprEvalStep *op, ExprContext *econtext,
|
||||
{
|
||||
struct JsonCoercionState *jcstate;
|
||||
JsonbValue *jbv = JsonPathValue(item, path, &empty, error,
|
||||
op->d.jsonexpr.args);
|
||||
jsestate->args);
|
||||
|
||||
if (error && *error)
|
||||
return (Datum) 0;
|
||||
@ -4940,8 +4945,8 @@ ExecEvalJsonExpr(ExprEvalStep *op, ExprContext *econtext,
|
||||
|
||||
/* Use coercion from SQL/JSON item type to the output type */
|
||||
res = ExecPrepareJsonItemCoercion(jbv,
|
||||
op->d.jsonexpr.jsexpr->returning,
|
||||
&op->d.jsonexpr.coercions,
|
||||
jsestate->jsexpr->returning,
|
||||
&jsestate->coercions,
|
||||
&jcstate);
|
||||
|
||||
if (jcstate->coercion &&
|
||||
@ -4973,27 +4978,27 @@ ExecEvalJsonExpr(ExprEvalStep *op, ExprContext *econtext,
|
||||
|
||||
/* coerce using specific expression */
|
||||
estate = jcstate->estate;
|
||||
op->d.jsonexpr.coercion_expr->value = res;
|
||||
op->d.jsonexpr.coercion_expr->isnull = *resnull;
|
||||
jsestate->coercion_expr->value = res;
|
||||
jsestate->coercion_expr->isnull = *resnull;
|
||||
break;
|
||||
}
|
||||
|
||||
case JSON_EXISTS_OP:
|
||||
{
|
||||
bool exists = JsonPathExists(item, path,
|
||||
op->d.jsonexpr.args,
|
||||
jsestate->args,
|
||||
error);
|
||||
|
||||
*resnull = error && *error;
|
||||
res = BoolGetDatum(exists);
|
||||
|
||||
if (!op->d.jsonexpr.result_expr)
|
||||
if (!jsestate->result_expr)
|
||||
return res;
|
||||
|
||||
/* coerce using result expression */
|
||||
estate = op->d.jsonexpr.result_expr;
|
||||
op->d.jsonexpr.res_expr->value = res;
|
||||
op->d.jsonexpr.res_expr->isnull = *resnull;
|
||||
estate = jsestate->result_expr;
|
||||
jsestate->res_expr->value = res;
|
||||
jsestate->res_expr->isnull = *resnull;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -5029,11 +5034,11 @@ ExecEvalJsonExpr(ExprEvalStep *op, ExprContext *econtext,
|
||||
* Execute DEFAULT expression as a coercion expression, because
|
||||
* its result is already coerced to the target type.
|
||||
*/
|
||||
estate = op->d.jsonexpr.default_on_empty;
|
||||
estate = jsestate->default_on_empty;
|
||||
else
|
||||
/* Execute ON EMPTY behavior */
|
||||
res = ExecEvalJsonBehavior(econtext, jexpr->on_empty,
|
||||
op->d.jsonexpr.default_on_empty,
|
||||
jsestate->default_on_empty,
|
||||
resnull);
|
||||
}
|
||||
|
||||
@ -5066,7 +5071,8 @@ void
|
||||
ExecEvalJson(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
|
||||
{
|
||||
ExecEvalJsonExprContext cxt;
|
||||
JsonExpr *jexpr = op->d.jsonexpr.jsexpr;
|
||||
JsonExprState *jsestate = op->d.jsonexpr.jsestate;
|
||||
JsonExpr *jexpr = jsestate->jsexpr;
|
||||
Datum item;
|
||||
Datum res = (Datum) 0;
|
||||
JsonPath *path;
|
||||
@ -5078,7 +5084,7 @@ ExecEvalJson(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
|
||||
*op->resnull = true; /* until we get a result */
|
||||
*op->resvalue = (Datum) 0;
|
||||
|
||||
if (op->d.jsonexpr.formatted_expr->isnull || op->d.jsonexpr.pathspec->isnull)
|
||||
if (jsestate->formatted_expr->isnull || jsestate->pathspec->isnull)
|
||||
{
|
||||
/* execute domain checks for NULLs */
|
||||
(void) ExecEvalJsonExprCoercion(op, econtext, res, op->resnull,
|
||||
@ -5088,11 +5094,11 @@ ExecEvalJson(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
|
||||
return;
|
||||
}
|
||||
|
||||
item = op->d.jsonexpr.formatted_expr->value;
|
||||
path = DatumGetJsonPathP(op->d.jsonexpr.pathspec->value);
|
||||
item = jsestate->formatted_expr->value;
|
||||
path = DatumGetJsonPathP(jsestate->pathspec->value);
|
||||
|
||||
/* reset JSON path variable contexts */
|
||||
foreach(lc, op->d.jsonexpr.args)
|
||||
foreach(lc, jsestate->args)
|
||||
{
|
||||
JsonPathVariableEvalContext *var = lfirst(lc);
|
||||
|
||||
@ -5100,7 +5106,7 @@ ExecEvalJson(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
|
||||
var->evaluated = false;
|
||||
}
|
||||
|
||||
needSubtrans = ExecEvalJsonNeedsSubTransaction(jexpr, &op->d.jsonexpr.coercions);
|
||||
needSubtrans = ExecEvalJsonNeedsSubTransaction(jexpr, &jsestate->coercions);
|
||||
|
||||
cxt.path = path;
|
||||
cxt.error = throwErrors ? NULL : &error;
|
||||
@ -5115,7 +5121,7 @@ ExecEvalJson(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
|
||||
{
|
||||
/* Execute ON ERROR behavior */
|
||||
res = ExecEvalJsonBehavior(econtext, jexpr->on_error,
|
||||
op->d.jsonexpr.default_on_error,
|
||||
jsestate->default_on_error,
|
||||
op->resnull);
|
||||
|
||||
/* result is already coerced in DEFAULT behavior case */
|
||||
|
Reference in New Issue
Block a user