mirror of
https://github.com/postgres/postgres.git
synced 2025-07-31 22:04:40 +03:00
SQL/JSON: add standard JSON constructor functions
This commit introduces the SQL/JSON standard-conforming constructors for JSON types: JSON_ARRAY() JSON_ARRAYAGG() JSON_OBJECT() JSON_OBJECTAGG() Most of the functionality was already present in PostgreSQL-specific functions, but these include some new functionality such as the ability to skip or include NULL values, and to allow duplicate keys or throw error when they are found, as well as the standard specified syntax to specify output type and format. Author: Nikita Glukhov <n.gluhov@postgrespro.ru> Author: Teodor Sigaev <teodor@sigaev.ru> Author: Oleg Bartunov <obartunov@gmail.com> Author: Alexander Korotkov <aekorotkov@gmail.com> Author: Amit Langote <amitlangote09@gmail.com> 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/CAF4Au4w2x-5LTnN_bxky-mq4=WOqsGsxSpENCzHRAzSnEd8+WQ@mail.gmail.com Discussion: https://postgr.es/m/cd0bb935-0158-78a7-08b5-904886deac4b@postgrespro.ru Discussion: https://postgr.es/m/20220616233130.rparivafipt6doj3@alap3.anarazel.de Discussion: https://postgr.es/m/abd9b83b-aa66-f230-3d6d-734817f0995d%40postgresql.org
This commit is contained in:
@ -644,6 +644,22 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
|
||||
%type <defelt> hash_partbound_elem
|
||||
|
||||
|
||||
%type <node> json_format_clause_opt
|
||||
json_value_expr
|
||||
json_output_clause_opt
|
||||
json_name_and_value
|
||||
json_aggregate_func
|
||||
|
||||
%type <list> json_name_and_value_list
|
||||
json_value_expr_list
|
||||
json_array_aggregate_order_by_clause_opt
|
||||
|
||||
%type <ival> 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 +685,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
|
||||
|
||||
@ -695,7 +711,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
|
||||
EXTENSION EXTERNAL EXTRACT
|
||||
|
||||
FALSE_P FAMILY FETCH FILTER FINALIZE FIRST_P FLOAT_P FOLLOWING FOR
|
||||
FORCE FOREIGN FORWARD FREEZE FROM FULL FUNCTION FUNCTIONS
|
||||
FORCE FOREIGN FORMAT FORWARD FREEZE FROM FULL FUNCTION FUNCTIONS
|
||||
|
||||
GENERATED GLOBAL GRANT GRANTED GREATEST GROUP_P GROUPING GROUPS
|
||||
|
||||
@ -706,9 +722,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
|
||||
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,9 +788,10 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
|
||||
* NOT_LA exists so that productions such as NOT LIKE can be given the same
|
||||
* precedence as LIKE; otherwise they'd effectively have the same precedence
|
||||
* as NOT, at least with respect to their left-hand subexpression.
|
||||
* NULLS_LA and WITH_LA are needed to make the grammar LALR(1).
|
||||
* FORMAT_LA, NULLS_LA, WITH_LA, and WITHOUT_LA are needed to make the grammar
|
||||
* LALR(1).
|
||||
*/
|
||||
%token NOT_LA NULLS_LA WITH_LA
|
||||
%token FORMAT_LA NOT_LA NULLS_LA WITH_LA WITHOUT_LA
|
||||
|
||||
/*
|
||||
* The grammar likewise thinks these tokens are keywords, but they are never
|
||||
@ -792,6 +809,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
|
||||
|
||||
/* Precedence: lowest to highest */
|
||||
%nonassoc SET /* see relation_expr_opt_alias */
|
||||
%right FORMAT
|
||||
%left UNION EXCEPT
|
||||
%left INTERSECT
|
||||
%left OR
|
||||
@ -11698,6 +11716,7 @@ utility_option_elem:
|
||||
utility_option_name:
|
||||
NonReservedWord { $$ = $1; }
|
||||
| analyze_keyword { $$ = "analyze"; }
|
||||
| FORMAT_LA { $$ = "format"; }
|
||||
;
|
||||
|
||||
utility_option_arg:
|
||||
@ -15185,6 +15204,16 @@ 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; }
|
||||
;
|
||||
@ -15198,6 +15227,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; }
|
||||
;
|
||||
|
||||
/*
|
||||
@ -15543,6 +15573,79 @@ func_expr_common_subexpr:
|
||||
n->location = @1;
|
||||
$$ = (Node *) n;
|
||||
}
|
||||
| JSON_OBJECT '(' func_arg_list ')'
|
||||
{
|
||||
/* Support for legacy (non-standard) json_object() */
|
||||
$$ = (Node *) makeFuncCall(SystemFuncName("json_object"),
|
||||
$3, COERCE_EXPLICIT_CALL, @1);
|
||||
}
|
||||
| JSON_OBJECT '(' json_name_and_value_list
|
||||
json_object_constructor_null_clause_opt
|
||||
json_key_uniqueness_constraint_opt
|
||||
json_output_clause_opt ')'
|
||||
{
|
||||
JsonObjectConstructor *n = makeNode(JsonObjectConstructor);
|
||||
|
||||
n->exprs = $3;
|
||||
n->absent_on_null = $4;
|
||||
n->unique = $5;
|
||||
n->output = (JsonOutput *) $6;
|
||||
n->location = @1;
|
||||
$$ = (Node *) n;
|
||||
}
|
||||
| JSON_OBJECT '(' json_output_clause_opt ')'
|
||||
{
|
||||
JsonObjectConstructor *n = makeNode(JsonObjectConstructor);
|
||||
|
||||
n->exprs = NULL;
|
||||
n->absent_on_null = false;
|
||||
n->unique = false;
|
||||
n->output = (JsonOutput *) $3;
|
||||
n->location = @1;
|
||||
$$ = (Node *) n;
|
||||
}
|
||||
| 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 = (JsonFormat *) $4;
|
||||
n->absent_on_null = true; /* XXX */
|
||||
n->output = (JsonOutput *) $5;
|
||||
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;
|
||||
}
|
||||
;
|
||||
|
||||
/*
|
||||
@ -16267,6 +16370,132 @@ opt_asymmetric: ASYMMETRIC
|
||||
| /*EMPTY*/
|
||||
;
|
||||
|
||||
/* SQL/JSON support */
|
||||
json_value_expr:
|
||||
a_expr json_format_clause_opt
|
||||
{
|
||||
$$ = (Node *) makeJsonValueExpr((Expr *) $1, castNode(JsonFormat, $2));
|
||||
}
|
||||
;
|
||||
|
||||
json_format_clause_opt:
|
||||
FORMAT_LA JSON json_encoding_clause_opt
|
||||
{
|
||||
$$ = (Node *) makeJsonFormat(JS_FORMAT_JSON, $3, @1);
|
||||
}
|
||||
| /* EMPTY */
|
||||
{
|
||||
$$ = (Node *) makeJsonFormat(JS_FORMAT_DEFAULT, JS_ENC_DEFAULT, -1);
|
||||
}
|
||||
;
|
||||
|
||||
json_encoding_clause_opt:
|
||||
ENCODING name { $$ = makeJsonEncoding($2); }
|
||||
| /* EMPTY */ { $$ = JS_ENC_DEFAULT; }
|
||||
;
|
||||
|
||||
json_output_clause_opt:
|
||||
RETURNING Typename json_format_clause_opt
|
||||
{
|
||||
JsonOutput *n = makeNode(JsonOutput);
|
||||
|
||||
n->typeName = $2;
|
||||
n->returning = makeNode(JsonReturning);
|
||||
n->returning->format = (JsonFormat *) $3;
|
||||
$$ = (Node *) n;
|
||||
}
|
||||
| /* EMPTY */ { $$ = NULL; }
|
||||
;
|
||||
|
||||
/* KEYS is a noise word here */
|
||||
json_key_uniqueness_constraint_opt:
|
||||
WITH UNIQUE KEYS { $$ = true; }
|
||||
| WITH UNIQUE { $$ = true; }
|
||||
| WITHOUT_LA UNIQUE KEYS { $$ = false; }
|
||||
| WITHOUT_LA UNIQUE { $$ = false; }
|
||||
| /* EMPTY */ { $$ = false; }
|
||||
;
|
||||
|
||||
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:
|
||||
/* Supporting this syntax seems to require major surgery
|
||||
KEY c_expr VALUE_P json_value_expr
|
||||
{ $$ = makeJsonKeyValue($2, $4); }
|
||||
|
|
||||
*/
|
||||
c_expr VALUE_P json_value_expr
|
||||
{ $$ = makeJsonKeyValue($1, $3); }
|
||||
|
|
||||
a_expr ':' json_value_expr
|
||||
{ $$ = makeJsonKeyValue($1, $3); }
|
||||
;
|
||||
|
||||
/* empty means false for objects, true for arrays */
|
||||
json_object_constructor_null_clause_opt:
|
||||
NULL_P ON NULL_P { $$ = false; }
|
||||
| ABSENT ON NULL_P { $$ = true; }
|
||||
| /* EMPTY */ { $$ = false; }
|
||||
;
|
||||
|
||||
json_array_constructor_null_clause_opt:
|
||||
NULL_P ON NULL_P { $$ = false; }
|
||||
| ABSENT ON NULL_P { $$ = true; }
|
||||
| /* EMPTY */ { $$ = true; }
|
||||
;
|
||||
|
||||
json_value_expr_list:
|
||||
json_value_expr { $$ = list_make1($1); }
|
||||
| json_value_expr_list ',' json_value_expr { $$ = lappend($1, $3);}
|
||||
;
|
||||
|
||||
json_aggregate_func:
|
||||
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_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; }
|
||||
;
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
@ -16718,6 +16947,7 @@ BareColLabel: IDENT { $$ = $1; }
|
||||
*/
|
||||
unreserved_keyword:
|
||||
ABORT_P
|
||||
| ABSENT
|
||||
| ABSOLUTE_P
|
||||
| ACCESS
|
||||
| ACTION
|
||||
@ -16814,6 +17044,7 @@ unreserved_keyword:
|
||||
| FIRST_P
|
||||
| FOLLOWING
|
||||
| FORCE
|
||||
| FORMAT
|
||||
| FORWARD
|
||||
| FUNCTION
|
||||
| FUNCTIONS
|
||||
@ -16846,7 +17077,9 @@ unreserved_keyword:
|
||||
| INSTEAD
|
||||
| INVOKER
|
||||
| ISOLATION
|
||||
| JSON
|
||||
| KEY
|
||||
| KEYS
|
||||
| LABEL
|
||||
| LANGUAGE
|
||||
| LARGE_P
|
||||
@ -17058,6 +17291,10 @@ col_name_keyword:
|
||||
| INT_P
|
||||
| INTEGER
|
||||
| INTERVAL
|
||||
| JSON_ARRAY
|
||||
| JSON_ARRAYAGG
|
||||
| JSON_OBJECT
|
||||
| JSON_OBJECTAGG
|
||||
| LEAST
|
||||
| NATIONAL
|
||||
| NCHAR
|
||||
@ -17227,6 +17464,7 @@ reserved_keyword:
|
||||
*/
|
||||
bare_label_keyword:
|
||||
ABORT_P
|
||||
| ABSENT
|
||||
| ABSOLUTE_P
|
||||
| ACCESS
|
||||
| ACTION
|
||||
@ -17366,6 +17604,7 @@ bare_label_keyword:
|
||||
| FOLLOWING
|
||||
| FORCE
|
||||
| FOREIGN
|
||||
| FORMAT
|
||||
| FORWARD
|
||||
| FREEZE
|
||||
| FULL
|
||||
@ -17411,7 +17650,13 @@ bare_label_keyword:
|
||||
| IS
|
||||
| ISOLATION
|
||||
| JOIN
|
||||
| JSON
|
||||
| JSON_ARRAY
|
||||
| JSON_ARRAYAGG
|
||||
| JSON_OBJECT
|
||||
| JSON_OBJECTAGG
|
||||
| KEY
|
||||
| KEYS
|
||||
| LABEL
|
||||
| LANGUAGE
|
||||
| LARGE_P
|
||||
|
@ -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"
|
||||
@ -34,6 +36,7 @@
|
||||
#include "parser/parse_type.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/date.h"
|
||||
#include "utils/fmgroids.h"
|
||||
#include "utils/lsyscache.h"
|
||||
#include "utils/timestamp.h"
|
||||
#include "utils/xml.h"
|
||||
@ -72,6 +75,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,
|
||||
@ -294,6 +305,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));
|
||||
@ -3047,3 +3078,742 @@ ParseExprKindName(ParseExprKind exprKind)
|
||||
}
|
||||
return "unrecognized expression kind";
|
||||
}
|
||||
|
||||
/*
|
||||
* Make string Const node from JSON encoding name.
|
||||
*
|
||||
* UTF8 is default encoding.
|
||||
*/
|
||||
static Const *
|
||||
getJsonEncodingConst(JsonFormat *format)
|
||||
{
|
||||
JsonEncoding encoding;
|
||||
const char *enc;
|
||||
Name encname = palloc(sizeof(NameData));
|
||||
|
||||
if (!format ||
|
||||
format->format_type == JS_FORMAT_DEFAULT ||
|
||||
format->encoding == JS_ENC_DEFAULT)
|
||||
encoding = JS_ENC_UTF8;
|
||||
else
|
||||
encoding = format->encoding;
|
||||
|
||||
switch (encoding)
|
||||
{
|
||||
case JS_ENC_UTF16:
|
||||
enc = "UTF16";
|
||||
break;
|
||||
case JS_ENC_UTF32:
|
||||
enc = "UTF32";
|
||||
break;
|
||||
case JS_ENC_UTF8:
|
||||
enc = "UTF8";
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "invalid JSON encoding: %d", encoding);
|
||||
break;
|
||||
}
|
||||
|
||||
namestrcpy(encname, enc);
|
||||
|
||||
return makeConst(NAMEOID, -1, InvalidOid, NAMEDATALEN,
|
||||
NameGetDatum(encname), false, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Make bytea => text conversion using specified JSON format encoding.
|
||||
*/
|
||||
static Node *
|
||||
makeJsonByteaToTextConversion(Node *expr, JsonFormat *format, int location)
|
||||
{
|
||||
Const *encoding = getJsonEncodingConst(format);
|
||||
FuncExpr *fexpr = makeFuncExpr(F_CONVERT_FROM, TEXTOID,
|
||||
list_make2(expr, encoding),
|
||||
InvalidOid, InvalidOid,
|
||||
COERCE_EXPLICIT_CALL);
|
||||
|
||||
fexpr->location = location;
|
||||
|
||||
return (Node *) fexpr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make CaseTestExpr node.
|
||||
*/
|
||||
static Node *
|
||||
makeCaseTestExpr(Node *expr)
|
||||
{
|
||||
CaseTestExpr *placeholder = makeNode(CaseTestExpr);
|
||||
|
||||
placeholder->typeId = exprType(expr);
|
||||
placeholder->typeMod = exprTypmod(expr);
|
||||
placeholder->collation = exprCollation(expr);
|
||||
|
||||
return (Node *) placeholder;
|
||||
}
|
||||
|
||||
/*
|
||||
* Transform JSON value expression using specified input JSON format or
|
||||
* default format otherwise.
|
||||
*/
|
||||
static Node *
|
||||
transformJsonValueExpr(ParseState *pstate, JsonValueExpr *ve,
|
||||
JsonFormatType default_format)
|
||||
{
|
||||
Node *expr = transformExprRecurse(pstate, (Node *) ve->raw_expr);
|
||||
Node *rawexpr;
|
||||
JsonFormatType format;
|
||||
Oid exprtype;
|
||||
int location;
|
||||
char typcategory;
|
||||
bool typispreferred;
|
||||
|
||||
/*
|
||||
* Using JSON_VALUE here is slightly bogus: perhaps we need to be passed a
|
||||
* JsonConstructorType so that we can use one of JSON_OBJECTAGG, etc.
|
||||
*/
|
||||
if (exprType(expr) == UNKNOWNOID)
|
||||
expr = coerce_to_specific_type(pstate, expr, TEXTOID, "JSON_VALUE");
|
||||
|
||||
rawexpr = expr;
|
||||
exprtype = exprType(expr);
|
||||
location = exprLocation(expr);
|
||||
|
||||
get_type_category_preferred(exprtype, &typcategory, &typispreferred);
|
||||
|
||||
if (ve->format->format_type != JS_FORMAT_DEFAULT)
|
||||
{
|
||||
if (ve->format->encoding != JS_ENC_DEFAULT && exprtype != BYTEAOID)
|
||||
ereport(ERROR,
|
||||
errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||
errmsg("JSON ENCODING clause is only allowed for bytea input type"),
|
||||
parser_errposition(pstate, ve->format->location));
|
||||
|
||||
if (exprtype == JSONOID || exprtype == JSONBOID)
|
||||
{
|
||||
format = JS_FORMAT_DEFAULT; /* do not format json[b] types */
|
||||
ereport(WARNING,
|
||||
errmsg("FORMAT JSON has no effect for json and jsonb types"),
|
||||
parser_errposition(pstate, ve->format->location));
|
||||
}
|
||||
else
|
||||
format = ve->format->format_type;
|
||||
}
|
||||
else if (exprtype == JSONOID || exprtype == JSONBOID)
|
||||
format = JS_FORMAT_DEFAULT; /* do not format json[b] types */
|
||||
else
|
||||
format = default_format;
|
||||
|
||||
if (format != JS_FORMAT_DEFAULT)
|
||||
{
|
||||
Oid targettype = format == JS_FORMAT_JSONB ? JSONBOID : JSONOID;
|
||||
Node *orig = makeCaseTestExpr(expr);
|
||||
Node *coerced;
|
||||
|
||||
expr = orig;
|
||||
|
||||
if (exprtype != BYTEAOID && typcategory != TYPCATEGORY_STRING)
|
||||
ereport(ERROR,
|
||||
errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||
errmsg(ve->format->format_type == JS_FORMAT_DEFAULT ?
|
||||
"cannot use non-string types with implicit FORMAT JSON clause" :
|
||||
"cannot use non-string types with explicit FORMAT JSON clause"),
|
||||
parser_errposition(pstate, ve->format->location >= 0 ?
|
||||
ve->format->location : location));
|
||||
|
||||
/* Convert encoded JSON text from bytea. */
|
||||
if (format == JS_FORMAT_JSON && exprtype == BYTEAOID)
|
||||
{
|
||||
expr = makeJsonByteaToTextConversion(expr, ve->format, location);
|
||||
exprtype = TEXTOID;
|
||||
}
|
||||
|
||||
/* Try to coerce to the target type. */
|
||||
coerced = coerce_to_target_type(pstate, expr, exprtype,
|
||||
targettype, -1,
|
||||
COERCION_EXPLICIT,
|
||||
COERCE_EXPLICIT_CAST,
|
||||
location);
|
||||
|
||||
if (!coerced)
|
||||
{
|
||||
/* If coercion failed, use to_json()/to_jsonb() functions. */
|
||||
Oid fnoid = targettype == JSONOID ? F_TO_JSON : F_TO_JSONB;
|
||||
FuncExpr *fexpr = makeFuncExpr(fnoid, targettype,
|
||||
list_make1(expr),
|
||||
InvalidOid, InvalidOid,
|
||||
COERCE_EXPLICIT_CALL);
|
||||
|
||||
fexpr->location = location;
|
||||
|
||||
coerced = (Node *) fexpr;
|
||||
}
|
||||
|
||||
if (coerced == orig)
|
||||
expr = rawexpr;
|
||||
else
|
||||
{
|
||||
ve = copyObject(ve);
|
||||
ve->raw_expr = (Expr *) rawexpr;
|
||||
ve->formatted_expr = (Expr *) coerced;
|
||||
|
||||
expr = (Node *) 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 constructor 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_jsonb = false;
|
||||
|
||||
foreach(lc, args)
|
||||
{
|
||||
Node *expr = lfirst(lc);
|
||||
Oid typid = exprType(expr);
|
||||
|
||||
have_jsonb |= typid == JSONBOID;
|
||||
|
||||
if (have_jsonb)
|
||||
break;
|
||||
}
|
||||
|
||||
if (have_jsonb)
|
||||
{
|
||||
returning->typid = JSONBOID;
|
||||
returning->format->format_type = JS_FORMAT_JSONB;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* 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->format->location;
|
||||
|
||||
/* 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";
|
||||
else
|
||||
aggfnname = "pg_catalog.jsonb_object_agg_strict";
|
||||
else if (agg->unique)
|
||||
aggfnname = "pg_catalog.jsonb_object_agg_unique";
|
||||
else
|
||||
aggfnname = "pg_catalog.jsonb_object_agg";
|
||||
|
||||
aggtype = JSONBOID;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (agg->absent_on_null)
|
||||
if (agg->unique)
|
||||
aggfnname = "pg_catalog.json_object_agg_unique_strict";
|
||||
else
|
||||
aggfnname = "pg_catalog.json_object_agg_strict";
|
||||
else if (agg->unique)
|
||||
aggfnname = "pg_catalog.json_object_agg_unique";
|
||||
else
|
||||
aggfnname = "pg_catalog.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);
|
||||
}
|
||||
|
@ -1907,8 +1907,26 @@ FigureColnameInternal(Node *node, char **name)
|
||||
}
|
||||
break;
|
||||
case T_XmlSerialize:
|
||||
/* make XMLSERIALIZE act like a regular function */
|
||||
*name = "xmlserialize";
|
||||
return 2;
|
||||
case T_JsonObjectConstructor:
|
||||
/* make JSON_OBJECT act like a regular function */
|
||||
*name = "json_object";
|
||||
return 2;
|
||||
case T_JsonArrayConstructor:
|
||||
case T_JsonArrayQueryConstructor:
|
||||
/* make JSON_ARRAY act like a regular function */
|
||||
*name = "json_array";
|
||||
return 2;
|
||||
case T_JsonObjectAgg:
|
||||
/* make JSON_OBJECTAGG act like a regular function */
|
||||
*name = "json_objectagg";
|
||||
return 2;
|
||||
case T_JsonArrayAgg:
|
||||
/* make JSON_ARRAYAGG act like a regular function */
|
||||
*name = "json_arrayagg";
|
||||
return 2;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -137,6 +137,9 @@ base_yylex(YYSTYPE *lvalp, YYLTYPE *llocp, core_yyscan_t yyscanner)
|
||||
*/
|
||||
switch (cur_token)
|
||||
{
|
||||
case FORMAT:
|
||||
cur_token_length = 6;
|
||||
break;
|
||||
case NOT:
|
||||
cur_token_length = 3;
|
||||
break;
|
||||
@ -150,6 +153,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;
|
||||
}
|
||||
@ -188,6 +194,16 @@ base_yylex(YYSTYPE *lvalp, YYLTYPE *llocp, core_yyscan_t yyscanner)
|
||||
/* Replace cur_token if needed, based on lookahead */
|
||||
switch (cur_token)
|
||||
{
|
||||
case FORMAT:
|
||||
/* Replace FORMAT by FORMAT_LA if it's followed by JSON */
|
||||
switch (next_token)
|
||||
{
|
||||
case JSON:
|
||||
cur_token = FORMAT_LA;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case NOT:
|
||||
/* Replace NOT by NOT_LA if it's followed by BETWEEN, IN, etc */
|
||||
switch (next_token)
|
||||
@ -224,6 +240,16 @@ base_yylex(YYSTYPE *lvalp, YYLTYPE *llocp, core_yyscan_t yyscanner)
|
||||
}
|
||||
break;
|
||||
|
||||
case WITHOUT:
|
||||
/* Replace WITHOUT by WITHOUT_LA if it's followed by UNIQUE */
|
||||
switch (next_token)
|
||||
{
|
||||
case UNIQUE:
|
||||
cur_token = WITHOUT_LA;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case UIDENT:
|
||||
case USCONST:
|
||||
/* Look ahead for UESCAPE */
|
||||
|
Reference in New Issue
Block a user