From 2591e623f5fc80a49347fb5e106aa1e03f713429 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Sat, 9 Aug 2014 17:31:16 -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 9216511931..68132aea4b 100644 --- a/src/backend/utils/adt/json.c +++ b/src/backend/utils/adt/json.c @@ -401,7 +401,7 @@ parse_object(JsonLexContext *lex, JsonSemAction *sem) { /* * an object is a possibly empty sequence of object fields, separated by - * commas and surrounde by curly braces. + * commas and surrounded by curly braces. */ json_struct_action ostart = sem->object_start; json_struct_action oend = sem->object_end; @@ -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, ", "); } - 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. + * 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. */ - 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); - } + val_type = get_fn_expr_argtype(fcinfo->flinfo, 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", 1))); + if (PG_ARGISNULL(1)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("field name must not be null"))); + + 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)); - } /* -- 2.40.0