aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/parser')
-rw-r--r--src/backend/parser/gram.y265
-rw-r--r--src/backend/parser/parse_expr.c589
-rw-r--r--src/backend/parser/parse_target.c13
-rw-r--r--src/backend/parser/parser.c16
4 files changed, 875 insertions, 8 deletions
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index a324983f5d8..c6613af9fe6 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -639,11 +639,31 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <node> json_format_clause_opt
json_representation
json_value_expr
+ json_func_expr
json_output_clause_opt
+ json_value_constructor
+ json_object_constructor
+ json_object_constructor_args
+ json_object_constructor_args_opt
+ json_object_args
+ json_object_func_args
+ json_array_constructor
+ json_name_and_value
+ json_aggregate_func
+ json_object_aggregate_constructor
+ json_array_aggregate_constructor
+
+%type <list> json_name_and_value_list
+ json_value_expr_list
+ json_array_aggregate_order_by_clause_opt
%type <ival> json_encoding
json_encoding_clause_opt
+%type <boolean> json_key_uniqueness_constraint_opt
+ json_object_constructor_null_clause_opt
+ json_array_constructor_null_clause_opt
+
/*
* Non-keyword token types. These are hard-wired into the "flex" lexer.
* They must be listed first so that their numeric codes do not depend on
@@ -669,7 +689,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
*/
/* ordinary key words in alphabetical order */
-%token <keyword> ABORT_P ABSOLUTE_P ACCESS ACTION ADD_P ADMIN AFTER
+%token <keyword> ABORT_P ABSENT ABSOLUTE_P ACCESS ACTION ADD_P ADMIN AFTER
AGGREGATE ALL ALSO ALTER ALWAYS ANALYSE ANALYZE AND ANY ARRAY AS ASC
ASENSITIVE ASSERTION ASSIGNMENT ASYMMETRIC ATOMIC AT ATTACH ATTRIBUTE AUTHORIZATION
@@ -706,9 +726,9 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
INNER_P INOUT INPUT_P INSENSITIVE INSERT INSTEAD INT_P INTEGER
INTERSECT INTERVAL INTO INVOKER IS ISNULL ISOLATION
- JOIN JSON
+ JOIN JSON JSON_ARRAY JSON_ARRAYAGG JSON_OBJECT JSON_OBJECTAGG
- KEY
+ KEY KEYS
LABEL LANGUAGE LARGE_P LAST_P LATERAL_P
LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL
@@ -772,7 +792,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
* as NOT, at least with respect to their left-hand subexpression.
* NULLS_LA and WITH_LA are needed to make the grammar LALR(1).
*/
-%token NOT_LA NULLS_LA WITH_LA
+%token NOT_LA NULLS_LA WITH_LA WITH_LA_UNIQUE WITHOUT_LA
/*
* The grammar likewise thinks these tokens are keywords, but they are never
@@ -826,11 +846,13 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
* Using the same precedence as IDENT seems right for the reasons given above.
*/
%nonassoc UNBOUNDED /* ideally would have same precedence as IDENT */
+%nonassoc ABSENT UNIQUE
%nonassoc IDENT PARTITION RANGE ROWS GROUPS PRECEDING FOLLOWING CUBE ROLLUP
%left Op OPERATOR /* multi-character ops and user-defined operators */
%left '+' '-'
%left '*' '/' '%'
%left '^'
+%left KEYS /* UNIQUE [ KEYS ] */
/* Unary Operators */
%left AT /* sets precedence for AT TIME ZONE */
%left COLLATE
@@ -848,6 +870,9 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
*/
%left JOIN CROSS LEFT FULL RIGHT INNER_P NATURAL
+%nonassoc empty_json_unique
+%left WITHOUT WITH_LA_UNIQUE
+
%%
/*
@@ -13415,7 +13440,7 @@ ConstInterval:
opt_timezone:
WITH_LA TIME ZONE { $$ = true; }
- | WITHOUT TIME ZONE { $$ = false; }
+ | WITHOUT_LA TIME ZONE { $$ = false; }
| /*EMPTY*/ { $$ = false; }
;
@@ -14028,6 +14053,17 @@ b_expr: c_expr
}
;
+json_key_uniqueness_constraint_opt:
+ WITH_LA_UNIQUE unique_keys { $$ = true; }
+ | WITHOUT unique_keys { $$ = false; }
+ | /* EMPTY */ %prec empty_json_unique { $$ = false; }
+ ;
+
+unique_keys:
+ UNIQUE
+ | UNIQUE KEYS
+ ;
+
/*
* Productions that can be used in both a_expr and b_expr.
*
@@ -14280,6 +14316,15 @@ func_expr: func_application within_group_clause filter_clause over_clause
n->over = $4;
$$ = (Node *) n;
}
+ | json_aggregate_func filter_clause over_clause
+ {
+ JsonAggConstructor *n = IsA($1, JsonObjectAgg) ?
+ ((JsonObjectAgg *) $1)->constructor :
+ ((JsonArrayAgg *) $1)->constructor;
+ n->agg_filter = $2;
+ n->over = $3;
+ $$ = (Node *) $1;
+ }
| func_expr_common_subexpr
{ $$ = $1; }
;
@@ -14293,6 +14338,7 @@ func_expr: func_application within_group_clause filter_clause over_clause
func_expr_windowless:
func_application { $$ = $1; }
| func_expr_common_subexpr { $$ = $1; }
+ | json_aggregate_func { $$ = $1; }
;
/*
@@ -14580,6 +14626,8 @@ func_expr_common_subexpr:
n->location = @1;
$$ = (Node *)n;
}
+ | json_func_expr
+ { $$ = $1; }
;
/*
@@ -15279,11 +15327,14 @@ opt_asymmetric: ASYMMETRIC
;
/* SQL/JSON support */
+json_func_expr:
+ json_value_constructor
+ ;
json_value_expr:
a_expr json_format_clause_opt
{
- $$ = (Node *) makeJsonValueExpr((Expr *) $1, $2);
+ $$ = (Node *) makeJsonValueExpr((Expr *) $1, castNode(JsonFormat, $2));
}
;
@@ -15291,7 +15342,7 @@ json_format_clause_opt:
FORMAT json_representation
{
$$ = $2;
- $$.location = @1;
+ castNode(JsonFormat, $$)->location = @1;
}
| /* EMPTY */
{
@@ -15321,10 +15372,196 @@ json_output_clause_opt:
{
JsonOutput *n = makeNode(JsonOutput);
n->typeName = $2;
- n->returning.format = $3;
+ n->returning = makeNode(JsonReturning);
+ n->returning->format = (JsonFormat *) $3;
$$ = (Node *) n;
}
| /* EMPTY */ { $$ = NULL; }
+ ;
+
+json_value_constructor:
+ json_object_constructor
+ | json_array_constructor
+ ;
+
+json_object_constructor:
+ JSON_OBJECT '(' json_object_args ')'
+ {
+ $$ = $3;
+ }
+ ;
+
+json_object_args:
+ json_object_constructor_args
+ | json_object_func_args
+ ;
+
+json_object_func_args:
+ func_arg_list
+ {
+ List *func = list_make1(makeString("json_object"));
+ $$ = (Node *) makeFuncCall(func, $1, COERCE_EXPLICIT_CALL, @1);
+ }
+ ;
+
+json_object_constructor_args:
+ json_object_constructor_args_opt json_output_clause_opt
+ {
+ JsonObjectConstructor *n = (JsonObjectConstructor *) $1;
+ n->output = (JsonOutput *) $2;
+ n->location = @1;
+ $$ = (Node *) n;
+ }
+ ;
+
+json_object_constructor_args_opt:
+ json_name_and_value_list
+ json_object_constructor_null_clause_opt
+ json_key_uniqueness_constraint_opt
+ {
+ JsonObjectConstructor *n = makeNode(JsonObjectConstructor);
+ n->exprs = $1;
+ n->absent_on_null = $2;
+ n->unique = $3;
+ $$ = (Node *) n;
+ }
+ | /* EMPTY */
+ {
+ JsonObjectConstructor *n = makeNode(JsonObjectConstructor);
+ n->exprs = NULL;
+ n->absent_on_null = false;
+ n->unique = false;
+ $$ = (Node *) n;
+ }
+ ;
+
+json_name_and_value_list:
+ json_name_and_value
+ { $$ = list_make1($1); }
+ | json_name_and_value_list ',' json_name_and_value
+ { $$ = lappend($1, $3); }
+ ;
+
+json_name_and_value:
+/* TODO This is not supported due to conflicts
+ KEY c_expr VALUE_P json_value_expr %prec POSTFIXOP
+ { $$ = makeJsonKeyValue($2, $4); }
+ |
+*/
+ c_expr VALUE_P json_value_expr
+ { $$ = makeJsonKeyValue($1, $3); }
+ |
+ a_expr ':' json_value_expr
+ { $$ = makeJsonKeyValue($1, $3); }
+ ;
+
+json_object_constructor_null_clause_opt:
+ NULL_P ON NULL_P { $$ = false; }
+ | ABSENT ON NULL_P { $$ = true; }
+ | /* EMPTY */ { $$ = false; }
+ ;
+
+json_array_constructor:
+ JSON_ARRAY '('
+ json_value_expr_list
+ json_array_constructor_null_clause_opt
+ json_output_clause_opt
+ ')'
+ {
+ JsonArrayConstructor *n = makeNode(JsonArrayConstructor);
+ n->exprs = $3;
+ n->absent_on_null = $4;
+ n->output = (JsonOutput *) $5;
+ n->location = @1;
+ $$ = (Node *) n;
+ }
+ | JSON_ARRAY '('
+ select_no_parens
+ /* json_format_clause_opt */
+ /* json_array_constructor_null_clause_opt */
+ json_output_clause_opt
+ ')'
+ {
+ JsonArrayQueryConstructor *n = makeNode(JsonArrayQueryConstructor);
+ n->query = $3;
+ n->format = makeJsonFormat(JS_FORMAT_DEFAULT, JS_ENC_DEFAULT, -1);
+ /* n->format = $4; */
+ n->absent_on_null = true /* $5 */;
+ n->output = (JsonOutput *) $4;
+ n->location = @1;
+ $$ = (Node *) n;
+ }
+ | JSON_ARRAY '('
+ json_output_clause_opt
+ ')'
+ {
+ JsonArrayConstructor *n = makeNode(JsonArrayConstructor);
+ n->exprs = NIL;
+ n->absent_on_null = true;
+ n->output = (JsonOutput *) $3;
+ n->location = @1;
+ $$ = (Node *) n;
+ }
+ ;
+
+json_value_expr_list:
+ json_value_expr { $$ = list_make1($1); }
+ | json_value_expr_list ',' json_value_expr { $$ = lappend($1, $3);}
+ ;
+
+json_array_constructor_null_clause_opt:
+ NULL_P ON NULL_P { $$ = false; }
+ | ABSENT ON NULL_P { $$ = true; }
+ | /* EMPTY */ { $$ = true; }
+ ;
+
+json_aggregate_func:
+ json_object_aggregate_constructor
+ | json_array_aggregate_constructor
+ ;
+
+json_object_aggregate_constructor:
+ JSON_OBJECTAGG '('
+ json_name_and_value
+ json_object_constructor_null_clause_opt
+ json_key_uniqueness_constraint_opt
+ json_output_clause_opt
+ ')'
+ {
+ JsonObjectAgg *n = makeNode(JsonObjectAgg);
+ n->arg = (JsonKeyValue *) $3;
+ n->absent_on_null = $4;
+ n->unique = $5;
+ n->constructor = makeNode(JsonAggConstructor);
+ n->constructor->output = (JsonOutput *) $6;
+ n->constructor->agg_order = NULL;
+ n->constructor->location = @1;
+ $$ = (Node *) n;
+ }
+ ;
+
+json_array_aggregate_constructor:
+ JSON_ARRAYAGG '('
+ json_value_expr
+ json_array_aggregate_order_by_clause_opt
+ json_array_constructor_null_clause_opt
+ json_output_clause_opt
+ ')'
+ {
+ JsonArrayAgg *n = makeNode(JsonArrayAgg);
+ n->arg = (JsonValueExpr *) $3;
+ n->absent_on_null = $5;
+ n->constructor = makeNode(JsonAggConstructor);
+ n->constructor->agg_order = $4;
+ n->constructor->output = (JsonOutput *) $6;
+ n->constructor->location = @1;
+ $$ = (Node *) n;
+ }
+ ;
+
+json_array_aggregate_order_by_clause_opt:
+ ORDER BY sortby_list { $$ = $3; }
+ | /* EMPTY */ { $$ = NIL; }
;
/*****************************************************************************
@@ -15771,6 +16008,7 @@ BareColLabel: IDENT { $$ = $1; }
*/
unreserved_keyword:
ABORT_P
+ | ABSENT
| ABSOLUTE_P
| ACCESS
| ACTION
@@ -15901,6 +16139,7 @@ unreserved_keyword:
| ISOLATION
| JSON
| KEY
+ | KEYS
| LABEL
| LANGUAGE
| LARGE_P
@@ -16109,6 +16348,10 @@ col_name_keyword:
| INT_P
| INTEGER
| INTERVAL
+ | JSON_ARRAY
+ | JSON_ARRAYAGG
+ | JSON_OBJECT
+ | JSON_OBJECTAGG
| LEAST
| NATIONAL
| NCHAR
@@ -16277,6 +16520,7 @@ reserved_keyword:
*/
bare_label_keyword:
ABORT_P
+ | ABSENT
| ABSOLUTE_P
| ACCESS
| ACTION
@@ -16462,7 +16706,12 @@ bare_label_keyword:
| ISOLATION
| JOIN
| JSON
+ | JSON_ARRAY
+ | JSON_ARRAYAGG
+ | JSON_OBJECT
+ | JSON_OBJECTAGG
| KEY
+ | KEYS
| LABEL
| LANGUAGE
| LARGE_P
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 985ddbedf11..6b93a76bca6 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -15,6 +15,8 @@
#include "postgres.h"
+#include "catalog/pg_aggregate.h"
+#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "commands/dbcommands.h"
#include "miscadmin.h"
@@ -75,6 +77,14 @@ static Node *transformWholeRowRef(ParseState *pstate,
static Node *transformIndirection(ParseState *pstate, A_Indirection *ind);
static Node *transformTypeCast(ParseState *pstate, TypeCast *tc);
static Node *transformCollateClause(ParseState *pstate, CollateClause *c);
+static Node *transformJsonObjectConstructor(ParseState *pstate,
+ JsonObjectConstructor *ctor);
+static Node *transformJsonArrayConstructor(ParseState *pstate,
+ JsonArrayConstructor *ctor);
+static Node *transformJsonArrayQueryConstructor(ParseState *pstate,
+ JsonArrayQueryConstructor *ctor);
+static Node *transformJsonObjectAgg(ParseState *pstate, JsonObjectAgg *agg);
+static Node *transformJsonArrayAgg(ParseState *pstate, JsonArrayAgg *agg);
static Node *make_row_comparison_op(ParseState *pstate, List *opname,
List *largs, List *rargs, int location);
static Node *make_row_distinct_op(ParseState *pstate, List *opname,
@@ -302,6 +312,26 @@ transformExprRecurse(ParseState *pstate, Node *expr)
break;
}
+ case T_JsonObjectConstructor:
+ result = transformJsonObjectConstructor(pstate, (JsonObjectConstructor *) expr);
+ break;
+
+ case T_JsonArrayConstructor:
+ result = transformJsonArrayConstructor(pstate, (JsonArrayConstructor *) expr);
+ break;
+
+ case T_JsonArrayQueryConstructor:
+ result = transformJsonArrayQueryConstructor(pstate, (JsonArrayQueryConstructor *) expr);
+ break;
+
+ case T_JsonObjectAgg:
+ result = transformJsonObjectAgg(pstate, (JsonObjectAgg *) expr);
+ break;
+
+ case T_JsonArrayAgg:
+ result = transformJsonArrayAgg(pstate, (JsonArrayAgg *) expr);
+ break;
+
default:
/* should not reach here */
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
@@ -3280,3 +3310,562 @@ transformJsonValueExpr(ParseState *pstate, JsonValueExpr *ve,
return expr;
}
+
+/*
+ * Checks specified output format for its applicability to the target type.
+ */
+static void
+checkJsonOutputFormat(ParseState *pstate, const JsonFormat *format,
+ Oid targettype, bool allow_format_for_non_strings)
+{
+ if (!allow_format_for_non_strings &&
+ format->format_type != JS_FORMAT_DEFAULT &&
+ (targettype != BYTEAOID &&
+ targettype != JSONOID &&
+ targettype != JSONBOID))
+ {
+ char typcategory;
+ bool typispreferred;
+
+ get_type_category_preferred(targettype, &typcategory, &typispreferred);
+
+ if (typcategory != TYPCATEGORY_STRING)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ parser_errposition(pstate, format->location),
+ errmsg("cannot use JSON format with non-string output types")));
+ }
+
+ if (format->format_type == JS_FORMAT_JSON)
+ {
+ JsonEncoding enc = format->encoding != JS_ENC_DEFAULT ?
+ format->encoding : JS_ENC_UTF8;
+
+ if (targettype != BYTEAOID &&
+ format->encoding != JS_ENC_DEFAULT)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ parser_errposition(pstate, format->location),
+ errmsg("cannot set JSON encoding for non-bytea output types")));
+
+ if (enc != JS_ENC_UTF8)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("unsupported JSON encoding"),
+ errhint("only UTF8 JSON encoding is supported"),
+ parser_errposition(pstate, format->location)));
+ }
+}
+
+/*
+ * Transform JSON output clause.
+ *
+ * Assigns target type oid and modifier.
+ * Assigns default format or checks specified format for its applicability to
+ * the target type.
+ */
+static JsonReturning *
+transformJsonOutput(ParseState *pstate, const JsonOutput *output,
+ bool allow_format)
+{
+ JsonReturning *ret;
+
+ /* if output clause is not specified, make default clause value */
+ if (!output)
+ {
+ ret = makeNode(JsonReturning);
+
+ ret->format = makeJsonFormat(JS_FORMAT_DEFAULT, JS_ENC_DEFAULT, -1);
+ ret->typid = InvalidOid;
+ ret->typmod = -1;
+
+ return ret;
+ }
+
+ ret = copyObject(output->returning);
+
+ typenameTypeIdAndMod(pstate, output->typeName, &ret->typid, &ret->typmod);
+
+ if (output->typeName->setof)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("returning SETOF types is not supported in SQL/JSON functions")));
+
+ if (ret->format->format_type == JS_FORMAT_DEFAULT)
+ /* assign JSONB format when returning jsonb, or JSON format otherwise */
+ ret->format->format_type =
+ ret->typid == JSONBOID ? JS_FORMAT_JSONB : JS_FORMAT_JSON;
+ else
+ checkJsonOutputFormat(pstate, ret->format, ret->typid, allow_format);
+
+ return ret;
+}
+
+/*
+ * Transform JSON output clause of JSON contructor functions.
+ *
+ * Derive RETURNING type, if not specified, from argument types.
+ */
+static JsonReturning *
+transformJsonConstructorOutput(ParseState *pstate, JsonOutput *output,
+ List *args)
+{
+ JsonReturning *returning = transformJsonOutput(pstate, output, true);
+
+ if (!OidIsValid(returning->typid))
+ {
+ ListCell *lc;
+ bool have_json = false;
+ bool have_jsonb = false;
+
+ foreach(lc, args)
+ {
+ Node *expr = lfirst(lc);
+ Oid typid = exprType(expr);
+
+ have_json |= typid == JSONOID;
+ have_jsonb |= typid == JSONBOID;
+
+ if (have_jsonb)
+ break;
+ }
+
+ if (have_jsonb)
+ {
+ returning->typid = JSONBOID;
+ returning->format->format_type = JS_FORMAT_JSONB;
+ }
+ else
+ {
+ /* Note: this includes the have_json case */
+
+ /* XXX TEXT is default by the standard, but we return JSON */
+ returning->typid = JSONOID;
+ returning->format->format_type = JS_FORMAT_JSON;
+ }
+
+ returning->typmod = -1;
+ }
+
+ return returning;
+}
+
+/*
+ * Coerce json[b]-valued function expression to the output type.
+ */
+static Node *
+coerceJsonFuncExpr(ParseState *pstate, Node *expr,
+ const JsonReturning *returning, bool report_error)
+{
+ Node *res;
+ int location;
+ Oid exprtype = exprType(expr);
+
+ /* if output type is not specified or equals to function type, return */
+ if (!OidIsValid(returning->typid) || returning->typid == exprtype)
+ return expr;
+
+ location = exprLocation(expr);
+
+ if (location < 0)
+ location = returning ? returning->format->location : -1;
+
+ /* special case for RETURNING bytea FORMAT json */
+ if (returning->format->format_type == JS_FORMAT_JSON &&
+ returning->typid == BYTEAOID)
+ {
+ /* encode json text into bytea using pg_convert_to() */
+ Node *texpr = coerce_to_specific_type(pstate, expr, TEXTOID,
+ "JSON_FUNCTION");
+ Const *enc = getJsonEncodingConst(returning->format);
+ FuncExpr *fexpr = makeFuncExpr(F_CONVERT_TO, BYTEAOID,
+ list_make2(texpr, enc),
+ InvalidOid, InvalidOid,
+ COERCE_EXPLICIT_CALL);
+ fexpr->location = location;
+
+ return (Node *) fexpr;
+ }
+
+ /* try to coerce expression to the output type */
+ res = coerce_to_target_type(pstate, expr, exprtype,
+ returning->typid, returning->typmod,
+ /* XXX throwing errors when casting to char(N) */
+ COERCION_EXPLICIT,
+ COERCE_EXPLICIT_CAST,
+ location);
+
+ if (!res && report_error)
+ ereport(ERROR,
+ (errcode(ERRCODE_CANNOT_COERCE),
+ errmsg("cannot cast type %s to %s",
+ format_type_be(exprtype),
+ format_type_be(returning->typid)),
+ parser_coercion_errposition(pstate, location, expr)));
+
+ return res;
+}
+
+static Node *
+makeJsonConstructorExpr(ParseState *pstate, JsonConstructorType type,
+ List *args, Expr *fexpr, JsonReturning *returning,
+ bool unique, bool absent_on_null, int location)
+{
+ JsonConstructorExpr *jsctor = makeNode(JsonConstructorExpr);
+ Node *placeholder;
+ Node *coercion;
+ Oid intermediate_typid =
+ returning->format->format_type == JS_FORMAT_JSONB ? JSONBOID : JSONOID;
+
+ jsctor->args = args;
+ jsctor->func = fexpr;
+ jsctor->type = type;
+ jsctor->returning = returning;
+ jsctor->unique = unique;
+ jsctor->absent_on_null = absent_on_null;
+ jsctor->location = location;
+
+ if (fexpr)
+ placeholder = makeCaseTestExpr((Node *) fexpr);
+ else
+ {
+ CaseTestExpr *cte = makeNode(CaseTestExpr);
+
+ cte->typeId = intermediate_typid;
+ cte->typeMod = -1;
+ cte->collation = InvalidOid;
+
+ placeholder = (Node *) cte;
+ }
+
+ coercion = coerceJsonFuncExpr(pstate, placeholder, returning, true);
+
+ if (coercion != placeholder)
+ jsctor->coercion = (Expr *) coercion;
+
+ return (Node *) jsctor;
+}
+
+/*
+ * Transform JSON_OBJECT() constructor.
+ *
+ * JSON_OBJECT() is transformed into json[b]_build_object[_ext]() call
+ * depending on the output JSON format. The first two arguments of
+ * json[b]_build_object_ext() are absent_on_null and check_key_uniqueness.
+ *
+ * Then function call result is coerced to the target type.
+ */
+static Node *
+transformJsonObjectConstructor(ParseState *pstate, JsonObjectConstructor *ctor)
+{
+ JsonReturning *returning;
+ List *args = NIL;
+
+ /* transform key-value pairs, if any */
+ if (ctor->exprs)
+ {
+ ListCell *lc;
+
+ /* transform and append key-value arguments */
+ foreach(lc, ctor->exprs)
+ {
+ JsonKeyValue *kv = castNode(JsonKeyValue, lfirst(lc));
+ Node *key = transformExprRecurse(pstate, (Node *) kv->key);
+ Node *val = transformJsonValueExpr(pstate, kv->value,
+ JS_FORMAT_DEFAULT);
+
+ args = lappend(args, key);
+ args = lappend(args, val);
+ }
+ }
+
+ returning = transformJsonConstructorOutput(pstate, ctor->output, args);
+
+ return makeJsonConstructorExpr(pstate, JSCTOR_JSON_OBJECT, args, NULL,
+ returning, ctor->unique,
+ ctor->absent_on_null, ctor->location);
+}
+
+/*
+ * Transform JSON_ARRAY(query [FORMAT] [RETURNING] [ON NULL]) into
+ * (SELECT JSON_ARRAYAGG(a [FORMAT] [RETURNING] [ON NULL]) FROM (query) q(a))
+ */
+static Node *
+transformJsonArrayQueryConstructor(ParseState *pstate,
+ JsonArrayQueryConstructor *ctor)
+{
+ SubLink *sublink = makeNode(SubLink);
+ SelectStmt *select = makeNode(SelectStmt);
+ RangeSubselect *range = makeNode(RangeSubselect);
+ Alias *alias = makeNode(Alias);
+ ResTarget *target = makeNode(ResTarget);
+ JsonArrayAgg *agg = makeNode(JsonArrayAgg);
+ ColumnRef *colref = makeNode(ColumnRef);
+ Query *query;
+ ParseState *qpstate;
+
+ /* Transform query only for counting target list entries. */
+ qpstate = make_parsestate(pstate);
+
+ query = transformStmt(qpstate, ctor->query);
+
+ if (count_nonjunk_tlist_entries(query->targetList) != 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("subquery must return only one column"),
+ parser_errposition(pstate, ctor->location)));
+
+ free_parsestate(qpstate);
+
+ colref->fields = list_make2(makeString(pstrdup("q")),
+ makeString(pstrdup("a")));
+ colref->location = ctor->location;
+
+ agg->arg = makeJsonValueExpr((Expr *) colref, ctor->format);
+ agg->absent_on_null = ctor->absent_on_null;
+ agg->constructor = makeNode(JsonAggConstructor);
+ agg->constructor->agg_order = NIL;
+ agg->constructor->output = ctor->output;
+ agg->constructor->location = ctor->location;
+
+ target->name = NULL;
+ target->indirection = NIL;
+ target->val = (Node *) agg;
+ target->location = ctor->location;
+
+ alias->aliasname = pstrdup("q");
+ alias->colnames = list_make1(makeString(pstrdup("a")));
+
+ range->lateral = false;
+ range->subquery = ctor->query;
+ range->alias = alias;
+
+ select->targetList = list_make1(target);
+ select->fromClause = list_make1(range);
+
+ sublink->subLinkType = EXPR_SUBLINK;
+ sublink->subLinkId = 0;
+ sublink->testexpr = NULL;
+ sublink->operName = NIL;
+ sublink->subselect = (Node *) select;
+ sublink->location = ctor->location;
+
+ return transformExprRecurse(pstate, (Node *) sublink);
+}
+
+/*
+ * Common code for JSON_OBJECTAGG and JSON_ARRAYAGG transformation.
+ */
+static Node *
+transformJsonAggConstructor(ParseState *pstate, JsonAggConstructor *agg_ctor,
+ JsonReturning *returning, List *args,
+ const char *aggfn, Oid aggtype,
+ JsonConstructorType ctor_type,
+ bool unique, bool absent_on_null)
+{
+ Oid aggfnoid;
+ Node *node;
+ Expr *aggfilter = agg_ctor->agg_filter ? (Expr *)
+ transformWhereClause(pstate, agg_ctor->agg_filter,
+ EXPR_KIND_FILTER, "FILTER") : NULL;
+
+ aggfnoid = DatumGetInt32(DirectFunctionCall1(regprocin,
+ CStringGetDatum(aggfn)));
+
+ if (agg_ctor->over)
+ {
+ /* window function */
+ WindowFunc *wfunc = makeNode(WindowFunc);
+
+ wfunc->winfnoid = aggfnoid;
+ wfunc->wintype = aggtype;
+ /* wincollid and inputcollid will be set by parse_collate.c */
+ wfunc->args = args;
+ /* winref will be set by transformWindowFuncCall */
+ wfunc->winstar = false;
+ wfunc->winagg = true;
+ wfunc->aggfilter = aggfilter;
+ wfunc->location = agg_ctor->location;
+
+ /*
+ * ordered aggs not allowed in windows yet
+ */
+ if (agg_ctor->agg_order != NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("aggregate ORDER BY is not implemented for window functions"),
+ parser_errposition(pstate, agg_ctor->location)));
+
+ /* parse_agg.c does additional window-func-specific processing */
+ transformWindowFuncCall(pstate, wfunc, agg_ctor->over);
+
+ node = (Node *) wfunc;
+ }
+ else
+ {
+ Aggref *aggref = makeNode(Aggref);
+
+ aggref->aggfnoid = aggfnoid;
+ aggref->aggtype = aggtype;
+
+ /* aggcollid and inputcollid will be set by parse_collate.c */
+ aggref->aggtranstype = InvalidOid; /* will be set by planner */
+ /* aggargtypes will be set by transformAggregateCall */
+ /* aggdirectargs and args will be set by transformAggregateCall */
+ /* aggorder and aggdistinct will be set by transformAggregateCall */
+ aggref->aggfilter = aggfilter;
+ aggref->aggstar = false;
+ aggref->aggvariadic = false;
+ aggref->aggkind = AGGKIND_NORMAL;
+ /* agglevelsup will be set by transformAggregateCall */
+ aggref->aggsplit = AGGSPLIT_SIMPLE; /* planner might change this */
+ aggref->location = agg_ctor->location;
+
+ transformAggregateCall(pstate, aggref, args, agg_ctor->agg_order, false);
+
+ node = (Node *) aggref;
+ }
+
+ return makeJsonConstructorExpr(pstate, ctor_type, NIL, (Expr *) node,
+ returning, unique, absent_on_null,
+ agg_ctor->location);
+}
+
+/*
+ * Transform JSON_OBJECTAGG() aggregate function.
+ *
+ * JSON_OBJECTAGG() is transformed into
+ * json[b]_objectagg(key, value, absent_on_null, check_unique) call depending on
+ * the output JSON format. Then the function call result is coerced to the
+ * target output type.
+ */
+static Node *
+transformJsonObjectAgg(ParseState *pstate, JsonObjectAgg *agg)
+{
+ JsonReturning *returning;
+ Node *key;
+ Node *val;
+ List *args;
+ const char *aggfnname;
+ Oid aggtype;
+
+ key = transformExprRecurse(pstate, (Node *) agg->arg->key);
+ val = transformJsonValueExpr(pstate, agg->arg->value, JS_FORMAT_DEFAULT);
+ args = list_make2(key, val);
+
+ returning = transformJsonConstructorOutput(pstate, agg->constructor->output,
+ args);
+
+ if (returning->format->format_type == JS_FORMAT_JSONB)
+ {
+ if (agg->absent_on_null)
+ if (agg->unique)
+ aggfnname = "pg_catalog.jsonb_object_agg_unique_strict"; /* F_JSONB_OBJECT_AGG_UNIQUE_STRICT */
+ else
+ aggfnname = "pg_catalog.jsonb_object_agg_strict"; /* F_JSONB_OBJECT_AGG_STRICT */
+ else
+ if (agg->unique)
+ aggfnname = "pg_catalog.jsonb_object_agg_unique"; /* F_JSONB_OBJECT_AGG_UNIQUE */
+ else
+ aggfnname = "pg_catalog.jsonb_object_agg"; /* F_JSONB_OBJECT_AGG */
+
+ aggtype = JSONBOID;
+ }
+ else
+ {
+ if (agg->absent_on_null)
+ if (agg->unique)
+ aggfnname = "pg_catalog.json_object_agg_unique_strict"; /* F_JSON_OBJECT_AGG_UNIQUE_STRICT */
+ else
+ aggfnname = "pg_catalog.json_object_agg_strict"; /* F_JSON_OBJECT_AGG_STRICT */
+ else
+ if (agg->unique)
+ aggfnname = "pg_catalog.json_object_agg_unique"; /* F_JSON_OBJECT_AGG_UNIQUE */
+ else
+ aggfnname = "pg_catalog.json_object_agg"; /* F_JSON_OBJECT_AGG */
+
+ aggtype = JSONOID;
+ }
+
+ return transformJsonAggConstructor(pstate, agg->constructor, returning,
+ args, aggfnname, aggtype,
+ JSCTOR_JSON_OBJECTAGG,
+ agg->unique, agg->absent_on_null);
+}
+
+/*
+ * Transform JSON_ARRAYAGG() aggregate function.
+ *
+ * JSON_ARRAYAGG() is transformed into json[b]_agg[_strict]() call depending
+ * on the output JSON format and absent_on_null. Then the function call result
+ * is coerced to the target output type.
+ */
+static Node *
+transformJsonArrayAgg(ParseState *pstate, JsonArrayAgg *agg)
+{
+ JsonReturning *returning;
+ Node *arg;
+ const char *aggfnname;
+ Oid aggtype;
+
+ arg = transformJsonValueExpr(pstate, agg->arg, JS_FORMAT_DEFAULT);
+
+ returning = transformJsonConstructorOutput(pstate, agg->constructor->output,
+ list_make1(arg));
+
+ if (returning->format->format_type == JS_FORMAT_JSONB)
+ {
+ aggfnname = agg->absent_on_null ?
+ "pg_catalog.jsonb_agg_strict" : "pg_catalog.jsonb_agg";
+ aggtype = JSONBOID;
+ }
+ else
+ {
+ aggfnname = agg->absent_on_null ?
+ "pg_catalog.json_agg_strict" : "pg_catalog.json_agg";
+ aggtype = JSONOID;
+ }
+
+ return transformJsonAggConstructor(pstate, agg->constructor, returning,
+ list_make1(arg), aggfnname, aggtype,
+ JSCTOR_JSON_ARRAYAGG,
+ false, agg->absent_on_null);
+}
+
+/*
+ * Transform JSON_ARRAY() constructor.
+ *
+ * JSON_ARRAY() is transformed into json[b]_build_array[_ext]() call
+ * depending on the output JSON format. The first argument of
+ * json[b]_build_array_ext() is absent_on_null.
+ *
+ * Then function call result is coerced to the target type.
+ */
+static Node *
+transformJsonArrayConstructor(ParseState *pstate, JsonArrayConstructor *ctor)
+{
+ JsonReturning *returning;
+ List *args = NIL;
+
+ /* transform element expressions, if any */
+ if (ctor->exprs)
+ {
+ ListCell *lc;
+
+ /* transform and append element arguments */
+ foreach(lc, ctor->exprs)
+ {
+ JsonValueExpr *jsval = castNode(JsonValueExpr, lfirst(lc));
+ Node *val = transformJsonValueExpr(pstate, jsval,
+ JS_FORMAT_DEFAULT);
+
+ args = lappend(args, val);
+ }
+ }
+
+ returning = transformJsonConstructorOutput(pstate, ctor->output, args);
+
+ return makeJsonConstructorExpr(pstate, JSCTOR_JSON_ARRAY, args, NULL,
+ returning, false, ctor->absent_on_null,
+ ctor->location);
+}
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index 059eeb9e94d..204d2857733 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -1957,6 +1957,19 @@ FigureColnameInternal(Node *node, char **name)
case T_XmlSerialize:
*name = "xmlserialize";
return 2;
+ case T_JsonObjectConstructor:
+ *name = "json_object";
+ return 2;
+ case T_JsonArrayConstructor:
+ case T_JsonArrayQueryConstructor:
+ *name = "json_array";
+ return 2;
+ case T_JsonObjectAgg:
+ *name = "json_objectagg";
+ return 2;
+ case T_JsonArrayAgg:
+ *name = "json_arrayagg";
+ return 2;
default:
break;
}
diff --git a/src/backend/parser/parser.c b/src/backend/parser/parser.c
index 50227cc0989..eee0a29c08f 100644
--- a/src/backend/parser/parser.c
+++ b/src/backend/parser/parser.c
@@ -150,6 +150,9 @@ base_yylex(YYSTYPE *lvalp, YYLTYPE *llocp, core_yyscan_t yyscanner)
case USCONST:
cur_token_length = strlen(yyextra->core_yy_extra.scanbuf + *llocp);
break;
+ case WITHOUT:
+ cur_token_length = 7;
+ break;
default:
return cur_token;
}
@@ -221,6 +224,19 @@ base_yylex(YYSTYPE *lvalp, YYLTYPE *llocp, core_yyscan_t yyscanner)
case ORDINALITY:
cur_token = WITH_LA;
break;
+ case UNIQUE:
+ cur_token = WITH_LA_UNIQUE;
+ break;
+ }
+ break;
+
+ case WITHOUT:
+ /* Replace WITHOUT by WITHOUT_LA if it's followed by TIME */
+ switch (next_token)
+ {
+ case TIME:
+ cur_token = WITHOUT_LA;
+ break;
}
break;