Skip to content

Commit a9d199f

Browse files
committed
Fix json_to_record() bug with nested objects.
A thinko concerning nesting depth caused json_to_record() to produce bogus output if a field of its input object contained a sub-object with a field name matching one of the requested output column names. Per bug #13996 from Johann Visagie. I added a regression test case based on his example, plus parallel tests for json_to_recordset, jsonb_to_record, jsonb_to_recordset. The latter three do not exhibit the same bug (which suggests that we may be missing some opportunities to share code...) but testing seems like a good idea in any case. Back-patch to 9.4 where these functions were introduced.
1 parent eb43e85 commit a9d199f

File tree

5 files changed

+48
-1
lines changed

5 files changed

+48
-1
lines changed

src/backend/utils/adt/jsonfuncs.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2438,7 +2438,7 @@ hash_object_field_end(void *state, char *fname, bool isnull)
24382438
/*
24392439
* Ignore nested fields.
24402440
*/
2441-
if (_state->lex->lex_level > 2)
2441+
if (_state->lex->lex_level > 1)
24422442
return;
24432443

24442444
/*

src/test/regress/expected/json.out

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1599,6 +1599,22 @@ select * from json_to_recordset('[{"a":1,"b":{"d":"foo"},"c":true},{"a":2,"c":fa
15991599
2 | {"d":"bar"} | f
16001600
(2 rows)
16011601

1602+
select *, c is null as c_is_null
1603+
from json_to_record('{"a":1, "b":{"c":16, "d":2}, "x":8}'::json)
1604+
as t(a int, b json, c text, x int);
1605+
a | b | c | x | c_is_null
1606+
---+-----------------+---+---+-----------
1607+
1 | {"c":16, "d":2} | | 8 | t
1608+
(1 row)
1609+
1610+
select *, c is null as c_is_null
1611+
from json_to_recordset('[{"a":1, "b":{"c":16, "d":2}, "x":8}]'::json)
1612+
as t(a int, b json, c text, x int);
1613+
a | b | c | x | c_is_null
1614+
---+-----------------+---+---+-----------
1615+
1 | {"c":16, "d":2} | | 8 | t
1616+
(1 row)
1617+
16021618
-- json_strip_nulls
16031619
select json_strip_nulls(null);
16041620
json_strip_nulls

src/test/regress/expected/jsonb.out

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2001,6 +2001,22 @@ select * from jsonb_to_recordset('[{"a":1,"b":"foo","d":false},{"a":2,"b":"bar",
20012001
2 | bar | t
20022002
(2 rows)
20032003

2004+
select *, c is null as c_is_null
2005+
from jsonb_to_record('{"a":1, "b":{"c":16, "d":2}, "x":8}'::jsonb)
2006+
as t(a int, b jsonb, c text, x int);
2007+
a | b | c | x | c_is_null
2008+
---+-------------------+---+---+-----------
2009+
1 | {"c": 16, "d": 2} | | 8 | t
2010+
(1 row)
2011+
2012+
select *, c is null as c_is_null
2013+
from jsonb_to_recordset('[{"a":1, "b":{"c":16, "d":2}, "x":8}]'::jsonb)
2014+
as t(a int, b jsonb, c text, x int);
2015+
a | b | c | x | c_is_null
2016+
---+-------------------+---+---+-----------
2017+
1 | {"c": 16, "d": 2} | | 8 | t
2018+
(1 row)
2019+
20042020
-- indexing
20052021
SELECT count(*) FROM testjsonb WHERE j @> '{"wait":null}';
20062022
count

src/test/regress/sql/json.sql

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,13 @@ select * from json_to_recordset('[{"a":1,"b":"foo","d":false},{"a":2,"b":"bar","
519519
select * from json_to_recordset('[{"a":1,"b":{"d":"foo"},"c":true},{"a":2,"c":false,"b":{"d":"bar"}}]')
520520
as x(a int, b json, c boolean);
521521

522+
select *, c is null as c_is_null
523+
from json_to_record('{"a":1, "b":{"c":16, "d":2}, "x":8}'::json)
524+
as t(a int, b json, c text, x int);
525+
526+
select *, c is null as c_is_null
527+
from json_to_recordset('[{"a":1, "b":{"c":16, "d":2}, "x":8}]'::json)
528+
as t(a int, b json, c text, x int);
522529

523530
-- json_strip_nulls
524531

src/test/regress/sql/jsonb.sql

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,14 @@ select * from jsonb_to_record('{"a":1,"b":"foo","c":"bar"}')
508508
select * from jsonb_to_recordset('[{"a":1,"b":"foo","d":false},{"a":2,"b":"bar","c":true}]')
509509
as x(a int, b text, c boolean);
510510

511+
select *, c is null as c_is_null
512+
from jsonb_to_record('{"a":1, "b":{"c":16, "d":2}, "x":8}'::jsonb)
513+
as t(a int, b jsonb, c text, x int);
514+
515+
select *, c is null as c_is_null
516+
from jsonb_to_recordset('[{"a":1, "b":{"c":16, "d":2}, "x":8}]'::jsonb)
517+
as t(a int, b jsonb, c text, x int);
518+
511519
-- indexing
512520
SELECT count(*) FROM testjsonb WHERE j @> '{"wait":null}';
513521
SELECT count(*) FROM testjsonb WHERE j @> '{"wait":"CC"}';

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy