Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit d9a356f

Browse files
committed
Fix treatment of nulls in jsonb_agg and jsonb_object_agg
The wrong is_null flag was being passed to datum_to_json. Also, null object key values are not permitted, and this was not being checked for. Add regression tests covering these cases, and also add those tests to the json set, even though it was doing the right thing. Fixes bug #13514, initially diagnosed by Tom Lane.
1 parent c1ca3a1 commit d9a356f

File tree

7 files changed

+107
-10
lines changed

7 files changed

+107
-10
lines changed

src/backend/utils/adt/jsonb.c

+9-3
Original file line numberDiff line numberDiff line change
@@ -705,6 +705,7 @@ datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
705705

706706
if (is_null)
707707
{
708+
Assert(!key_scalar);
708709
jb.type = jbvNull;
709710
}
710711
else if (key_scalar &&
@@ -1606,7 +1607,7 @@ jsonb_agg_transfn(PG_FUNCTION_ARGS)
16061607

16071608
memset(&elem, 0, sizeof(JsonbInState));
16081609

1609-
datum_to_jsonb(val, false, &elem, tcategory, outfuncoid, false);
1610+
datum_to_jsonb(val, PG_ARGISNULL(1), &elem, tcategory, outfuncoid, false);
16101611

16111612
jbelem = JsonbValueToJsonb(elem.res);
16121613

@@ -1752,7 +1753,12 @@ jsonb_object_agg_transfn(PG_FUNCTION_ARGS)
17521753
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
17531754
errmsg("could not determine input data type")));
17541755

1755-
val = PG_ARGISNULL(1) ? (Datum) 0 : PG_GETARG_DATUM(1);
1756+
if (PG_ARGISNULL(1))
1757+
ereport(ERROR,
1758+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1759+
errmsg("field name must not be null")));
1760+
1761+
val = PG_GETARG_DATUM(1);
17561762

17571763
jsonb_categorize_type(val_type,
17581764
&tcategory, &outfuncoid);
@@ -1777,7 +1783,7 @@ jsonb_object_agg_transfn(PG_FUNCTION_ARGS)
17771783

17781784
memset(&elem, 0, sizeof(JsonbInState));
17791785

1780-
datum_to_jsonb(val, false, &elem, tcategory, outfuncoid, false);
1786+
datum_to_jsonb(val, PG_ARGISNULL(2), &elem, tcategory, outfuncoid, false);
17811787

17821788
jbval = JsonbValueToJsonb(elem.res);
17831789

src/test/regress/expected/json.out

+20-1
Original file line numberDiff line numberDiff line change
@@ -465,7 +465,7 @@ SELECT json_agg(q)
465465
{"b":"a2","c":5,"z":[{"f1":2,"f2":[1,2,3]},{"f1":5,"f2":[4,5,6]}]}]
466466
(1 row)
467467

468-
SELECT json_agg(q)
468+
SELECT json_agg(q ORDER BY x, y)
469469
FROM rows q;
470470
json_agg
471471
-----------------------
@@ -474,6 +474,16 @@ SELECT json_agg(q)
474474
{"x":3,"y":"txt3"}]
475475
(1 row)
476476

477+
UPDATE rows SET x = NULL WHERE x = 1;
478+
SELECT json_agg(q ORDER BY x NULLS FIRST, y)
479+
FROM rows q;
480+
json_agg
481+
--------------------------
482+
[{"x":null,"y":"txt1"}, +
483+
{"x":2,"y":"txt2"}, +
484+
{"x":3,"y":"txt3"}]
485+
(1 row)
486+
477487
-- non-numeric output
478488
SELECT row_to_json(q)
479489
FROM (SELECT 'NaN'::float8 AS "float8field") q;
@@ -1574,6 +1584,15 @@ FROM foo;
15741584
{"turbines" : { "847001" : {"name" : "t15", "type" : "GE1043"}, "847002" : {"name" : "t16", "type" : "GE1043"}, "847003" : {"name" : "sub-alpha", "type" : "GESS90"} }}
15751585
(1 row)
15761586

1587+
SELECT json_object_agg(name, type) FROM foo;
1588+
json_object_agg
1589+
----------------------------------------------------------------
1590+
{ "t15" : "GE1043", "t16" : "GE1043", "sub-alpha" : "GESS90" }
1591+
(1 row)
1592+
1593+
INSERT INTO foo VALUES (999999, NULL, 'bar');
1594+
SELECT json_object_agg(name, type) FROM foo;
1595+
ERROR: field name must not be null
15771596
-- json_object
15781597
-- one dimension
15791598
SELECT json_object('{a,1,b,2,3,NULL,"d e f","a b c"}');

src/test/regress/expected/json_1.out

+20-1
Original file line numberDiff line numberDiff line change
@@ -465,7 +465,7 @@ SELECT json_agg(q)
465465
{"b":"a2","c":5,"z":[{"f1":2,"f2":[1,2,3]},{"f1":5,"f2":[4,5,6]}]}]
466466
(1 row)
467467

