Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 40b736f

Browse files
author
Nikita Glukhov
committed
Add JSON_ARRAY(subquery) transformation
1 parent 397c243 commit 40b736f

File tree

6 files changed

+153
-0
lines changed

6 files changed

+153
-0
lines changed

src/backend/nodes/copyfuncs.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2413,6 +2413,23 @@ _copyJsonArrayAgg(const JsonArrayAgg *from)
24132413
return newnode;
24142414
}
24152415

2416+
/*
2417+
* _copyJsonArrayQueryCtor
2418+
*/
2419+
static JsonArrayQueryCtor *
2420+
_copyJsonArrayQueryCtor(const JsonArrayQueryCtor *from)
2421+
{
2422+
JsonArrayQueryCtor *newnode = makeNode(JsonArrayQueryCtor);
2423+
2424+
COPY_NODE_FIELD(query);
2425+
COPY_NODE_FIELD(output);
2426+
COPY_SCALAR_FIELD(format);
2427+
COPY_SCALAR_FIELD(absent_on_null);
2428+
COPY_LOCATION_FIELD(location);
2429+
2430+
return newnode;
2431+
}
2432+
24162433
/* ****************************************************************
24172434
* pathnodes.h copy functions
24182435
*
@@ -5337,6 +5354,9 @@ copyObjectImpl(const void *from)
53375354
case T_JsonArrayCtor:
53385355
retval = _copyJsonArrayCtor(from);
53395356
break;
5357+
case T_JsonArrayQueryCtor:
5358+
retval = _copyJsonArrayQueryCtor(from);
5359+
break;
53405360
case T_JsonArrayAgg:
53415361
retval = _copyJsonArrayAgg(from);
53425362
break;

src/backend/nodes/nodeFuncs.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4052,6 +4052,16 @@ raw_expression_tree_walker(Node *node,
40524052
return true;
40534053
}
40544054
break;
4055+
case T_JsonArrayQueryCtor:
4056+
{
4057+
JsonArrayQueryCtor *jaqc = (JsonArrayQueryCtor *) node;
4058+
4059+
if (walker(jaqc->output, context))
4060+
return true;
4061+
if (walker(jaqc->query, context))
4062+
return true;
4063+
}
4064+
break;
40554065
default:
40564066
elog(ERROR, "unrecognized node type: %d",
40574067
(int) nodeTag(node));

src/backend/parser/parse_expr.c

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,8 @@ static Node *transformTypeCast(ParseState *pstate, TypeCast *tc);
125125
static Node *transformCollateClause(ParseState *pstate, CollateClause *c);
126126
static Node *transformJsonObjectCtor(ParseState *pstate, JsonObjectCtor *ctor);
127127
static Node *transformJsonArrayCtor(ParseState *pstate, JsonArrayCtor *ctor);
128+
static Node *transformJsonArrayQueryCtor(ParseState *pstate,
129+
JsonArrayQueryCtor *ctor);
128130
static Node *transformJsonObjectAgg(ParseState *pstate, JsonObjectAgg *agg);
129131
static Node *transformJsonArrayAgg(ParseState *pstate, JsonArrayAgg *agg);
130132
static Node *make_row_comparison_op(ParseState *pstate, List *opname,
@@ -383,6 +385,10 @@ transformExprRecurse(ParseState *pstate, Node *expr)
383385
result = transformJsonArrayCtor(pstate, (JsonArrayCtor *) expr);
384386
break;
385387

388+
case T_JsonArrayQueryCtor:
389+
result = transformJsonArrayQueryCtor(pstate, (JsonArrayQueryCtor *) expr);
390+
break;
391+
386392
case T_JsonObjectAgg:
387393
result = transformJsonObjectAgg(pstate, (JsonObjectAgg *) expr);
388394
break;
@@ -4012,6 +4018,71 @@ transformJsonObjectCtor(ParseState *pstate, JsonObjectCtor *ctor)
40124018
return coerceJsonFuncExpr(pstate, (Node *) jsctor, jsctor->returning, true);
40134019
}
40144020

4021+
/*
4022+
* Transform JSON_ARRAY(query [FORMAT] [RETURNING] [ON NULL]) into
4023+
* (SELECT JSON_ARRAYAGG(a [FORMAT] [RETURNING] [ON NULL]) FROM (query) q(a))
4024+
*/
4025+
static Node *
4026+
transformJsonArrayQueryCtor(ParseState *pstate, JsonArrayQueryCtor *ctor)
4027+
{
4028+
SubLink *sublink = makeNode(SubLink);
4029+
SelectStmt *select = makeNode(SelectStmt);
4030+
RangeSubselect *range = makeNode(RangeSubselect);
4031+
Alias *alias = makeNode(Alias);
4032+
ResTarget *target = makeNode(ResTarget);
4033+
JsonArrayAgg *agg = makeNode(JsonArrayAgg);
4034+
ColumnRef *colref = makeNode(ColumnRef);
4035+
Query *query;
4036+
ParseState *qpstate;
4037+
4038+
/* Transform query only for counting target list entries. */
4039+
qpstate = make_parsestate(pstate);
4040+
4041+
query = transformStmt(qpstate, ctor->query);
4042+
4043+
if (count_nonjunk_tlist_entries(query->targetList) != 1)
4044+
ereport(ERROR,
4045+
(errcode(ERRCODE_SYNTAX_ERROR),
4046+
errmsg("subquery must return only one column"),
4047+
parser_errposition(pstate, ctor->location)));
4048+
4049+
free_parsestate(qpstate);
4050+
4051+
colref->fields = list_make2(makeString(pstrdup("q")),
4052+
makeString(pstrdup("a")));
4053+
colref->location = ctor->location;
4054+
4055+
agg->arg = makeJsonValueExpr((Expr *) colref, ctor->format);
4056+
agg->ctor.agg_order = NIL;
4057+
agg->ctor.output = ctor->output;
4058+
agg->absent_on_null = ctor->absent_on_null;
4059+
agg->ctor.location = ctor->location;
4060+
4061+
target->name = NULL;
4062+
target->indirection = NIL;
4063+
target->val = (Node *) agg;
4064+
target->location = ctor->location;
4065+
4066+
alias->aliasname = pstrdup("q");
4067+
alias->colnames = list_make1(makeString(pstrdup("a")));
4068+
4069+
range->lateral = false;
4070+
range->subquery = ctor->query;
4071+
range->alias = alias;
4072+
4073+
select->targetList = list_make1(target);
4074+
select->fromClause = list_make1(range);
4075+
4076+
sublink->subLinkType = EXPR_SUBLINK;
4077+
sublink->subLinkId = 0;
4078+
sublink->testexpr = NULL;
4079+
sublink->operName = NIL;
4080+
sublink->subselect = (Node *) select;
4081+
sublink->location = ctor->location;
4082+
4083+
return transformExprRecurse(pstate, (Node *) sublink);
4084+
}
4085+
40154086
/*
40164087
* Common code for JSON_OBJECTAGG and JSON_ARRAYAGG transformation.
40174088
*/

