Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit57d8c12

Browse files
committed
Fix handling of nested JSON objects in json_populate_recordset and friends.
populate_recordset_object_start() improperly created a new hash table(overwriting the link to the existing one) if called at nest levelsgreater than one. This resulted in previous fields not appearing inthe final output, as reported by Matti Hameister in bug #10728.In 9.4 the problem also affects json_to_recordset.This perhaps missed detection earlier because the default behavior is tothrow an error for nested objects: you have to pass use_json_as_text = trueto see the problem.In addition, fix query-lifespan leakage of the hashtable created byjson_populate_record(). This is pretty much the same problem recentlyfixed in dblink: creating an intended-to-be-temporary context underneaththe executor's per-tuple context isn't enough to make it go away at theend of the tuple cycle, because MemoryContextReset is notMemoryContextResetAndDeleteChildren.Michael Paquier and Tom Lane
1 parent0f74827 commit57d8c12

File tree

4 files changed

+58
-6
lines changed

4 files changed

+58
-6
lines changed

‎src/backend/utils/adt/jsonfuncs.c

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2075,8 +2075,10 @@ populate_record_worker(FunctionCallInfo fcinfo, bool have_record_arg)
20752075
* with domain nulls.
20762076
*/
20772077
if (hash_get_num_entries(json_hash)==0&&rec)
2078+
{
2079+
hash_destroy(json_hash);
20782080
PG_RETURN_POINTER(rec);
2079-
2081+
}
20802082
}
20812083
else
20822084
{
@@ -2250,6 +2252,9 @@ populate_record_worker(FunctionCallInfo fcinfo, bool have_record_arg)
22502252

22512253
ReleaseTupleDesc(tupdesc);
22522254

2255+
if (json_hash)
2256+
hash_destroy(json_hash);
2257+
22532258
PG_RETURN_DATUM(HeapTupleGetDatum(rettuple));
22542259
}
22552260

@@ -2735,16 +2740,23 @@ populate_recordset_object_start(void *state)
27352740
intlex_level=_state->lex->lex_level;
27362741
HASHCTLctl;
27372742

2743+
/* Reject object at top level: we must have an array at level 0 */
27382744
if (lex_level==0)
27392745
ereport(ERROR,
27402746
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
27412747
errmsg("cannot call json_populate_recordset on an object")));
2742-
elseif (lex_level>1&& !_state->use_json_as_text)
2743-
ereport(ERROR,
2744-
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2745-
errmsg("cannot call json_populate_recordset with nested objects")));
27462748

2747-
/* set up a new hash for this entry */
2749+
/* Nested objects, if allowed, require no special processing */
2750+
if (lex_level>1)
2751+
{
2752+
if (!_state->use_json_as_text)
2753+
ereport(ERROR,
2754+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2755+
errmsg("cannot call json_populate_recordset with nested objects")));
2756+
return;
2757+
}
2758+
2759+
/* Object at level 1: set up a new hash table for this object */
27482760
memset(&ctl,0,sizeof(ctl));
27492761
ctl.keysize=NAMEDATALEN;
27502762
ctl.entrysize=sizeof(JsonHashEntry);
@@ -2771,9 +2783,11 @@ populate_recordset_object_end(void *state)
27712783
HeapTupleHeaderrec=_state->rec;
27722784
HeapTuplerettuple;
27732785

2786+
/* Nested objects require no special processing */
27742787
if (_state->lex->lex_level>1)
27752788
return;
27762789

2790+
/* Otherwise, construct and return a tuple based on this level-1 object */
27772791
values= (Datum*)palloc(ncolumns*sizeof(Datum));
27782792
nulls= (bool*)palloc(ncolumns*sizeof(bool));
27792793

@@ -2865,7 +2879,9 @@ populate_recordset_object_end(void *state)
28652879

28662880
tuplestore_puttuple(_state->tuple_store,rettuple);
28672881

2882+
/* Done with hash for this object */
28682883
hash_destroy(json_hash);
2884+
_state->json_hash=NULL;
28692885
}
28702886

28712887
staticvoid

‎src/test/regress/expected/json.out

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1007,6 +1007,13 @@ select * from json_populate_recordset(row('def',99,null)::jpop,'[{"a":[100,200,3
10071007

10081008
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;
10091009
ERROR: invalid input syntax for type timestamp: "[100,200,300]"
1010+
create type jpop2 as (a int, b json, c int, d int);
1011+
select * from json_populate_recordset(null::jpop2, '[{"a":2,"c":3,"b":{"z":4},"d":6}]',true) q;
1012+
a | b | c | d
1013+
---+---------+---+---
1014+
2 | {"z":4} | 3 | 6
1015+
(1 row)
1016+
10101017
-- using the default use_json_as_text argument
10111018
select * from json_populate_recordset(null::jpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q;
10121019
a | b | c
@@ -1223,3 +1230,11 @@ select * from json_to_recordset('[{"a":1,"b":"foo","d":false},{"a":2,"b":"bar","
12231230
2 | bar | t
12241231
(2 rows)
12251232

1233+
select * from json_to_recordset('[{"a":1,"b":{"d":"foo"},"c":true},{"a":2,"c":false,"b":{"d":"bar"}}]', true)
1234+
as x(a int, b json, c boolean);
1235+
a | b | c
1236+
---+-------------+---
1237+
1 | {"d":"foo"} | t
1238+
2 | {"d":"bar"} | f
1239+
(2 rows)
1240+

‎src/test/regress/expected/json_1.out

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1007,6 +1007,13 @@ select * from json_populate_recordset(row('def',99,null)::jpop,'[{"a":[100,200,3
10071007

10081008
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;
10091009
ERROR: invalid input syntax for type timestamp: "[100,200,300]"
1010+
create type jpop2 as (a int, b json, c int, d int);
1011+
select * from json_populate_recordset(null::jpop2, '[{"a":2,"c":3,"b":{"z":4},"d":6}]',true) q;
1012+
a | b | c | d
1013+
---+---------+---+---
1014+
2 | {"z":4} | 3 | 6
1015+
(1 row)
1016+
10101017
-- using the default use_json_as_text argument
10111018
select * from json_populate_recordset(null::jpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q;
10121019
a | b | c
@@ -1219,3 +1226,11 @@ select * from json_to_recordset('[{"a":1,"b":"foo","d":false},{"a":2,"b":"bar","
12191226
2 | bar | t
12201227
(2 rows)
12211228

1229+
select * from json_to_recordset('[{"a":1,"b":{"d":"foo"},"c":true},{"a":2,"c":false,"b":{"d":"bar"}}]', true)
1230+
as x(a int, b json, c boolean);
1231+
a | b | c
1232+
---+-------------+---
1233+
1 | {"d":"foo"} | t
1234+
2 | {"d":"bar"} | f
1235+
(2 rows)
1236+

‎src/test/regress/sql/json.sql

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,9 @@ select * from json_populate_recordset(row('def',99,null)::jpop,'[{"a":"blurfl","
325325
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;
326326
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;
327327

328+
createtypejpop2as (aint, b json, cint, dint);
329+
select*from json_populate_recordset(null::jpop2,'[{"a":2,"c":3,"b":{"z":4},"d":6}]',true) q;
330+
328331
-- using the default use_json_as_text argument
329332

330333
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)
447450

448451
select*from json_to_recordset('[{"a":1,"b":"foo","d":false},{"a":2,"b":"bar","c":true}]',false)
449452
as x(aint, btext, cboolean);
453+
454+
select*from json_to_recordset('[{"a":1,"b":{"d":"foo"},"c":true},{"a":2,"c":false,"b":{"d":"bar"}}]', true)
455+
as x(aint, b json, cboolean);

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp