aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt
diff options
context:
space:
mode:
authorAndrew Dunstan <andrew@dunslane.net>2022-09-01 17:07:14 -0400
committerAndrew Dunstan <andrew@dunslane.net>2022-09-01 17:07:14 -0400
commit2f2b18bd3f554e96a8cc885b177211be12288e4a (patch)
tree344a5d33738de735f68b98361a54eb5944726f8f /src/backend/utils/adt
parent90247e742f849794d061a0444071647728054b45 (diff)
downloadpostgresql-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.c4
-rw-r--r--src/backend/utils/adt/formatting.c45
-rw-r--r--src/backend/utils/adt/json.c553
-rw-r--r--src/backend/utils/adt/jsonb.c352
-rw-r--r--src/backend/utils/adt/jsonb_util.c39
-rw-r--r--src/backend/utils/adt/jsonfuncs.c71
-rw-r--r--src/backend/utils/adt/jsonpath.c257
-rw-r--r--src/backend/utils/adt/jsonpath_exec.c844
-rw-r--r--src/backend/utils/adt/ruleutils.c719
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
*