Skip to content

Commit 11bea7b

Browse files
author
Nikita Glukhov
committed
Allow variable jsonpath specifications in JSON_TABLE
1 parent 1b6366f commit 11bea7b

File tree

5 files changed

+42
-10
lines changed

5 files changed

+42
-10
lines changed

src/backend/parser/gram.y

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -854,7 +854,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
854854
*/
855855
%nonassoc UNBOUNDED /* ideally should have same precedence as IDENT */
856856
%nonassoc ERROR_P EMPTY_P DEFAULT ABSENT /* JSON error/empty behavior */
857-
%nonassoc FALSE_P KEEP OMIT PASSING TRUE_P UNKNOWN
857+
%nonassoc COLUMNS FALSE_P KEEP OMIT PASSING TRUE_P UNKNOWN
858858
%nonassoc IDENT GENERATED NULL_P PARTITION RANGE ROWS GROUPS PRECEDING FOLLOWING CUBE ROLLUP
859859
%left Op OPERATOR /* multi-character ops and user-defined operators */
860860
%left '+' '-'
@@ -15147,7 +15147,7 @@ json_table_error_clause_opt:
1514715147
;
1514815148

1514915149
json_table_column_path_specification_clause_opt:
15150-
PATH json_path_specification { $$ = $2; }
15150+
PATH Sconst { $$ = $2; }
1515115151
| /* EMPTY */ %prec json_table_column { $$ = NULL; }
1515215152
;
1515315153

@@ -15179,7 +15179,7 @@ json_table_formatted_column_definition:
1517915179
;
1518015180

1518115181
json_table_nested_columns:
15182-
NESTED path_opt json_path_specification
15182+
NESTED path_opt Sconst
1518315183
json_as_path_name_clause_opt
1518415184
json_table_columns_clause
1518515185
{

src/backend/parser/parse_clause.c

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1065,6 +1065,18 @@ transformRangeTableSample(ParseState *pstate, RangeTableSample *rts)
10651065
return tablesample;
10661066
}
10671067

1068+
static Node *
1069+
makeStringConst(char *str, int location)
1070+
{
1071+
A_Const *n = makeNode(A_Const);
1072+
1073+
n->val.type = T_String;
1074+
n->val.val.str = str;
1075+
n->location = location;
1076+
1077+
return (Node *)n;
1078+
}
1079+
10681080
/*
10691081
* getRTEForSpecialRelationTypes
10701082
*
@@ -1108,6 +1120,7 @@ transformJsonTableColumn(JsonTableColumn *jtc, Node *contextItemExpr,
11081120
JsonValueExpr *jvexpr = makeNode(JsonValueExpr);
11091121
JsonCommon *common = makeNode(JsonCommon);
11101122
JsonOutput *output = makeNode(JsonOutput);
1123+
JsonPathSpec pathspec;
11111124

11121125
jfexpr->op = jtc->coltype == JTC_REGULAR ? IS_JSON_VALUE : IS_JSON_QUERY;
11131126
jfexpr->common = common;
@@ -1128,7 +1141,7 @@ transformJsonTableColumn(JsonTableColumn *jtc, Node *contextItemExpr,
11281141
common->passing = passingArgs;
11291142

11301143
if (jtc->pathspec)
1131-
common->pathspec = jtc->pathspec;
1144+
pathspec = jtc->pathspec;
11321145
else
11331146
{
11341147
/* Construct default path as '$."column_name"' */
@@ -1139,9 +1152,11 @@ transformJsonTableColumn(JsonTableColumn *jtc, Node *contextItemExpr,
11391152
appendStringInfoString(&path, "$.");
11401153
escape_json(&path, jtc->name);
11411154

1142-
common->pathspec = path.data;
1155+
pathspec = path.data;
11431156
}
11441157

1158+
common->pathspec = makeStringConst(pathspec, -1);
1159+
11451160
jvexpr->expr = (Expr *) contextItemExpr;
11461161
jvexpr->format.type = JS_FORMAT_DEFAULT;
11471162
jvexpr->format.encoding = JS_ENC_DEFAULT;
@@ -1642,6 +1657,7 @@ transformJsonTable(ParseState *pstate, JsonTable *jt)
16421657
JsonCommon *jscommon;
16431658
JsonTablePlan *plan = jt->plan;
16441659
char *rootPathName = jt->common->pathname;
1660+
char *rootPath;
16451661
bool is_lateral;
16461662

