diff options
author | Tom Lane | 2016-03-03 04:31:39 +0000 |
---|---|---|
committer | Tom Lane | 2016-03-03 04:31:39 +0000 |
commit | a9d199f6d3b998929cdb8e8aa61e5cd8db9b220f (patch) | |
tree | 9b82a1bccdedf2b6cd7ebcb64eed8c427ccc3a7f | |
parent | eb43e851d6b3fa0c4516efcfdf29a183f7717a43 (diff) |
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.
-rw-r--r-- | src/backend/utils/adt/jsonfuncs.c | 2 | ||||
-rw-r--r-- | src/test/regress/expected/json.out | 16 | ||||
-rw-r--r-- | src/test/regress/expected/jsonb.out | 16 | ||||
-rw-r--r-- | src/test/regress/sql/json.sql | 7 | ||||
-rw-r--r-- | src/test/regress/sql/jsonb.sql | 8 |
5 files changed, 48 insertions, 1 deletions
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index 88225aae4d..363afa7699 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -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; /* diff --git a/src/test/regress/expected/json.out b/src/test/regress/expected/json.out index 502f983889..efcdc4141e 100644 --- a/src/test/regress/expected/json.out +++ b/src/test/regress/expected/json.out @@ -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 diff --git a/src/test/regress/expected/jsonb.out b/src/test/regress/expected/jsonb.out index 4789e4e57b..416918dd9f 100644 --- a/src/test/regress/expected/jsonb.out +++ b/src/test/regress/expected/jsonb.out @@ -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 diff --git a/src/test/regress/sql/json.sql b/src/test/regress/sql/json.sql index 39f1b70f4d..603288bd1a 100644 --- a/src/test/regress/sql/json.sql +++ b/src/test/regress/sql/json.sql @@ -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 diff --git a/src/test/regress/sql/jsonb.sql b/src/test/regress/sql/jsonb.sql index 4b24477609..c671684134 100644 --- a/src/test/regress/sql/jsonb.sql +++ b/src/test/regress/sql/jsonb.sql @@ -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"}'; |