From 3587cbc34fe87e1bde6c202daba791b68e44cb3a Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Thu, 15 Oct 2015 13:46:09 -0400 Subject: [PATCH] Fix NULL handling in datum_to_jsonb(). The function failed to adhere to its specification that the "tcategory" argument should not be examined when the input value is NULL. This resulted in a crash in some cases. Per bug #13680 from Boyko Yordanov. In passing, re-pgindent some recent changes in jsonb.c, and fix a rather ungrammatical comment. Diagnosis and patch by Michael Paquier, cosmetic changes by me --- src/backend/utils/adt/jsonb.c | 27 +++++++++++++++------------ src/test/regress/expected/jsonb.out | 9 +++++++++ src/test/regress/sql/jsonb.sql | 4 ++++ 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/src/backend/utils/adt/jsonb.c b/src/backend/utils/adt/jsonb.c index 8b1cab488b..aa156c432c 100644 --- a/src/backend/utils/adt/jsonb.c +++ b/src/backend/utils/adt/jsonb.c @@ -61,11 +61,11 @@ typedef enum /* type categories for datum_to_jsonb */ typedef struct JsonbAggState { - JsonbInState *res; - JsonbTypeCategory key_category; - Oid key_output_func; - JsonbTypeCategory val_category; - Oid val_output_func; + JsonbInState *res; + JsonbTypeCategory key_category; + Oid key_output_func; + JsonbTypeCategory val_category; + Oid val_output_func; } JsonbAggState; static inline Datum jsonb_from_cstring(char *json, int len); @@ -714,6 +714,7 @@ datum_to_jsonb(Datum val, bool is_null, JsonbInState *result, check_stack_depth(); + /* Convert val to a JsonbValue in jb (in most cases) */ if (is_null) { Assert(!key_scalar); @@ -936,8 +937,10 @@ datum_to_jsonb(Datum val, bool is_null, JsonbInState *result, break; } } - if (tcategory >= JSONBTYPE_JSON && tcategory <= JSONBTYPE_JSONCAST && - !scalar_jsonb) + + /* Now insert jb into result, unless we did it recursively */ + if (!is_null && !scalar_jsonb && + tcategory >= JSONBTYPE_JSON && tcategory <= JSONBTYPE_JSONCAST) { /* work has been done recursively */ return; @@ -1607,8 +1610,7 @@ jsonb_agg_transfn(PG_FUNCTION_ARGS) if (PG_ARGISNULL(0)) { - - Oid arg_type = get_fn_expr_argtype(fcinfo->flinfo, 1); + Oid arg_type = get_fn_expr_argtype(fcinfo->flinfo, 1); if (arg_type == InvalidOid) ereport(ERROR, @@ -1762,7 +1764,7 @@ jsonb_object_agg_transfn(PG_FUNCTION_ARGS) if (PG_ARGISNULL(0)) { - Oid arg_type; + Oid arg_type; oldcontext = MemoryContextSwitchTo(aggcontext); state = palloc(sizeof(JsonbAggState)); @@ -1950,8 +1952,9 @@ jsonb_object_agg_finalfn(PG_FUNCTION_ARGS) /* * We need to do a shallow clone of the argument's res field in case the * final function is called more than once, so we avoid changing the - * it. A shallow clone is sufficient as we aren't going to change any of - * the values, just add the final object end marker. + * aggregate state value. A shallow clone is sufficient as we aren't + * going to change any of the values, just add the final object end + * marker. */ result.parseState = clone_parse_state(arg->res->parseState); diff --git a/src/test/regress/expected/jsonb.out b/src/test/regress/expected/jsonb.out index 07091a9bfb..bee95a3a9f 100644 --- a/src/test/regress/expected/jsonb.out +++ b/src/test/regress/expected/jsonb.out @@ -1362,6 +1362,15 @@ SELECT jsonb_build_object(json '{"a":1,"b":2}', 3); ERROR: key value must be scalar, not array, composite or json SELECT jsonb_build_object('{1,2,3}'::int[], 3); ERROR: key value must be scalar, not array, composite or json +-- handling of NULL values +SELECT jsonb_object_agg(1, NULL::jsonb); + jsonb_object_agg +------------------ + {"1": null} +(1 row) + +SELECT jsonb_object_agg(NULL, '{"a":1}'); +ERROR: field name must not be null CREATE TEMP TABLE foo (serial_num int, name text, type text); INSERT INTO foo VALUES (847001,'t15','GE1043'); INSERT INTO foo VALUES (847002,'t16','GE1043'); diff --git a/src/test/regress/sql/jsonb.sql b/src/test/regress/sql/jsonb.sql index 9700b7ca7e..d93b8c3de8 100644 --- a/src/test/regress/sql/jsonb.sql +++ b/src/test/regress/sql/jsonb.sql @@ -330,6 +330,10 @@ SELECT jsonb_build_object(json '{"a":1,"b":2}', 3); SELECT jsonb_build_object('{1,2,3}'::int[], 3); +-- handling of NULL values +SELECT jsonb_object_agg(1, NULL::jsonb); +SELECT jsonb_object_agg(NULL, '{"a":1}'); + CREATE TEMP TABLE foo (serial_num int, name text, type text); INSERT INTO foo VALUES (847001,'t15','GE1043'); INSERT INTO foo VALUES (847002,'t16','GE1043');