diff options
Diffstat (limited to 'src/backend/utils/adt/jsonb.c')
-rw-r--r-- | src/backend/utils/adt/jsonb.c | 352 |
1 files changed, 75 insertions, 277 deletions
diff --git a/src/backend/utils/adt/jsonb.c b/src/backend/utils/adt/jsonb.c index f700c5b4c93..88b0000f9a9 100644 --- a/src/backend/utils/adt/jsonb.c +++ b/src/backend/utils/adt/jsonb.c @@ -14,7 +14,6 @@ #include "access/htup_details.h" #include "access/transam.h" -#include "catalog/pg_proc.h" #include "catalog/pg_type.h" #include "funcapi.h" #include "libpq/pqformat.h" @@ -34,9 +33,25 @@ typedef struct JsonbInState { JsonbParseState *parseState; JsonbValue *res; - bool unique_keys; } JsonbInState; +/* unlike with json categories, we need to treat json and jsonb differently */ +typedef enum /* type categories for datum_to_jsonb */ +{ + JSONBTYPE_NULL, /* null, so we didn't bother to identify */ + JSONBTYPE_BOOL, /* boolean (built-in types only) */ + JSONBTYPE_NUMERIC, /* numeric (ditto) */ + JSONBTYPE_DATE, /* we use special formatting for datetimes */ + JSONBTYPE_TIMESTAMP, /* we use special formatting for timestamp */ + JSONBTYPE_TIMESTAMPTZ, /* ... and timestamptz */ + JSONBTYPE_JSON, /* JSON */ + JSONBTYPE_JSONB, /* JSONB */ + JSONBTYPE_ARRAY, /* array */ + JSONBTYPE_COMPOSITE, /* composite */ + JSONBTYPE_JSONCAST, /* something with an explicit cast to JSON */ + JSONBTYPE_OTHER /* all else */ +} JsonbTypeCategory; + typedef struct JsonbAggState { JsonbInState *res; @@ -46,7 +61,7 @@ typedef struct JsonbAggState Oid val_output_func; } JsonbAggState; -static inline Datum jsonb_from_cstring(char *json, int len, bool unique_keys); +static inline Datum jsonb_from_cstring(char *json, int len); static size_t checkStringLen(size_t len); static void jsonb_in_object_start(void *pstate); static void jsonb_in_object_end(void *pstate); @@ -55,11 +70,17 @@ static void jsonb_in_array_end(void *pstate); static void jsonb_in_object_field_start(void *pstate, char *fname, bool isnull); static void jsonb_put_escaped_value(StringInfo out, JsonbValue *scalarVal); static void jsonb_in_scalar(void *pstate, char *token, JsonTokenType tokentype); +static void jsonb_categorize_type(Oid typoid, + JsonbTypeCategory *tcategory, + Oid *outfuncoid); static void composite_to_jsonb(Datum composite, JsonbInState *result); static void array_dim_to_jsonb(JsonbInState *result, int dim, int ndims, int *dims, Datum *vals, bool *nulls, int *valcount, JsonbTypeCategory tcategory, Oid outfuncoid); static void array_to_jsonb_internal(Datum array, JsonbInState *result); +static void jsonb_categorize_type(Oid typoid, + JsonbTypeCategory *tcategory, + Oid *outfuncoid); static void datum_to_jsonb(Datum val, bool is_null, JsonbInState *result, JsonbTypeCategory tcategory, Oid outfuncoid, bool key_scalar); @@ -77,7 +98,7 @@ jsonb_in(PG_FUNCTION_ARGS) { char *json = PG_GETARG_CSTRING(0); - return jsonb_from_cstring(json, strlen(json), false); + return jsonb_from_cstring(json, strlen(json)); } /* @@ -101,7 +122,7 @@ jsonb_recv(PG_FUNCTION_ARGS) else elog(ERROR, "unsupported jsonb version number %d", version); - return jsonb_from_cstring(str, nbytes, false); + return jsonb_from_cstring(str, nbytes); } /* @@ -142,14 +163,6 @@ jsonb_send(PG_FUNCTION_ARGS) PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); } -Datum -jsonb_from_text(text *js, bool unique_keys) -{ - return jsonb_from_cstring(VARDATA_ANY(js), - VARSIZE_ANY_EXHDR(js), - unique_keys); -} - /* * Get the type name of a jsonb container. */ @@ -240,7 +253,7 @@ jsonb_typeof(PG_FUNCTION_ARGS) * Uses the json parser (with hooks) to construct a jsonb. */ static inline Datum -jsonb_from_cstring(char *json, int len, bool unique_keys) +jsonb_from_cstring(char *json, int len) { JsonLexContext *lex; JsonbInState state; @@ -250,8 +263,6 @@ jsonb_from_cstring(char *json, int len, bool unique_keys) memset(&sem, 0, sizeof(sem)); lex = makeJsonLexContextCstringLen(json, len, GetDatabaseEncoding(), true); - state.unique_keys = unique_keys; - sem.semstate = (void *) &state; sem.object_start = jsonb_in_object_start; @@ -286,7 +297,6 @@ jsonb_in_object_start(void *pstate) JsonbInState *_state = (JsonbInState *) pstate; _state->res = pushJsonbValue(&_state->parseState, WJB_BEGIN_OBJECT, NULL); - _state->parseState->unique_keys = _state->unique_keys; } static void @@ -609,7 +619,7 @@ add_indent(StringInfo out, bool indent, int level) * output function OID. If the returned category is JSONBTYPE_JSONCAST, * we return the OID of the relevant cast function instead. */ -void +static void jsonb_categorize_type(Oid typoid, JsonbTypeCategory *tcategory, Oid *outfuncoid) @@ -1115,51 +1125,6 @@ add_jsonb(Datum val, bool is_null, JsonbInState *result, datum_to_jsonb(val, is_null, result, tcategory, outfuncoid, key_scalar); } -Datum -to_jsonb_worker(Datum val, JsonbTypeCategory tcategory, Oid outfuncoid) -{ - JsonbInState result; - - memset(&result, 0, sizeof(JsonbInState)); - - datum_to_jsonb(val, false, &result, tcategory, outfuncoid, false); - - return JsonbPGetDatum(JsonbValueToJsonb(result.res)); -} - -bool -to_jsonb_is_immutable(Oid typoid) -{ - JsonbTypeCategory tcategory; - Oid outfuncoid; - - jsonb_categorize_type(typoid, &tcategory, &outfuncoid); - - switch (tcategory) - { - case JSONBTYPE_BOOL: - case JSONBTYPE_JSON: - case JSONBTYPE_JSONB: - return true; - - case JSONBTYPE_DATE: - case JSONBTYPE_TIMESTAMP: - case JSONBTYPE_TIMESTAMPTZ: - return false; - - case JSONBTYPE_ARRAY: - return false; /* TODO recurse into elements */ - - case JSONBTYPE_COMPOSITE: - return false; /* TODO recurse into fields */ - - case JSONBTYPE_NUMERIC: - case JSONBTYPE_JSONCAST: - default: - return func_volatile(outfuncoid) == PROVOLATILE_IMMUTABLE; - } -} - /* * SQL function to_jsonb(anyvalue) */ @@ -1168,6 +1133,7 @@ to_jsonb(PG_FUNCTION_ARGS) { Datum val = PG_GETARG_DATUM(0); Oid val_type = get_fn_expr_argtype(fcinfo->flinfo, 0); + JsonbInState result; JsonbTypeCategory tcategory; Oid outfuncoid; @@ -1179,15 +1145,31 @@ to_jsonb(PG_FUNCTION_ARGS) jsonb_categorize_type(val_type, &tcategory, &outfuncoid); - PG_RETURN_DATUM(to_jsonb_worker(val, tcategory, outfuncoid)); + memset(&result, 0, sizeof(JsonbInState)); + + datum_to_jsonb(val, false, &result, tcategory, outfuncoid, false); + + PG_RETURN_POINTER(JsonbValueToJsonb(result.res)); } +/* + * SQL function jsonb_build_object(variadic "any") + */ Datum -jsonb_build_object_worker(int nargs, Datum *args, bool *nulls, Oid *types, - bool absent_on_null, bool unique_keys) +jsonb_build_object(PG_FUNCTION_ARGS) { + int nargs; int i; JsonbInState result; + Datum *args; + bool *nulls; + Oid *types; + + /* build argument values to build the object */ + nargs = extract_variadic_args(fcinfo, 0, true, &args, &types, &nulls); + + if (nargs < 0) + PG_RETURN_NULL(); if (nargs % 2 != 0) ereport(ERROR, @@ -1200,26 +1182,15 @@ jsonb_build_object_worker(int nargs, Datum *args, bool *nulls, Oid *types, memset(&result, 0, sizeof(JsonbInState)); result.res = pushJsonbValue(&result.parseState, WJB_BEGIN_OBJECT, NULL); - result.parseState->unique_keys = unique_keys; - result.parseState->skip_nulls = absent_on_null; for (i = 0; i < nargs; i += 2) { /* process key */ - bool skip; - if (nulls[i]) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("argument %d: key must not be null", i + 1))); - /* skip null values if absent_on_null */ - skip = absent_on_null && nulls[i + 1]; - - /* we need to save skipped keys for the key uniqueness check */ - if (skip && !unique_keys) - continue; - add_jsonb(args[i], false, &result, types[i], true); /* process value */ @@ -1228,27 +1199,7 @@ jsonb_build_object_worker(int nargs, Datum *args, bool *nulls, Oid *types, result.res = pushJsonbValue(&result.parseState, WJB_END_OBJECT, NULL); - return JsonbPGetDatum(JsonbValueToJsonb(result.res)); -} - -/* - * SQL function jsonb_build_object(variadic "any") - */ -Datum -jsonb_build_object(PG_FUNCTION_ARGS) -{ - Datum *args; - bool *nulls; - Oid *types; - - /* build argument values to build the object */ - int nargs = extract_variadic_args(fcinfo, 0, true, - &args, &types, &nulls); - - if (nargs < 0) - PG_RETURN_NULL(); - - PG_RETURN_DATUM(jsonb_build_object_worker(nargs, args, nulls, types, false, false)); + PG_RETURN_POINTER(JsonbValueToJsonb(result.res)); } /* @@ -1267,50 +1218,36 @@ jsonb_build_object_noargs(PG_FUNCTION_ARGS) PG_RETURN_POINTER(JsonbValueToJsonb(result.res)); } -Datum -jsonb_build_array_worker(int nargs, Datum *args, bool *nulls, Oid *types, - bool absent_on_null) -{ - int i; - JsonbInState result; - - memset(&result, 0, sizeof(JsonbInState)); - - result.res = pushJsonbValue(&result.parseState, WJB_BEGIN_ARRAY, NULL); - - for (i = 0; i < nargs; i++) - { - if (absent_on_null && nulls[i]) - continue; - - add_jsonb(args[i], nulls[i], &result, types[i], false); - } - - result.res = pushJsonbValue(&result.parseState, WJB_END_ARRAY, NULL); - - return JsonbPGetDatum(JsonbValueToJsonb(result.res)); -} - /* * SQL function jsonb_build_array(variadic "any") */ Datum jsonb_build_array(PG_FUNCTION_ARGS) { + int nargs; + int i; + JsonbInState result; Datum *args; bool *nulls; Oid *types; - /* build argument values to build the object */ - int nargs = extract_variadic_args(fcinfo, 0, true, - &args, &types, &nulls); + /* build argument values to build the array */ + nargs = extract_variadic_args(fcinfo, 0, true, &args, &types, &nulls); if (nargs < 0) PG_RETURN_NULL(); - PG_RETURN_DATUM(jsonb_build_array_worker(nargs, args, nulls, types, false)); -} + memset(&result, 0, sizeof(JsonbInState)); + + result.res = pushJsonbValue(&result.parseState, WJB_BEGIN_ARRAY, NULL); + for (i = 0; i < nargs; i++) + add_jsonb(args[i], nulls[i], &result, types[i], false); + + result.res = pushJsonbValue(&result.parseState, WJB_END_ARRAY, NULL); + + PG_RETURN_POINTER(JsonbValueToJsonb(result.res)); +} /* * degenerate case of jsonb_build_array where it gets 0 arguments. @@ -1545,8 +1482,6 @@ clone_parse_state(JsonbParseState *state) { ocursor->contVal = icursor->contVal; ocursor->size = icursor->size; - ocursor->unique_keys = icursor->unique_keys; - ocursor->skip_nulls = icursor->skip_nulls; icursor = icursor->next; if (icursor == NULL) break; @@ -1558,8 +1493,12 @@ clone_parse_state(JsonbParseState *state) return result; } -static Datum -jsonb_agg_transfn_worker(FunctionCallInfo fcinfo, bool absent_on_null) + +/* + * jsonb_agg aggregate function + */ +Datum +jsonb_agg_transfn(PG_FUNCTION_ARGS) { MemoryContext oldcontext, aggcontext; @@ -1607,9 +1546,6 @@ jsonb_agg_transfn_worker(FunctionCallInfo fcinfo, bool absent_on_null) result = state->res; } - if (absent_on_null && PG_ARGISNULL(1)) - PG_RETURN_POINTER(state); - /* turn the argument into jsonb in the normal function context */ val = PG_ARGISNULL(1) ? (Datum) 0 : PG_GETARG_DATUM(1); @@ -1679,24 +1615,6 @@ jsonb_agg_transfn_worker(FunctionCallInfo fcinfo, bool absent_on_null) PG_RETURN_POINTER(state); } -/* - * jsonb_agg aggregate function - */ -Datum -jsonb_agg_transfn(PG_FUNCTION_ARGS) -{ - return jsonb_agg_transfn_worker(fcinfo, false); -} - -/* - * jsonb_agg_strict aggregate function - */ -Datum -jsonb_agg_strict_transfn(PG_FUNCTION_ARGS) -{ - return jsonb_agg_transfn_worker(fcinfo, true); -} - Datum jsonb_agg_finalfn(PG_FUNCTION_ARGS) { @@ -1729,9 +1647,11 @@ jsonb_agg_finalfn(PG_FUNCTION_ARGS) PG_RETURN_POINTER(out); } -static Datum -jsonb_object_agg_transfn_worker(FunctionCallInfo fcinfo, - bool absent_on_null, bool unique_keys) +/* + * jsonb_object_agg aggregate function + */ +Datum +jsonb_object_agg_transfn(PG_FUNCTION_ARGS) { MemoryContext oldcontext, aggcontext; @@ -1745,7 +1665,6 @@ jsonb_object_agg_transfn_worker(FunctionCallInfo fcinfo, *jbval; JsonbValue v; JsonbIteratorToken type; - bool skip; if (!AggCheckCallContext(fcinfo, &aggcontext)) { @@ -1765,9 +1684,6 @@ jsonb_object_agg_transfn_worker(FunctionCallInfo fcinfo, state->res = result; result->res = pushJsonbValue(&result->parseState, WJB_BEGIN_OBJECT, NULL); - result->parseState->unique_keys = unique_keys; - result->parseState->skip_nulls = absent_on_null; - MemoryContextSwitchTo(oldcontext); arg_type = get_fn_expr_argtype(fcinfo->flinfo, 1); @@ -1803,15 +1719,6 @@ jsonb_object_agg_transfn_worker(FunctionCallInfo fcinfo, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("field name must not be null"))); - /* - * Skip null values if absent_on_null unless key uniqueness check is - * needed (because we must save keys in this case). - */ - skip = absent_on_null && PG_ARGISNULL(2); - - if (skip && !unique_keys) - PG_RETURN_POINTER(state); - val = PG_GETARG_DATUM(1); memset(&elem, 0, sizeof(JsonbInState)); @@ -1867,16 +1774,6 @@ jsonb_object_agg_transfn_worker(FunctionCallInfo fcinfo, } result->res = pushJsonbValue(&result->parseState, WJB_KEY, &v); - - if (skip) - { - v.type = jbvNull; - result->res = pushJsonbValue(&result->parseState, - WJB_VALUE, &v); - MemoryContextSwitchTo(oldcontext); - PG_RETURN_POINTER(state); - } - break; case WJB_END_ARRAY: break; @@ -1949,43 +1846,6 @@ jsonb_object_agg_transfn_worker(FunctionCallInfo fcinfo, PG_RETURN_POINTER(state); } -/* - * jsonb_object_agg aggregate function - */ -Datum -jsonb_object_agg_transfn(PG_FUNCTION_ARGS) -{ - return jsonb_object_agg_transfn_worker(fcinfo, false, false); -} - - -/* - * jsonb_object_agg_strict aggregate function - */ -Datum -jsonb_object_agg_strict_transfn(PG_FUNCTION_ARGS) -{ - return jsonb_object_agg_transfn_worker(fcinfo, true, false); -} - -/* - * jsonb_object_agg_unique aggregate function - */ -Datum -jsonb_object_agg_unique_transfn(PG_FUNCTION_ARGS) -{ - return jsonb_object_agg_transfn_worker(fcinfo, false, true); -} - -/* - * jsonb_object_agg_unique_strict aggregate function - */ -Datum -jsonb_object_agg_unique_strict_transfn(PG_FUNCTION_ARGS) -{ - return jsonb_object_agg_transfn_worker(fcinfo, true, true); -} - Datum jsonb_object_agg_finalfn(PG_FUNCTION_ARGS) { @@ -2217,65 +2077,3 @@ jsonb_float8(PG_FUNCTION_ARGS) PG_RETURN_DATUM(retValue); } - -/* - * Construct an empty array jsonb. - */ -Jsonb * -JsonbMakeEmptyArray(void) -{ - JsonbValue jbv; - - jbv.type = jbvArray; - jbv.val.array.elems = NULL; - jbv.val.array.nElems = 0; - jbv.val.array.rawScalar = false; - - return JsonbValueToJsonb(&jbv); -} - -/* - * Construct an empty object jsonb. - */ -Jsonb * -JsonbMakeEmptyObject(void) -{ - JsonbValue jbv; - - jbv.type = jbvObject; - jbv.val.object.pairs = NULL; - jbv.val.object.nPairs = 0; - - return JsonbValueToJsonb(&jbv); -} - -/* - * Convert jsonb to a C-string stripping quotes from scalar strings. - */ -char * -JsonbUnquote(Jsonb *jb) -{ - if (JB_ROOT_IS_SCALAR(jb)) - { - JsonbValue v; - - (void) JsonbExtractScalar(&jb->root, &v); - - if (v.type == jbvString) - return pnstrdup(v.val.string.val, v.val.string.len); - else if (v.type == jbvBool) - return pstrdup(v.val.boolean ? "true" : "false"); - else if (v.type == jbvNumeric) - return DatumGetCString(DirectFunctionCall1(numeric_out, - PointerGetDatum(v.val.numeric))); - else if (v.type == jbvNull) - return pstrdup("null"); - else - { - elog(ERROR, "unrecognized jsonb value type %d", v.type); - return NULL; - } - } - else - return JsonbToCString(NULL, &jb->root, VARSIZE(jb)); -} |