From 595220a3a3a89e5648b7b6be726502efdc9ec806 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Thu, 22 Nov 2018 15:14:01 -0500 Subject: [PATCH] Fix another crash in json{b}_populate_recordset and json{b}_to_recordset. populate_recordset_worker() failed to consider the possibility that the supplied JSON data contains no rows, so that update_cached_tupdesc never got called. This led to a null-pointer dereference since commit 9a5e8ed28; before that it led to a bogus "set-valued function called in context that cannot accept a set" error. Fix by forcing the update to happen. Per bug #15514. Back-patch to v11 as 9a5e8ed28 was. (If we were excited about the bogus error, we could perhaps go back further, but it'd take more work to figure out how to fix it in older branches. Given the lack of field complaints about that aspect, I'm not excited.) Discussion: https://postgr.es/m/15514-59d5b4c4065b178b@postgresql.org --- src/backend/utils/adt/jsonfuncs.c | 6 ++++++ src/test/regress/expected/json.out | 13 +++++++++++++ src/test/regress/expected/jsonb.out | 13 +++++++++++++ src/test/regress/sql/json.sql | 5 +++++ src/test/regress/sql/jsonb.sql | 5 +++++ 5 files changed, 42 insertions(+) diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index 06f8d281685..5c3f2fb9f6f 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3658,6 +3658,12 @@ populate_recordset_worker(FunctionCallInfo fcinfo, const char *funcname, if (PG_ARGISNULL(json_arg_num)) PG_RETURN_NULL(); + /* + * Forcibly update the cached tupdesc, to ensure we have the right tupdesc + * to return even if the JSON contains no rows. + */ + update_cached_tupdesc(&cache->c.io.composite, cache->fn_mcxt); + state = palloc0(sizeof(PopulateRecordsetState)); /* make tuplestore in a sufficiently long-lived memory context */ diff --git a/src/test/regress/expected/json.out b/src/test/regress/expected/json.out index 6020feeea41..ea1be61effc 100644 --- a/src/test/regress/expected/json.out +++ b/src/test/regress/expected/json.out @@ -1851,6 +1851,19 @@ FROM (VALUES (1),(2)) v(i); 2 | (2,43) (4 rows) +-- empty array is a corner case +SELECT json_populate_recordset(null::record, '[]'); +ERROR: record type has not been registered +SELECT json_populate_recordset(row(1,2), '[]'); + json_populate_recordset +------------------------- +(0 rows) + +SELECT * FROM json_populate_recordset(NULL::jpop,'[]') q; + a | b | c +---+---+--- +(0 rows) + -- composite domain SELECT json_populate_recordset(null::j_ordered_pair, '[{"x": 0, "y": 1}]'); json_populate_recordset diff --git a/src/test/regress/expected/jsonb.out b/src/test/regress/expected/jsonb.out index f045e085384..4fddd2de140 100644 --- a/src/test/regress/expected/jsonb.out +++ b/src/test/regress/expected/jsonb.out @@ -2533,6 +2533,19 @@ FROM (VALUES (1),(2)) v(i); 2 | (2,43) (4 rows) +-- empty array is a corner case +SELECT jsonb_populate_recordset(null::record, '[]'); +ERROR: record type has not been registered +SELECT jsonb_populate_recordset(row(1,2), '[]'); + jsonb_populate_recordset +-------------------------- +(0 rows) + +SELECT * FROM jsonb_populate_recordset(NULL::jbpop,'[]') q; + a | b | c +---+---+--- +(0 rows) + -- composite domain SELECT jsonb_populate_recordset(null::jb_ordered_pair, '[{"x": 0, "y": 1}]'); jsonb_populate_recordset diff --git a/src/test/regress/sql/json.sql b/src/test/regress/sql/json.sql index 97c75420e9c..d1e40084277 100644 --- a/src/test/regress/sql/json.sql +++ b/src/test/regress/sql/json.sql @@ -550,6 +550,11 @@ SELECT json_populate_recordset(row(1,2), '[{"f1": 0, "f2": 1}]'); SELECT i, json_populate_recordset(row(i,50), '[{"f1":"42"},{"f2":"43"}]') FROM (VALUES (1),(2)) v(i); +-- empty array is a corner case +SELECT json_populate_recordset(null::record, '[]'); +SELECT json_populate_recordset(row(1,2), '[]'); +SELECT * FROM json_populate_recordset(NULL::jpop,'[]') q; + -- composite domain SELECT json_populate_recordset(null::j_ordered_pair, '[{"x": 0, "y": 1}]'); SELECT json_populate_recordset(row(1,2)::j_ordered_pair, '[{"x": 0}, {"y": 3}]'); diff --git a/src/test/regress/sql/jsonb.sql b/src/test/regress/sql/jsonb.sql index bd82fd13f7d..6cbdfe43958 100644 --- a/src/test/regress/sql/jsonb.sql +++ b/src/test/regress/sql/jsonb.sql @@ -666,6 +666,11 @@ SELECT jsonb_populate_recordset(row(1,2), '[{"f1": 0, "f2": 1}]'); SELECT i, jsonb_populate_recordset(row(i,50), '[{"f1":"42"},{"f2":"43"}]') FROM (VALUES (1),(2)) v(i); +-- empty array is a corner case +SELECT jsonb_populate_recordset(null::record, '[]'); +SELECT jsonb_populate_recordset(row(1,2), '[]'); +SELECT * FROM jsonb_populate_recordset(NULL::jbpop,'[]') q; + -- composite domain SELECT jsonb_populate_recordset(null::jb_ordered_pair, '[{"x": 0, "y": 1}]'); SELECT jsonb_populate_recordset(row(1,2)::jb_ordered_pair, '[{"x": 0}, {"y": 3}]');