468-
SELECT json_agg(q)
468+
SELECT json_agg(q ORDER BY x, y)
469469
FROM rows q;
470470
json_agg
471471
-----------------------
@@ -474,6 +474,16 @@ SELECT json_agg(q)
474474
{"x":3,"y":"txt3"}]
475475
(1 row)
476476

477+
UPDATE rows SET x = NULL WHERE x = 1;
478+
SELECT json_agg(q ORDER BY x NULLS FIRST, y)
479+
FROM rows q;
480+
json_agg
481+
--------------------------
482+
[{"x":null,"y":"txt1"}, +
483+
{"x":2,"y":"txt2"}, +
484+
{"x":3,"y":"txt3"}]
485+
(1 row)
486+
477487
-- non-numeric output
478488
SELECT row_to_json(q)
479489
FROM (SELECT 'NaN'::float8 AS "float8field") q;
@@ -1570,6 +1580,15 @@ FROM foo;
15701580
{"turbines" : { "847001" : {"name" : "t15", "type" : "GE1043"}, "847002" : {"name" : "t16", "type" : "GE1043"}, "847003" : {"name" : "sub-alpha", "type" : "GESS90"} }}
15711581
(1 row)
15721582

1583+
SELECT json_object_agg(name, type) FROM foo;
1584+
json_object_agg
1585+
----------------------------------------------------------------
1586+
{ "t15" : "GE1043", "t16" : "GE1043", "sub-alpha" : "GESS90" }
1587+
(1 row)
1588+
1589+
INSERT INTO foo VALUES (999999, NULL, 'bar');
1590+
SELECT json_object_agg(name, type) FROM foo;
1591+
ERROR: field name must not be null
15731592
-- json_object
15741593
-- one dimension
15751594
SELECT json_object('{a,1,b,2,3,NULL,"d e f","a b c"}');

src/test/regress/expected/jsonb.out

+18-1
Original file line numberDiff line numberDiff line change
@@ -369,13 +369,21 @@ SELECT jsonb_agg(q)
369369
[{"b": "a1", "c": 4, "z": [{"f1": 1, "f2": [1, 2, 3]}, {"f1": 4, "f2": [4, 5, 6]}]}, {"b": "a1", "c": 5, "z": [{"f1": 1, "f2": [1, 2, 3]}, {"f1": 5, "f2": [4, 5, 6]}]}, {"b": "a2", "c": 4, "z": [{"f1": 2, "f2": [1, 2, 3]}, {"f1": 4, "f2": [4, 5, 6]}]}, {"b": "a2", "c": 5, "z": [{"f1": 2, "f2": [1, 2, 3]}, {"f1": 5, "f2": [4, 5, 6]}]}]
370370
(1 row)
371371

372-
SELECT jsonb_agg(q)
372+
SELECT jsonb_agg(q ORDER BY x, y)
373373
FROM rows q;
374374
jsonb_agg
375375
-----------------------------------------------------------------------
376376
[{"x": 1, "y": "txt1"}, {"x": 2, "y": "txt2"}, {"x": 3, "y": "txt3"}]
377377
(1 row)
378378

