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

Commit 220afcb

Browse files
Nikita GlukhovCommitfest Bot
Nikita Glukhov
authored and
Commitfest Bot
committed
Allow wild card member access for jsonb
1 parent 02a27b0 commit 220afcb

File tree

11 files changed

+317
-43
lines changed

11 files changed

+317
-43
lines changed

src/backend/parser/gram.y

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19000,6 +19000,7 @@ check_func_name(List *names, core_yyscan_t yyscanner)
1900019000
static List *
1900119001
check_indirection(List *indirection, core_yyscan_t yyscanner)
1900219002
{
19003+
#if 0
1900319004
ListCell *l;
1900419005

1900519006
foreach(l, indirection)
@@ -19010,6 +19011,7 @@ check_indirection(List *indirection, core_yyscan_t yyscanner)
1901019011
parser_yyerror("improper use of \"*\"");
1901119012
}
1901219013
}
19014+
#endif
1901319015
return indirection;
1901419016
}
1901519017

src/backend/parser/parse_expr.c

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,6 @@ static Node *transformColumnRef(ParseState *pstate, ColumnRef *cref);
7474
static Node *transformWholeRowRef(ParseState *pstate,
7575
ParseNamespaceItem *nsitem,
7676
int sublevels_up, int location);
77-
static Node *transformIndirection(ParseState *pstate, A_Indirection *ind);
7877
static Node *transformTypeCast(ParseState *pstate, TypeCast *tc);
7978
static Node *transformCollateClause(ParseState *pstate, CollateClause *c);
8079
static Node *transformJsonObjectConstructor(ParseState *pstate,
@@ -158,7 +157,7 @@ transformExprRecurse(ParseState *pstate, Node *expr)
158157
break;
159158

160159
case T_A_Indirection:
161-
result = transformIndirection(pstate, (A_Indirection *) expr);
160+
result = transformIndirection(pstate, (A_Indirection *) expr, NULL);
162161
break;
163162

164163
case T_A_ArrayExpr:
@@ -432,8 +431,9 @@ unknown_attribute(ParseState *pstate, Node *relref, const char *attname,
432431
}
433432
}
434433

435-
static Node *
436-
transformIndirection(ParseState *pstate, A_Indirection *ind)
434+
Node *
435+
transformIndirection(ParseState *pstate, A_Indirection *ind,
436+
bool *trailing_star_expansion)
437437
{
438438
Node *last_srf = pstate->p_last_srf;
439439
Node *result = transformExprRecurse(pstate, ind->arg);
@@ -454,12 +454,7 @@ transformIndirection(ParseState *pstate, A_Indirection *ind)
454454
if (IsA(n, A_Indices))
455455
subscripts = lappend(subscripts, n);
456456
else if (IsA(n, A_Star))
457-
{
458-
ereport(ERROR,
459-
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
460-
errmsg("row expansion via \"*\" is not supported here"),
461-
parser_errposition(pstate, location)));
462-
}
457+
subscripts = lappend(subscripts, n);
463458
else
464459
{
465460
Assert(IsA(n, String));
@@ -491,7 +486,21 @@ transformIndirection(ParseState *pstate, A_Indirection *ind)
491486

492487
n = linitial(subscripts);
493488

494-
if (!IsA(n, String))
489+
if (IsA(n, A_Star))
490+
{
491+
/* Success, if trailing star expansion is allowed */
492+
if (trailing_star_expansion && list_length(subscripts) == 1)
493+
{
494+
*trailing_star_expansion = true;
495+
return result;
496+
}
497+
498+
ereport(ERROR,
499+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
500+
errmsg("row expansion via \"*\" is not supported here"),
501+
parser_errposition(pstate, location)));
502+
}
503+
else if (!IsA(n, String))
495504
ereport(ERROR,
496505
(errcode(ERRCODE_DATATYPE_MISMATCH),
497506
errmsg("cannot subscript type %s because it does not support subscripting",
@@ -517,6 +526,9 @@ transformIndirection(ParseState *pstate, A_Indirection *ind)
517526
result = newresult;
518527
}
519528

529+
if (trailing_star_expansion)
530+
*trailing_star_expansion = false;
531+
520532
return result;
521533
}
522534

src/backend/parser/parse_target.c

Lines changed: 46 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ static Node *transformAssignmentSubscripts(ParseState *pstate,
4848
static List *ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref,
4949
bool make_target_entry);
5050
static List *ExpandAllTables(ParseState *pstate, int location);
51-
static List *ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind,
51+
static Node *ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind,
5252
bool make_target_entry, ParseExprKind exprKind);
5353
static List *ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem,
5454
int sublevels_up, int location,
@@ -134,6 +134,7 @@ transformTargetList(ParseState *pstate, List *targetlist,
134134
foreach(o_target, targetlist)
135135
{
136136
ResTarget *res = (ResTarget *) lfirst(o_target);
137+
Node *transformed = NULL;
137138

138139
/*
139140
* Check for "something.*". Depending on the complexity of the
@@ -162,13 +163,19 @@ transformTargetList(ParseState *pstate, List *targetlist,
162163

163164
if (IsA(llast(ind->indirection), A_Star))
164165
{
165-
/* It is something.*, expand into multiple items */
166-
p_target = list_concat(p_target,
167-
ExpandIndirectionStar(pstate,
168-
ind,
169-
true,
170-
exprKind));
171-
continue;
166+
Node *columns = ExpandIndirectionStar(pstate,
167+
ind,
168+
true,
169+
exprKind);
170+
171+
if (IsA(columns, List))
172+
{
173+
/* It is something.*, expand into multiple items */
174+
p_target = list_concat(p_target, (List *) columns);
175+
continue;
176+
}
177+
178+
transformed = (Node *) columns;
172179
}
173180
}
174181
}
@@ -180,7 +187,7 @@ transformTargetList(ParseState *pstate, List *targetlist,
180187
p_target = lappend(p_target,
181188
transformTargetEntry(pstate,
182189
res->val,
183-
NULL,
190+
transformed,
184191
exprKind,
185192
res->name,
186193
false));
@@ -251,10 +258,15 @@ transformExpressionList(ParseState *pstate, List *exprlist,
251258

252259
if (IsA(llast(ind->indirection), A_Star))
253260
{
254-
/* It is something.*, expand into multiple items */
255-
result = list_concat(result,
256-
ExpandIndirectionStar(pstate, ind,
257-
false, exprKind));
261+
Node *cols = ExpandIndirectionStar(pstate, ind,
262+
false, exprKind);
263+
264+
if (!cols || IsA(cols, List))
265+
/* It is something.*, expand into multiple items */
266+
result = list_concat(result, (List *) cols);
267+
else
268+
result = lappend(result, cols);
269+
258270
continue;
259271
}
260272
}
@@ -1345,22 +1357,30 @@ ExpandAllTables(ParseState *pstate, int location)
13451357
* For robustness, we use a separate "make_target_entry" flag to control
13461358
* this rather than relying on exprKind.
13471359
*/
1348-
static List *
1360+
static Node *
13491361
ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind,
13501362
bool make_target_entry, ParseExprKind exprKind)
13511363
{
13521364
Node *expr;
1365+
ParseExprKind sv_expr_kind;
1366+
bool trailing_star_expansion = false;
1367+
1368+
/* Save and restore identity of expression type we're parsing */
1369+
Assert(exprKind != EXPR_KIND_NONE);
1370+
sv_expr_kind = pstate->p_expr_kind;
1371+
pstate->p_expr_kind = exprKind;
13531372

13541373
/* Strip off the '*' to create a reference to the rowtype object */
1355-
ind = copyObject(ind);
1356-
ind->indirection = list_truncate(ind->indirection,
1357-
list_length(ind->indirection) - 1);
1374+
expr = transformIndirection(pstate, ind, &trailing_star_expansion);
1375+
1376+
pstate->p_expr_kind = sv_expr_kind;
13581377

1359-
/* And transform that */
1360-
expr = transformExpr(pstate, (Node *) ind, exprKind);
1378+
/* '*' was consumed by generic type subscripting */
1379+
if (!trailing_star_expansion)
1380+
return expr;
13611381

13621382
/* Expand the rowtype expression into individual fields */
1363-
return ExpandRowReference(pstate, expr, make_target_entry);
1383+
return (Node *) ExpandRowReference(pstate, expr, make_target_entry);
13641384
}
13651385

