/*------------------------------------------------------------------------- * * json_op.c * Manipulation procedures for JSON data type. * * Copyright (c) 2010, PostgreSQL Global Development Group * Written by Joey Adams . * *------------------------------------------------------------------------- */ #include "json.h" #include "jsonpath.h" #include "util.h" static EnumLabel enum_labels[JSON_TYPE_COUNT] = { {JSON_NULL, "null"}, {JSON_STRING, "string"}, {JSON_NUMBER, "number"}, {JSON_BOOL, "bool"}, {JSON_OBJECT, "object"}, {JSON_ARRAY, "array"} }; /* json_validate(text). Renamed to avoid clashing * with the C function json_validate. */ PG_FUNCTION_INFO_V1(json_validate_f); Datum json_validate_f(PG_FUNCTION_ARGS); Datum json_validate_f(PG_FUNCTION_ARGS) { char *string; bool ret; string = text_to_utf8_cstring(PG_GETARG_JSON_P(0)); ret = json_validate(string); pfree(string); PG_RETURN_BOOL(ret); } /* json_type(json). Renamed to avoid clashing * with the typedef enum of the same name. */ PG_FUNCTION_INFO_V1(json_get_type); Datum json_get_type(PG_FUNCTION_ARGS); Datum json_get_type(PG_FUNCTION_ARGS) { jsontype *t = PG_GETARG_JSON_P(0); json_type type; Oid *label_oids; /* * No need to convert to UTF-8 before calling json_text_type, as it looks * solely at ASCII characters. */ type = json_text_type(VARDATA_ANY(t), VARSIZE_ANY_EXHDR(t)); if (!json_type_is_valid(type)) report_corrupt_json(); label_oids = fcinfo->flinfo->fn_extra; if (label_oids == NULL) { label_oids = fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, JSON_TYPE_COUNT * sizeof(Oid)); getEnumLabelOids("json_type", enum_labels, label_oids, JSON_TYPE_COUNT); } PG_RETURN_OID(label_oids[type]); } PG_FUNCTION_INFO_V1(json_condense); Datum json_condense(PG_FUNCTION_ARGS); Datum json_condense(PG_FUNCTION_ARGS) { char *string; JSON *json; char *condensed; string = text_to_utf8_cstring(PG_GETARG_JSON_P(0)); json = json_decode(string); condensed = json_encode(json, 0); if (condensed == NULL) report_corrupt_json(); PG_RETURN_JSON_P(utf8_cstring_to_text(condensed)); } /* * json_path_base * Given the function call info of a PostgreSQL function * with arguments (JSON, jsonpath TEXT [, ...]), * match the JSONPath string given in the second argument * against the JSON tree given in the first argument. * * This returns a List of JPRef* items, just like jp_match. * jpref_encode() is used to convert them to JSON-formatted strings. */ static List * json_path_base(FunctionCallInfo fcinfo) { char *json_string = text_to_utf8_cstring(PG_GETARG_JSON_P(0)); char *path_string = text_to_utf8_cstring(PG_GETARG_TEXT_PP(1)); JSONPath *jpath = jp_parse(path_string); JSON *json = json_decode(json_string); List *result_list; if (jpath == NULL) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid JSONPath expression"))); if (json == NULL) report_corrupt_json(); result_list = jp_match(jpath, json); return result_list; } PG_FUNCTION_INFO_V1(json_get); Datum json_get(PG_FUNCTION_ARGS); Datum json_get(PG_FUNCTION_ARGS) { List *result_list = json_path_base(fcinfo); ListCell *result; char *rs_json; jsontype *result_vardata; int length = list_length(result_list); if (length == 0) { PG_RETURN_NULL(); } else if (length == 1) { result = list_head(result_list); rs_json = jpref_encode(lfirst(result)); Assert(rs_json != NULL); Assert(json_validate(rs_json) == true); result_vardata = (jsontype *) utf8_cstring_to_text(rs_json); pfree(rs_json); PG_RETURN_JSON_P(result_vardata); } else { ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("JSONPath expression matched multiple results"))); PG_RETURN_NULL(); } } PG_FUNCTION_INFO_V1(json_set); Datum json_set(PG_FUNCTION_ARGS); Datum json_set(PG_FUNCTION_ARGS) { char *json_string = text_to_utf8_cstring(PG_GETARG_JSON_P(0)); char *path_string = text_to_utf8_cstring(PG_GETARG_TEXT_PP(1)); char *rvalue_string = text_to_utf8_cstring(PG_GETARG_JSON_P(2)); JSONPath *jpath = jp_parse(path_string); JSON *json = json_decode(json_string); JSON *rvalue = json_decode(rvalue_string); char *result; jsontype *result_text; if (jpath == NULL) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid JSONPath expression"))); if (json == NULL || rvalue == NULL) report_corrupt_json(); jp_set(jpath, json, rvalue); result = json_encode(json, JSONOPT_USE_ORIG | JSONOPT_NO_TRIM); result_text = utf8_cstring_to_text(result); pfree(result); PG_RETURN_JSON_P(result_text); } PG_FUNCTION_INFO_V1(json_path); Datum json_path(PG_FUNCTION_ARGS); Datum json_path(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; ListCell *result; /* TODO: consider returning the entire result set in one call. */ if (SRF_IS_FIRSTCALL()) { MemoryContext oldcontext; List *results; funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); results = json_path_base(fcinfo); funcctx->user_fctx = list_head(results); MemoryContextSwitchTo(oldcontext); } funcctx = SRF_PERCALL_SETUP(); result = funcctx->user_fctx; if (result != NULL) { char *json_string; jsontype *json_text; json_string = jpref_encode(lfirst(result)); Assert(json_string != NULL); Assert(json_validate(json_string) == true); json_text = utf8_cstring_to_text(json_string); funcctx->user_fctx = lnext(result); SRF_RETURN_NEXT(funcctx, JSONPGetDatum(json_text)); } SRF_RETURN_DONE(funcctx); } PG_FUNCTION_INFO_V1(parse_json_path); Datum parse_json_path(PG_FUNCTION_ARGS); Datum parse_json_path(PG_FUNCTION_ARGS) { char *string = text_to_utf8_cstring(PG_GETARG_TEXT_PP(0)); JSONPath *jpath = jp_parse(string); char *normalized; if (jpath == NULL) PG_RETURN_NULL(); normalized = jp_show(jpath); PG_RETURN_TEXT_P(utf8_cstring_to_text(normalized)); }