Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Process variadic arguments consistently in json functions
authorAndrew Dunstan <andrew@dunslane.net>
Wed, 25 Oct 2017 11:34:00 +0000 (07:34 -0400)
committerAndrew Dunstan <andrew@dunslane.net>
Wed, 25 Oct 2017 11:52:45 +0000 (07:52 -0400)
json_build_object and json_build_array and the jsonb equivalents did not
correctly process explicit VARIADIC arguments. They are modified to use
the new extract_variadic_args() utility function which abstracts away
the details of the call method.

Michael Paquier, reviewed by Tom Lane and Dmitry Dolgov.

Backpatch to 9.5 for the jsonb fixes and 9.4 for the json fixes, as
that's where they originated.

src/backend/utils/adt/json.c
src/test/regress/expected/json.out
src/test/regress/sql/json.sql

index 7c3992661f5d8701855d79ca347dbde7b675f804..72f4781c46190e81a02cf0e77e9618f7881eadfd 100644 (file)
@@ -18,6 +18,7 @@
 #include "catalog/pg_cast.h"
 #include "catalog/pg_type.h"
 #include "executor/spi.h"
+#include "funcapi.h"
 #include "lib/stringinfo.h"
 #include "libpq/pqformat.h"
 #include "mb/pg_wchar.h"
@@ -2035,10 +2036,17 @@ json_build_object(PG_FUNCTION_ARGS)
 {
    int         nargs = PG_NARGS();
    int         i;
-   Datum       arg;
    const char *sep = "";
    StringInfo  result;
-   Oid         val_type;
+   Datum      *args;
+   bool       *nulls;
+   Oid        *types;
+
+   /* fetch argument values to build the object */
+   nargs = extract_variadic_args(fcinfo, 0, false, &args, &types, &nulls);
+
+   if (nargs < 0)
+       PG_RETURN_NULL();
 
    if (nargs % 2 != 0)
        ereport(ERROR,
@@ -2052,52 +2060,22 @@ json_build_object(PG_FUNCTION_ARGS)
 
    for (i = 0; i < nargs; i += 2)
    {
-       /*
-        * Note: since json_build_object() is declared as taking type "any",
-        * the parser will not do any type conversion on unknown-type literals
-        * (that is, undecorated strings or NULLs).  Such values will arrive
-        * here as type UNKNOWN, which fortunately does not matter to us,
-        * since unknownout() works fine.
-        */
        appendStringInfoString(result, sep);
        sep = ", ";
 
        /* process key */
-       val_type = get_fn_expr_argtype(fcinfo->flinfo, i);
-
-       if (val_type == InvalidOid)
-           ereport(ERROR,
-                   (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                    errmsg("could not determine data type for argument %d",
-                           i + 1)));
-
-       if (PG_ARGISNULL(i))
+       if (nulls[i])
            ereport(ERROR,
                    (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                     errmsg("argument %d cannot be null", i + 1),
                     errhint("Object keys should be text.")));
 
-       arg = PG_GETARG_DATUM(i);
-
-       add_json(arg, false, result, val_type, true);
+       add_json(args[i], false, result, types[i], true);
 
        appendStringInfoString(result, " : ");
 
        /* process value */
-       val_type = get_fn_expr_argtype(fcinfo->flinfo, i + 1);
-
-       if (val_type == InvalidOid)
-           ereport(ERROR,
-                   (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                    errmsg("could not determine data type for argument %d",
-                           i + 2)));
-
-       if (PG_ARGISNULL(i + 1))
-           arg = (Datum) 0;
-       else
-           arg = PG_GETARG_DATUM(i + 1);
-
-       add_json(arg, PG_ARGISNULL(i + 1), result, val_type, false);
+       add_json(args[i + 1], nulls[i + 1], result, types[i + 1], false);
    }
 
    appendStringInfoChar(result, '}');
@@ -2120,12 +2098,19 @@ json_build_object_noargs(PG_FUNCTION_ARGS)
 Datum
 json_build_array(PG_FUNCTION_ARGS)
 {
-   int         nargs = PG_NARGS();
+   int         nargs;
    int         i;
-   Datum       arg;
    const char *sep = "";
    StringInfo  result;
-   Oid         val_type;
+   Datum      *args;
+   bool       *nulls;
+   Oid        *types;
+
+   /* fetch argument values to build the array */
+   nargs = extract_variadic_args(fcinfo, 0, false, &args, &types, &nulls);
+
+   if (nargs < 0)
+       PG_RETURN_NULL();
 
    result = makeStringInfo();
 
@@ -2133,30 +2118,9 @@ json_build_array(PG_FUNCTION_ARGS)
 
    for (i = 0; i < nargs; i++)
    {
-       /*
-        * Note: since json_build_array() is declared as taking type "any",
-        * the parser will not do any type conversion on unknown-type literals
-        * (that is, undecorated strings or NULLs).  Such values will arrive
-        * here as type UNKNOWN, which fortunately does not matter to us,
-        * since unknownout() works fine.
-        */
        appendStringInfoString(result, sep);
        sep = ", ";
-
-       val_type = get_fn_expr_argtype(fcinfo->flinfo, i);
-
-       if (val_type == InvalidOid)
-           ereport(ERROR,
-                   (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                    errmsg("could not determine data type for argument %d",
-                           i + 1)));
-
-       if (PG_ARGISNULL(i))
-           arg = (Datum) 0;
-       else
-           arg = PG_GETARG_DATUM(i);
-
-       add_json(arg, PG_ARGISNULL(i), result, val_type, false);
+       add_json(args[i], nulls[i], result, types[i], false);
    }
 
    appendStringInfoChar(result, ']');
index 8573b58e7c4b672b822f41cfd5c1ab3a83d60a40..d0d95f2adb3e0e111eed4655bc99889980a8679e 100644 (file)
@@ -1420,6 +1420,54 @@ SELECT json_build_array('a',1,'b',1.2,'c',true,'d',null,'e',json '{"x": 3, "y":
  ["a", 1, "b", 1.2, "c", true, "d", null, "e", {"x": 3, "y": [1,2,3]}]
 (1 row)
 
+SELECT json_build_array('a', NULL); -- ok
+ json_build_array 
+------------------
+ ["a", null]
+(1 row)
+
+SELECT json_build_array(VARIADIC NULL::text[]); -- ok
+ json_build_array 
+------------------
+(1 row)
+
+SELECT json_build_array(VARIADIC '{}'::text[]); -- ok
+ json_build_array 
+------------------
+ []
+(1 row)
+
+SELECT json_build_array(VARIADIC '{a,b,c}'::text[]); -- ok
+ json_build_array 
+------------------
+ ["a", "b", "c"]
+(1 row)
+
+SELECT json_build_array(VARIADIC ARRAY['a', NULL]::text[]); -- ok
+ json_build_array 
+------------------
+ ["a", null]
+(1 row)
+
+SELECT json_build_array(VARIADIC '{1,2,3,4}'::text[]); -- ok
+   json_build_array   
+----------------------
+ ["1", "2", "3", "4"]
+(1 row)
+
+SELECT json_build_array(VARIADIC '{1,2,3,4}'::int[]); -- ok
+ json_build_array 
+------------------
+ [1, 2, 3, 4]
+(1 row)
+
+SELECT json_build_array(VARIADIC '{{1,4},{2,5},{3,6}}'::int[][]); -- ok
+  json_build_array  
+--------------------
+ [1, 4, 2, 5, 3, 6]
+(1 row)
+
 SELECT json_build_object('a',1,'b',1.2,'c',true,'d',null,'e',json '{"x": 3, "y": [1,2,3]}');
                              json_build_object                              
 ----------------------------------------------------------------------------
@@ -1435,6 +1483,65 @@ SELECT json_build_object(
  {"a" : {"b" : false, "c" : 99}, "d" : {"e" : [9,8,7], "f" : {"relkind":"r","name":"pg_class"}}}
 (1 row)
 
+SELECT json_build_object('{a,b,c}'::text[]); -- error
+ERROR:  argument list must have even number of elements
+HINT:  The arguments of json_build_object() must consist of alternating keys and values.
+SELECT json_build_object('{a,b,c}'::text[], '{d,e,f}'::text[]); -- error, key cannot be array
+ERROR:  key value must be scalar, not array, composite, or json
+SELECT json_build_object('a', 'b', 'c'); -- error
+ERROR:  argument list must have even number of elements
+HINT:  The arguments of json_build_object() must consist of alternating keys and values.
+SELECT json_build_object(NULL, 'a'); -- error, key cannot be NULL
+ERROR:  argument 1 cannot be null
+HINT:  Object keys should be text.
+SELECT json_build_object('a', NULL); -- ok
+ json_build_object 
+-------------------
+ {"a" : null}
+(1 row)
+
+SELECT json_build_object(VARIADIC NULL::text[]); -- ok
+ json_build_object 
+-------------------
+(1 row)
+
+SELECT json_build_object(VARIADIC '{}'::text[]); -- ok
+ json_build_object 
+-------------------
+ {}
+(1 row)
+
+SELECT json_build_object(VARIADIC '{a,b,c}'::text[]); -- error
+ERROR:  argument list must have even number of elements
+HINT:  The arguments of json_build_object() must consist of alternating keys and values.
+SELECT json_build_object(VARIADIC ARRAY['a', NULL]::text[]); -- ok
+ json_build_object 
+-------------------
+ {"a" : null}
+(1 row)
+
+SELECT json_build_object(VARIADIC ARRAY[NULL, 'a']::text[]); -- error, key cannot be NULL
+ERROR:  argument 1 cannot be null
+HINT:  Object keys should be text.
+SELECT json_build_object(VARIADIC '{1,2,3,4}'::text[]); -- ok
+   json_build_object    
+------------------------
+ {"1" : "2", "3" : "4"}
+(1 row)
+
+SELECT json_build_object(VARIADIC '{1,2,3,4}'::int[]); -- ok
+ json_build_object  
+--------------------
+ {"1" : 2, "3" : 4}
+(1 row)
+
+SELECT json_build_object(VARIADIC '{{1,4},{2,5},{3,6}}'::int[][]); -- ok
+      json_build_object      
+-----------------------------
+ {"1" : 4, "2" : 5, "3" : 6}
+(1 row)
+
 -- empty objects/arrays
 SELECT json_build_array();
  json_build_array 
index 346e5b8363fcd892300e3fec6455382dcea78bf7..587bcb8dd4a3cc2463c8a7daa8eb643633c77bd9 100644 (file)
@@ -412,6 +412,14 @@ select value, json_typeof(value)
 -- json_build_array, json_build_object, json_object_agg
 
 SELECT json_build_array('a',1,'b',1.2,'c',true,'d',null,'e',json '{"x": 3, "y": [1,2,3]}');
+SELECT json_build_array('a', NULL); -- ok
+SELECT json_build_array(VARIADIC NULL::text[]); -- ok
+SELECT json_build_array(VARIADIC '{}'::text[]); -- ok
+SELECT json_build_array(VARIADIC '{a,b,c}'::text[]); -- ok
+SELECT json_build_array(VARIADIC ARRAY['a', NULL]::text[]); -- ok
+SELECT json_build_array(VARIADIC '{1,2,3,4}'::text[]); -- ok
+SELECT json_build_array(VARIADIC '{1,2,3,4}'::int[]); -- ok
+SELECT json_build_array(VARIADIC '{{1,4},{2,5},{3,6}}'::int[][]); -- ok
 
 SELECT json_build_object('a',1,'b',1.2,'c',true,'d',null,'e',json '{"x": 3, "y": [1,2,3]}');
 
@@ -419,6 +427,19 @@ SELECT json_build_object(
        'a', json_build_object('b',false,'c',99),
        'd', json_build_object('e',array[9,8,7]::int[],
            'f', (select row_to_json(r) from ( select relkind, oid::regclass as name from pg_class where relname = 'pg_class') r)));
+SELECT json_build_object('{a,b,c}'::text[]); -- error
+SELECT json_build_object('{a,b,c}'::text[], '{d,e,f}'::text[]); -- error, key cannot be array
+SELECT json_build_object('a', 'b', 'c'); -- error
+SELECT json_build_object(NULL, 'a'); -- error, key cannot be NULL
+SELECT json_build_object('a', NULL); -- ok
+SELECT json_build_object(VARIADIC NULL::text[]); -- ok
+SELECT json_build_object(VARIADIC '{}'::text[]); -- ok
+SELECT json_build_object(VARIADIC '{a,b,c}'::text[]); -- error
+SELECT json_build_object(VARIADIC ARRAY['a', NULL]::text[]); -- ok
+SELECT json_build_object(VARIADIC ARRAY[NULL, 'a']::text[]); -- error, key cannot be NULL
+SELECT json_build_object(VARIADIC '{1,2,3,4}'::text[]); -- ok
+SELECT json_build_object(VARIADIC '{1,2,3,4}'::int[]); -- ok
+SELECT json_build_object(VARIADIC '{{1,4},{2,5},{3,6}}'::int[][]); -- ok
 
 
 -- empty objects/arrays