v1-0004-Allow-JSON_OBJECT-JSON_ARRAY-JSON_SCALAR-to-cache.patch
application/octet-stream
Filename: v1-0004-Allow-JSON_OBJECT-JSON_ARRAY-JSON_SCALAR-to-cache.patch
Type: application/octet-stream
Part: 3
Patch
Format: format-patch
Series: patch v1-0004
Subject: Allow JSON_OBJECT, JSON_ARRAY, JSON_SCALAR to cache JSON type information.
| File | + | − |
|---|---|---|
| src/backend/executor/execExpr.c | 14 | 14 |
| src/backend/executor/execExprInterp.c | 8 | 7 |
| src/backend/utils/adt/jsonb.c | 42 | 46 |
| src/backend/utils/adt/json.c | 42 | 44 |
| src/include/executor/execExpr.h | 3 | 6 |
| src/include/utils/jsonb.h | 10 | 5 |
| src/include/utils/json.h | 10 | 5 |
From b7b25c5f168a9e79aad5897286f98b722b61cdbe Mon Sep 17 00:00:00 2001
From: Robert Haas <rhaas@postgresql.org>
Date: Sat, 4 Apr 2026 19:16:26 -0400
Subject: [PATCH v1 4/5] Allow JSON_OBJECT, JSON_ARRAY, JSON_SCALAR to cache
JSON type information.
This may significantly improve performance in queries which
execute these functions repeatedly.
---
src/backend/executor/execExpr.c | 28 ++++-----
src/backend/executor/execExprInterp.c | 15 ++---
src/backend/utils/adt/json.c | 86 +++++++++++++-------------
src/backend/utils/adt/jsonb.c | 88 +++++++++++++--------------
src/include/executor/execExpr.h | 9 +--
src/include/utils/json.h | 15 +++--
src/include/utils/jsonb.h | 15 +++--
7 files changed, 129 insertions(+), 127 deletions(-)
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index 871fd17e1ca..766cd9448f6 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -2399,15 +2399,12 @@ ExecInitExprRec(Expr *node, ExprState *state,
jcstate->constructor = ctor;
jcstate->arg_values = palloc_array(Datum, nargs);
jcstate->arg_nulls = palloc_array(bool, nargs);
- jcstate->arg_types = palloc_array(Oid, nargs);
jcstate->nargs = nargs;
foreach(lc, args)
{
Expr *arg = (Expr *) lfirst(lc);
- jcstate->arg_types[argno] = exprType((Node *) arg);
-
if (IsA(arg, Const))
{
/* Don't evaluate const arguments every round */
@@ -2425,25 +2422,28 @@ ExecInitExprRec(Expr *node, ExprState *state,
argno++;
}
- /* prepare type cache for datum_to_json[b]() */
- if (ctor->type == JSCTOR_JSON_SCALAR)
+ /*
+ * Prepare type cache for json_build_*_worker and
+ * datum_to_json[b], which are used for SCALAR, OBJECT,
+ * and ARRAY constructors.
+ */
+ if (ctor->type != JSCTOR_JSON_PARSE)
{
bool is_jsonb =
ctor->returning->format->format_type == JS_FORMAT_JSONB;
- jcstate->arg_type_cache =
- palloc(sizeof(*jcstate->arg_type_cache) * nargs);
+ jcstate->arg_categories =
+ palloc_array(JsonTypeCategory, nargs);
+ jcstate->arg_outflinfos =
+ palloc0_array(FmgrInfo, nargs);
for (int i = 0; i < nargs; i++)
{
- JsonTypeCategory category;
- Oid typid = jcstate->arg_types[i];
-
- json_categorize_type(typid, is_jsonb,
- &category,
- &jcstate->arg_type_cache[i].outflinfo);
+ Node *arg = (Node *) list_nth(args, i);
- jcstate->arg_type_cache[i].category = (int) category;
+ json_categorize_type(exprType(arg), is_jsonb,
+ &jcstate->arg_categories[i],
+ &jcstate->arg_outflinfos[i]);
}
}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 1ebfb190e67..a57be73be47 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -4745,7 +4745,8 @@ ExecEvalJsonConstructor(ExprState *state, ExprEvalStep *op,
json_build_array_worker) (jcstate->nargs,
jcstate->arg_values,
jcstate->arg_nulls,
- jcstate->arg_types,
+ jcstate->arg_categories,
+ jcstate->arg_outflinfos,
jcstate->constructor->absent_on_null);
else if (ctor->type == JSCTOR_JSON_OBJECT)
res = (is_jsonb ?
@@ -4753,7 +4754,8 @@ ExecEvalJsonConstructor(ExprState *state, ExprEvalStep *op,
json_build_object_worker) (jcstate->nargs,
jcstate->arg_values,
jcstate->arg_nulls,
- jcstate->arg_types,
+ jcstate->arg_categories,
+ jcstate->arg_outflinfos,
jcstate->constructor->absent_on_null,
jcstate->constructor->unique);
else if (ctor->type == JSCTOR_JSON_SCALAR)
@@ -4766,14 +4768,13 @@ ExecEvalJsonConstructor(ExprState *state, ExprEvalStep *op,
else
{
Datum value = jcstate->arg_values[0];
- FmgrInfo *outflinfo = &jcstate->arg_type_cache[0].outflinfo;
- JsonTypeCategory category = (JsonTypeCategory)
- jcstate->arg_type_cache[0].category;
if (is_jsonb)
- res = datum_to_jsonb(value, category, outflinfo);
+ res = datum_to_jsonb(value, jcstate->arg_categories[0],
+ &jcstate->arg_outflinfos[0]);
else
- res = datum_to_json(value, category, outflinfo);
+ res = datum_to_json(value, jcstate->arg_categories[0],
+ &jcstate->arg_outflinfos[0]);
}
}
else if (ctor->type == JSCTOR_JSON_PARSE)
diff --git a/src/backend/utils/adt/json.c b/src/backend/utils/adt/json.c
index 788237f6b48..24b41f0a376 100644
--- a/src/backend/utils/adt/json.c
+++ b/src/backend/utils/adt/json.c
@@ -95,8 +95,6 @@ static void array_to_json_internal(Datum array, StringInfo result,
static void datum_to_json_internal(Datum val, bool is_null, StringInfo result,
JsonTypeCategory tcategory,
FmgrInfo *outflinfo, bool key_scalar);
-static void add_json(Datum val, bool is_null, StringInfo result,
- Oid val_type, bool key_scalar);
static text *catenate_stringinfo_string(StringInfo buffer, const char *addon);
/*
@@ -592,35 +590,6 @@ composite_to_json(Datum composite, StringInfo result, bool use_line_feeds)
ReleaseTupleDesc(tupdesc);
}
-/*
- * Append JSON text for "val" to "result".
- *
- * This is just a thin wrapper around datum_to_json. If the same type will be
- * printed many times, avoid using this; better to do the json_categorize_type
- * lookups only once.
- */
-static void
-add_json(Datum val, bool is_null, StringInfo result,
- Oid val_type, bool key_scalar)
-{
- JsonTypeCategory tcategory;
- FmgrInfo outflinfo;
-
- if (val_type == InvalidOid)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("could not determine input data type")));
-
- if (is_null)
- tcategory = JSONTYPE_NULL;
- else
- json_categorize_type(val_type, false,
- &tcategory, &outflinfo);
-
- datum_to_json_internal(val, is_null, result, tcategory, &outflinfo,
- key_scalar);
-}
-
/*
* SQL function array_to_json(row)
*/
@@ -1200,7 +1169,9 @@ catenate_stringinfo_string(StringInfo buffer, const char *addon)
}
Datum
-json_build_object_worker(int nargs, const Datum *args, const bool *nulls, const Oid *types,
+json_build_object_worker(int nargs, const Datum *args, const bool *nulls,
+ const JsonTypeCategory *categories,
+ FmgrInfo *outflinfos,
bool absent_on_null, bool unique_keys)
{
int i;
@@ -1256,7 +1227,8 @@ json_build_object_worker(int nargs, const Datum *args, const bool *nulls, const
/* save key offset before appending it */
key_offset = out->len;
- add_json(args[i], false, out, types[i], true);
+ datum_to_json_internal(args[i], false, out,
+ categories[i], &outflinfos[i], true);
if (unique_keys)
{
@@ -1282,7 +1254,9 @@ json_build_object_worker(int nargs, const Datum *args, const bool *nulls, const
appendStringInfoString(result, " : ");
/* process value */
- add_json(args[i + 1], nulls[i + 1], result, types[i + 1], false);
+ datum_to_json_internal(args[i + 1], nulls[i + 1], result,
+ categories[i + 1], &outflinfos[i + 1],
+ false);
}
appendStringInfoChar(result, '}');
@@ -1299,15 +1273,26 @@ json_build_object(PG_FUNCTION_ARGS)
Datum *args;
bool *nulls;
Oid *types;
+ int nargs;
+ JsonTypeCategory *categories;
+ FmgrInfo *outflinfos;
/* build argument values to build the object */
- int nargs = extract_variadic_args(fcinfo, 0, true,
- &args, &types, &nulls);
+ nargs = extract_variadic_args(fcinfo, 0, true,
+ &args, &types, &nulls);
if (nargs < 0)
PG_RETURN_NULL();
- PG_RETURN_DATUM(json_build_object_worker(nargs, args, nulls, types, false, false));
+ categories = palloc_array(JsonTypeCategory, nargs);
+ outflinfos = palloc0_array(FmgrInfo, nargs);
+ for (int i = 0; i < nargs; i++)
+ json_categorize_type(types[i], false,
+ &categories[i], &outflinfos[i]);
+
+ PG_RETURN_DATUM(json_build_object_worker(nargs, args, nulls,
+ categories, outflinfos,
+ false, false));
}
/*
@@ -1320,8 +1305,9 @@ json_build_object_noargs(PG_FUNCTION_ARGS)
}
Datum
-json_build_array_worker(int nargs, const Datum *args, const bool *nulls, const Oid *types,
- bool absent_on_null)
+json_build_array_worker(int nargs, const Datum *args, const bool *nulls,
+ const JsonTypeCategory *categories,
+ FmgrInfo *outflinfos, bool absent_on_null)
{
int i;
const char *sep = "";
@@ -1338,7 +1324,8 @@ json_build_array_worker(int nargs, const Datum *args, const bool *nulls, const O
appendStringInfoString(&result, sep);
sep = ", ";
- add_json(args[i], nulls[i], &result, types[i], false);
+ datum_to_json_internal(args[i], nulls[i], &result,
+ categories[i], &outflinfos[i], false);
}
appendStringInfoChar(&result, ']');
@@ -1355,15 +1342,26 @@ json_build_array(PG_FUNCTION_ARGS)
Datum *args;
bool *nulls;
Oid *types;
+ int nargs;
+ JsonTypeCategory *categories;
+ FmgrInfo *outflinfos;
- /* build argument values to build the object */
- int nargs = extract_variadic_args(fcinfo, 0, true,
- &args, &types, &nulls);
+ /* build argument values to build the array */
+ nargs = extract_variadic_args(fcinfo, 0, true,
+ &args, &types, &nulls);
if (nargs < 0)
PG_RETURN_NULL();
- PG_RETURN_DATUM(json_build_array_worker(nargs, args, nulls, types, false));
+ categories = palloc_array(JsonTypeCategory, nargs);
+ outflinfos = palloc0_array(FmgrInfo, nargs);
+ for (int i = 0; i < nargs; i++)
+ json_categorize_type(types[i], false,
+ &categories[i], &outflinfos[i]);
+
+ PG_RETURN_DATUM(json_build_array_worker(nargs, args, nulls,
+ categories, outflinfos,
+ false));
}
/*
diff --git a/src/backend/utils/adt/jsonb.c b/src/backend/utils/adt/jsonb.c
index 94fc3d4ecfb..56e94acd2a8 100644
--- a/src/backend/utils/adt/jsonb.c
+++ b/src/backend/utils/adt/jsonb.c
@@ -52,8 +52,6 @@ static void array_to_jsonb_internal(Datum array, JsonbInState *result);
static void datum_to_jsonb_internal(Datum val, bool is_null, JsonbInState *result,
JsonTypeCategory tcategory,
FmgrInfo *outflinfo, bool key_scalar);
-static void add_jsonb(Datum val, bool is_null, JsonbInState *result,
- Oid val_type, bool key_scalar);
static char *JsonbToCStringWorker(StringInfo out, JsonbContainer *in, int estimated_len, bool indent);
static void add_indent(StringInfo out, bool indent, int level);
@@ -1041,37 +1039,6 @@ composite_to_jsonb(Datum composite, JsonbInState *result)
ReleaseTupleDesc(tupdesc);
}
-/*
- * Append JSON text for "val" to "result".
- *
- * This is just a thin wrapper around datum_to_jsonb. If the same type will be
- * printed many times, avoid using this; better to do the json_categorize_type
- * lookups only once.
- */
-
-static void
-add_jsonb(Datum val, bool is_null, JsonbInState *result,
- Oid val_type, bool key_scalar)
-{
- JsonTypeCategory tcategory;
- FmgrInfo outflinfo;
-
- if (val_type == InvalidOid)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("could not determine input data type")));
-
- if (is_null)
- tcategory = JSONTYPE_NULL;
- else
- json_categorize_type(val_type, true,
- &tcategory, &outflinfo);
-
- datum_to_jsonb_internal(val, is_null, result, tcategory, &outflinfo,
- key_scalar);
-}
-
-
/*
* Is the given type immutable when coming out of a JSONB context?
*/
@@ -1129,7 +1096,9 @@ datum_to_jsonb(Datum val, JsonTypeCategory tcategory, FmgrInfo *outflinfo)
}
Datum
-jsonb_build_object_worker(int nargs, const Datum *args, const bool *nulls, const Oid *types,
+jsonb_build_object_worker(int nargs, const Datum *args, const bool *nulls,
+ const JsonTypeCategory *categories,
+ FmgrInfo *outflinfos,
bool absent_on_null, bool unique_keys)
{
int i;
@@ -1166,10 +1135,13 @@ jsonb_build_object_worker(int nargs, const Datum *args, const bool *nulls, const
if (skip && !unique_keys)
continue;
- add_jsonb(args[i], false, &result, types[i], true);
+ datum_to_jsonb_internal(args[i], false, &result,
+ categories[i], &outflinfos[i], true);
/* process value */
- add_jsonb(args[i + 1], nulls[i + 1], &result, types[i + 1], false);
+ datum_to_jsonb_internal(args[i + 1], nulls[i + 1], &result,
+ categories[i + 1], &outflinfos[i + 1],
+ false);
}
pushJsonbValue(&result, WJB_END_OBJECT, NULL);
@@ -1186,15 +1158,26 @@ jsonb_build_object(PG_FUNCTION_ARGS)
Datum *args;
bool *nulls;
Oid *types;
+ int nargs;
+ JsonTypeCategory *categories;
+ FmgrInfo *outflinfos;
/* build argument values to build the object */
- int nargs = extract_variadic_args(fcinfo, 0, true,
- &args, &types, &nulls);
+ nargs = extract_variadic_args(fcinfo, 0, true,
+ &args, &types, &nulls);
if (nargs < 0)
PG_RETURN_NULL();
- PG_RETURN_DATUM(jsonb_build_object_worker(nargs, args, nulls, types, false, false));
+ categories = palloc_array(JsonTypeCategory, nargs);
+ outflinfos = palloc0_array(FmgrInfo, nargs);
+ for (int i = 0; i < nargs; i++)
+ json_categorize_type(types[i], true,
+ &categories[i], &outflinfos[i]);
+
+ PG_RETURN_DATUM(jsonb_build_object_worker(nargs, args, nulls,
+ categories, outflinfos,
+ false, false));
}
/*
@@ -1214,8 +1197,9 @@ jsonb_build_object_noargs(PG_FUNCTION_ARGS)
}
Datum
-jsonb_build_array_worker(int nargs, const Datum *args, const bool *nulls, const Oid *types,
- bool absent_on_null)
+jsonb_build_array_worker(int nargs, const Datum *args, const bool *nulls,
+ const JsonTypeCategory *categories,
+ FmgrInfo *outflinfos, bool absent_on_null)
{
int i;
JsonbInState result;
@@ -1229,7 +1213,8 @@ jsonb_build_array_worker(int nargs, const Datum *args, const bool *nulls, const
if (absent_on_null && nulls[i])
continue;
- add_jsonb(args[i], nulls[i], &result, types[i], false);
+ datum_to_jsonb_internal(args[i], nulls[i], &result,
+ categories[i], &outflinfos[i], false);
}
pushJsonbValue(&result, WJB_END_ARRAY, NULL);
@@ -1246,15 +1231,26 @@ jsonb_build_array(PG_FUNCTION_ARGS)
Datum *args;
bool *nulls;
Oid *types;
+ int nargs;
+ JsonTypeCategory *categories;
+ FmgrInfo *outflinfos;
- /* build argument values to build the object */
- int nargs = extract_variadic_args(fcinfo, 0, true,
- &args, &types, &nulls);
+ /* build argument values to build the array */
+ nargs = extract_variadic_args(fcinfo, 0, true,
+ &args, &types, &nulls);
if (nargs < 0)
PG_RETURN_NULL();
- PG_RETURN_DATUM(jsonb_build_array_worker(nargs, args, nulls, types, false));
+ categories = palloc_array(JsonTypeCategory, nargs);
+ outflinfos = palloc0_array(FmgrInfo, nargs);
+ for (int i = 0; i < nargs; i++)
+ json_categorize_type(types[i], true,
+ &categories[i], &outflinfos[i]);
+
+ PG_RETURN_DATUM(jsonb_build_array_worker(nargs, args, nulls,
+ categories, outflinfos,
+ false));
}
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index 8650ff57952..168a90876fd 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -17,6 +17,7 @@
#include "executor/nodeAgg.h"
#include "nodes/execnodes.h"
#include "nodes/miscnodes.h"
+#include "utils/jsontypes.h"
/* forward references to avoid circularity */
struct ExprEvalStep;
@@ -827,12 +828,8 @@ typedef struct JsonConstructorExprState
JsonConstructorExpr *constructor;
Datum *arg_values;
bool *arg_nulls;
- Oid *arg_types;
- struct
- {
- int category;
- FmgrInfo outflinfo;
- } *arg_type_cache; /* cache for datum_to_json[b]() */
+ JsonTypeCategory *arg_categories;
+ FmgrInfo *arg_outflinfos;
int nargs;
} JsonConstructorExprState;
diff --git a/src/include/utils/json.h b/src/include/utils/json.h
index 31f88fbbff6..3f9c5bd7ea3 100644
--- a/src/include/utils/json.h
+++ b/src/include/utils/json.h
@@ -28,11 +28,16 @@ extern void escape_json_text(StringInfo buf, const text *txt);
extern char *JsonEncodeDateTime(char *buf, Datum value, Oid typid,
const int *tzp);
extern bool to_json_is_immutable(Oid typoid);
-extern Datum json_build_object_worker(int nargs, const Datum *args, const bool *nulls,
- const Oid *types, bool absent_on_null,
- bool unique_keys);
-extern Datum json_build_array_worker(int nargs, const Datum *args, const bool *nulls,
- const Oid *types, bool absent_on_null);
+extern Datum json_build_object_worker(int nargs, const Datum *args,
+ const bool *nulls,
+ const JsonTypeCategory *categories,
+ FmgrInfo *outflinfos,
+ bool absent_on_null, bool unique_keys);
+extern Datum json_build_array_worker(int nargs, const Datum *args,
+ const bool *nulls,
+ const JsonTypeCategory *categories,
+ FmgrInfo *outflinfos,
+ bool absent_on_null);
extern bool json_validate(text *json, bool check_unique_keys, bool throw_error);
#endif /* JSON_H */
diff --git a/src/include/utils/jsonb.h b/src/include/utils/jsonb.h
index 67e4675a908..33f0e716420 100644
--- a/src/include/utils/jsonb.h
+++ b/src/include/utils/jsonb.h
@@ -461,10 +461,15 @@ extern Datum jsonb_get_element(Jsonb *jb, const Datum *path, int npath,
extern Datum datum_to_jsonb(Datum val, JsonTypeCategory tcategory,
FmgrInfo *outflinfo);
extern bool to_jsonb_is_immutable(Oid typoid);
-extern Datum jsonb_build_object_worker(int nargs, const Datum *args, const bool *nulls,
- const Oid *types, bool absent_on_null,
- bool unique_keys);
-extern Datum jsonb_build_array_worker(int nargs, const Datum *args, const bool *nulls,
- const Oid *types, bool absent_on_null);
+extern Datum jsonb_build_object_worker(int nargs, const Datum *args,
+ const bool *nulls,
+ const JsonTypeCategory *categories,
+ FmgrInfo *outflinfos,
+ bool absent_on_null, bool unique_keys);
+extern Datum jsonb_build_array_worker(int nargs, const Datum *args,
+ const bool *nulls,
+ const JsonTypeCategory *categories,
+ FmgrInfo *outflinfos,
+ bool absent_on_null);
#endif /* __JSONB_H__ */
--
2.50.1 (Apple Git-155)