379+
UPDATE rows SET x = NULL WHERE x = 1;
380+
SELECT jsonb_agg(q ORDER BY x NULLS FIRST, y)
381+
FROM rows q;
382+
jsonb_agg
383+
--------------------------------------------------------------------------
384+
[{"x": null, "y": "txt1"}, {"x": 2, "y": "txt2"}, {"x": 3, "y": "txt3"}]
385+
(1 row)
386+
379387
-- jsonb extraction functions
380388
CREATE TEMP TABLE test_jsonb (
381389
json_type text,
@@ -1393,6 +1401,15 @@ FROM foo;
13931401
{"turbines": {"847001": {"name": "t15", "type": "GE1043"}, "847002": {"name": "t16", "type": "GE1043"}, "847003": {"name": "sub-alpha", "type": "GESS90"}}}
13941402
(1 row)
13951403

1404+
SELECT jsonb_object_agg(name, type) FROM foo;
1405+
jsonb_object_agg
1406+
-----------------------------------------------------------
1407+
{"t15": "GE1043", "t16": "GE1043", "sub-alpha": "GESS90"}
1408+
(1 row)
1409+
1410+
INSERT INTO foo VALUES (999999, NULL, 'bar');
1411+
SELECT jsonb_object_agg(name, type) FROM foo;
1412+
ERROR: field name must not be null
13961413
-- jsonb_object
13971414
-- one dimension
13981415
SELECT jsonb_object('{a,1,b,2,3,NULL,"d e f","a b c"}');

src/test/regress/expected/jsonb_1.out

+18-1
Original file line numberDiff line numberDiff line change
@@ -369,13 +369,21 @@ SELECT jsonb_agg(q)
369369
[{"b": "a1", "c": 4, "z": [{"f1": 1, "f2": [1, 2, 3]}, {"f1": 4, "f2": [4, 5, 6]}]}, {"b": "a1", "c": 5, "z": [{"f1": 1, "f2": [1, 2, 3]}, {"f1": 5, "f2": [4, 5, 6]}]}, {"b": "a2", "c": 4, "z": [{"f1": 2, "f2": [1, 2, 3]}, {"f1": 4, "f2": [4, 5, 6]}]}, {"b": "a2", "c": 5, "z": [{"f1": 2, "f2": [1, 2, 3]}, {"f1": 5, "f2": [4, 5, 6]}]}]
370370
(1 row)
371371

372-
SELECT jsonb_agg(q)
372+
SELECT jsonb_agg(q ORDER BY x, y)
373373
FROM rows q;
374374
jsonb_agg
375375
-----------------------------------------------------------------------
376376
[{"x": 1, "y": "txt1"}, {"x": 2, "y": "txt2"}, {"x": 3, "y": "txt3"}]
377377
(1 row)
378378

379+
UPDATE rows SET x = NULL WHERE x = 1;
380+
SELECT jsonb_agg(q ORDER BY x NULLS FIRST, y)
381+
FROM rows q;
382+
jsonb_agg
383+
--------------------------------------------------------------------------
384+
[{"x": null, "y": "txt1"}, {"x": 2, "y": "txt2"}, {"x": 3, "y": "txt3"}]
385+
(1 row)
386+
379387
-- jsonb extraction functions
380388
CREATE TEMP TABLE test_jsonb (
381389
json_type text,
@@ -1393,6 +1401,15 @@ FROM foo;
13931401
{"turbines": {"847001": {"name": "t15", "type": "GE1043"}, "847002": {"name": "t16", "type": "GE1043"}, "847003": {"name": "sub-alpha", "type": "GESS90"}}}
13941402
(1 row)
13951403

1404+
SELECT jsonb_object_agg(name, type) FROM foo;
1405+
jsonb_object_agg
1406+
-----------------------------------------------------------
1407+
{"t15": "GE1043", "t16": "GE1043", "sub-alpha": "GESS90"}
1408+
(1 row)
1409+
1410+
INSERT INTO foo VALUES (999999, NULL, 'bar');
1411+
SELECT jsonb_object_agg(name, type) FROM foo;
1412+
ERROR: field name must not be null
13961413
-- jsonb_object
13971414
-- one dimension
13981415
SELECT jsonb_object('{a,1,b,2,3,NULL,"d e f","a b c"}');

src/test/regress/sql/json.sql

+11-2
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,12 @@ SELECT json_agg(q)
126126
FROM generate_series(1,2) x,
127127
generate_series(4,5) y) q;
128128

129-
SELECT json_agg(q)
129+
SELECT json_agg(q ORDER BY x, y)
130+
FROM rows q;
131+
132+
UPDATE rows SET x = NULL WHERE x = 1;
133+
134+
SELECT json_agg(q ORDER BY x NULLS FIRST, y)
130135
FROM rows q;
131136

132137
-- non-numeric output
@@ -442,7 +447,6 @@ SELECT json_build_object(
442447
'd', json_build_object('e',array[9,8,7]::int[],
443448
'f', (select row_to_json(r) from ( select relkind, oid::regclass as name from pg_class where relname = 'pg_class') r)));
444449

445-
446450
-- empty objects/arrays
447451
SELECT json_build_array();
448452

@@ -468,6 +472,11 @@ INSERT INTO foo VALUES (847003,'sub-alpha','GESS90');
468472
SELECT json_build_object('turbines',json_object_agg(serial_num,json_build_object('name',name,'type',type)))
469473
FROM foo;
470474

475+
SELECT json_object_agg(name, type) FROM foo;
476+
477+
INSERT INTO foo VALUES (999999, NULL, 'bar');
478+
SELECT json_object_agg(name, type) FROM foo;
479+
471480
-- json_object
472481

473482
-- one dimension

src/test/regress/sql/jsonb.sql

+11-1
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,12 @@ SELECT jsonb_agg(q)
9393
FROM generate_series(1,2) x,
9494
generate_series(4,5) y) q;
9595

96-
SELECT jsonb_agg(q)
96+
SELECT jsonb_agg(q ORDER BY x, y)
97+
FROM rows q;
98+
99+
UPDATE rows SET x = NULL WHERE x = 1;
100+
101+
SELECT jsonb_agg(q ORDER BY x NULLS FIRST, y)
97102
FROM rows q;
98103

99104
-- jsonb extraction functions
@@ -334,6 +339,11 @@ INSERT INTO foo VALUES (847003,'sub-alpha','GESS90');
334339
SELECT jsonb_build_object('turbines',jsonb_object_agg(serial_num,jsonb_build_object('name',name,'type',type)))
335340
FROM foo;
336341

342+
SELECT jsonb_object_agg(name, type) FROM foo;
343+
344+
INSERT INTO foo VALUES (999999, NULL, 'bar');
345+
SELECT jsonb_object_agg(name, type) FROM foo;
346+
337347
-- jsonb_object
338348

339349
-- one dimension

0 commit comments

Comments
 (0)