16471663
cxt.table = jt;
@@ -1671,7 +1687,7 @@ transformJsonTable(ParseState *pstate, JsonTable *jt)
16711687
}
16721688

16731689
jscommon = copyObject(jt->common);
1674-
jscommon->pathspec = pstrdup("$");
1690+
jscommon->pathspec = makeStringConst(pstrdup("$"), -1);
16751691

16761692
jfe->op = IS_JSON_TABLE;
16771693
jfe->common = jscommon;
@@ -1695,10 +1711,19 @@ transformJsonTable(ParseState *pstate, JsonTable *jt)
16951711

16961712
cxt.contextItemTypid = exprType(tf->docexpr);
16971713

1714+
if (!IsA(jt->common->pathspec, A_Const) ||
1715+
castNode(A_Const, jt->common->pathspec)->val.type != T_String)
1716+
ereport(ERROR,
1717+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1718+
errmsg("only string constants supported in JSON_TABLE path specification"),
1719+
parser_errposition(pstate,
1720+
exprLocation(jt->common->pathspec))));
1721+
1722+
rootPath = castNode(A_Const, jt->common->pathspec)->val.val.str;
1723+
16981724
tf->plan = (Node *) transformJsonTableColumns(pstate, &cxt, plan,
16991725
jt->columns,
1700-
jt->common->pathspec,
1701-
&rootPathName,
1726+
rootPath, &rootPathName,
17021727
jt->common->location);
17031728

17041729
tf->ordinalitycol = -1; /* undefine ordinality column number */

src/backend/utils/adt/ruleutils.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10233,7 +10233,8 @@ get_json_table_columns(TableFunc *tf, JsonTableParentNode *node,
1023310233
" FORMAT JSONB" : " FORMAT JSON");
1023410234

1023510235
appendStringInfoString(buf, " PATH ");
10236-
get_const_expr(colexpr->path_spec, context, -1);
10236+
10237+
get_json_path_spec(colexpr->path_spec, context, showimplicit);
1023710238

1023810239
if (colexpr->wrapper == JSW_CONDITIONAL)
1023910240
appendStringInfo(buf, " WITH CONDITIONAL WRAPPER");

src/test/regress/expected/jsonb_sqljson.out

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1899,6 +1899,11 @@ SELECT JSON_QUERY(jsonb '{"a": 123}', '$' || '.' || 'a' WITH WRAPPER);
18991899
SELECT JSON_QUERY(jsonb '{"a": 123}', 'error' || ' ' || 'error');
19001900
ERROR: bad jsonpath representation
19011901
DETAIL: syntax error, unexpected IDENT_P at or near " "
1902+
-- Should fail (not supported)
1903+
SELECT * FROM JSON_TABLE(jsonb '{"a": 123}', '$' || '.' || 'a' COLUMNS (foo int));
1904+
ERROR: only string constants supported in JSON_TABLE path specification
1905+
LINE 1: SELECT * FROM JSON_TABLE(jsonb '{"a": 123}', '$' || '.' || '...
1906+
^
19021907
-- Test parallel JSON_VALUE()
19031908
CREATE TABLE test_parallel_jsonb_value AS
19041909
SELECT i::text::jsonb AS js

src/test/regress/sql/jsonb_sqljson.sql

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -861,6 +861,8 @@ SELECT JSON_QUERY(jsonb '{"a": 123}', '$' || '.' || 'a');
861861
SELECT JSON_QUERY(jsonb '{"a": 123}', '$' || '.' || 'a' WITH WRAPPER);
862862
-- Should fail (invalid path)
863863
SELECT JSON_QUERY(jsonb '{"a": 123}', 'error' || ' ' || 'error');
864+
-- Should fail (not supported)
865+
SELECT * FROM JSON_TABLE(jsonb '{"a": 123}', '$' || '.' || 'a' COLUMNS (foo int));
864866

865867
-- Test parallel JSON_VALUE()
866868
CREATE TABLE test_parallel_jsonb_value AS
@@ -876,4 +878,3 @@ SELECT sum(JSON_VALUE(js, '$' RETURNING numeric)) FROM test_parallel_jsonb_value
876878
EXPLAIN (COSTS OFF)
877879
SELECT sum(JSON_VALUE(js, '$' RETURNING numeric ERROR ON ERROR)) FROM test_parallel_jsonb_value;
878880
SELECT sum(JSON_VALUE(js, '$' RETURNING numeric ERROR ON ERROR)) FROM test_parallel_jsonb_value;
879-

0 commit comments

Comments
 (0)