]> granicus.if.org Git - postgresql/commitdiff
Fix json_to_record() bug with nested objects.
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 3 Mar 2016 04:31:39 +0000 (23:31 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
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 88225aae4dc76e8901123cc827b86544a459ff73..363afa7699074e2a83e75deb92b523bf8ad02756 100644 (file)
@@ -2438,7 +2438,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 502f9838897344fc916c9d02cf554ea330a431db..efcdc4141e322294c005507ca726db91bb0022af 100644 (file)
@@ -1599,6 +1599,22 @@ 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)
+
 -- json_strip_nulls
 select json_strip_nulls(null);
  json_strip_nulls 
index 4789e4e57b9e2a87908c28eb663ffc4d4dc3b419..416918dd9fbe307caf2bff6df528de7776300b36 100644 (file)
@@ -2001,6 +2001,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 39f1b70f4da10bb8ff47941d4f720e71d23f6f7f..603288bd1a1ef6d67a5ad4873790dcc024c2821f 100644 (file)
@@ -519,6 +519,13 @@ 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);
 
 -- json_strip_nulls
 
index 4b24477609505c06061d4bbcfcbab3cb673986d4..c6716841344c3744e1ef0908355305ed36b993c8 100644 (file)
@@ -508,6 +508,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"}';