]> granicus.if.org Git - postgresql/commitdiff
Fix NULL handling in datum_to_jsonb().
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 15 Oct 2015 17:46:09 +0000 (13:46 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 15 Oct 2015 17:46:09 +0000 (13:46 -0400)
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
src/test/regress/expected/jsonb.out
src/test/regress/sql/jsonb.sql

index 8b1cab488bb80bbce8e2c99aef4aa29a9df76f34..aa156c432c6c7073df265ec8be8762672fddbca3 100644 (file)
@@ -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);
index 07091a9bfb4c3d38ebdf29bc8f9401606e8376a8..bee95a3a9f17190fddfac4011be98f86548fd48e 100644 (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
 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');
index 9700b7ca7e0e7bd3aa3a7626ff857d2355d3e9fd..d93b8c3de88f221d8504e629cb522dc109f5aceb 100644 (file)
@@ -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');