aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/jsonfuncs.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2022-12-12 16:17:49 -0500
committerTom Lane <tgl@sss.pgh.pa.us>2022-12-12 16:17:54 -0500
commitb0feda79fdf02710a6039a324299525845a4a9d4 (patch)
tree488eb42fc75b6554156f6bd359d8e15a9fd37c32 /src/backend/utils/adt/jsonfuncs.c
parent101c37cd342a3ae134bb3e5e0abb14ae46692b56 (diff)
downloadpostgresql-b0feda79fdf02710a6039a324299525845a4a9d4.tar.gz
postgresql-b0feda79fdf02710a6039a324299525845a4a9d4.zip
Fix jsonb subscripting to cope with toasted subscript values.
jsonb_get_element() was incautious enough to use VARDATA() and VARSIZE() directly on an arbitrary text Datum. That of course fails if the Datum is short-header, compressed, or out-of-line. The typical result would be failing to match any element of a jsonb object, though matching the wrong one seems possible as well. setPathObject() was slightly brighter, in that it used VARDATA_ANY and VARSIZE_ANY_EXHDR, but that only kept it out of trouble for short-header Datums. push_path() had the same issue. This could result in faulty subscripted insertions, though keys long enough to cause a problem are likely rare in the wild. Having seen these, I looked around for unsafe usages in the rest of the adt/json* files. There are a couple of places where it's not immediately obvious that the Datum can't be compressed or out-of-line, so I added pg_detoast_datum_packed() to cope if it is. Also, remove some other usages of VARDATA/VARSIZE on Datums we just extracted from a text array. Those aren't actively broken, but they will become so if we ever start allowing short-header array elements, which does not seem like a terribly unreasonable thing to do. In any case they are not great coding examples, and they could also do with comments pointing out that we're assuming we don't need pg_detoast_datum_packed. Per report from exe-dealer@yandex.ru. Patch by me, but thanks to David Johnston for initial investigation. Back-patch to v14 where jsonb subscripting was introduced. Discussion: https://postgr.es/m/205321670615953@mail.yandex.ru
Diffstat (limited to 'src/backend/utils/adt/jsonfuncs.c')
-rw-r--r--src/backend/utils/adt/jsonfuncs.c41
1 files changed, 29 insertions, 12 deletions
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index 376a9b7ab82..2d293da18ef 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -527,6 +527,12 @@ pg_parse_json_or_errsave(JsonLexContext *lex, JsonSemAction *sem,
JsonLexContext *
makeJsonLexContext(text *json, bool need_escapes)
{
+ /*
+ * Most callers pass a detoasted datum, but it's not clear that they all
+ * do. pg_detoast_datum_packed() is cheap insurance.
+ */
+ json = pg_detoast_datum_packed(json);
+
return makeJsonLexContextCstringLen(VARDATA_ANY(json),
VARSIZE_ANY_EXHDR(json),
GetDatabaseEncoding(),
@@ -1559,9 +1565,11 @@ jsonb_get_element(Jsonb *jb, Datum *path, int npath, bool *isnull, bool as_text)
{
if (have_object)
{
+ text *subscr = DatumGetTextPP(path[i]);
+
jbvp = getKeyJsonValueFromContainer(container,
- VARDATA(path[i]),
- VARSIZE(path[i]) - VARHDRSZ,
+ VARDATA_ANY(subscr),
+ VARSIZE_ANY_EXHDR(subscr),
NULL);
}
else if (have_array)
@@ -1734,8 +1742,8 @@ push_path(JsonbParseState **st, int level, Datum *path_elems,
{
/* text, an object is expected */
newkey.type = jbvString;
- newkey.val.string.len = VARSIZE_ANY_EXHDR(path_elems[i]);
- newkey.val.string.val = VARDATA_ANY(path_elems[i]);
+ newkey.val.string.val = c;
+ newkey.val.string.len = strlen(c);
(void) pushJsonbValue(st, WJB_BEGIN_OBJECT, NULL);
(void) pushJsonbValue(st, WJB_KEY, &newkey);
@@ -4456,6 +4464,7 @@ jsonb_delete_array(PG_FUNCTION_ARGS)
if (keys_nulls[i])
continue;
+ /* We rely on the array elements not being toasted */
keyptr = VARDATA_ANY(keys_elems[i]);
keylen = VARSIZE_ANY_EXHDR(keys_elems[i]);
if (keylen == v.val.string.len &&
@@ -4977,6 +4986,7 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
int path_len, JsonbParseState **st, int level,
JsonbValue *newval, uint32 npairs, int op_type)
{
+ text *pathelem = NULL;
int i;
JsonbValue k,
v;
@@ -4984,6 +4994,11 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
if (level >= path_len || path_nulls[level])
done = true;
+ else
+ {
+ /* The path Datum could be toasted, in which case we must detoast it */
+ pathelem = DatumGetTextPP(path_elems[level]);
+ }
/* empty object is a special case for create */
if ((npairs == 0) && (op_type & JB_PATH_CREATE_OR_INSERT) &&
@@ -4992,8 +5007,8 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
JsonbValue newkey;
newkey.type = jbvString;
- newkey.val.string.len = VARSIZE_ANY_EXHDR(path_elems[level]);
- newkey.val.string.val = VARDATA_ANY(path_elems[level]);
+ newkey.val.string.val = VARDATA_ANY(pathelem);
+ newkey.val.string.len = VARSIZE_ANY_EXHDR(pathelem);
(void) pushJsonbValue(st, WJB_KEY, &newkey);
(void) pushJsonbValue(st, WJB_VALUE, newval);
@@ -5006,8 +5021,8 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
Assert(r == WJB_KEY);
if (!done &&
- k.val.string.len == VARSIZE_ANY_EXHDR(path_elems[level]) &&
- memcmp(k.val.string.val, VARDATA_ANY(path_elems[level]),
+ k.val.string.len == VARSIZE_ANY_EXHDR(pathelem) &&
+ memcmp(k.val.string.val, VARDATA_ANY(pathelem),
k.val.string.len) == 0)
{
done = true;
@@ -5047,8 +5062,8 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
JsonbValue newkey;
newkey.type = jbvString;
- newkey.val.string.len = VARSIZE_ANY_EXHDR(path_elems[level]);
- newkey.val.string.val = VARDATA_ANY(path_elems[level]);
+ newkey.val.string.val = VARDATA_ANY(pathelem);
+ newkey.val.string.len = VARSIZE_ANY_EXHDR(pathelem);
(void) pushJsonbValue(st, WJB_KEY, &newkey);
(void) pushJsonbValue(st, WJB_VALUE, newval);
@@ -5091,8 +5106,8 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
JsonbValue newkey;
newkey.type = jbvString;
- newkey.val.string.len = VARSIZE_ANY_EXHDR(path_elems[level]);
- newkey.val.string.val = VARDATA_ANY(path_elems[level]);
+ newkey.val.string.val = VARDATA_ANY(pathelem);
+ newkey.val.string.len = VARSIZE_ANY_EXHDR(pathelem);
(void) pushJsonbValue(st, WJB_KEY, &newkey);
(void) push_path(st, level, path_elems, path_nulls,
@@ -5505,6 +5520,8 @@ transform_jsonb_string_values(Jsonb *jsonb, void *action_state,
if ((type == WJB_VALUE || type == WJB_ELEM) && v.type == jbvString)
{
out = transform_action(action_state, v.val.string.val, v.val.string.len);
+ /* out is probably not toasted, but let's be sure */
+ out = pg_detoast_datum_packed(out);
v.val.string.val = VARDATA_ANY(out);
v.val.string.len = VARSIZE_ANY_EXHDR(out);
res = pushJsonbValue(&st, type, type < WJB_BEGIN_ARRAY ? &v : NULL);