Fix json_to_record() bug with nested objects.
authorTom Lane
Thu, 3 Mar 2016 04:31:39 +0000 (23:31 -0500)
committerTom Lane
Thu, 3 Mar 2016 04:31:39 +0000 (23:31 -0500)
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.

src/backend/utils/adt/jsonfuncs.c
src/test/regress/expected/json.out
src/test/regress/expected/jsonb.out
src/test/regress/sql/json.sql
src/test/regress/sql/jsonb.sql

index b49a5dd61e4f6e9a6e2accad6c70c1b44aef0586..18e7b9c8798887e666ad10e5efb706fe874c5808 100644 (file)
@@ -2350,7 +2350,7 @@ hash_object_field_end(void *state, char *fname, bool isnull)
    /*
     * Ignore nested fields.
     */
-   if (_state->lex->lex_level > 2)
+   if (_state->lex->lex_level > 1)
        return;
 
    /*
index 65c43c9b65c8e5bc5164e8ac3596b7f8fbb22daf..8573b58e7c4b672b822f41cfd5c1ab3a83d60a40 100644 (file)
@@ -1552,3 +1552,19 @@ select * from json_to_recordset('[{"a":1,"b":{"d":"foo"},"c":true},{"a":2,"c":fa
  2 | {"d":"bar"} | f
 (2 rows)
 
+select *, c is null as c_is_null
+from json_to_record('{"a":1, "b":{"c":16, "d":2}, "x":8}'::json)
+    as t(a int, b json, c text, x int);
+ a |        b        | c | x | c_is_null 
+---+-----------------+---+---+-----------
+ 1 | {"c":16, "d":2} |   | 8 | t
+(1 row)
+
+select *, c is null as c_is_null
+from json_to_recordset('[{"a":1, "b":{"c":16, "d":2}, "x":8}]'::json)
+    as t(a int, b json, c text, x int);
+ a |        b        | c | x | c_is_null 
+---+-----------------+---+---+-----------
+ 1 | {"c":16, "d":2} |   | 8 | t
+(1 row)
+
index e2cb57097d287ef603a638734501be5e45784f15..0ef36329ba9c12c244f6b35faf978a9a2914d6e7 100644 (file)
@@ -1760,6 +1760,22 @@ select * from jsonb_to_recordset('[{"a":1,"b":"foo","d":false},{"a":2,"b":"bar",
  2 | bar | t
 (2 rows)
 
+select *, c is null as c_is_null
+from jsonb_to_record('{"a":1, "b":{"c":16, "d":2}, "x":8}'::jsonb)
+    as t(a int, b jsonb, c text, x int);
+ a |         b         | c | x | c_is_null 
+---+-------------------+---+---+-----------
+ 1 | {"c": 16, "d": 2} |   | 8 | t
+(1 row)
+
+select *, c is null as c_is_null
+from jsonb_to_recordset('[{"a":1, "b":{"c":16, "d":2}, "x":8}]'::jsonb)
+    as t(a int, b jsonb, c text, x int);
+ a |         b         | c | x | c_is_null 
+---+-------------------+---+---+-----------
+ 1 | {"c": 16, "d": 2} |   | 8 | t
+(1 row)
+
 -- indexing
 SELECT count(*) FROM testjsonb WHERE j @> '{"wait":null}';
  count 
index bf540c06a5fedb421ec06489594fdde2d25f265c..346e5b8363fcd892300e3fec6455382dcea78bf7 100644 (file)
@@ -487,7 +487,6 @@ select json_object('{a,b,NULL,"d e f"}','{1,2,3,"a b c"}');
 
 select json_object('{a,b,"","d e f"}','{1,2,3,"a b c"}');
 
-
 -- json_to_record and json_to_recordset
 
 select * from json_to_record('{"a":1,"b":"foo","c":"bar"}')
@@ -498,3 +497,11 @@ select * from json_to_recordset('[{"a":1,"b":"foo","d":false},{"a":2,"b":"bar","
 
 select * from json_to_recordset('[{"a":1,"b":{"d":"foo"},"c":true},{"a":2,"c":false,"b":{"d":"bar"}}]')
     as x(a int, b json, c boolean);
+
+select *, c is null as c_is_null
+from json_to_record('{"a":1, "b":{"c":16, "d":2}, "x":8}'::json)
+    as t(a int, b json, c text, x int);
+
+select *, c is null as c_is_null
+from json_to_recordset('[{"a":1, "b":{"c":16, "d":2}, "x":8}]'::json)
+    as t(a int, b json, c text, x int);
index 74d34dcab4fd38f5a32ca9b5482b00e3a94b4cb9..c84ad54ba3537a1793076e310184dfdd7c0860c4 100644 (file)
@@ -372,6 +372,14 @@ select * from jsonb_to_record('{"a":1,"b":"foo","c":"bar"}')
 select * from jsonb_to_recordset('[{"a":1,"b":"foo","d":false},{"a":2,"b":"bar","c":true}]')
     as x(a int, b text, c boolean);
 
+select *, c is null as c_is_null
+from jsonb_to_record('{"a":1, "b":{"c":16, "d":2}, "x":8}'::jsonb)
+    as t(a int, b jsonb, c text, x int);
+
+select *, c is null as c_is_null
+from jsonb_to_recordset('[{"a":1, "b":{"c":16, "d":2}, "x":8}]'::jsonb)
+    as t(a int, b jsonb, c text, x int);
+
 -- indexing
 SELECT count(*) FROM testjsonb WHERE j @> '{"wait":null}';
 SELECT count(*) FROM testjsonb WHERE j @> '{"wait":"CC"}';