From 92f57c9ae95668636bc77d3cda3edbdfd83320d9 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Sat, 9 Aug 2014 17:31:13 -0400 Subject: [PATCH] Clean up handling of unknown-type inputs in json_build_object and friends. There's actually no need for any special case for unknown-type literals, since we only need to push the value through its output function and unknownout() works fine. The code that was here was completely bizarre anyway, and would fail outright in cases that should work, not to mention suffering from some copy-and-paste bugs. --- src/backend/utils/adt/json.c | 168 +++++++++++++++-------------------- 1 file changed, 73 insertions(+), 95 deletions(-) diff --git a/src/backend/utils/adt/json.c b/src/backend/utils/adt/json.c index d1dc87e679..68132aea4b 100644 --- a/src/backend/utils/adt/json.c +++ b/src/backend/utils/adt/json.c @@ -1869,10 +1869,10 @@ json_object_agg_transfn(PG_FUNCTION_ARGS) if (PG_ARGISNULL(0)) { /* - * Make this StringInfo in a context where it will persist for the - * duration off the aggregate call. It's only needed for this initial - * piece, as the StringInfo routines make sure they use the right - * context to enlarge the object if necessary. + * Make the StringInfo in a context where it will persist for the + * duration of the aggregate call. Switching context is only needed + * for this initial step, as the StringInfo routines make sure they + * use the right context to enlarge the object if necessary. */ oldcontext = MemoryContextSwitchTo(aggcontext); state = makeStringInfo(); @@ -1886,56 +1886,43 @@ json_object_agg_transfn(PG_FUNCTION_ARGS) appendStringInfoString(state, ", "); } + /* + * Note: since json_object_agg() 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. + */ + val_type = get_fn_expr_argtype(fcinfo->flinfo, 1); + + if (val_type == InvalidOid) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("could not determine data type for argument %d", 1))); + if (PG_ARGISNULL(1)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("field name must not be null"))); - val_type = get_fn_expr_argtype(fcinfo->flinfo, 1); - - /* - * turn a constant (more or less literal) value that's of unknown type - * into text. Unknowns come in as a cstring pointer. - */ - if (val_type == UNKNOWNOID && get_fn_expr_arg_stable(fcinfo->flinfo, 1)) - { - val_type = TEXTOID; - arg = CStringGetTextDatum(PG_GETARG_POINTER(1)); - } - else - { - arg = PG_GETARG_DATUM(1); - } - - if (val_type == InvalidOid || val_type == UNKNOWNOID) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("could not determine data type for argument %d", 1))); + arg = PG_GETARG_DATUM(1); add_json(arg, false, state, val_type, true); appendStringInfoString(state, " : "); val_type = get_fn_expr_argtype(fcinfo->flinfo, 2); - /* see comments above */ - if (val_type == UNKNOWNOID && get_fn_expr_arg_stable(fcinfo->flinfo, 2)) - { - val_type = TEXTOID; - if (PG_ARGISNULL(2)) - arg = (Datum) 0; - else - arg = CStringGetTextDatum(PG_GETARG_POINTER(2)); - } - else - { - arg = PG_GETARG_DATUM(2); - } - if (val_type == InvalidOid || val_type == UNKNOWNOID) + if (val_type == InvalidOid) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("could not determine data type for argument %d", 2))); + if (PG_ARGISNULL(2)) + arg = (Datum) 0; + else + arg = PG_GETARG_DATUM(2); + add_json(arg, PG_ARGISNULL(2), state, val_type, false); PG_RETURN_POINTER(state); @@ -1972,7 +1959,7 @@ json_build_object(PG_FUNCTION_ARGS) int nargs = PG_NARGS(); int i; Datum arg; - char *sep = ""; + const char *sep = ""; StringInfo result; Oid val_type; @@ -1988,60 +1975,51 @@ 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)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("argument %d cannot be null", i + 1), errhint("Object keys should be text."))); - val_type = get_fn_expr_argtype(fcinfo->flinfo, i); - /* - * turn a constant (more or less literal) value that's of unknown type - * into text. Unknowns come in as a cstring pointer. - */ - if (val_type == UNKNOWNOID && get_fn_expr_arg_stable(fcinfo->flinfo, i)) - { - val_type = TEXTOID; - arg = CStringGetTextDatum(PG_GETARG_POINTER(i)); - } - else - { - arg = PG_GETARG_DATUM(i); - } - if (val_type == InvalidOid || val_type == UNKNOWNOID) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("could not determine data type for argument %d", - i + 1))); - appendStringInfoString(result, sep); - sep = ", "; + arg = PG_GETARG_DATUM(i); + add_json(arg, false, result, val_type, true); appendStringInfoString(result, " : "); /* process value */ - val_type = get_fn_expr_argtype(fcinfo->flinfo, i + 1); - /* see comments above */ - if (val_type == UNKNOWNOID && get_fn_expr_arg_stable(fcinfo->flinfo, i + 1)) - { - val_type = TEXTOID; - if (PG_ARGISNULL(i + 1)) - arg = (Datum) 0; - else - arg = CStringGetTextDatum(PG_GETARG_POINTER(i + 1)); - } - else - { - arg = PG_GETARG_DATUM(i + 1); - } - if (val_type == InvalidOid || val_type == UNKNOWNOID) + + 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); } @@ -2068,45 +2046,45 @@ json_build_array(PG_FUNCTION_ARGS) int nargs = PG_NARGS(); int i; Datum arg; - char *sep = ""; + const char *sep = ""; StringInfo result; Oid val_type; - result = makeStringInfo(); appendStringInfoChar(result, '['); 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); - arg = PG_GETARG_DATUM(i + 1); - /* see comments in json_build_object above */ - if (val_type == UNKNOWNOID && get_fn_expr_arg_stable(fcinfo->flinfo, i)) - { - val_type = TEXTOID; - if (PG_ARGISNULL(i)) - arg = (Datum) 0; - else - arg = CStringGetTextDatum(PG_GETARG_POINTER(i)); - } - else - { - arg = PG_GETARG_DATUM(i); - } - if (val_type == InvalidOid || val_type == UNKNOWNOID) + + if (val_type == InvalidOid) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("could not determine data type for argument %d", i + 1))); - appendStringInfoString(result, sep); - sep = ", "; + + if (PG_ARGISNULL(i)) + arg = (Datum) 0; + else + arg = PG_GETARG_DATUM(i); + add_json(arg, PG_ARGISNULL(i), result, val_type, false); } + appendStringInfoChar(result, ']'); PG_RETURN_TEXT_P(cstring_to_text_with_len(result->data, result->len)); - } /*