1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-30 21:42:05 +03:00

SQL/JSON constructors

This patch introduces the SQL/JSON standard constructors for JSON:

JSON()
JSON_ARRAY()
JSON_ARRAYAGG()
JSON_OBJECT()
JSON_OBJECTAGG()

For the most part these functions provide facilities that mimic
existing json/jsonb functions. However, they also offer some useful
additional functionality. In addition to text input, the JSON() function
accepts bytea input, which it will decode and constuct a json value from.
The other functions provide useful options for handling duplicate keys
and null values.

This series of patches will be followed by a consolidated documentation
patch.

Nikita Glukhov

Reviewers have included (in no particular order) Andres Freund, Alexander
Korotkov, Pavel Stehule, Andrew Alsup, Erik Rijkers, Zihong Yu,
Himanshu Upadhyaya, Daniel Gustafsson, Justin Pryzby.

Discussion: https://postgr.es/m/cd0bb935-0158-78a7-08b5-904886deac4b@postgrespro.ru
This commit is contained in:
Andrew Dunstan
2022-03-03 13:02:10 -05:00
parent f79b803dcc
commit f4fb45d15c
37 changed files with 3615 additions and 130 deletions

View File

@ -457,6 +457,12 @@ 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);
@ -6245,7 +6251,8 @@ 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, WindowFunc)
|| IsA(expr, JsonConstructorExpr));
if (need_paren)
appendStringInfoChar(context->buf, '(');
@ -8093,6 +8100,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
case T_GroupingFunc:
case T_WindowFunc:
case T_FuncExpr:
case T_JsonConstructorExpr:
/* function-like: name(..) or name[..] */
return true;
@ -8380,12 +8388,12 @@ get_rule_expr_paren(Node *node, deparse_context *context,
* get_json_format - Parse back a JsonFormat node
*/
static void
get_json_format(JsonFormat *format, deparse_context *context)
get_json_format(JsonFormat *format, StringInfo buf)
{
if (format->format_type == JS_FORMAT_DEFAULT)
return;
appendStringInfoString(context->buf,
appendStringInfoString(buf,
format->format_type == JS_FORMAT_JSONB ?
" FORMAT JSONB" : " FORMAT JSON");
@ -8395,7 +8403,7 @@ get_json_format(JsonFormat *format, deparse_context *context)
format->encoding == JS_ENC_UTF16 ? "UTF16" :
format->encoding == JS_ENC_UTF32 ? "UTF32" : "UTF8";
appendStringInfo(context->buf, " ENCODING %s", encoding);
appendStringInfo(buf, " ENCODING %s", encoding);
}
}
@ -8403,20 +8411,20 @@ get_json_format(JsonFormat *format, deparse_context *context)
* get_json_returning - Parse back a JsonReturning structure
*/
static void
get_json_returning(JsonReturning *returning, deparse_context *context,
get_json_returning(JsonReturning *returning, StringInfo buf,
bool json_format_by_default)
{
if (!OidIsValid(returning->typid))
return;
appendStringInfo(context->buf, " RETURNING %s",
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, context);
get_json_format(returning->format, buf);
}
/* ----------
@ -9583,10 +9591,14 @@ get_rule_expr(Node *node, deparse_context *context,
JsonValueExpr *jve = (JsonValueExpr *) node;
get_rule_expr((Node *) jve->raw_expr, context, false);
get_json_format(jve->format, context);
get_json_format(jve->format, context->buf);
}
break;
case T_JsonConstructorExpr:
get_json_constructor((JsonConstructorExpr *) node, context, false);
break;
case T_List:
{
char *sep;
@ -9855,17 +9867,89 @@ 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");
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_OBJECT:
funcname = "JSON_OBJECT";
break;
case JSCTOR_JSON_ARRAY:
funcname = "JSON_ARRAY";
break;
case JSCTOR_JSON_OBJECTAGG:
return get_json_agg_constructor(ctor, context, "JSON_OBJECTAGG", true);
case JSCTOR_JSON_ARRAYAGG:
return get_json_agg_constructor(ctor, context, "JSON_ARRAYAGG", false);
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 - Parse back an Aggref node
* get_agg_expr_helper - Parse back an Aggref node
*/
static void
get_agg_expr(Aggref *aggref, deparse_context *context,
Aggref *original_aggref)
get_agg_expr_helper(Aggref *aggref, deparse_context *context,
Aggref *original_aggref, const char *funcname,
const char *options, bool is_json_objectagg)
{
StringInfo buf = context->buf;
Oid argtypes[FUNC_MAX_ARGS];
int nargs;
bool use_variadic;
bool use_variadic = false;
/*
* For a combining aggregate, we look up and deparse the corresponding
@ -9895,13 +9979,14 @@ get_agg_expr(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",
generate_function_name(aggref->aggfnoid, nargs,
NIL, argtypes,
aggref->aggvariadic,
&use_variadic,
context->special_exprkind),
appendStringInfo(buf, "%s(%s", funcname,
(aggref->aggdistinct != NIL) ? "DISTINCT " : "");
if (AGGKIND_IS_ORDERED_SET(aggref->aggkind))
@ -9937,7 +10022,17 @@ get_agg_expr(Aggref *aggref, deparse_context *context,
if (tle->resjunk)
continue;
if (i++ > 0)
appendStringInfoString(buf, ", ");
{
if (is_json_objectagg)
{
if (i > 2)
break; /* skip ABSENT ON NULL and WITH UNIQUE args */
appendStringInfoString(buf, " : ");
}
else
appendStringInfoString(buf, ", ");
}
if (use_variadic && i == nargs)
appendStringInfoString(buf, "VARIADIC ");
get_rule_expr(arg, context, true);
@ -9951,6 +10046,9 @@ get_agg_expr(Aggref *aggref, deparse_context *context,
}
}
if (options)
appendStringInfoString(buf, options);
if (aggref->aggfilter != NULL)
{
appendStringInfoString(buf, ") FILTER (WHERE ");
@ -9960,6 +10058,16 @@ get_agg_expr(Aggref *aggref, deparse_context *context,
appendStringInfoChar(buf, ')');
}
/*
* get_agg_expr - Parse back an Aggref node
*/
static void
get_agg_expr(Aggref *aggref, deparse_context *context, Aggref *original_aggref)
{
return 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
@ -9979,10 +10087,12 @@ get_agg_combine_expr(Node *node, deparse_context *context, void *callback_arg)
}
/*
* get_windowfunc_expr - Parse back a WindowFunc node
* get_windowfunc_expr_helper - Parse back a WindowFunc node
*/
static void
get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context)
get_windowfunc_expr_helper(WindowFunc *wfunc, deparse_context *context,
const char *funcname, const char *options,
bool is_json_objectagg)
{
StringInfo buf = context->buf;
Oid argtypes[FUNC_MAX_ARGS];
@ -10006,16 +10116,30 @@ get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context)
nargs++;
}
appendStringInfo(buf, "%s(",
generate_function_name(wfunc->winfnoid, nargs,
argnames, argtypes,
false, NULL,
context->special_exprkind));
if (!funcname)
funcname = generate_function_name(wfunc->winfnoid, nargs, argnames,
argtypes, false, NULL,
context->special_exprkind);
appendStringInfo(buf, "%s(", funcname);
/* winstar can be set only in zero-argument aggregates */
if (wfunc->winstar)
appendStringInfoChar(buf, '*');
else
get_rule_expr((Node *) wfunc->args, context, true);
{
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);
if (wfunc->aggfilter != NULL)
{
@ -10052,6 +10176,15 @@ get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context)
}
}
/*
* get_windowfunc_expr - Parse back a WindowFunc node
*/
static void
get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context)
{
return get_windowfunc_expr_helper(wfunc, context, NULL, NULL, false);
}
/*
* get_func_sql_syntax - Parse back a SQL-syntax function call
*
@ -10292,6 +10425,31 @@ 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))
return get_agg_expr_helper((Aggref *) ctor->func, context,
(Aggref *) ctor->func,
funcname, options.data, is_json_objectagg);
else if (IsA(ctor->func, WindowFunc))
return 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
*