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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy