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
This commit is contained in:
Tom Lane 2015-10-15 13:46:09 -04:00
parent 08fbad0afd
commit 3587cbc34f
3 changed files with 28 additions and 12 deletions

View File

@ -61,11 +61,11 @@ typedef enum /* type categories for datum_to_jsonb */
typedef struct JsonbAggState typedef struct JsonbAggState
{ {
JsonbInState *res; JsonbInState *res;
JsonbTypeCategory key_category; JsonbTypeCategory key_category;
Oid key_output_func; Oid key_output_func;
JsonbTypeCategory val_category; JsonbTypeCategory val_category;
Oid val_output_func; Oid val_output_func;
} JsonbAggState; } JsonbAggState;
static inline Datum jsonb_from_cstring(char *json, int len); 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(); check_stack_depth();
/* Convert val to a JsonbValue in jb (in most cases) */
if (is_null) if (is_null)
{ {
Assert(!key_scalar); Assert(!key_scalar);
@ -936,8 +937,10 @@ datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
break; 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 */ /* work has been done recursively */
return; return;
@ -1607,8 +1610,7 @@ jsonb_agg_transfn(PG_FUNCTION_ARGS)
if (PG_ARGISNULL(0)) 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) if (arg_type == InvalidOid)
ereport(ERROR, ereport(ERROR,
@ -1762,7 +1764,7 @@ jsonb_object_agg_transfn(PG_FUNCTION_ARGS)
if (PG_ARGISNULL(0)) if (PG_ARGISNULL(0))
{ {
Oid arg_type; Oid arg_type;
oldcontext = MemoryContextSwitchTo(aggcontext); oldcontext = MemoryContextSwitchTo(aggcontext);
state = palloc(sizeof(JsonbAggState)); 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 * 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 * 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 * aggregate state value. A shallow clone is sufficient as we aren't
* the values, just add the final object end marker. * going to change any of the values, just add the final object end
* marker.
*/ */
result.parseState = clone_parse_state(arg->res->parseState); result.parseState = clone_parse_state(arg->res->parseState);

View File

@ -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 ERROR: key value must be scalar, not array, composite or json
SELECT jsonb_build_object('{1,2,3}'::int[], 3); SELECT jsonb_build_object('{1,2,3}'::int[], 3);
ERROR: key value must be scalar, not array, composite or json 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); CREATE TEMP TABLE foo (serial_num int, name text, type text);
INSERT INTO foo VALUES (847001,'t15','GE1043'); INSERT INTO foo VALUES (847001,'t15','GE1043');
INSERT INTO foo VALUES (847002,'t16','GE1043'); INSERT INTO foo VALUES (847002,'t16','GE1043');

View File

@ -330,6 +330,10 @@ SELECT jsonb_build_object(json '{"a":1,"b":2}', 3);
SELECT jsonb_build_object('{1,2,3}'::int[], 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); CREATE TEMP TABLE foo (serial_num int, name text, type text);
INSERT INTO foo VALUES (847001,'t15','GE1043'); INSERT INTO foo VALUES (847001,'t15','GE1043');
INSERT INTO foo VALUES (847002,'t16','GE1043'); INSERT INTO foo VALUES (847002,'t16','GE1043');