13661386
/*
@@ -1785,13 +1805,18 @@ FigureColnameInternal(Node *node, char **name)
17851805
char *fname = NULL;
17861806
ListCell *l;
17871807

1788-
/* find last field name, if any, ignoring "*" and subscripts */
1808+
/*
1809+
* find last field name, if any, ignoring subscripts, and use
1810+
* '?column?' when there's a trailing '*'.
1811+
*/
17891812
foreach(l, ind->indirection)
17901813
{
17911814
Node *i = lfirst(l);
17921815

17931816
if (IsA(i, String))
17941817
fname = strVal(i);
1818+
else if (IsA(i, A_Star))
1819+
fname = "?column?";
17951820
}
17961821
if (fname)
17971822
{

src/backend/utils/adt/ruleutils.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13008,7 +13008,11 @@ printSubscripts(SubscriptingRef *sbsref, deparse_context *context)
1300813008
{
1300913009
Node *up = (Node *) lfirst(uplist_item);
1301013010

13011-
if (IsA(up, String))
13011+
if (!up)
13012+
{
13013+
appendStringInfoString(buf, ".*");
13014+
}
13015+
else if (IsA(up, String))
1301213016
{
1301313017
appendStringInfoChar(buf, '.');
1301413018
appendStringInfoString(buf, quote_identifier(strVal(up)));

src/include/parser/parse_expr.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,7 @@ extern Node *transformExpr(ParseState *pstate, Node *expr, ParseExprKind exprKin
2222

2323
extern const char *ParseExprKindName(ParseExprKind exprKind);
2424

25+
extern Node *transformIndirection(ParseState *pstate, A_Indirection *ind,
26+
bool *trailing_star_expansion);
27+
2528
#endif /* PARSE_EXPR_H */

src/interfaces/ecpg/test/expected/sql-sqljson.c

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -515,14 +515,24 @@ if (sqlca.sqlcode < 0) sqlprint();}
515515
if (sqlca.sqlcode < 0) sqlprint();}
516516
#line 145 "sqljson.pgc"
517517

518-
// error
518+
printf("Found json=%s\n", json);
519519

520-
{ ECPGdisconnect(__LINE__, "CURRENT");
520+
{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select json ( ( '{\"a\": {\"b\": 1, \"c\": 2}, \"b\": [{\"x\": 1}, {\"x\": [12, {\"y\":1}]}]}' :: jsonb ) . * . x )", ECPGt_EOIT,
521+
ECPGt_char,(json),(long)1024,(long)1,(1024)*sizeof(char),
522+
ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
521523
#line 148 "sqljson.pgc"
522524

523525
if (sqlca.sqlcode < 0) sqlprint();}
524526
#line 148 "sqljson.pgc"
525527

528+
printf("Found json=%s\n", json);
529+
530+
{ ECPGdisconnect(__LINE__, "CURRENT");
531+
#line 151 "sqljson.pgc"
532+
533+
if (sqlca.sqlcode < 0) sqlprint();}
534+
#line 151 "sqljson.pgc"
535+
526536

527537
return 0;
528538
}

src/interfaces/ecpg/test/expected/sql-sqljson.stderr

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -347,12 +347,17 @@ SQL error: schema "jsonb" does not exist on line 121
347347
[NO_PID]: sqlca: code: 0, state: 00000
348348
[NO_PID]: ecpg_execute on line 145: using PQexec
349349
[NO_PID]: sqlca: code: 0, state: 00000
350-
[NO_PID]: ecpg_check_PQresult on line 145: bad response - ERROR: row expansion via "*" is not supported here
351-
LINE 1: select json ( ( '{"a": {"b": 1, "c": 2}, "b": [{"x": 1}, {"x...
352-
^
350+
[NO_PID]: ecpg_process_output on line 145: correctly got 1 tuples with 1 fields
351+
[NO_PID]: sqlca: code: 0, state: 00000
352+
[NO_PID]: ecpg_get_data on line 145: RESULT: [{"b": 1, "c": 2}, [{"x": 1}, {"x": [12, {"y": 1}]}]] offset: -1; array: no
353+
[NO_PID]: sqlca: code: 0, state: 00000
354+
[NO_PID]: ecpg_execute on line 148: query: select json ( ( '{"a": {"b": 1, "c": 2}, "b": [{"x": 1}, {"x": [12, {"y":1}]}]}' :: jsonb ) . * . x ); with 0 parameter(s) on connection ecpg1_regression
355+
[NO_PID]: sqlca: code: 0, state: 00000
356+
[NO_PID]: ecpg_execute on line 148: using PQexec
357+
[NO_PID]: sqlca: code: 0, state: 00000
358+
[NO_PID]: ecpg_process_output on line 148: correctly got 1 tuples with 1 fields
359+
[NO_PID]: sqlca: code: 0, state: 00000
360+
[NO_PID]: ecpg_get_data on line 148: RESULT: [1, [12, {"y": 1}]] offset: -1; array: no
353361
[NO_PID]: sqlca: code: 0, state: 00000
354-
[NO_PID]: raising sqlstate 0A000 (sqlcode -400): row expansion via "*" is not supported here on line 145
355-
[NO_PID]: sqlca: code: -400, state: 0A000
356-
SQL error: row expansion via "*" is not supported here on line 145
357362
[NO_PID]: ecpg_finish: connection ecpg1_regression closed
358363
[NO_PID]: sqlca: code: 0, state: 00000

src/interfaces/ecpg/test/expected/sql-sqljson.stdout

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,5 @@ Found json={"x": 1}
3636
Found json=[12, {"y": 1}]
3737
Found json=[{"x": 1}, {"x": [12, {"y": 1}]}]
3838
Found json=[1, [12, {"y": 1}]]
39+
Found json=[{"b": 1, "c": 2}, [{"x": 1}, {"x": [12, {"y": 1}]}]]
40+
Found json=[1, [12, {"y": 1}]]

src/interfaces/ecpg/test/sql/sqljson.pgc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,10 @@ EXEC SQL END DECLARE SECTION;
143143
printf("Found json=%s\n", json);
144144

145145
EXEC SQL SELECT JSON(('{"a": {"b": 1, "c": 2}, "b": [{"x": 1}, {"x": [12, {"y":1}]}]}'::jsonb).*) INTO :json;
146-
// error
146+
printf("Found json=%s\n", json);
147+
148+
EXEC SQL SELECT JSON(('{"a": {"b": 1, "c": 2}, "b": [{"x": 1}, {"x": [12, {"y":1}]}]}'::jsonb).*.x) INTO :json;
149+
printf("Found json=%s\n", json);
147150

148151
EXEC SQL DISCONNECT;
149152

0 commit comments

Comments
 (0)