diff options
author | Andrew Dunstan <andrew@dunslane.net> | 2022-09-01 17:07:14 -0400 |
---|---|---|
committer | Andrew Dunstan <andrew@dunslane.net> | 2022-09-01 17:07:14 -0400 |
commit | 2f2b18bd3f554e96a8cc885b177211be12288e4a (patch) | |
tree | 344a5d33738de735f68b98361a54eb5944726f8f /src/backend/utils/adt | |
parent | 90247e742f849794d061a0444071647728054b45 (diff) | |
download | postgresql-2f2b18bd3f554e96a8cc885b177211be12288e4a.tar.gz postgresql-2f2b18bd3f554e96a8cc885b177211be12288e4a.zip |
Revert SQL/JSON features
The reverts the following and makes some associated cleanups:
commit f79b803dc: Common SQL/JSON clauses
commit f4fb45d15: SQL/JSON constructors
commit 5f0adec25: Make STRING an unreserved_keyword.
commit 33a377608: IS JSON predicate
commit 1a36bc9db: SQL/JSON query functions
commit 606948b05: SQL JSON functions
commit 49082c2cc: RETURNING clause for JSON() and JSON_SCALAR()
commit 4e34747c8: JSON_TABLE
commit fadb48b00: PLAN clauses for JSON_TABLE
commit 2ef6f11b0: Reduce running time of jsonb_sqljson test
commit 14d3f24fa: Further improve jsonb_sqljson parallel test
commit a6baa4bad: Documentation for SQL/JSON features
commit b46bcf7a4: Improve readability of SQL/JSON documentation.
commit 112fdb352: Fix finalization for json_objectagg and friends
commit fcdb35c32: Fix transformJsonBehavior
commit 4cd8717af: Improve a couple of sql/json error messages
commit f7a605f63: Small cleanups in SQL/JSON code
commit 9c3d25e17: Fix JSON_OBJECTAGG uniquefying bug
commit a79153b7a: Claim SQL standard compliance for SQL/JSON features
commit a1e7616d6: Rework SQL/JSON documentation
commit 8d9f9634e: Fix errors in copyfuncs/equalfuncs support for JSON node types.
commit 3c633f32b: Only allow returning string types or bytea from json_serialize
commit 67b26703b: expression eval: Fix EEOP_JSON_CONSTRUCTOR and EEOP_JSONEXPR size.
The release notes are also adjusted.
Backpatch to release 15.
Discussion: https://postgr.es/m/40d2c882-bcac-19a9-754d-4299e1d87ac7@postgresql.org
Diffstat (limited to 'src/backend/utils/adt')
-rw-r--r-- | src/backend/utils/adt/format_type.c | 4 | ||||
-rw-r--r-- | src/backend/utils/adt/formatting.c | 45 | ||||
-rw-r--r-- | src/backend/utils/adt/json.c | 553 | ||||
-rw-r--r-- | src/backend/utils/adt/jsonb.c | 352 | ||||
-rw-r--r-- | src/backend/utils/adt/jsonb_util.c | 39 | ||||
-rw-r--r-- | src/backend/utils/adt/jsonfuncs.c | 71 | ||||
-rw-r--r-- | src/backend/utils/adt/jsonpath.c | 257 | ||||
-rw-r--r-- | src/backend/utils/adt/jsonpath_exec.c | 844 | ||||
-rw-r--r-- | src/backend/utils/adt/ruleutils.c | 719 |
9 files changed, 231 insertions, 2653 deletions
diff --git a/src/backend/utils/adt/format_type.c b/src/backend/utils/adt/format_type.c index 060fd7e183b..2918fdbfb65 100644 --- a/src/backend/utils/adt/format_type.c +++ b/src/backend/utils/adt/format_type.c @@ -294,10 +294,6 @@ format_type_extended(Oid type_oid, int32 typemod, bits16 flags) else buf = pstrdup("character varying"); break; - - case JSONOID: - buf = pstrdup("json"); - break; } if (buf == NULL) diff --git a/src/backend/utils/adt/formatting.c b/src/backend/utils/adt/formatting.c index 6f8734a947d..26f498b5df4 100644 --- a/src/backend/utils/adt/formatting.c +++ b/src/backend/utils/adt/formatting.c @@ -1045,6 +1045,11 @@ typedef struct NUMProc *L_currency_symbol; } NUMProc; +/* Return flags for DCH_from_char() */ +#define DCH_DATED 0x01 +#define DCH_TIMED 0x02 +#define DCH_ZONED 0x04 + /* ---------- * Functions * ---------- @@ -6707,43 +6712,3 @@ float8_to_char(PG_FUNCTION_ARGS) NUM_TOCHAR_finish; PG_RETURN_TEXT_P(result); } - -int -datetime_format_flags(const char *fmt_str, bool *have_error) -{ - bool incache; - int fmt_len = strlen(fmt_str); - int result; - FormatNode *format; - - if (fmt_len > DCH_CACHE_SIZE) - { - /* - * Allocate new memory if format picture is bigger than static cache - * and do not use cache (call parser always) - */ - incache = false; - - format = (FormatNode *) palloc((fmt_len + 1) * sizeof(FormatNode)); - - parse_format(format, fmt_str, DCH_keywords, - DCH_suff, DCH_index, DCH_FLAG, NULL); - } - else - { - /* - * Use cache buffers - */ - DCHCacheEntry *ent = DCH_cache_fetch(fmt_str, false); - - incache = true; - format = ent->format; - } - - result = DCH_datetime_type(format, have_error); - - if (!incache) - pfree(format); - - return result; -} diff --git a/src/backend/utils/adt/json.c b/src/backend/utils/adt/json.c index 5fdb7e32ce4..fee2ffb55c8 100644 --- a/src/backend/utils/adt/json.c +++ b/src/backend/utils/adt/json.c @@ -13,10 +13,7 @@ */ #include "postgres.h" -#include "access/hash.h" -#include "catalog/pg_proc.h" #include "catalog/pg_type.h" -#include "common/hashfn.h" #include "funcapi.h" #include "libpq/pqformat.h" #include "miscadmin.h" @@ -30,41 +27,20 @@ #include "utils/lsyscache.h" #include "utils/typcache.h" -/* Common context for key uniqueness check */ -typedef struct HTAB *JsonUniqueCheckState; /* hash table for key names */ - -/* Hash entry for JsonUniqueCheckState */ -typedef struct JsonUniqueHashEntry -{ - const char *key; - int key_len; - int object_id; -} JsonUniqueHashEntry; - -/* Context for key uniqueness check in builder functions */ -typedef struct JsonUniqueBuilderState -{ - JsonUniqueCheckState check; /* unique check */ - StringInfoData skipped_keys; /* skipped keys with NULL values */ - MemoryContext mcxt; /* context for saving skipped keys */ -} JsonUniqueBuilderState; - -/* Element of object stack for key uniqueness check during json parsing */ -typedef struct JsonUniqueStackEntry +typedef enum /* type categories for datum_to_json */ { - struct JsonUniqueStackEntry *parent; - int object_id; -} JsonUniqueStackEntry; - -/* State for key uniqueness check during json parsing */ -typedef struct JsonUniqueParsingState -{ - JsonLexContext *lex; - JsonUniqueCheckState check; - JsonUniqueStackEntry *stack; - int id_counter; - bool unique; -} JsonUniqueParsingState; + JSONTYPE_NULL, /* null, so we didn't bother to identify */ + JSONTYPE_BOOL, /* boolean (built-in types only) */ + JSONTYPE_NUMERIC, /* numeric (ditto) */ + JSONTYPE_DATE, /* we use special formatting for datetimes */ + JSONTYPE_TIMESTAMP, + JSONTYPE_TIMESTAMPTZ, + JSONTYPE_JSON, /* JSON itself (and JSONB) */ + JSONTYPE_ARRAY, /* array */ + JSONTYPE_COMPOSITE, /* composite */ + JSONTYPE_CAST, /* something with an explicit cast to JSON */ + JSONTYPE_OTHER /* all else */ +} JsonTypeCategory; typedef struct JsonAggState { @@ -73,7 +49,6 @@ typedef struct JsonAggState Oid key_output_func; JsonTypeCategory val_category; Oid val_output_func; - JsonUniqueBuilderState unique_check; } JsonAggState; static void composite_to_json(Datum composite, StringInfo result, @@ -84,6 +59,9 @@ static void array_dim_to_json(StringInfo result, int dim, int ndims, int *dims, bool use_line_feeds); static void array_to_json_internal(Datum array, StringInfo result, bool use_line_feeds); +static void json_categorize_type(Oid typoid, + JsonTypeCategory *tcategory, + Oid *outfuncoid); static void datum_to_json(Datum val, bool is_null, StringInfo result, JsonTypeCategory tcategory, Oid outfuncoid, bool key_scalar); @@ -162,7 +140,7 @@ json_recv(PG_FUNCTION_ARGS) * output function OID. If the returned category is JSONTYPE_CAST, we * return the OID of the type->JSON cast function instead. */ -void +static void json_categorize_type(Oid typoid, JsonTypeCategory *tcategory, Oid *outfuncoid) @@ -744,48 +722,6 @@ row_to_json_pretty(PG_FUNCTION_ARGS) PG_RETURN_TEXT_P(cstring_to_text_with_len(result->data, result->len)); } -Datum -to_json_worker(Datum val, JsonTypeCategory tcategory, Oid outfuncoid) -{ - StringInfo result = makeStringInfo(); - - datum_to_json(val, false, result, tcategory, outfuncoid, false); - - return PointerGetDatum(cstring_to_text_with_len(result->data, result->len)); -} - -bool -to_json_is_immutable(Oid typoid) -{ - JsonTypeCategory tcategory; - Oid outfuncoid; - - json_categorize_type(typoid, &tcategory, &outfuncoid); - - switch (tcategory) - { - case JSONTYPE_BOOL: - case JSONTYPE_JSON: - return true; - - case JSONTYPE_DATE: - case JSONTYPE_TIMESTAMP: - case JSONTYPE_TIMESTAMPTZ: - return false; - - case JSONTYPE_ARRAY: - return false; /* TODO recurse into elements */ - - case JSONTYPE_COMPOSITE: - return false; /* TODO recurse into fields */ - - case JSONTYPE_NUMERIC: - case JSONTYPE_CAST: - default: - return func_volatile(outfuncoid) == PROVOLATILE_IMMUTABLE; - } -} - /* * SQL function to_json(anyvalue) */ @@ -794,6 +730,7 @@ to_json(PG_FUNCTION_ARGS) { Datum val = PG_GETARG_DATUM(0); Oid val_type = get_fn_expr_argtype(fcinfo->flinfo, 0); + StringInfo result; JsonTypeCategory tcategory; Oid outfuncoid; @@ -805,7 +742,11 @@ to_json(PG_FUNCTION_ARGS) json_categorize_type(val_type, &tcategory, &outfuncoid); - PG_RETURN_DATUM(to_json_worker(val, tcategory, outfuncoid)); + result = makeStringInfo(); + + datum_to_json(val, false, result, tcategory, outfuncoid, false); + + PG_RETURN_TEXT_P(cstring_to_text_with_len(result->data, result->len)); } /* @@ -813,8 +754,8 @@ to_json(PG_FUNCTION_ARGS) * * aggregate input column as a json array value. */ -static Datum -json_agg_transfn_worker(FunctionCallInfo fcinfo, bool absent_on_null) +Datum +json_agg_transfn(PG_FUNCTION_ARGS) { MemoryContext aggcontext, oldcontext; @@ -854,13 +795,8 @@ json_agg_transfn_worker(FunctionCallInfo fcinfo, bool absent_on_null) else { state = (JsonAggState *) PG_GETARG_POINTER(0); - } - - if (absent_on_null && PG_ARGISNULL(1)) - PG_RETURN_POINTER(state); - - if (state->str->len > 1) appendStringInfoString(state->str, ", "); + } /* fast path for NULLs */ if (PG_ARGISNULL(1)) @@ -873,7 +809,7 @@ json_agg_transfn_worker(FunctionCallInfo fcinfo, bool absent_on_null) val = PG_GETARG_DATUM(1); /* add some whitespace if structured type and not first item */ - if (!PG_ARGISNULL(0) && state->str->len > 1 && + if (!PG_ARGISNULL(0) && (state->val_category == JSONTYPE_ARRAY || state->val_category == JSONTYPE_COMPOSITE)) { @@ -891,25 +827,6 @@ json_agg_transfn_worker(FunctionCallInfo fcinfo, bool absent_on_null) PG_RETURN_POINTER(state); } - -/* - * json_agg aggregate function - */ -Datum -json_agg_transfn(PG_FUNCTION_ARGS) -{ - return json_agg_transfn_worker(fcinfo, false); -} - -/* - * json_agg_strict aggregate function - */ -Datum -json_agg_strict_transfn(PG_FUNCTION_ARGS) -{ - return json_agg_transfn_worker(fcinfo, true); -} - /* * json_agg final function */ @@ -933,108 +850,18 @@ json_agg_finalfn(PG_FUNCTION_ARGS) PG_RETURN_TEXT_P(catenate_stringinfo_string(state->str, "]")); } -/* Functions implementing hash table for key uniqueness check */ -static uint32 -json_unique_hash(const void *key, Size keysize) -{ - const JsonUniqueHashEntry *entry = (JsonUniqueHashEntry *) key; - uint32 hash = hash_bytes_uint32(entry->object_id); - - hash ^= hash_bytes((const unsigned char *) entry->key, entry->key_len); - - return DatumGetUInt32(hash); -} - -static int -json_unique_hash_match(const void *key1, const void *key2, Size keysize) -{ - const JsonUniqueHashEntry *entry1 = (const JsonUniqueHashEntry *) key1; - const JsonUniqueHashEntry *entry2 = (const JsonUniqueHashEntry *) key2; - - if (entry1->object_id != entry2->object_id) - return entry1->object_id > entry2->object_id ? 1 : -1; - - if (entry1->key_len != entry2->key_len) - return entry1->key_len > entry2->key_len ? 1 : -1; - - return strncmp(entry1->key, entry2->key, entry1->key_len); -} - -/* Functions implementing object key uniqueness check */ -static void -json_unique_check_init(JsonUniqueCheckState *cxt) -{ - HASHCTL ctl; - - memset(&ctl, 0, sizeof(ctl)); - ctl.keysize = sizeof(JsonUniqueHashEntry); - ctl.entrysize = sizeof(JsonUniqueHashEntry); - ctl.hcxt = CurrentMemoryContext; - ctl.hash = json_unique_hash; - ctl.match = json_unique_hash_match; - - *cxt = hash_create("json object hashtable", - 32, - &ctl, - HASH_ELEM | HASH_CONTEXT | HASH_FUNCTION | HASH_COMPARE); -} - -static bool -json_unique_check_key(JsonUniqueCheckState *cxt, const char *key, int object_id) -{ - JsonUniqueHashEntry entry; - bool found; - - entry.key = key; - entry.key_len = strlen(key); - entry.object_id = object_id; - - (void) hash_search(*cxt, &entry, HASH_ENTER, &found); - - return !found; -} - -static void -json_unique_builder_init(JsonUniqueBuilderState *cxt) -{ - json_unique_check_init(&cxt->check); - cxt->mcxt = CurrentMemoryContext; - cxt->skipped_keys.data = NULL; -} - -/* On-demand initialization of skipped_keys StringInfo structure */ -static StringInfo -json_unique_builder_get_skipped_keys(JsonUniqueBuilderState *cxt) -{ - StringInfo out = &cxt->skipped_keys; - - if (!out->data) - { - MemoryContext oldcxt = MemoryContextSwitchTo(cxt->mcxt); - - initStringInfo(out); - MemoryContextSwitchTo(oldcxt); - } - - return out; -} - /* * json_object_agg transition function. * * aggregate two input columns as a single json object value. */ -static Datum -json_object_agg_transfn_worker(FunctionCallInfo fcinfo, - bool absent_on_null, bool unique_keys) +Datum +json_object_agg_transfn(PG_FUNCTION_ARGS) { MemoryContext aggcontext, oldcontext; JsonAggState *state; - StringInfo out; Datum arg; - bool skip; - int key_offset; if (!AggCheckCallContext(fcinfo, &aggcontext)) { @@ -1055,10 +882,6 @@ json_object_agg_transfn_worker(FunctionCallInfo fcinfo, oldcontext = MemoryContextSwitchTo(aggcontext); state = (JsonAggState *) palloc(sizeof(JsonAggState)); state->str = makeStringInfo(); - if (unique_keys) - json_unique_builder_init(&state->unique_check); - else - memset(&state->unique_check, 0, sizeof(state->unique_check)); MemoryContextSwitchTo(oldcontext); arg_type = get_fn_expr_argtype(fcinfo->flinfo, 1); @@ -1086,6 +909,7 @@ json_object_agg_transfn_worker(FunctionCallInfo fcinfo, else { state = (JsonAggState *) PG_GETARG_POINTER(0); + appendStringInfoString(state->str, ", "); } /* @@ -1101,49 +925,11 @@ json_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 */ - skip = absent_on_null && PG_ARGISNULL(2); - - if (skip) - { - /* If key uniqueness check is needed we must save skipped keys */ - if (!unique_keys) - PG_RETURN_POINTER(state); - - out = json_unique_builder_get_skipped_keys(&state->unique_check); - } - else - { - out = state->str; - - /* - * Append comma delimiter only if we have already outputted some - * fields after the initial string "{ ". - */ - if (out->len > 2) - appendStringInfoString(out, ", "); - } - arg = PG_GETARG_DATUM(1); - key_offset = out->len; - - datum_to_json(arg, false, out, state->key_category, + datum_to_json(arg, false, state->str, state->key_category, state->key_output_func, true); - if (unique_keys) - { - const char *key = &out->data[key_offset]; - - if (!json_unique_check_key(&state->unique_check.check, key, 0)) - ereport(ERROR, - (errcode(ERRCODE_DUPLICATE_JSON_OBJECT_KEY_VALUE), - errmsg("duplicate JSON key %s", key))); - - if (skip) - PG_RETURN_POINTER(state); - } - appendStringInfoString(state->str, " : "); if (PG_ARGISNULL(2)) @@ -1158,42 +944,6 @@ json_object_agg_transfn_worker(FunctionCallInfo fcinfo, } /* - * json_object_agg aggregate function - */ -Datum -json_object_agg_transfn(PG_FUNCTION_ARGS) -{ - return json_object_agg_transfn_worker(fcinfo, false, false); -} - -/* - * json_object_agg_strict aggregate function - */ -Datum -json_object_agg_strict_transfn(PG_FUNCTION_ARGS) -{ - return json_object_agg_transfn_worker(fcinfo, true, false); -} - -/* - * json_object_agg_unique aggregate function - */ -Datum -json_object_agg_unique_transfn(PG_FUNCTION_ARGS) -{ - return json_object_agg_transfn_worker(fcinfo, false, true); -} - -/* - * json_object_agg_unique_strict aggregate function - */ -Datum -json_object_agg_unique_strict_transfn(PG_FUNCTION_ARGS) -{ - return json_object_agg_transfn_worker(fcinfo, true, true); -} - -/* * json_object_agg final function. */ Datum @@ -1234,14 +984,25 @@ catenate_stringinfo_string(StringInfo buffer, const char *addon) return result; } +/* + * SQL function json_build_object(variadic "any") + */ Datum -json_build_object_worker(int nargs, Datum *args, bool *nulls, Oid *types, - bool absent_on_null, bool unique_keys) +json_build_object(PG_FUNCTION_ARGS) { + int nargs; int i; const char *sep = ""; StringInfo result; - JsonUniqueBuilderState unique_check; + 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, @@ -1255,32 +1016,10 @@ json_build_object_worker(int nargs, Datum *args, bool *nulls, Oid *types, appendStringInfoChar(result, '{'); - if (unique_keys) - json_unique_builder_init(&unique_check); - for (i = 0; i < nargs; i += 2) { - StringInfo out; - bool skip; - int key_offset; - - /* Skip null values if absent_on_null */ - skip = absent_on_null && nulls[i + 1]; - - if (skip) - { - /* If key uniqueness check is needed we must save skipped keys */ - if (!unique_keys) - continue; - - out = json_unique_builder_get_skipped_keys(&unique_check); - } - else - { - appendStringInfoString(result, sep); - sep = ", "; - out = result; - } + appendStringInfoString(result, sep); + sep = ", "; /* process key */ if (nulls[i]) @@ -1289,24 +1028,7 @@ json_build_object_worker(int nargs, Datum *args, bool *nulls, Oid *types, errmsg("argument %d cannot be null", i + 1), errhint("Object keys should be text."))); - /* save key offset before key appending */ - key_offset = out->len; - - add_json(args[i], false, out, types[i], true); - - if (unique_keys) - { - /* check key uniqueness after key appending */ - const char *key = &out->data[key_offset]; - - if (!json_unique_check_key(&unique_check.check, key, 0)) - ereport(ERROR, - (errcode(ERRCODE_DUPLICATE_JSON_OBJECT_KEY_VALUE), - errmsg("duplicate JSON key %s", key))); - - if (skip) - continue; - } + add_json(args[i], false, result, types[i], true); appendStringInfoString(result, " : "); @@ -1316,27 +1038,7 @@ json_build_object_worker(int nargs, Datum *args, bool *nulls, Oid *types, appendStringInfoChar(result, '}'); - return PointerGetDatum(cstring_to_text_with_len(result->data, result->len)); -} - -/* - * SQL function json_build_object(variadic "any") - */ -Datum -json_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(json_build_object_worker(nargs, args, nulls, types, false, false)); + PG_RETURN_TEXT_P(cstring_to_text_with_len(result->data, result->len)); } /* @@ -1348,13 +1050,25 @@ json_build_object_noargs(PG_FUNCTION_ARGS) PG_RETURN_TEXT_P(cstring_to_text_with_len("{}", 2)); } +/* + * SQL function json_build_array(variadic "any") + */ Datum -json_build_array_worker(int nargs, Datum *args, bool *nulls, Oid *types, - bool absent_on_null) +json_build_array(PG_FUNCTION_ARGS) { + int nargs; int i; const char *sep = ""; StringInfo result; + 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(); @@ -1362,9 +1076,6 @@ json_build_array_worker(int nargs, Datum *args, bool *nulls, Oid *types, for (i = 0; i < nargs; i++) { - if (absent_on_null && nulls[i]) - continue; - appendStringInfoString(result, sep); sep = ", "; add_json(args[i], nulls[i], result, types[i], false); @@ -1372,27 +1083,7 @@ json_build_array_worker(int nargs, Datum *args, bool *nulls, Oid *types, appendStringInfoChar(result, ']'); - return PointerGetDatum(cstring_to_text_with_len(result->data, result->len)); -} - -/* - * SQL function json_build_array(variadic "any") - */ -Datum -json_build_array(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(json_build_array_worker(nargs, args, nulls, types, false)); + PG_RETURN_TEXT_P(cstring_to_text_with_len(result->data, result->len)); } /* @@ -1618,106 +1309,6 @@ escape_json(StringInfo buf, const char *str) appendStringInfoCharMacro(buf, '"'); } -/* Semantic actions for key uniqueness check */ -static void -json_unique_object_start(void *_state) -{ - JsonUniqueParsingState *state = _state; - JsonUniqueStackEntry *entry; - - if (!state->unique) - return; - - /* push object entry to stack */ - entry = palloc(sizeof(*entry)); - entry->object_id = state->id_counter++; - entry->parent = state->stack; - state->stack = entry; -} - -static void -json_unique_object_end(void *_state) -{ - JsonUniqueParsingState *state = _state; - JsonUniqueStackEntry *entry; - - if (!state->unique) - return; - - entry = state->stack; - state->stack = entry->parent; /* pop object from stack */ - pfree(entry); -} - -static void -json_unique_object_field_start(void *_state, char *field, bool isnull) -{ - JsonUniqueParsingState *state = _state; - JsonUniqueStackEntry *entry; - - if (!state->unique) - return; - - /* find key collision in the current object */ - if (json_unique_check_key(&state->check, field, state->stack->object_id)) - return; - - state->unique = false; - - /* pop all objects entries */ - while ((entry = state->stack)) - { - state->stack = entry->parent; - pfree(entry); - } -} - -/* Validate JSON text and additionally check key uniqueness */ -bool -json_validate(text *json, bool check_unique_keys, bool throw_error) -{ - JsonLexContext *lex = makeJsonLexContext(json, check_unique_keys); - JsonSemAction uniqueSemAction = {0}; - JsonUniqueParsingState state; - JsonParseErrorType result; - - if (check_unique_keys) - { - state.lex = lex; - state.stack = NULL; - state.id_counter = 0; - state.unique = true; - json_unique_check_init(&state.check); - - uniqueSemAction.semstate = &state; - uniqueSemAction.object_start = json_unique_object_start; - uniqueSemAction.object_field_start = json_unique_object_field_start; - uniqueSemAction.object_end = json_unique_object_end; - } - - result = pg_parse_json(lex, check_unique_keys ? &uniqueSemAction : &nullSemAction); - - if (result != JSON_SUCCESS) - { - if (throw_error) - json_ereport_error(result, lex); - - return false; /* invalid json */ - } - - if (check_unique_keys && !state.unique) - { - if (throw_error) - ereport(ERROR, - (errcode(ERRCODE_DUPLICATE_JSON_OBJECT_KEY_VALUE), - errmsg("duplicate JSON object key value"))); - - return false; /* not unique keys */ - } - - return true; /* ok */ -} - /* * SQL function json_typeof(json) -> text * @@ -1733,13 +1324,21 @@ json_validate(text *json, bool check_unique_keys, bool throw_error) Datum json_typeof(PG_FUNCTION_ARGS) { - text *json = PG_GETARG_TEXT_PP(0); - char *type; + text *json; + + JsonLexContext *lex; JsonTokenType tok; + char *type; + JsonParseErrorType result; - /* Lex exactly one token from the input and check its type. */ - tok = json_get_first_token(json, true); + json = PG_GETARG_TEXT_PP(0); + lex = makeJsonLexContext(json, false); + /* Lex exactly one token from the input and check its type. */ + result = json_lex(lex); + if (result != JSON_SUCCESS) + json_ereport_error(result, lex); + tok = lex->token_type; switch (tok) { case JSON_TOKEN_OBJECT_START: 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)); -} diff --git a/src/backend/utils/adt/jsonb_util.c b/src/backend/utils/adt/jsonb_util.c index 5318eda9cfb..60442758b32 100644 --- a/src/backend/utils/adt/jsonb_util.c +++ b/src/backend/utils/adt/jsonb_util.c @@ -64,8 +64,7 @@ static int lengthCompareJsonbStringValue(const void *a, const void *b); static int lengthCompareJsonbString(const char *val1, int len1, const char *val2, int len2); static int lengthCompareJsonbPair(const void *a, const void *b, void *arg); -static void uniqueifyJsonbObject(JsonbValue *object, bool unique_keys, - bool skip_nulls); +static void uniqueifyJsonbObject(JsonbValue *object); static JsonbValue *pushJsonbValueScalar(JsonbParseState **pstate, JsonbIteratorToken seq, JsonbValue *scalarVal); @@ -690,9 +689,7 @@ pushJsonbValueScalar(JsonbParseState **pstate, JsonbIteratorToken seq, appendElement(*pstate, scalarVal); break; case WJB_END_OBJECT: - uniqueifyJsonbObject(&(*pstate)->contVal, - (*pstate)->unique_keys, - (*pstate)->skip_nulls); + uniqueifyJsonbObject(&(*pstate)->contVal); /* fall through! */ case WJB_END_ARRAY: /* Steps here common to WJB_END_OBJECT case */ @@ -735,9 +732,6 @@ pushState(JsonbParseState **pstate) JsonbParseState *ns = palloc(sizeof(JsonbParseState)); ns->next = *pstate; - ns->unique_keys = false; - ns->skip_nulls = false; - return ns; } @@ -1942,7 +1936,7 @@ lengthCompareJsonbPair(const void *a, const void *b, void *binequal) * Sort and unique-ify pairs in JsonbValue object */ static void -uniqueifyJsonbObject(JsonbValue *object, bool unique_keys, bool skip_nulls) +uniqueifyJsonbObject(JsonbValue *object) { bool hasNonUniq = false; @@ -1952,32 +1946,15 @@ uniqueifyJsonbObject(JsonbValue *object, bool unique_keys, bool skip_nulls) qsort_arg(object->val.object.pairs, object->val.object.nPairs, sizeof(JsonbPair), lengthCompareJsonbPair, &hasNonUniq); - if (hasNonUniq && unique_keys) - ereport(ERROR, - (errcode(ERRCODE_DUPLICATE_JSON_OBJECT_KEY_VALUE), - errmsg("duplicate JSON object key value"))); - - if (hasNonUniq || skip_nulls) + if (hasNonUniq) { - JsonbPair *ptr, - *res; - - while (skip_nulls && object->val.object.nPairs > 0 && - object->val.object.pairs->value.type == jbvNull) - { - /* If skip_nulls is true, remove leading items with null */ - object->val.object.pairs++; - object->val.object.nPairs--; - } - - ptr = object->val.object.pairs + 1; - res = object->val.object.pairs; + JsonbPair *ptr = object->val.object.pairs + 1, + *res = object->val.object.pairs; while (ptr - object->val.object.pairs < object->val.object.nPairs) { - /* Avoid copying over duplicate or null */ - if (lengthCompareJsonbStringValue(ptr, res) != 0 && - (!skip_nulls || ptr->value.type != jbvNull)) + /* Avoid copying over duplicate */ + if (lengthCompareJsonbStringValue(ptr, res) != 0) { res++; if (ptr != res) diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index 9819e1a45ce..82c43617d85 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -2656,11 +2656,11 @@ populate_array_dim_jsonb(PopulateArrayContext *ctx, /* context */ check_stack_depth(); - if (jbv->type != jbvBinary || - !JsonContainerIsArray(jbc) || - JsonContainerIsScalar(jbc)) + if (jbv->type != jbvBinary || !JsonContainerIsArray(jbc)) populate_array_report_expected_array(ctx, ndim - 1); + Assert(!JsonContainerIsScalar(jbc)); + it = JsonbIteratorInit(jbc); tok = JsonbIteratorNext(&it, &val, true); @@ -3132,51 +3132,6 @@ populate_record_field(ColumnIOData *col, } } -/* recursively populate specified type from a json/jsonb value */ -Datum -json_populate_type(Datum json_val, Oid json_type, Oid typid, int32 typmod, - void **cache, MemoryContext mcxt, bool *isnull) -{ - JsValue jsv = {0}; - JsonbValue jbv; - - jsv.is_json = json_type == JSONOID; - - if (*isnull) - { - if (jsv.is_json) - jsv.val.json.str = NULL; - else - jsv.val.jsonb = NULL; - } - else if (jsv.is_json) - { - text *json = DatumGetTextPP(json_val); - - jsv.val.json.str = VARDATA_ANY(json); - jsv.val.json.len = VARSIZE_ANY_EXHDR(json); - jsv.val.json.type = JSON_TOKEN_INVALID; /* not used in - * populate_composite() */ - } - else - { - Jsonb *jsonb = DatumGetJsonbP(json_val); - - jsv.val.jsonb = &jbv; - - /* fill binary jsonb value pointing to jb */ - jbv.type = jbvBinary; - jbv.val.binary.data = &jsonb->root; - jbv.val.binary.len = VARSIZE(jsonb) - VARHDRSZ; - } - - if (!*cache) - *cache = MemoryContextAllocZero(mcxt, sizeof(ColumnIOData)); - - return populate_record_field(*cache, typid, typmod, NULL, mcxt, - PointerGetDatum(NULL), &jsv, isnull); -} - static RecordIOData * allocate_record_info(MemoryContext mcxt, int ncolumns) { @@ -5566,23 +5521,3 @@ transform_string_values_scalar(void *state, char *token, JsonTokenType tokentype else appendStringInfoString(_state->strval, token); } - -JsonTokenType -json_get_first_token(text *json, bool throw_error) -{ - JsonLexContext *lex; - JsonParseErrorType result; - - lex = makeJsonLexContext(json, false); - - /* Lex exactly one token from the input and check its type. */ - result = json_lex(lex); - - if (result == JSON_SUCCESS) - return lex->token_type; - - if (throw_error) - json_ereport_error(result, lex); - - return JSON_TOKEN_INVALID; /* invalid json */ -} diff --git a/src/backend/utils/adt/jsonpath.c b/src/backend/utils/adt/jsonpath.c index da9df4ae766..91af0300952 100644 --- a/src/backend/utils/adt/jsonpath.c +++ b/src/backend/utils/adt/jsonpath.c @@ -67,9 +67,7 @@ #include "lib/stringinfo.h" #include "libpq/pqformat.h" #include "miscadmin.h" -#include "nodes/nodeFuncs.h" #include "utils/builtins.h" -#include "utils/formatting.h" #include "utils/json.h" #include "utils/jsonpath.h" @@ -1079,258 +1077,3 @@ jspGetArraySubscript(JsonPathItem *v, JsonPathItem *from, JsonPathItem *to, return true; } - -/* SQL/JSON datatype status: */ -typedef enum JsonPathDatatypeStatus -{ - jpdsNonDateTime, /* null, bool, numeric, string, array, object */ - jpdsUnknownDateTime, /* unknown datetime type */ - jpdsDateTimeZoned, /* timetz, timestamptz */ - jpdsDateTimeNonZoned /* time, timestamp, date */ -} JsonPathDatatypeStatus; - -/* Context for jspIsMutableWalker() */ -typedef struct JsonPathMutableContext -{ - List *varnames; /* list of variable names */ - List *varexprs; /* list of variable expressions */ - JsonPathDatatypeStatus current; /* status of @ item */ - bool lax; /* jsonpath is lax or strict */ - bool mutable; /* resulting mutability status */ -} JsonPathMutableContext; - -/* - * Recursive walker for jspIsMutable() - */ -static JsonPathDatatypeStatus -jspIsMutableWalker(JsonPathItem *jpi, JsonPathMutableContext *cxt) -{ - JsonPathItem next; - JsonPathDatatypeStatus status = jpdsNonDateTime; - - while (!cxt->mutable) - { - JsonPathItem arg; - JsonPathDatatypeStatus leftStatus; - JsonPathDatatypeStatus rightStatus; - - switch (jpi->type) - { - case jpiRoot: - Assert(status == jpdsNonDateTime); - break; - - case jpiCurrent: - Assert(status == jpdsNonDateTime); - status = cxt->current; - break; - - case jpiFilter: - { - JsonPathDatatypeStatus prevStatus = cxt->current; - - cxt->current = status; - jspGetArg(jpi, &arg); - jspIsMutableWalker(&arg, cxt); - - cxt->current = prevStatus; - break; - } - - case jpiVariable: - { - int32 len; - const char *name = jspGetString(jpi, &len); - ListCell *lc1; - ListCell *lc2; - - Assert(status == jpdsNonDateTime); - - forboth(lc1, cxt->varnames, lc2, cxt->varexprs) - { - String *varname = lfirst_node(String, lc1); - Node *varexpr = lfirst(lc2); - - if (strncmp(varname->sval, name, len)) - continue; - - switch (exprType(varexpr)) - { - case DATEOID: - case TIMEOID: - case TIMESTAMPOID: - status = jpdsDateTimeNonZoned; - break; - - case TIMETZOID: - case TIMESTAMPTZOID: - status = jpdsDateTimeZoned; - break; - - default: - status = jpdsNonDateTime; - break; - } - - break; - } - break; - } - - case jpiEqual: - case jpiNotEqual: - case jpiLess: - case jpiGreater: - case jpiLessOrEqual: - case jpiGreaterOrEqual: - Assert(status == jpdsNonDateTime); - jspGetLeftArg(jpi, &arg); - leftStatus = jspIsMutableWalker(&arg, cxt); - - jspGetRightArg(jpi, &arg); - rightStatus = jspIsMutableWalker(&arg, cxt); - - /* - * Comparison of datetime type with different timezone status - * is mutable. - */ - if (leftStatus != jpdsNonDateTime && - rightStatus != jpdsNonDateTime && - (leftStatus == jpdsUnknownDateTime || - rightStatus == jpdsUnknownDateTime || - leftStatus != rightStatus)) - cxt->mutable = true; - break; - - case jpiNot: - case jpiIsUnknown: - case jpiExists: - case jpiPlus: - case jpiMinus: - Assert(status == jpdsNonDateTime); - jspGetArg(jpi, &arg); - jspIsMutableWalker(&arg, cxt); - break; - - case jpiAnd: - case jpiOr: - case jpiAdd: - case jpiSub: - case jpiMul: - case jpiDiv: - case jpiMod: - case jpiStartsWith: - Assert(status == jpdsNonDateTime); - jspGetLeftArg(jpi, &arg); - jspIsMutableWalker(&arg, cxt); - jspGetRightArg(jpi, &arg); - jspIsMutableWalker(&arg, cxt); - break; - - case jpiIndexArray: - for (int i = 0; i < jpi->content.array.nelems; i++) - { - JsonPathItem from; - JsonPathItem to; - - if (jspGetArraySubscript(jpi, &from, &to, i)) - jspIsMutableWalker(&to, cxt); - - jspIsMutableWalker(&from, cxt); - } - /* FALLTHROUGH */ - - case jpiAnyArray: - if (!cxt->lax) - status = jpdsNonDateTime; - break; - - case jpiAny: - if (jpi->content.anybounds.first > 0) - status = jpdsNonDateTime; - break; - - case jpiDatetime: - if (jpi->content.arg) - { - char *template; - int flags; - - jspGetArg(jpi, &arg); - if (arg.type != jpiString) - { - status = jpdsNonDateTime; - break; /* there will be runtime error */ - } - - template = jspGetString(&arg, NULL); - flags = datetime_format_flags(template, NULL); - if (flags & DCH_ZONED) - status = jpdsDateTimeZoned; - else - status = jpdsDateTimeNonZoned; - } - else - { - status = jpdsUnknownDateTime; - } - break; - - case jpiLikeRegex: - Assert(status == jpdsNonDateTime); - jspInitByBuffer(&arg, jpi->base, jpi->content.like_regex.expr); - jspIsMutableWalker(&arg, cxt); - break; - - /* literals */ - case jpiNull: - case jpiString: - case jpiNumeric: - case jpiBool: - /* accessors */ - case jpiKey: - case jpiAnyKey: - /* special items */ - case jpiSubscript: - case jpiLast: - /* item methods */ - case jpiType: - case jpiSize: - case jpiAbs: - case jpiFloor: - case jpiCeiling: - case jpiDouble: - case jpiKeyValue: - status = jpdsNonDateTime; - break; - } - - if (!jspGetNext(jpi, &next)) - break; - - jpi = &next; - } - - return status; -} - -/* - * Check whether jsonpath expression is immutable or not. - */ -bool -jspIsMutable(JsonPath *path, List *varnames, List *varexprs) -{ - JsonPathMutableContext cxt; - JsonPathItem jpi; - - cxt.varnames = varnames; - cxt.varexprs = varexprs; - cxt.current = jpdsNonDateTime; - cxt.lax = (path->header & JSONPATH_LAX) != 0; - cxt.mutable = false; - - jspInit(&jpi, path); - jspIsMutableWalker(&jpi, &cxt); - - return cxt.mutable; -} diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c index 9c381ae7271..9f4192e0798 100644 --- a/src/backend/utils/adt/jsonpath_exec.c +++ b/src/backend/utils/adt/jsonpath_exec.c @@ -61,11 +61,9 @@ #include "catalog/pg_collation.h" #include "catalog/pg_type.h" -#include "executor/execExpr.h" #include "funcapi.h" #include "lib/stringinfo.h" #include "miscadmin.h" -#include "nodes/nodeFuncs.h" #include "regex/regex.h" #include "utils/builtins.h" #include "utils/date.h" @@ -76,8 +74,6 @@ #include "utils/guc.h" #include "utils/json.h" #include "utils/jsonpath.h" -#include "utils/lsyscache.h" -#include "utils/memutils.h" #include "utils/timestamp.h" #include "utils/varlena.h" @@ -90,16 +86,12 @@ typedef struct JsonBaseObjectInfo int id; } JsonBaseObjectInfo; -typedef int (*JsonPathVarCallback) (void *vars, char *varName, int varNameLen, - JsonbValue *val, JsonbValue *baseObject); - /* * Context of jsonpath execution. */ typedef struct JsonPathExecContext { - void *vars; /* variables to substitute into jsonpath */ - JsonPathVarCallback getVar; + Jsonb *vars; /* variables to substitute into jsonpath */ JsonbValue *root; /* for $ evaluation */ JsonbValue *current; /* for @ evaluation */ JsonBaseObjectInfo baseObject; /* "base object" for .keyvalue() @@ -159,59 +151,6 @@ typedef struct JsonValueListIterator ListCell *next; } JsonValueListIterator; -/* Structures for JSON_TABLE execution */ -typedef struct JsonTableScanState JsonTableScanState; -typedef struct JsonTableJoinState JsonTableJoinState; - -struct JsonTableScanState -{ - JsonTableScanState *parent; - JsonTableJoinState *nested; - MemoryContext mcxt; - JsonPath *path; - List *args; - JsonValueList found; - JsonValueListIterator iter; - Datum current; - int ordinal; - bool currentIsNull; - bool outerJoin; - bool errorOnError; - bool advanceNested; - bool reset; -}; - -struct JsonTableJoinState -{ - union - { - struct - { - JsonTableJoinState *left; - JsonTableJoinState *right; - bool cross; - bool advanceRight; - } join; - JsonTableScanState scan; - } u; - bool is_join; -}; - -/* random number to identify JsonTableContext */ -#define JSON_TABLE_CONTEXT_MAGIC 418352867 - -typedef struct JsonTableContext -{ - int magic; - struct - { - ExprState *expr; - JsonTableScanState *scan; - } *colexprs; - JsonTableScanState root; - bool empty; -} JsonTableContext; - /* strict/lax flags is decomposed into four [un]wrap/error flags */ #define jspStrictAbsenseOfErrors(cxt) (!(cxt)->laxMode) #define jspAutoUnwrap(cxt) ((cxt)->laxMode) @@ -234,8 +173,7 @@ typedef JsonPathBool (*JsonPathPredicateCallback) (JsonPathItem *jsp, void *param); typedef Numeric (*BinaryArithmFunc) (Numeric num1, Numeric num2, bool *error); -static JsonPathExecResult executeJsonPath(JsonPath *path, void *vars, - JsonPathVarCallback getVar, +static JsonPathExecResult executeJsonPath(JsonPath *path, Jsonb *vars, Jsonb *json, bool throwErrors, JsonValueList *result, bool useTz); static JsonPathExecResult executeItem(JsonPathExecContext *cxt, @@ -287,10 +225,7 @@ static JsonPathExecResult appendBoolResult(JsonPathExecContext *cxt, static void getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item, JsonbValue *value); static void getJsonPathVariable(JsonPathExecContext *cxt, - JsonPathItem *variable, JsonbValue *value); -static int getJsonPathVariableFromJsonb(void *varsJsonb, char *varName, - int varNameLen, JsonbValue *val, - JsonbValue *baseObject); + JsonPathItem *variable, Jsonb *vars, JsonbValue *value); static int JsonbArraySize(JsonbValue *jb); static JsonPathBool executeComparison(JsonPathItem *cmp, JsonbValue *lv, JsonbValue *rv, void *p); @@ -302,7 +237,6 @@ static JsonPathExecResult getArrayIndex(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb, int32 *index); static JsonBaseObjectInfo setBaseObject(JsonPathExecContext *cxt, JsonbValue *jbv, int32 id); -static void JsonValueListClear(JsonValueList *jvl); static void JsonValueListAppend(JsonValueList *jvl, JsonbValue *jbv); static int JsonValueListLength(const JsonValueList *jvl); static bool JsonValueListIsEmpty(JsonValueList *jvl); @@ -320,12 +254,6 @@ static JsonbValue *wrapItemsInArray(const JsonValueList *items); static int compareDatetime(Datum val1, Oid typid1, Datum val2, Oid typid2, bool useTz, bool *have_error); - -static JsonTableJoinState *JsonTableInitPlanState(JsonTableContext *cxt, - Node *plan, JsonTableScanState *parent); -static bool JsonTableNextRow(JsonTableScanState *scan); - - /****************** User interface to JsonPath executor ********************/ /* @@ -355,8 +283,7 @@ jsonb_path_exists_internal(FunctionCallInfo fcinfo, bool tz) silent = PG_GETARG_BOOL(3); } - res = executeJsonPath(jp, vars, getJsonPathVariableFromJsonb, - jb, !silent, NULL, tz); + res = executeJsonPath(jp, vars, jb, !silent, NULL, tz); PG_FREE_IF_COPY(jb, 0); PG_FREE_IF_COPY(jp, 1); @@ -411,8 +338,7 @@ jsonb_path_match_internal(FunctionCallInfo fcinfo, bool tz) silent = PG_GETARG_BOOL(3); } - (void) executeJsonPath(jp, vars, getJsonPathVariableFromJsonb, - jb, !silent, &found, tz); + (void) executeJsonPath(jp, vars, jb, !silent, &found, tz); PG_FREE_IF_COPY(jb, 0); PG_FREE_IF_COPY(jp, 1); @@ -490,8 +416,7 @@ jsonb_path_query_internal(FunctionCallInfo fcinfo, bool tz) vars = PG_GETARG_JSONB_P_COPY(2); silent = PG_GETARG_BOOL(3); - (void) executeJsonPath(jp, vars, getJsonPathVariableFromJsonb, - jb, !silent, &found, tz); + (void) executeJsonPath(jp, vars, jb, !silent, &found, tz); funcctx->user_fctx = JsonValueListGetList(&found); @@ -538,8 +463,7 @@ jsonb_path_query_array_internal(FunctionCallInfo fcinfo, bool tz) Jsonb *vars = PG_GETARG_JSONB_P(2); bool silent = PG_GETARG_BOOL(3); - (void) executeJsonPath(jp, vars, getJsonPathVariableFromJsonb, - jb, !silent, &found, tz); + (void) executeJsonPath(jp, vars, jb, !silent, &found, tz); PG_RETURN_JSONB_P(JsonbValueToJsonb(wrapItemsInArray(&found))); } @@ -570,8 +494,7 @@ jsonb_path_query_first_internal(FunctionCallInfo fcinfo, bool tz) Jsonb *vars = PG_GETARG_JSONB_P(2); bool silent = PG_GETARG_BOOL(3); - (void) executeJsonPath(jp, vars, getJsonPathVariableFromJsonb, - jb, !silent, &found, tz); + (void) executeJsonPath(jp, vars, jb, !silent, &found, tz); if (JsonValueListLength(&found) >= 1) PG_RETURN_JSONB_P(JsonbValueToJsonb(JsonValueListHead(&found))); @@ -613,9 +536,8 @@ jsonb_path_query_first_tz(PG_FUNCTION_ARGS) * In other case it tries to find all the satisfied result items. */ static JsonPathExecResult -executeJsonPath(JsonPath *path, void *vars, JsonPathVarCallback getVar, - Jsonb *json, bool throwErrors, JsonValueList *result, - bool useTz) +executeJsonPath(JsonPath *path, Jsonb *vars, Jsonb *json, bool throwErrors, + JsonValueList *result, bool useTz) { JsonPathExecContext cxt; JsonPathExecResult res; @@ -627,16 +549,22 @@ executeJsonPath(JsonPath *path, void *vars, JsonPathVarCallback getVar, if (!JsonbExtractScalar(&json->root, &jbv)) JsonbInitBinary(&jbv, json); + if (vars && !JsonContainerIsObject(&vars->root)) + { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("\"vars\" argument is not an object"), + errdetail("Jsonpath parameters should be encoded as key-value pairs of \"vars\" object."))); + } + cxt.vars = vars; - cxt.getVar = getVar; cxt.laxMode = (path->header & JSONPATH_LAX) != 0; cxt.ignoreStructuralErrors = cxt.laxMode; cxt.root = &jbv; cxt.current = &jbv; cxt.baseObject.jbc = NULL; cxt.baseObject.id = 0; - /* 1 + number of base objects in vars */ - cxt.lastGeneratedObjectId = 1 + getVar(vars, NULL, 0, NULL, NULL); + cxt.lastGeneratedObjectId = vars ? 2 : 1; cxt.innermostArraySize = -1; cxt.throwErrors = throwErrors; cxt.useTz = useTz; @@ -2165,7 +2093,7 @@ getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item, &value->val.string.len); break; case jpiVariable: - getJsonPathVariable(cxt, item, value); + getJsonPathVariable(cxt, item, cxt->vars, value); return; default: elog(ERROR, "unexpected jsonpath item type"); @@ -2177,63 +2105,42 @@ getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item, */ static void getJsonPathVariable(JsonPathExecContext *cxt, JsonPathItem *variable, - JsonbValue *value) + Jsonb *vars, JsonbValue *value) { char *varName; int varNameLength; - JsonbValue baseObject; - int baseObjectId; - - Assert(variable->type == jpiVariable); - varName = jspGetString(variable, &varNameLength); - - if (!cxt->vars || - (baseObjectId = cxt->getVar(cxt->vars, varName, varNameLength, value, - &baseObject)) < 0) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("could not find jsonpath variable \"%s\"", - pnstrdup(varName, varNameLength)))); - - if (baseObjectId > 0) - setBaseObject(cxt, &baseObject, baseObjectId); -} - -static int -getJsonPathVariableFromJsonb(void *varsJsonb, char *varName, int varNameLength, - JsonbValue *value, JsonbValue *baseObject) -{ - Jsonb *vars = varsJsonb; JsonbValue tmp; JsonbValue *v; - if (!varName) + if (!vars) { - if (vars && !JsonContainerIsObject(&vars->root)) - { - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("\"vars\" argument is not an object"), - errdetail("Jsonpath parameters should be encoded as key-value pairs of \"vars\" object."))); - } - - return vars ? 1 : 0; /* count of base objects */ + value->type = jbvNull; + return; } + Assert(variable->type == jpiVariable); + varName = jspGetString(variable, &varNameLength); tmp.type = jbvString; tmp.val.string.val = varName; tmp.val.string.len = varNameLength; v = findJsonbValueFromContainer(&vars->root, JB_FOBJECT, &tmp); - if (!v) - return -1; - - *value = *v; - pfree(v); + if (v) + { + *value = *v; + pfree(v); + } + else + { + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("could not find jsonpath variable \"%s\"", + pnstrdup(varName, varNameLength)))); + } - JsonbInitBinary(baseObject, vars); - return 1; + JsonbInitBinary(&tmp, vars); + setBaseObject(cxt, &tmp, 1); } /**************** Support functions for JsonPath execution *****************/ @@ -2523,13 +2430,6 @@ setBaseObject(JsonPathExecContext *cxt, JsonbValue *jbv, int32 id) } static void -JsonValueListClear(JsonValueList *jvl) -{ - jvl->singleton = NULL; - jvl->list = NULL; -} - -static void JsonValueListAppend(JsonValueList *jvl, JsonbValue *jbv) { if (jvl->singleton) @@ -2897,667 +2797,3 @@ compareDatetime(Datum val1, Oid typid1, Datum val2, Oid typid2, return DatumGetInt32(DirectFunctionCall2(cmpfunc, val1, val2)); } - -/********************Interface to pgsql's executor***************************/ - -bool -JsonPathExists(Datum jb, JsonPath *jp, List *vars, bool *error) -{ - JsonPathExecResult res = executeJsonPath(jp, vars, EvalJsonPathVar, - DatumGetJsonbP(jb), !error, NULL, - true); - - Assert(error || !jperIsError(res)); - - if (error && jperIsError(res)) - *error = true; - - return res == jperOk; -} - -Datum -JsonPathQuery(Datum jb, JsonPath *jp, JsonWrapper wrapper, bool *empty, - bool *error, List *vars) -{ - JsonbValue *first; - bool wrap; - JsonValueList found = {0}; - JsonPathExecResult res PG_USED_FOR_ASSERTS_ONLY; - int count; - - res = executeJsonPath(jp, vars, EvalJsonPathVar, DatumGetJsonbP(jb), !error, - &found, true); - - Assert(error || !jperIsError(res)); - - if (error && jperIsError(res)) - { - *error = true; - *empty = false; - return (Datum) 0; - } - - count = JsonValueListLength(&found); - - first = count ? JsonValueListHead(&found) : NULL; - - if (!first) - wrap = false; - else if (wrapper == JSW_NONE) - wrap = false; - else if (wrapper == JSW_UNCONDITIONAL) - wrap = true; - else if (wrapper == JSW_CONDITIONAL) - wrap = count > 1 || - IsAJsonbScalar(first) || - (first->type == jbvBinary && - JsonContainerIsScalar(first->val.binary.data)); - else - { - elog(ERROR, "unrecognized json wrapper %d", wrapper); - wrap = false; - } - - if (wrap) - return JsonbPGetDatum(JsonbValueToJsonb(wrapItemsInArray(&found))); - - if (count > 1) - { - if (error) - { - *error = true; - return (Datum) 0; - } - - ereport(ERROR, - (errcode(ERRCODE_MORE_THAN_ONE_SQL_JSON_ITEM), - errmsg("JSON path expression in JSON_QUERY should return " - "singleton item without wrapper"), - errhint("Use WITH WRAPPER clause to wrap SQL/JSON item " - "sequence into array."))); - } - - if (first) - return JsonbPGetDatum(JsonbValueToJsonb(first)); - - *empty = true; - return PointerGetDatum(NULL); -} - -JsonbValue * -JsonPathValue(Datum jb, JsonPath *jp, bool *empty, bool *error, List *vars) -{ - JsonbValue *res; - JsonValueList found = {0}; - JsonPathExecResult jper PG_USED_FOR_ASSERTS_ONLY; - int count; - - jper = executeJsonPath(jp, vars, EvalJsonPathVar, DatumGetJsonbP(jb), !error, - &found, true); - - Assert(error || !jperIsError(jper)); - - if (error && jperIsError(jper)) - { - *error = true; - *empty = false; - return NULL; - } - - count = JsonValueListLength(&found); - - *empty = !count; - - if (*empty) - return NULL; - - if (count > 1) - { - if (error) - { - *error = true; - return NULL; - } - - ereport(ERROR, - (errcode(ERRCODE_MORE_THAN_ONE_SQL_JSON_ITEM), - errmsg("JSON path expression in JSON_VALUE should return " - "singleton scalar item"))); - } - - res = JsonValueListHead(&found); - - if (res->type == jbvBinary && - JsonContainerIsScalar(res->val.binary.data)) - JsonbExtractScalar(res->val.binary.data, res); - - if (!IsAJsonbScalar(res)) - { - if (error) - { - *error = true; - return NULL; - } - - ereport(ERROR, - (errcode(ERRCODE_SQL_JSON_SCALAR_REQUIRED), - errmsg("JSON path expression in JSON_VALUE should return " - "singleton scalar item"))); - } - - if (res->type == jbvNull) - return NULL; - - return res; -} - -static void -JsonbValueInitNumericDatum(JsonbValue *jbv, Datum num) -{ - jbv->type = jbvNumeric; - jbv->val.numeric = DatumGetNumeric(num); -} - -void -JsonItemFromDatum(Datum val, Oid typid, int32 typmod, JsonbValue *res) -{ - switch (typid) - { - case BOOLOID: - res->type = jbvBool; - res->val.boolean = DatumGetBool(val); - break; - case NUMERICOID: - JsonbValueInitNumericDatum(res, val); - break; - case INT2OID: - JsonbValueInitNumericDatum(res, DirectFunctionCall1(int2_numeric, val)); - break; - case INT4OID: - JsonbValueInitNumericDatum(res, DirectFunctionCall1(int4_numeric, val)); - break; - case INT8OID: - JsonbValueInitNumericDatum(res, DirectFunctionCall1(int8_numeric, val)); - break; - case FLOAT4OID: - JsonbValueInitNumericDatum(res, DirectFunctionCall1(float4_numeric, val)); - break; - case FLOAT8OID: - JsonbValueInitNumericDatum(res, DirectFunctionCall1(float8_numeric, val)); - break; - case TEXTOID: - case VARCHAROID: - res->type = jbvString; - res->val.string.val = VARDATA_ANY(val); - res->val.string.len = VARSIZE_ANY_EXHDR(val); - break; - case DATEOID: - case TIMEOID: - case TIMETZOID: - case TIMESTAMPOID: - case TIMESTAMPTZOID: - res->type = jbvDatetime; - res->val.datetime.value = val; - res->val.datetime.typid = typid; - res->val.datetime.typmod = typmod; - res->val.datetime.tz = 0; - break; - case JSONBOID: - { - JsonbValue *jbv = res; - Jsonb *jb = DatumGetJsonbP(val); - - if (JsonContainerIsScalar(&jb->root)) - { - bool result PG_USED_FOR_ASSERTS_ONLY; - - result = JsonbExtractScalar(&jb->root, jbv); - Assert(result); - } - else - JsonbInitBinary(jbv, jb); - break; - } - case JSONOID: - { - text *txt = DatumGetTextP(val); - char *str = text_to_cstring(txt); - Jsonb *jb = - DatumGetJsonbP(DirectFunctionCall1(jsonb_in, - CStringGetDatum(str))); - - pfree(str); - - JsonItemFromDatum(JsonbPGetDatum(jb), JSONBOID, -1, res); - break; - } - default: - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("only bool, numeric, and text types could be " - "casted to supported jsonpath types."))); - } -} - -/************************ JSON_TABLE functions ***************************/ - -/* - * Returns private data from executor state. Ensure validity by check with - * MAGIC number. - */ -static inline JsonTableContext * -GetJsonTableContext(TableFuncScanState *state, const char *fname) -{ - JsonTableContext *result; - - if (!IsA(state, TableFuncScanState)) - elog(ERROR, "%s called with invalid TableFuncScanState", fname); - result = (JsonTableContext *) state->opaque; - if (result->magic != JSON_TABLE_CONTEXT_MAGIC) - elog(ERROR, "%s called with invalid TableFuncScanState", fname); - - return result; -} - -/* Recursively initialize JSON_TABLE scan state */ -static void -JsonTableInitScanState(JsonTableContext *cxt, JsonTableScanState *scan, - JsonTableParent *node, JsonTableScanState *parent, - List *args, MemoryContext mcxt) -{ - int i; - - scan->parent = parent; - scan->outerJoin = node->outerJoin; - scan->errorOnError = node->errorOnError; - scan->path = DatumGetJsonPathP(node->path->constvalue); - scan->args = args; - scan->mcxt = AllocSetContextCreate(mcxt, "JsonTableContext", - ALLOCSET_DEFAULT_SIZES); - scan->nested = node->child ? - JsonTableInitPlanState(cxt, node->child, scan) : NULL; - scan->current = PointerGetDatum(NULL); - scan->currentIsNull = true; - - for (i = node->colMin; i <= node->colMax; i++) - cxt->colexprs[i].scan = scan; -} - -/* Recursively initialize JSON_TABLE scan state */ -static JsonTableJoinState * -JsonTableInitPlanState(JsonTableContext *cxt, Node *plan, - JsonTableScanState *parent) -{ - JsonTableJoinState *state = palloc0(sizeof(*state)); - - if (IsA(plan, JsonTableSibling)) - { - JsonTableSibling *join = castNode(JsonTableSibling, plan); - - state->is_join = true; - state->u.join.cross = join->cross; - state->u.join.left = JsonTableInitPlanState(cxt, join->larg, parent); - state->u.join.right = JsonTableInitPlanState(cxt, join->rarg, parent); - } - else - { - JsonTableParent *node = castNode(JsonTableParent, plan); - - state->is_join = false; - - JsonTableInitScanState(cxt, &state->u.scan, node, parent, - parent->args, parent->mcxt); - } - - return state; -} - -/* - * JsonTableInitOpaque - * Fill in TableFuncScanState->opaque for JsonTable processor - */ -static void -JsonTableInitOpaque(TableFuncScanState *state, int natts) -{ - JsonTableContext *cxt; - PlanState *ps = &state->ss.ps; - TableFuncScan *tfs = castNode(TableFuncScan, ps->plan); - TableFunc *tf = tfs->tablefunc; - JsonExpr *ci = castNode(JsonExpr, tf->docexpr); - JsonTableParent *root = castNode(JsonTableParent, tf->plan); - List *args = NIL; - ListCell *lc; - int i; - - cxt = palloc0(sizeof(JsonTableContext)); - cxt->magic = JSON_TABLE_CONTEXT_MAGIC; - - if (ci->passing_values) - { - ListCell *exprlc; - ListCell *namelc; - - forboth(exprlc, ci->passing_values, - namelc, ci->passing_names) - { - Expr *expr = (Expr *) lfirst(exprlc); - String *name = lfirst_node(String, namelc); - JsonPathVariableEvalContext *var = palloc(sizeof(*var)); - - var->name = pstrdup(name->sval); - var->typid = exprType((Node *) expr); - var->typmod = exprTypmod((Node *) expr); - var->estate = ExecInitExpr(expr, ps); - var->econtext = ps->ps_ExprContext; - var->mcxt = CurrentMemoryContext; - var->evaluated = false; - var->value = (Datum) 0; - var->isnull = true; - - args = lappend(args, var); - } - } - - cxt->colexprs = palloc(sizeof(*cxt->colexprs) * - list_length(tf->colvalexprs)); - - JsonTableInitScanState(cxt, &cxt->root, root, NULL, args, - CurrentMemoryContext); - - i = 0; - - foreach(lc, tf->colvalexprs) - { - Expr *expr = lfirst(lc); - - cxt->colexprs[i].expr = - ExecInitExprWithCaseValue(expr, ps, - &cxt->colexprs[i].scan->current, - &cxt->colexprs[i].scan->currentIsNull); - - i++; - } - - state->opaque = cxt; -} - -/* Reset scan iterator to the beginning of the item list */ -static void -JsonTableRescan(JsonTableScanState *scan) -{ - JsonValueListInitIterator(&scan->found, &scan->iter); - scan->current = PointerGetDatum(NULL); - scan->currentIsNull = true; - scan->advanceNested = false; - scan->ordinal = 0; -} - -/* Reset context item of a scan, execute JSON path and reset a scan */ -static void -JsonTableResetContextItem(JsonTableScanState *scan, Datum item) -{ - MemoryContext oldcxt; - JsonPathExecResult res; - Jsonb *js = (Jsonb *) DatumGetJsonbP(item); - - JsonValueListClear(&scan->found); - - MemoryContextResetOnly(scan->mcxt); - - oldcxt = MemoryContextSwitchTo(scan->mcxt); - - res = executeJsonPath(scan->path, scan->args, EvalJsonPathVar, js, - scan->errorOnError, &scan->found, false /* FIXME */ ); - - MemoryContextSwitchTo(oldcxt); - - if (jperIsError(res)) - { - Assert(!scan->errorOnError); - JsonValueListClear(&scan->found); /* EMPTY ON ERROR case */ - } - - JsonTableRescan(scan); -} - -/* - * JsonTableSetDocument - * Install the input document - */ -static void -JsonTableSetDocument(TableFuncScanState *state, Datum value) -{ - JsonTableContext *cxt = GetJsonTableContext(state, "JsonTableSetDocument"); - - JsonTableResetContextItem(&cxt->root, value); -} - -/* Recursively reset scan and its child nodes */ -static void -JsonTableRescanRecursive(JsonTableJoinState *state) -{ - if (state->is_join) - { - JsonTableRescanRecursive(state->u.join.left); - JsonTableRescanRecursive(state->u.join.right); - state->u.join.advanceRight = false; - } - else - { - JsonTableRescan(&state->u.scan); - if (state->u.scan.nested) - JsonTableRescanRecursive(state->u.scan.nested); - } -} - -/* - * Fetch next row from a cross/union joined scan. - * - * Returns false at the end of a scan, true otherwise. - */ -static bool -JsonTableNextJoinRow(JsonTableJoinState *state) -{ - if (!state->is_join) - return JsonTableNextRow(&state->u.scan); - - if (state->u.join.advanceRight) - { - /* fetch next inner row */ - if (JsonTableNextJoinRow(state->u.join.right)) - return true; - - /* inner rows are exhausted */ - if (state->u.join.cross) - state->u.join.advanceRight = false; /* next outer row */ - else - return false; /* end of scan */ - } - - while (!state->u.join.advanceRight) - { - /* fetch next outer row */ - bool left = JsonTableNextJoinRow(state->u.join.left); - - if (state->u.join.cross) - { - if (!left) - return false; /* end of scan */ - - JsonTableRescanRecursive(state->u.join.right); - - if (!JsonTableNextJoinRow(state->u.join.right)) - continue; /* next outer row */ - - state->u.join.advanceRight = true; /* next inner row */ - } - else if (!left) - { - if (!JsonTableNextJoinRow(state->u.join.right)) - return false; /* end of scan */ - - state->u.join.advanceRight = true; /* next inner row */ - } - - break; - } - - return true; -} - -/* Recursively set 'reset' flag of scan and its child nodes */ -static void -JsonTableJoinReset(JsonTableJoinState *state) -{ - if (state->is_join) - { - JsonTableJoinReset(state->u.join.left); - JsonTableJoinReset(state->u.join.right); - state->u.join.advanceRight = false; - } - else - { - state->u.scan.reset = true; - state->u.scan.advanceNested = false; - - if (state->u.scan.nested) - JsonTableJoinReset(state->u.scan.nested); - } -} - -/* - * Fetch next row from a simple scan with outer/inner joined nested subscans. - * - * Returns false at the end of a scan, true otherwise. - */ -static bool -JsonTableNextRow(JsonTableScanState *scan) -{ - /* reset context item if requested */ - if (scan->reset) - { - Assert(!scan->parent->currentIsNull); - JsonTableResetContextItem(scan, scan->parent->current); - scan->reset = false; - } - - if (scan->advanceNested) - { - /* fetch next nested row */ - scan->advanceNested = JsonTableNextJoinRow(scan->nested); - - if (scan->advanceNested) - return true; - } - - for (;;) - { - /* fetch next row */ - JsonbValue *jbv = JsonValueListNext(&scan->found, &scan->iter); - MemoryContext oldcxt; - - if (!jbv) - { - scan->current = PointerGetDatum(NULL); - scan->currentIsNull = true; - return false; /* end of scan */ - } - - /* set current row item */ - oldcxt = MemoryContextSwitchTo(scan->mcxt); - scan->current = JsonbPGetDatum(JsonbValueToJsonb(jbv)); - scan->currentIsNull = false; - MemoryContextSwitchTo(oldcxt); - - scan->ordinal++; - - if (!scan->nested) - break; - - JsonTableJoinReset(scan->nested); - - scan->advanceNested = JsonTableNextJoinRow(scan->nested); - - if (scan->advanceNested || scan->outerJoin) - break; - } - - return true; -} - -/* - * JsonTableFetchRow - * Prepare the next "current" tuple for upcoming GetValue calls. - * Returns FALSE if the row-filter expression returned no more rows. - */ -static bool -JsonTableFetchRow(TableFuncScanState *state) -{ - JsonTableContext *cxt = GetJsonTableContext(state, "JsonTableFetchRow"); - - if (cxt->empty) - return false; - - return JsonTableNextRow(&cxt->root); -} - -/* - * JsonTableGetValue - * Return the value for column number 'colnum' for the current row. - * - * This leaks memory, so be sure to reset often the context in which it's - * called. - */ -static Datum -JsonTableGetValue(TableFuncScanState *state, int colnum, - Oid typid, int32 typmod, bool *isnull) -{ - JsonTableContext *cxt = GetJsonTableContext(state, "JsonTableGetValue"); - ExprContext *econtext = state->ss.ps.ps_ExprContext; - ExprState *estate = cxt->colexprs[colnum].expr; - JsonTableScanState *scan = cxt->colexprs[colnum].scan; - Datum result; - - if (scan->currentIsNull) /* NULL from outer/union join */ - { - result = (Datum) 0; - *isnull = true; - } - else if (estate) /* regular column */ - { - result = ExecEvalExpr(estate, econtext, isnull); - } - else - { - result = Int32GetDatum(scan->ordinal); /* ordinality column */ - *isnull = false; - } - - return result; -} - -/* - * JsonTableDestroyOpaque - */ -static void -JsonTableDestroyOpaque(TableFuncScanState *state) -{ - JsonTableContext *cxt = GetJsonTableContext(state, "JsonTableDestroyOpaque"); - - /* not valid anymore */ - cxt->magic = 0; - - state->opaque = NULL; -} - -const TableFuncRoutine JsonbTableRoutine = -{ - JsonTableInitOpaque, - JsonTableSetDocument, - NULL, - NULL, - NULL, - JsonTableFetchRow, - JsonTableGetValue, - JsonTableDestroyOpaque -}; diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 9959f6910e9..6594a9aac70 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -466,12 +466,6 @@ static void get_coercion_expr(Node *arg, deparse_context *context, Node *parentNode); static void get_const_expr(Const *constval, deparse_context *context, int showtype); -static void get_json_constructor(JsonConstructorExpr *ctor, - deparse_context *context, bool showimplicit); -static void get_json_agg_constructor(JsonConstructorExpr *ctor, - deparse_context *context, - const char *funcname, - bool is_json_objectagg); static void get_const_collation(Const *constval, deparse_context *context); static void simple_quote_literal(StringInfo buf, const char *val); static void get_sublink_expr(SubLink *sublink, deparse_context *context); @@ -505,10 +499,6 @@ static char *generate_qualified_type_name(Oid typid); static text *string_to_text(char *str); static char *flatten_reloptions(Oid relid); static void get_reloptions(StringInfo buf, Datum reloptions); -static void get_json_path_spec(Node *path_spec, deparse_context *context, - bool showimplicit); -static void get_json_table_columns(TableFunc *tf, JsonTableParent *node, - deparse_context *context, bool showimplicit); #define only_marker(rte) ((rte)->inh ? "" : "ONLY ") @@ -6338,8 +6328,7 @@ get_rule_sortgroupclause(Index ref, List *tlist, bool force_colno, bool need_paren = (PRETTY_PAREN(context) || IsA(expr, FuncExpr) || IsA(expr, Aggref) - || IsA(expr, WindowFunc) - || IsA(expr, JsonConstructorExpr)); + || IsA(expr, WindowFunc)); if (need_paren) appendStringInfoChar(context->buf, '('); @@ -8198,8 +8187,6 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags) case T_GroupingFunc: case T_WindowFunc: case T_FuncExpr: - case T_JsonConstructorExpr: - case T_JsonExpr: /* function-like: name(..) or name[..] */ return true; @@ -8293,7 +8280,6 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags) case T_NullTest: case T_BooleanTest: case T_DistinctExpr: - case T_JsonIsPredicate: switch (nodeTag(parentNode)) { case T_FuncExpr: @@ -8318,7 +8304,6 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags) case T_GroupingFunc: /* own parentheses */ case T_WindowFunc: /* own parentheses */ case T_CaseExpr: /* other separators */ - case T_JsonExpr: /* own parentheses */ return true; default: return false; @@ -8375,11 +8360,6 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags) return false; } - case T_JsonValueExpr: - /* maybe simple, check args */ - return isSimpleNode((Node *) ((JsonValueExpr *) node)->raw_expr, - node, prettyFlags); - default: break; } @@ -8486,122 +8466,6 @@ get_rule_expr_paren(Node *node, deparse_context *context, } -/* - * get_json_path_spec - Parse back a JSON path specification - */ -static void -get_json_path_spec(Node *path_spec, deparse_context *context, bool showimplicit) -{ - if (IsA(path_spec, Const)) - get_const_expr((Const *) path_spec, context, -1); - else - get_rule_expr(path_spec, context, showimplicit); -} - -/* - * get_json_format - Parse back a JsonFormat node - */ -static void -get_json_format(JsonFormat *format, StringInfo buf) -{ - if (format->format_type == JS_FORMAT_DEFAULT) - return; - - appendStringInfoString(buf, - format->format_type == JS_FORMAT_JSONB ? - " FORMAT JSONB" : " FORMAT JSON"); - - if (format->encoding != JS_ENC_DEFAULT) - { - const char *encoding = - format->encoding == JS_ENC_UTF16 ? "UTF16" : - format->encoding == JS_ENC_UTF32 ? "UTF32" : "UTF8"; - - appendStringInfo(buf, " ENCODING %s", encoding); - } -} - -/* - * get_json_returning - Parse back a JsonReturning structure - */ -static void -get_json_returning(JsonReturning *returning, StringInfo buf, - bool json_format_by_default) -{ - if (!OidIsValid(returning->typid)) - return; - - appendStringInfo(buf, " RETURNING %s", - format_type_with_typemod(returning->typid, - returning->typmod)); - - if (!json_format_by_default || - returning->format->format_type != - (returning->typid == JSONBOID ? JS_FORMAT_JSONB : JS_FORMAT_JSON)) - get_json_format(returning->format, buf); -} - -static void -get_json_behavior(JsonBehavior *behavior, deparse_context *context, - const char *on) -{ - /* - * The order of array elements must correspond to the order of - * JsonBehaviorType members. - */ - const char *behavior_names[] = - { - " NULL", - " ERROR", - " EMPTY", - " TRUE", - " FALSE", - " UNKNOWN", - " EMPTY ARRAY", - " EMPTY OBJECT", - " DEFAULT " - }; - - if ((int) behavior->btype < 0 || behavior->btype >= lengthof(behavior_names)) - elog(ERROR, "invalid json behavior type: %d", behavior->btype); - - appendStringInfoString(context->buf, behavior_names[behavior->btype]); - - if (behavior->btype == JSON_BEHAVIOR_DEFAULT) - get_rule_expr(behavior->default_expr, context, false); - - appendStringInfo(context->buf, " ON %s", on); -} - -/* - * get_json_expr_options - * - * Parse back common options for JSON_QUERY, JSON_VALUE, JSON_EXISTS and - * JSON_TABLE columns. - */ -static void -get_json_expr_options(JsonExpr *jsexpr, deparse_context *context, - JsonBehaviorType default_behavior) -{ - if (jsexpr->op == JSON_QUERY_OP) - { - if (jsexpr->wrapper == JSW_CONDITIONAL) - appendStringInfo(context->buf, " WITH CONDITIONAL WRAPPER"); - else if (jsexpr->wrapper == JSW_UNCONDITIONAL) - appendStringInfo(context->buf, " WITH UNCONDITIONAL WRAPPER"); - - if (jsexpr->omit_quotes) - appendStringInfo(context->buf, " OMIT QUOTES"); - } - - if (jsexpr->op != JSON_EXISTS_OP && - jsexpr->on_empty->btype != default_behavior) - get_json_behavior(jsexpr->on_empty, context, "EMPTY"); - - if (jsexpr->on_error->btype != default_behavior) - get_json_behavior(jsexpr->on_error, context, "ERROR"); -} - /* ---------- * get_rule_expr - Parse back an expression * @@ -9760,116 +9624,6 @@ get_rule_expr(Node *node, deparse_context *context, } break; - - case T_JsonValueExpr: - { - JsonValueExpr *jve = (JsonValueExpr *) node; - - get_rule_expr((Node *) jve->raw_expr, context, false); - get_json_format(jve->format, context->buf); - } - break; - - case T_JsonConstructorExpr: - get_json_constructor((JsonConstructorExpr *) node, context, false); - break; - - case T_JsonIsPredicate: - { - JsonIsPredicate *pred = (JsonIsPredicate *) node; - - if (!PRETTY_PAREN(context)) - appendStringInfoChar(context->buf, '('); - - get_rule_expr_paren(pred->expr, context, true, node); - - appendStringInfoString(context->buf, " IS JSON"); - - /* TODO: handle FORMAT clause */ - - switch (pred->item_type) - { - case JS_TYPE_SCALAR: - appendStringInfoString(context->buf, " SCALAR"); - break; - case JS_TYPE_ARRAY: - appendStringInfoString(context->buf, " ARRAY"); - break; - case JS_TYPE_OBJECT: - appendStringInfoString(context->buf, " OBJECT"); - break; - default: - break; - } - - if (pred->unique_keys) - appendStringInfoString(context->buf, " WITH UNIQUE KEYS"); - - if (!PRETTY_PAREN(context)) - appendStringInfoChar(context->buf, ')'); - } - break; - - case T_JsonExpr: - { - JsonExpr *jexpr = (JsonExpr *) node; - - switch (jexpr->op) - { - case JSON_QUERY_OP: - appendStringInfoString(buf, "JSON_QUERY("); - break; - case JSON_VALUE_OP: - appendStringInfoString(buf, "JSON_VALUE("); - break; - case JSON_EXISTS_OP: - appendStringInfoString(buf, "JSON_EXISTS("); - break; - default: - elog(ERROR, "unexpected JsonExpr type: %d", jexpr->op); - break; - } - - get_rule_expr(jexpr->formatted_expr, context, showimplicit); - - appendStringInfoString(buf, ", "); - - get_json_path_spec(jexpr->path_spec, context, showimplicit); - - if (jexpr->passing_values) - { - ListCell *lc1, - *lc2; - bool needcomma = false; - - appendStringInfoString(buf, " PASSING "); - - forboth(lc1, jexpr->passing_names, - lc2, jexpr->passing_values) - { - if (needcomma) - appendStringInfoString(buf, ", "); - needcomma = true; - - get_rule_expr((Node *) lfirst(lc2), context, showimplicit); - appendStringInfo(buf, " AS %s", - ((String *) lfirst_node(String, lc1))->sval); - } - } - - if (jexpr->op != JSON_EXISTS_OP || - jexpr->returning->typid != BOOLOID) - get_json_returning(jexpr->returning, context->buf, - jexpr->op == JSON_QUERY_OP); - - get_json_expr_options(jexpr, context, - jexpr->op == JSON_EXISTS_OP ? - JSON_BEHAVIOR_FALSE : JSON_BEHAVIOR_NULL); - - appendStringInfoString(buf, ")"); - } - break; - case T_List: { char *sep; @@ -9993,7 +9747,6 @@ looks_like_function(Node *node) case T_MinMaxExpr: case T_SQLValueFunction: case T_XmlExpr: - case T_JsonExpr: /* these are all accepted by func_expr_common_subexpr */ return true; default: @@ -10139,103 +9892,17 @@ get_func_expr(FuncExpr *expr, deparse_context *context, appendStringInfoChar(buf, ')'); } -static void -get_json_constructor_options(JsonConstructorExpr *ctor, StringInfo buf) -{ - if (ctor->absent_on_null) - { - if (ctor->type == JSCTOR_JSON_OBJECT || - ctor->type == JSCTOR_JSON_OBJECTAGG) - appendStringInfoString(buf, " ABSENT ON NULL"); - } - else - { - if (ctor->type == JSCTOR_JSON_ARRAY || - ctor->type == JSCTOR_JSON_ARRAYAGG) - appendStringInfoString(buf, " NULL ON NULL"); - } - - if (ctor->unique) - appendStringInfoString(buf, " WITH UNIQUE KEYS"); - - if (!((ctor->type == JSCTOR_JSON_PARSE || - ctor->type == JSCTOR_JSON_SCALAR) && - ctor->returning->typid == JSONOID)) - get_json_returning(ctor->returning, buf, true); -} - -static void -get_json_constructor(JsonConstructorExpr *ctor, deparse_context *context, - bool showimplicit) -{ - StringInfo buf = context->buf; - const char *funcname; - int nargs; - ListCell *lc; - - switch (ctor->type) - { - case JSCTOR_JSON_PARSE: - funcname = "JSON"; - break; - case JSCTOR_JSON_SCALAR: - funcname = "JSON_SCALAR"; - break; - case JSCTOR_JSON_SERIALIZE: - funcname = "JSON_SERIALIZE"; - break; - case JSCTOR_JSON_OBJECT: - funcname = "JSON_OBJECT"; - break; - case JSCTOR_JSON_ARRAY: - funcname = "JSON_ARRAY"; - break; - case JSCTOR_JSON_OBJECTAGG: - get_json_agg_constructor(ctor, context, "JSON_OBJECTAGG", true); - return; - case JSCTOR_JSON_ARRAYAGG: - get_json_agg_constructor(ctor, context, "JSON_ARRAYAGG", false); - return; - default: - elog(ERROR, "invalid JsonConstructorExprType %d", ctor->type); - } - - appendStringInfo(buf, "%s(", funcname); - - nargs = 0; - foreach(lc, ctor->args) - { - if (nargs > 0) - { - const char *sep = ctor->type == JSCTOR_JSON_OBJECT && - (nargs % 2) != 0 ? " : " : ", "; - - appendStringInfoString(buf, sep); - } - - get_rule_expr((Node *) lfirst(lc), context, true); - - nargs++; - } - - get_json_constructor_options(ctor, buf); - - appendStringInfo(buf, ")"); -} - - /* - * get_agg_expr_helper - Parse back an Aggref node + * get_agg_expr - Parse back an Aggref node */ static void -get_agg_expr_helper(Aggref *aggref, deparse_context *context, - Aggref *original_aggref, const char *funcname, - const char *options, bool is_json_objectagg) +get_agg_expr(Aggref *aggref, deparse_context *context, + Aggref *original_aggref) { StringInfo buf = context->buf; Oid argtypes[FUNC_MAX_ARGS]; int nargs; - bool use_variadic = false; + bool use_variadic; /* * For a combining aggregate, we look up and deparse the corresponding @@ -10265,14 +9932,13 @@ get_agg_expr_helper(Aggref *aggref, deparse_context *context, /* Extract the argument types as seen by the parser */ nargs = get_aggregate_argtypes(aggref, argtypes); - if (!funcname) - funcname = generate_function_name(aggref->aggfnoid, nargs, NIL, - argtypes, aggref->aggvariadic, - &use_variadic, - context->special_exprkind); - /* Print the aggregate name, schema-qualified if needed */ - appendStringInfo(buf, "%s(%s", funcname, + appendStringInfo(buf, "%s(%s", + generate_function_name(aggref->aggfnoid, nargs, + NIL, argtypes, + aggref->aggvariadic, + &use_variadic, + context->special_exprkind), (aggref->aggdistinct != NIL) ? "DISTINCT " : ""); if (AGGKIND_IS_ORDERED_SET(aggref->aggkind)) @@ -10308,18 +9974,7 @@ get_agg_expr_helper(Aggref *aggref, deparse_context *context, if (tle->resjunk) continue; if (i++ > 0) - { - if (is_json_objectagg) - { - if (i > 2) - break; /* skip ABSENT ON NULL and WITH UNIQUE - * args */ - - appendStringInfoString(buf, " : "); - } - else - appendStringInfoString(buf, ", "); - } + appendStringInfoString(buf, ", "); if (use_variadic && i == nargs) appendStringInfoString(buf, "VARIADIC "); get_rule_expr(arg, context, true); @@ -10333,9 +9988,6 @@ get_agg_expr_helper(Aggref *aggref, deparse_context *context, } } - if (options) - appendStringInfoString(buf, options); - if (aggref->aggfilter != NULL) { appendStringInfoString(buf, ") FILTER (WHERE "); @@ -10346,16 +9998,6 @@ get_agg_expr_helper(Aggref *aggref, deparse_context *context, } /* - * get_agg_expr - Parse back an Aggref node - */ -static void -get_agg_expr(Aggref *aggref, deparse_context *context, Aggref *original_aggref) -{ - get_agg_expr_helper(aggref, context, original_aggref, NULL, NULL, - false); -} - -/* * This is a helper function for get_agg_expr(). It's used when we deparse * a combining Aggref; resolve_special_varno locates the corresponding partial * Aggref and then calls this. @@ -10374,12 +10016,10 @@ get_agg_combine_expr(Node *node, deparse_context *context, void *callback_arg) } /* - * get_windowfunc_expr_helper - Parse back a WindowFunc node + * get_windowfunc_expr - Parse back a WindowFunc node */ static void -get_windowfunc_expr_helper(WindowFunc *wfunc, deparse_context *context, - const char *funcname, const char *options, - bool is_json_objectagg) +get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context) { StringInfo buf = context->buf; Oid argtypes[FUNC_MAX_ARGS]; @@ -10403,30 +10043,16 @@ get_windowfunc_expr_helper(WindowFunc *wfunc, deparse_context *context, nargs++; } - if (!funcname) - funcname = generate_function_name(wfunc->winfnoid, nargs, argnames, - argtypes, false, NULL, - context->special_exprkind); - - appendStringInfo(buf, "%s(", funcname); - + appendStringInfo(buf, "%s(", + generate_function_name(wfunc->winfnoid, nargs, + argnames, argtypes, + false, NULL, + context->special_exprkind)); /* winstar can be set only in zero-argument aggregates */ if (wfunc->winstar) appendStringInfoChar(buf, '*'); else - { - if (is_json_objectagg) - { - get_rule_expr((Node *) linitial(wfunc->args), context, false); - appendStringInfoString(buf, " : "); - get_rule_expr((Node *) lsecond(wfunc->args), context, false); - } - else - get_rule_expr((Node *) wfunc->args, context, true); - } - - if (options) - appendStringInfoString(buf, options); + get_rule_expr((Node *) wfunc->args, context, true); if (wfunc->aggfilter != NULL) { @@ -10464,15 +10090,6 @@ get_windowfunc_expr_helper(WindowFunc *wfunc, deparse_context *context, } /* - * get_windowfunc_expr - Parse back a WindowFunc node - */ -static void -get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context) -{ - get_windowfunc_expr_helper(wfunc, context, NULL, NULL, false); -} - -/* * get_func_sql_syntax - Parse back a SQL-syntax function call * * Returns true if we successfully deparsed, false if we did not @@ -10712,31 +10329,6 @@ get_func_sql_syntax(FuncExpr *expr, deparse_context *context) return false; } -/* - * get_json_agg_constructor - Parse back an aggregate JsonConstructorExpr node - */ -static void -get_json_agg_constructor(JsonConstructorExpr *ctor, deparse_context *context, - const char *funcname, bool is_json_objectagg) -{ - StringInfoData options; - - initStringInfo(&options); - get_json_constructor_options(ctor, &options); - - if (IsA(ctor->func, Aggref)) - get_agg_expr_helper((Aggref *) ctor->func, context, - (Aggref *) ctor->func, - funcname, options.data, is_json_objectagg); - else if (IsA(ctor->func, WindowFunc)) - get_windowfunc_expr_helper((WindowFunc *) ctor->func, context, - funcname, options.data, - is_json_objectagg); - else - elog(ERROR, "invalid JsonConstructorExpr underlying node type: %d", - nodeTag(ctor->func)); -} - /* ---------- * get_coercion_expr * @@ -11106,14 +10698,16 @@ get_sublink_expr(SubLink *sublink, deparse_context *context) /* ---------- - * get_xmltable - Parse back a XMLTABLE function + * get_tablefunc - Parse back a table function * ---------- */ static void -get_xmltable(TableFunc *tf, deparse_context *context, bool showimplicit) +get_tablefunc(TableFunc *tf, deparse_context *context, bool showimplicit) { StringInfo buf = context->buf; + /* XMLTABLE is the only existing implementation. */ + appendStringInfoString(buf, "XMLTABLE("); if (tf->ns_uris != NIL) @@ -11204,271 +10798,6 @@ get_xmltable(TableFunc *tf, deparse_context *context, bool showimplicit) appendStringInfoChar(buf, ')'); } -/* - * get_json_nested_columns - Parse back nested JSON_TABLE columns - */ -static void -get_json_table_nested_columns(TableFunc *tf, Node *node, - deparse_context *context, bool showimplicit, - bool needcomma) -{ - if (IsA(node, JsonTableSibling)) - { - JsonTableSibling *n = (JsonTableSibling *) node; - - get_json_table_nested_columns(tf, n->larg, context, showimplicit, - needcomma); - get_json_table_nested_columns(tf, n->rarg, context, showimplicit, true); - } - else - { - JsonTableParent *n = castNode(JsonTableParent, node); - - if (needcomma) - appendStringInfoChar(context->buf, ','); - - appendStringInfoChar(context->buf, ' '); - appendContextKeyword(context, "NESTED PATH ", 0, 0, 0); - get_const_expr(n->path, context, -1); - appendStringInfo(context->buf, " AS %s", quote_identifier(n->name)); - get_json_table_columns(tf, n, context, showimplicit); - } -} - -/* - * get_json_table_plan - Parse back a JSON_TABLE plan - */ -static void -get_json_table_plan(TableFunc *tf, Node *node, deparse_context *context, - bool parenthesize) -{ - if (parenthesize) - appendStringInfoChar(context->buf, '('); - - if (IsA(node, JsonTableSibling)) - { - JsonTableSibling *n = (JsonTableSibling *) node; - - get_json_table_plan(tf, n->larg, context, - IsA(n->larg, JsonTableSibling) || - castNode(JsonTableParent, n->larg)->child); - - appendStringInfoString(context->buf, n->cross ? " CROSS " : " UNION "); - - get_json_table_plan(tf, n->rarg, context, - IsA(n->rarg, JsonTableSibling) || - castNode(JsonTableParent, n->rarg)->child); - } - else - { - JsonTableParent *n = castNode(JsonTableParent, node); - - appendStringInfoString(context->buf, quote_identifier(n->name)); - - if (n->child) - { - appendStringInfoString(context->buf, - n->outerJoin ? " OUTER " : " INNER "); - get_json_table_plan(tf, n->child, context, - IsA(n->child, JsonTableSibling)); - } - } - - if (parenthesize) - appendStringInfoChar(context->buf, ')'); -} - -/* - * get_json_table_columns - Parse back JSON_TABLE columns - */ -static void -get_json_table_columns(TableFunc *tf, JsonTableParent *node, - deparse_context *context, bool showimplicit) -{ - StringInfo buf = context->buf; - JsonExpr *jexpr = castNode(JsonExpr, tf->docexpr); - ListCell *lc_colname; - ListCell *lc_coltype; - ListCell *lc_coltypmod; - ListCell *lc_colvarexpr; - int colnum = 0; - - appendStringInfoChar(buf, ' '); - appendContextKeyword(context, "COLUMNS (", 0, 0, 0); - - if (PRETTY_INDENT(context)) - context->indentLevel += PRETTYINDENT_VAR; - - forfour(lc_colname, tf->colnames, - lc_coltype, tf->coltypes, - lc_coltypmod, tf->coltypmods, - lc_colvarexpr, tf->colvalexprs) - { - char *colname = strVal(lfirst(lc_colname)); - JsonExpr *colexpr; - Oid typid; - int32 typmod; - bool ordinality; - JsonBehaviorType default_behavior; - - typid = lfirst_oid(lc_coltype); - typmod = lfirst_int(lc_coltypmod); - colexpr = castNode(JsonExpr, lfirst(lc_colvarexpr)); - - if (colnum < node->colMin) - { - colnum++; - continue; - } - - if (colnum > node->colMax) - break; - - if (colnum > node->colMin) - appendStringInfoString(buf, ", "); - - colnum++; - - ordinality = !colexpr; - - appendContextKeyword(context, "", 0, 0, 0); - - appendStringInfo(buf, "%s %s", quote_identifier(colname), - ordinality ? "FOR ORDINALITY" : - format_type_with_typemod(typid, typmod)); - if (ordinality) - continue; - - if (colexpr->op == JSON_EXISTS_OP) - { - appendStringInfoString(buf, " EXISTS"); - default_behavior = JSON_BEHAVIOR_FALSE; - } - else - { - if (colexpr->op == JSON_QUERY_OP) - { - char typcategory; - bool typispreferred; - - get_type_category_preferred(typid, &typcategory, &typispreferred); - - if (typcategory == TYPCATEGORY_STRING) - appendStringInfoString(buf, - colexpr->format->format_type == JS_FORMAT_JSONB ? - " FORMAT JSONB" : " FORMAT JSON"); - } - - default_behavior = JSON_BEHAVIOR_NULL; - } - - if (jexpr->on_error->btype == JSON_BEHAVIOR_ERROR) - default_behavior = JSON_BEHAVIOR_ERROR; - - appendStringInfoString(buf, " PATH "); - - get_json_path_spec(colexpr->path_spec, context, showimplicit); - - get_json_expr_options(colexpr, context, default_behavior); - } - - if (node->child) - get_json_table_nested_columns(tf, node->child, context, showimplicit, - node->colMax >= node->colMin); - - if (PRETTY_INDENT(context)) - context->indentLevel -= PRETTYINDENT_VAR; - - appendContextKeyword(context, ")", 0, 0, 0); -} - -/* ---------- - * get_json_table - Parse back a JSON_TABLE function - * ---------- - */ -static void -get_json_table(TableFunc *tf, deparse_context *context, bool showimplicit) -{ - StringInfo buf = context->buf; - JsonExpr *jexpr = castNode(JsonExpr, tf->docexpr); - JsonTableParent *root = castNode(JsonTableParent, tf->plan); - - appendStringInfoString(buf, "JSON_TABLE("); - - if (PRETTY_INDENT(context)) - context->indentLevel += PRETTYINDENT_VAR; - - appendContextKeyword(context, "", 0, 0, 0); - - get_rule_expr(jexpr->formatted_expr, context, showimplicit); - - appendStringInfoString(buf, ", "); - - get_const_expr(root->path, context, -1); - - appendStringInfo(buf, " AS %s", quote_identifier(root->name)); - - if (jexpr->passing_values) - { - ListCell *lc1, - *lc2; - bool needcomma = false; - - appendStringInfoChar(buf, ' '); - appendContextKeyword(context, "PASSING ", 0, 0, 0); - - if (PRETTY_INDENT(context)) - context->indentLevel += PRETTYINDENT_VAR; - - forboth(lc1, jexpr->passing_names, - lc2, jexpr->passing_values) - { - if (needcomma) - appendStringInfoString(buf, ", "); - needcomma = true; - - appendContextKeyword(context, "", 0, 0, 0); - - get_rule_expr((Node *) lfirst(lc2), context, false); - appendStringInfo(buf, " AS %s", - quote_identifier((lfirst_node(String, lc1))->sval) - ); - } - - if (PRETTY_INDENT(context)) - context->indentLevel -= PRETTYINDENT_VAR; - } - - get_json_table_columns(tf, root, context, showimplicit); - - appendStringInfoChar(buf, ' '); - appendContextKeyword(context, "PLAN ", 0, 0, 0); - get_json_table_plan(tf, (Node *) root, context, true); - - if (jexpr->on_error->btype != JSON_BEHAVIOR_EMPTY) - get_json_behavior(jexpr->on_error, context, "ERROR"); - - if (PRETTY_INDENT(context)) - context->indentLevel -= PRETTYINDENT_VAR; - - appendContextKeyword(context, ")", 0, 0, 0); -} - -/* ---------- - * get_tablefunc - Parse back a table function - * ---------- - */ -static void -get_tablefunc(TableFunc *tf, deparse_context *context, bool showimplicit) -{ - /* XMLTABLE and JSON_TABLE are the only existing implementations. */ - - if (tf->functype == TFT_XMLTABLE) - get_xmltable(tf, context, showimplicit); - else if (tf->functype == TFT_JSON_TABLE) - get_json_table(tf, context, showimplicit); -} - /* ---------- * get_from_clause - Parse back a FROM clause * |