src/backend/parser/parse_target.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1935,6 +1935,7 @@ FigureColnameInternal(Node *node, char **name)
19351935
*name = "json_object";
19361936
return 2;
19371937
case T_JsonArrayCtor:
1938+
case T_JsonArrayQueryCtor:
19381939
*name = "json_array";
19391940
return 2;
19401941
case T_JsonObjectAgg:

src/test/regress/expected/sqljson.out

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,46 @@ SELECT JSON_ARRAY(JSON_ARRAY('{ "a" : 123 }' FORMAT JSON RETURNING text) FORMAT
423423
[[{ "a" : 123 }]]
424424
(1 row)
425425

426+
SELECT JSON_ARRAY(SELECT i FROM (VALUES (1), (2), (NULL), (4)) foo(i));
427+
json_array
428+
------------
429+
[1, 2, 4]
430+
(1 row)
431+
432+
SELECT JSON_ARRAY(SELECT i FROM (VALUES (NULL::int[]), ('{1,2}'), (NULL), (NULL), ('{3,4}'), (NULL)) foo(i));
433+
json_array
434+
------------
435+
[[1,2], +
436+
[3,4]]
437+
(1 row)
438+
439+
SELECT JSON_ARRAY(SELECT i FROM (VALUES (NULL::int[]), ('{1,2}'), (NULL), (NULL), ('{3,4}'), (NULL)) foo(i) RETURNING jsonb);
440+
json_array
441+
------------------
442+
[[1, 2], [3, 4]]
443+
(1 row)
444+
445+
--SELECT JSON_ARRAY(SELECT i FROM (VALUES (NULL::int[]), ('{1,2}'), (NULL), (NULL), ('{3,4}'), (NULL)) foo(i) NULL ON NULL);
446+
--SELECT JSON_ARRAY(SELECT i FROM (VALUES (NULL::int[]), ('{1,2}'), (NULL), (NULL), ('{3,4}'), (NULL)) foo(i) NULL ON NULL RETURNING jsonb);
447+
SELECT JSON_ARRAY(SELECT i FROM (VALUES (3), (1), (NULL), (2)) foo(i) ORDER BY i);
448+
json_array
449+
------------
450+
[1, 2, 3]
451+
(1 row)
452+
453+
-- Should fail
454+
SELECT JSON_ARRAY(SELECT FROM (VALUES (1)) foo(i));
455+
ERROR: subquery must return only one column
456+
LINE 1: SELECT JSON_ARRAY(SELECT FROM (VALUES (1)) foo(i));
457+
^
458+
SELECT JSON_ARRAY(SELECT i, i FROM (VALUES (1)) foo(i));
459+
ERROR: subquery must return only one column
460+
LINE 1: SELECT JSON_ARRAY(SELECT i, i FROM (VALUES (1)) foo(i));
461+
^
462+
SELECT JSON_ARRAY(SELECT * FROM (VALUES (1, 2)) foo(i, j));
463+
ERROR: subquery must return only one column
464+
LINE 1: SELECT JSON_ARRAY(SELECT * FROM (VALUES (1, 2)) foo(i, j));
465+
^
426466
-- JSON_ARRAYAGG()
427467
SELECT JSON_ARRAYAGG(i) IS NULL,
428468
JSON_ARRAYAGG(i RETURNING jsonb) IS NULL

src/test/regress/sql/sqljson.sql

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,17 @@ SELECT JSON_ARRAY(JSON_ARRAY('{ "a" : 123 }' RETURNING text));
116116
SELECT JSON_ARRAY(JSON_ARRAY('{ "a" : 123 }' FORMAT JSON RETURNING text));
117117
SELECT JSON_ARRAY(JSON_ARRAY('{ "a" : 123 }' FORMAT JSON RETURNING text) FORMAT JSON);
118118

119+
SELECT JSON_ARRAY(SELECT i FROM (VALUES (1), (2), (NULL), (4)) foo(i));
120+
SELECT JSON_ARRAY(SELECT i FROM (VALUES (NULL::int[]), ('{1,2}'), (NULL), (NULL), ('{3,4}'), (NULL)) foo(i));
121+
SELECT JSON_ARRAY(SELECT i FROM (VALUES (NULL::int[]), ('{1,2}'), (NULL), (NULL), ('{3,4}'), (NULL)) foo(i) RETURNING jsonb);
122+
--SELECT JSON_ARRAY(SELECT i FROM (VALUES (NULL::int[]), ('{1,2}'), (NULL), (NULL), ('{3,4}'), (NULL)) foo(i) NULL ON NULL);
123+
--SELECT JSON_ARRAY(SELECT i FROM (VALUES (NULL::int[]), ('{1,2}'), (NULL), (NULL), ('{3,4}'), (NULL)) foo(i) NULL ON NULL RETURNING jsonb);
124+
SELECT JSON_ARRAY(SELECT i FROM (VALUES (3), (1), (NULL), (2)) foo(i) ORDER BY i);
125+
-- Should fail
126+
SELECT JSON_ARRAY(SELECT FROM (VALUES (1)) foo(i));
127+
SELECT JSON_ARRAY(SELECT i, i FROM (VALUES (1)) foo(i));
128+
SELECT JSON_ARRAY(SELECT * FROM (VALUES (1, 2)) foo(i, j));
129+
119130
-- JSON_ARRAYAGG()
120131
SELECT JSON_ARRAYAGG(i) IS NULL,
121132
JSON_ARRAYAGG(i RETURNING jsonb) IS NULL

0 commit comments

Comments
 (0)