diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index 71179f65518..f303860fdbb 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -2075,8 +2075,10 @@ populate_record_worker(FunctionCallInfo fcinfo, bool have_record_arg) * with domain nulls. */ if (hash_get_num_entries(json_hash) == 0 && rec) + { + hash_destroy(json_hash); PG_RETURN_POINTER(rec); - + } } else { @@ -2250,6 +2252,9 @@ populate_record_worker(FunctionCallInfo fcinfo, bool have_record_arg) ReleaseTupleDesc(tupdesc); + if (json_hash) + hash_destroy(json_hash); + PG_RETURN_DATUM(HeapTupleGetDatum(rettuple)); } @@ -2735,16 +2740,23 @@ populate_recordset_object_start(void *state) int lex_level = _state->lex->lex_level; HASHCTL ctl; + /* Reject object at top level: we must have an array at level 0 */ if (lex_level == 0) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("cannot call json_populate_recordset on an object"))); - else if (lex_level > 1 && !_state->use_json_as_text) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("cannot call json_populate_recordset with nested objects"))); - /* set up a new hash for this entry */ + /* Nested objects, if allowed, require no special processing */ + if (lex_level > 1) + { + if (!_state->use_json_as_text) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("cannot call json_populate_recordset with nested objects"))); + return; + } + + /* Object at level 1: set up a new hash table for this object */ memset(&ctl, 0, sizeof(ctl)); ctl.keysize = NAMEDATALEN; ctl.entrysize = sizeof(JsonHashEntry); @@ -2771,9 +2783,11 @@ populate_recordset_object_end(void *state) HeapTupleHeader rec = _state->rec; HeapTuple rettuple; + /* Nested objects require no special processing */ if (_state->lex->lex_level > 1) return; + /* Otherwise, construct and return a tuple based on this level-1 object */ values = (Datum *) palloc(ncolumns * sizeof(Datum)); nulls = (bool *) palloc(ncolumns * sizeof(bool)); @@ -2865,7 +2879,9 @@ populate_recordset_object_end(void *state) tuplestore_puttuple(_state->tuple_store, rettuple); + /* Done with hash for this object */ hash_destroy(json_hash); + _state->json_hash = NULL; } static void diff --git a/src/test/regress/expected/json.out b/src/test/regress/expected/json.out index 8b8556b2e04..d1e32a19a52 100644 --- a/src/test/regress/expected/json.out +++ b/src/test/regress/expected/json.out @@ -1007,6 +1007,13 @@ select * from json_populate_recordset(row('def',99,null)::jpop,'[{"a":[100,200,3 select * from json_populate_recordset(row('def',99,null)::jpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]',true) q; ERROR: invalid input syntax for type timestamp: "[100,200,300]" +create type jpop2 as (a int, b json, c int, d int); +select * from json_populate_recordset(null::jpop2, '[{"a":2,"c":3,"b":{"z":4},"d":6}]',true) q; + a | b | c | d +---+---------+---+--- + 2 | {"z":4} | 3 | 6 +(1 row) + -- using the default use_json_as_text argument select * from json_populate_recordset(null::jpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q; a | b | c @@ -1223,3 +1230,11 @@ select * from json_to_recordset('[{"a":1,"b":"foo","d":false},{"a":2,"b":"bar"," 2 | bar | t (2 rows) +select * from json_to_recordset('[{"a":1,"b":{"d":"foo"},"c":true},{"a":2,"c":false,"b":{"d":"bar"}}]', true) + as x(a int, b json, c boolean); + a | b | c +---+-------------+--- + 1 | {"d":"foo"} | t + 2 | {"d":"bar"} | f +(2 rows) + diff --git a/src/test/regress/expected/json_1.out b/src/test/regress/expected/json_1.out index b32c3ed09f3..93cb693b2fb 100644 --- a/src/test/regress/expected/json_1.out +++ b/src/test/regress/expected/json_1.out @@ -1007,6 +1007,13 @@ select * from json_populate_recordset(row('def',99,null)::jpop,'[{"a":[100,200,3 select * from json_populate_recordset(row('def',99,null)::jpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]',true) q; ERROR: invalid input syntax for type timestamp: "[100,200,300]" +create type jpop2 as (a int, b json, c int, d int); +select * from json_populate_recordset(null::jpop2, '[{"a":2,"c":3,"b":{"z":4},"d":6}]',true) q; + a | b | c | d +---+---------+---+--- + 2 | {"z":4} | 3 | 6 +(1 row) + -- using the default use_json_as_text argument select * from json_populate_recordset(null::jpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q; a | b | c @@ -1219,3 +1226,11 @@ select * from json_to_recordset('[{"a":1,"b":"foo","d":false},{"a":2,"b":"bar"," 2 | bar | t (2 rows) +select * from json_to_recordset('[{"a":1,"b":{"d":"foo"},"c":true},{"a":2,"c":false,"b":{"d":"bar"}}]', true) + as x(a int, b json, c boolean); + a | b | c +---+-------------+--- + 1 | {"d":"foo"} | t + 2 | {"d":"bar"} | f +(2 rows) + diff --git a/src/test/regress/sql/json.sql b/src/test/regress/sql/json.sql index 3d5ed50126e..bc8bb629781 100644 --- a/src/test/regress/sql/json.sql +++ b/src/test/regress/sql/json.sql @@ -325,6 +325,9 @@ select * from json_populate_recordset(row('def',99,null)::jpop,'[{"a":"blurfl"," select * from json_populate_recordset(row('def',99,null)::jpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]',true) q; select * from json_populate_recordset(row('def',99,null)::jpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]',true) q; +create type jpop2 as (a int, b json, c int, d int); +select * from json_populate_recordset(null::jpop2, '[{"a":2,"c":3,"b":{"z":4},"d":6}]',true) q; + -- using the default use_json_as_text argument select * from json_populate_recordset(null::jpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q; @@ -447,3 +450,6 @@ select * from json_to_record('{"a":1,"b":"foo","c":"bar"}',true) select * from json_to_recordset('[{"a":1,"b":"foo","d":false},{"a":2,"b":"bar","c":true}]',false) as x(a int, b text, c boolean); + +select * from json_to_recordset('[{"a":1,"b":{"d":"foo"},"c":true},{"a":2,"c":false,"b":{"d":"bar"}}]', true) + as x(a int, b json, c boolean);