PostgreSQL Source Code git master
parse_jsontable.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * parse_jsontable.c
4 * parsing of JSON_TABLE
5 *
6 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
8 *
9 *
10 * IDENTIFICATION
11 * src/backend/parser/parse_jsontable.c
12 *
13 *-------------------------------------------------------------------------
14 */
15
16#include "postgres.h"
17
18#include "catalog/pg_type.h"
19#include "nodes/makefuncs.h"
20#include "nodes/nodeFuncs.h"
21#include "optimizer/optimizer.h"
22#include "parser/parse_clause.h"
24#include "parser/parse_expr.h"
26#include "parser/parse_type.h"
27#include "utils/fmgrprotos.h"
28#include "utils/json.h"
29#include "utils/lsyscache.h"
30
31/* Context for transformJsonTableColumns() */
33{
37 List *pathNames; /* list of all path and columns names */
38 int pathNameId; /* path name id counter */
40
42 List *columns,
43 List *passingArgs,
44 JsonTablePathSpec *pathspec);
46 List *passingArgs,
47 List *columns);
49 Node *contextItemExpr,
50 List *passingArgs);
51static bool isCompositeType(Oid typid);
53 bool errorOnError,
54 int colMin, int colMax,
55 JsonTablePlan *childplan);
57 List *columns);
61 JsonTablePlan *rplan);
62
63/*
64 * transformJsonTable -
65 * Transform a raw JsonTable into TableFunc
66 *
67 * Mainly, this transforms the JSON_TABLE() document-generating expression
68 * (jt->context_item) and the column-generating expressions (jt->columns) to
69 * populate TableFunc.docexpr and TableFunc.colvalexprs, respectively. Also,
70 * the PASSING values (jt->passing) are transformed and added into
71 * TableFunc.passingvalexprs.
72 */
75{
76 TableFunc *tf;
77 JsonFuncExpr *jfe;
78 JsonExpr *je;
79 JsonTablePathSpec *rootPathSpec = jt->pathspec;
80 bool is_lateral;
81 JsonTableParseContext cxt = {pstate};
82
83 Assert(IsA(rootPathSpec->string, A_Const) &&
84 castNode(A_Const, rootPathSpec->string)->val.node.type == T_String);
85
86 if (jt->on_error &&
91 errcode(ERRCODE_SYNTAX_ERROR),
92 errmsg("invalid %s behavior", "ON ERROR"),
93 errdetail("Only EMPTY [ ARRAY ] or ERROR is allowed in the top-level ON ERROR clause."),
95
96 cxt.pathNameId = 0;
97 if (rootPathSpec->name == NULL)
98 rootPathSpec->name = generateJsonTablePathName(&cxt);
99 cxt.pathNames = list_make1(rootPathSpec->name);
101
102 /*
103 * We make lateral_only names of this level visible, whether or not the
104 * RangeTableFunc is explicitly marked LATERAL. This is needed for SQL
105 * spec compliance and seems useful on convenience grounds for all
106 * functions in FROM.
107 *
108 * (LATERAL can't nest within a single pstate level, so we don't need
109 * save/restore logic here.)
110 */
111 Assert(!pstate->p_lateral_active);
112 pstate->p_lateral_active = true;
113
114 tf = makeNode(TableFunc);
116
117 /*
118 * Transform JsonFuncExpr representing the top JSON_TABLE context_item and
119 * pathspec into a dummy JSON_TABLE_OP JsonExpr.
120 */
121 jfe = makeNode(JsonFuncExpr);
122 jfe->op = JSON_TABLE_OP;
123 jfe->context_item = jt->context_item;
124 jfe->pathspec = (Node *) rootPathSpec->string;
125 jfe->passing = jt->passing;
126 jfe->on_empty = NULL;
127 jfe->on_error = jt->on_error;
128 jfe->location = jt->location;
129 tf->docexpr = transformExpr(pstate, (Node *) jfe, EXPR_KIND_FROM_FUNCTION);
130
131 /*
132 * Create a JsonTablePlan that will generate row pattern that becomes
133 * source data for JSON path expressions in jt->columns. This also adds
134 * the columns' transformed JsonExpr nodes into tf->colvalexprs.
135 */
136 cxt.jt = jt;
137 cxt.tf = tf;
138 tf->plan = (Node *) transformJsonTableColumns(&cxt, jt->columns,
139 jt->passing,
140 rootPathSpec);
141
142 /*
143 * Copy the transformed PASSING arguments into the TableFunc node, because
144 * they are evaluated separately from the JsonExpr that we just put in
145 * TableFunc.docexpr. JsonExpr.passing_values is still kept around for
146 * get_json_table().
147 */
148 je = (JsonExpr *) tf->docexpr;
149 tf->passingvalexprs = copyObject(je->passing_values);
150
151 tf->ordinalitycol = -1; /* undefine ordinality column number */
152 tf->location = jt->location;
153
154 pstate->p_lateral_active = false;
155
156 /*
157 * Mark the RTE as LATERAL if the user said LATERAL explicitly, or if
158 * there are any lateral cross-references in it.
159 */
160 is_lateral = jt->lateral || contain_vars_of_level((Node *) tf, 0);
161
162 return addRangeTableEntryForTableFunc(pstate,
163 tf, jt->alias, is_lateral, true);
164}
165
166/*
167 * Check if a column / path name is duplicated in the given shared list of
168 * names.
169 */
170static void
172 List *columns)
173{
174 ListCell *lc1;
175
176 foreach(lc1, columns)
177 {
179
180 if (jtc->coltype == JTC_NESTED)
181 {
182 if (jtc->pathspec->name)
183 {
184 if (LookupPathOrColumnName(cxt, jtc->pathspec->name))
186 errcode(ERRCODE_DUPLICATE_ALIAS),
187 errmsg("duplicate JSON_TABLE column or path name: %s",
188 jtc->pathspec->name),
190 jtc->pathspec->name_location));
191 cxt->pathNames = lappend(cxt->pathNames, jtc->pathspec->name);
192 }
193
195 }
196 else
197 {
198 if (LookupPathOrColumnName(cxt, jtc->name))
200 errcode(ERRCODE_DUPLICATE_ALIAS),
201 errmsg("duplicate JSON_TABLE column or path name: %s",
202 jtc->name),
204 cxt->pathNames = lappend(cxt->pathNames, jtc->name);
205 }
206 }
207}
208
209/*
210 * Lookup a column/path name in the given name list, returning true if already
211 * there.
212 */
213static bool
215{
216 ListCell *lc;
217
218 foreach(lc, cxt->pathNames)
219 {
220 if (strcmp(name, (const char *) lfirst(lc)) == 0)
221 return true;
222 }
223
224 return false;
225}
226
227/* Generate a new unique JSON_TABLE path name. */
228static char *
230{
231 char namebuf[32];
232 char *name = namebuf;
233
234 snprintf(namebuf, sizeof(namebuf), "json_table_path_%d",
235 cxt->pathNameId++);
236
237 name = pstrdup(name);
238 cxt->pathNames = lappend(cxt->pathNames, name);
239
240 return name;
241}
242
243/*
244 * Create a JsonTablePlan that will supply the source row for 'columns'
245 * using 'pathspec' and append the columns' transformed JsonExpr nodes and
246 * their type/collation information to cxt->tf.
247 */
248static JsonTablePlan *
250 List *passingArgs,
251 JsonTablePathSpec *pathspec)
252{
253 ParseState *pstate = cxt->pstate;
254 JsonTable *jt = cxt->jt;
255 TableFunc *tf = cxt->tf;
256 ListCell *col;
257 bool ordinality_found = false;
258 bool errorOnError = jt->on_error &&
260 Oid contextItemTypid = exprType(tf->docexpr);
261 int colMin,
262 colMax;
263 JsonTablePlan *childplan;
264
265 /* Start of column range */
266 colMin = list_length(tf->colvalexprs);
267
268 foreach(col, columns)
269 {
271 Oid typid;
272 int32 typmod;
273 Oid typcoll = InvalidOid;
274 Node *colexpr;
275
276 if (rawc->coltype != JTC_NESTED)
277 {
278 Assert(rawc->name);
279 tf->colnames = lappend(tf->colnames,
280 makeString(pstrdup(rawc->name)));
281 }
282
283 /*
284 * Determine the type and typmod for the new column. FOR ORDINALITY
285 * columns are INTEGER by standard; the others are user-specified.
286 */
287 switch (rawc->coltype)
288 {
290 if (ordinality_found)
292 (errcode(ERRCODE_SYNTAX_ERROR),
293 errmsg("only one FOR ORDINALITY column is allowed"),
294 parser_errposition(pstate, rawc->location)));
295 ordinality_found = true;
296 colexpr = NULL;
297 typid = INT4OID;
298 typmod = -1;
299 break;
300
301 case JTC_REGULAR:
302 typenameTypeIdAndMod(pstate, rawc->typeName, &typid, &typmod);
303
304 /*
305 * Use JTC_FORMATTED so as to use JSON_QUERY for this column
306 * if the specified type is one that's better handled using
307 * JSON_QUERY() or if non-default WRAPPER or QUOTES behavior
308 * is specified.
309 */
310 if (isCompositeType(typid) ||
311 rawc->quotes != JS_QUOTES_UNSPEC ||
312 rawc->wrapper != JSW_UNSPEC)
313 rawc->coltype = JTC_FORMATTED;
314
315 /* FALLTHROUGH */
316 case JTC_FORMATTED:
317 case JTC_EXISTS:
318 {
319 JsonFuncExpr *jfe;
321
322 param->collation = InvalidOid;
323 param->typeId = contextItemTypid;
324 param->typeMod = -1;
325
326 jfe = transformJsonTableColumn(rawc, (Node *) param,
327 passingArgs);
328
329 colexpr = transformExpr(pstate, (Node *) jfe,
331 assign_expr_collations(pstate, colexpr);
332
333 typid = exprType(colexpr);
334 typmod = exprTypmod(colexpr);
335 typcoll = exprCollation(colexpr);
336 break;
337 }
338
339 case JTC_NESTED:
340 continue;
341
342 default:
343 elog(ERROR, "unknown JSON_TABLE column type: %d", (int) rawc->coltype);
344 break;
345 }
346
347 tf->coltypes = lappend_oid(tf->coltypes, typid);
348 tf->coltypmods = lappend_int(tf->coltypmods, typmod);
349 tf->colcollations = lappend_oid(tf->colcollations, typcoll);
350 tf->colvalexprs = lappend(tf->colvalexprs, colexpr);
351 }
352
353 /* End of column range. */
354 if (list_length(tf->colvalexprs) == colMin)
355 {
356 /* No columns in this Scan beside the nested ones. */
357 colMax = colMin = -1;
358 }
359 else
360 colMax = list_length(tf->colvalexprs) - 1;
361
362 /* Recursively transform nested columns */
363 childplan = transformJsonTableNestedColumns(cxt, passingArgs, columns);
364
365 /* Create a "parent" scan responsible for all columns handled above. */
366 return makeJsonTablePathScan(pathspec, errorOnError, colMin, colMax,
367 childplan);
368}
369
370/*
371 * Check if the type is "composite" for the purpose of checking whether to use
372 * JSON_VALUE() or JSON_QUERY() for a given JsonTableColumn.
373 */
374static bool
376{
377 char typtype = get_typtype(typid);
378
379 return typid == JSONOID ||
380 typid == JSONBOID ||
381 typid == RECORDOID ||
382 type_is_array(typid) ||
383 typtype == TYPTYPE_COMPOSITE ||
384 /* domain over one of the above? */
385 (typtype == TYPTYPE_DOMAIN &&
387}
388
389/*
390 * Transform JSON_TABLE column definition into a JsonFuncExpr
391 * This turns:
392 * - regular column into JSON_VALUE()
393 * - FORMAT JSON column into JSON_QUERY()
394 * - EXISTS column into JSON_EXISTS()
395 */
396static JsonFuncExpr *
398 List *passingArgs)
399{
400 Node *pathspec;
402
403 if (jtc->coltype == JTC_REGULAR)
404 jfexpr->op = JSON_VALUE_OP;
405 else if (jtc->coltype == JTC_EXISTS)
406 jfexpr->op = JSON_EXISTS_OP;
407 else
408 jfexpr->op = JSON_QUERY_OP;
409
410 /* Pass the column name so any runtime JsonExpr errors can print it. */
411 Assert(jtc->name != NULL);
412 jfexpr->column_name = pstrdup(jtc->name);
413
414 jfexpr->context_item = makeJsonValueExpr((Expr *) contextItemExpr, NULL,
417 -1));
418 if (jtc->pathspec)
419 pathspec = (Node *) jtc->pathspec->string;
420 else
421 {
422 /* Construct default path as '$."column_name"' */
423 StringInfoData path;
424
425 initStringInfo(&path);
426
427 appendStringInfoString(&path, "$.");
428 escape_json(&path, jtc->name);
429
430 pathspec = makeStringConst(path.data, -1);
431 }
432 jfexpr->pathspec = pathspec;
433 jfexpr->passing = passingArgs;
434 jfexpr->output = makeNode(JsonOutput);
435 jfexpr->output->typeName = jtc->typeName;
436 jfexpr->output->returning = makeNode(JsonReturning);
437 jfexpr->output->returning->format = jtc->format;
438 jfexpr->on_empty = jtc->on_empty;
439 jfexpr->on_error = jtc->on_error;
440 jfexpr->quotes = jtc->quotes;
441 jfexpr->wrapper = jtc->wrapper;
442 jfexpr->location = jtc->location;
443
444 return jfexpr;
445}
446
447/*
448 * Recursively transform nested columns and create child plan(s) that will be
449 * used to evaluate their row patterns.
450 */
451static JsonTablePlan *
453 List *passingArgs,
454 List *columns)
455{
456 JsonTablePlan *plan = NULL;
457 ListCell *lc;
458
459 /*
460 * If there are multiple NESTED COLUMNS clauses in 'columns', their
461 * respective plans will be combined using a "sibling join" plan, which
462 * effectively does a UNION of the sets of rows coming from each nested
463 * plan.
464 */
465 foreach(lc, columns)
466 {
468 JsonTablePlan *nested;
469
470 if (jtc->coltype != JTC_NESTED)
471 continue;
472
473 if (jtc->pathspec->name == NULL)
475
476 nested = transformJsonTableColumns(cxt, jtc->columns, passingArgs,
477 jtc->pathspec);
478
479 if (plan)
481 else
482 plan = nested;
483 }
484
485 return plan;
486}
487
488/*
489 * Create a JsonTablePlan for given path and ON ERROR behavior.
490 *
491 * colMin and colMin give the range of columns computed by this scan in the
492 * global flat list of column expressions that will be passed to the
493 * JSON_TABLE's TableFunc. Both are -1 when all of columns are nested and
494 * thus computed by 'childplan'.
495 */
496static JsonTablePlan *
497makeJsonTablePathScan(JsonTablePathSpec *pathspec, bool errorOnError,
498 int colMin, int colMax,
499 JsonTablePlan *childplan)
500{
502 char *pathstring;
503 Const *value;
504
505 Assert(IsA(pathspec->string, A_Const));
506 pathstring = castNode(A_Const, pathspec->string)->val.sval.sval;
507 value = makeConst(JSONPATHOID, -1, InvalidOid, -1,
509 CStringGetDatum(pathstring)),
510 false, false);
511
512 scan->plan.type = T_JsonTablePathScan;
513 scan->path = makeJsonTablePath(value, pathspec->name);
514 scan->errorOnError = errorOnError;
515
516 scan->child = childplan;
517
518 scan->colMin = colMin;
519 scan->colMax = colMax;
520
521 return (JsonTablePlan *) scan;
522}
523
524/*
525 * Create a JsonTablePlan that will perform a join of the rows coming from
526 * 'lplan' and 'rplan'.
527 *
528 * The default way of "joining" the rows is to perform a UNION between the
529 * sets of rows from 'lplan' and 'rplan'.
530 */
531static JsonTablePlan *
533{
535
536 join->plan.type = T_JsonTableSiblingJoin;
537 join->lplan = lplan;
538 join->rplan = rplan;
539
540 return (JsonTablePlan *) join;
541}
#define Assert(condition)
Definition: c.h:815
int32_t int32
Definition: c.h:484
int errdetail(const char *fmt,...)
Definition: elog.c:1203
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
#define ereport(elevel,...)
Definition: elog.h:149
#define DirectFunctionCall1(func, arg1)
Definition: fmgr.h:641
static struct @162 value
void escape_json(StringInfo buf, const char *str)
Definition: json.c:1602
Datum jsonpath_in(PG_FUNCTION_ARGS)
Definition: jsonpath.c:98
List * lappend(List *list, void *datum)
Definition: list.c:339
List * lappend_int(List *list, int datum)
Definition: list.c:357
List * lappend_oid(List *list, Oid datum)
Definition: list.c:375
char get_typtype(Oid typid)
Definition: lsyscache.c:2629
Oid getBaseType(Oid typid)
Definition: lsyscache.c:2521
#define type_is_array(typid)
Definition: lsyscache.h:209
JsonTablePath * makeJsonTablePath(Const *pathvalue, char *pathname)
Definition: makefuncs.c:979
Node * makeStringConst(char *str, int location)
Definition: makefuncs.c:571
JsonValueExpr * makeJsonValueExpr(Expr *raw_expr, Expr *formatted_expr, JsonFormat *format)
Definition: makefuncs.c:891
JsonFormat * makeJsonFormat(JsonFormatType type, JsonEncoding encoding, int location)
Definition: makefuncs.c:875
Const * makeConst(Oid consttype, int32 consttypmod, Oid constcollid, int constlen, Datum constvalue, bool constisnull, bool constbyval)
Definition: makefuncs.c:303
char * pstrdup(const char *in)
Definition: mcxt.c:1696
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:42
int32 exprTypmod(const Node *expr)
Definition: nodeFuncs.c:301
Oid exprCollation(const Node *expr)
Definition: nodeFuncs.c:821
#define IsA(nodeptr, _type_)
Definition: nodes.h:158
#define copyObject(obj)
Definition: nodes.h:224
#define makeNode(_type_)
Definition: nodes.h:155
#define castNode(_type_, nodeptr)
Definition: nodes.h:176
void assign_expr_collations(ParseState *pstate, Node *expr)
Node * transformExpr(ParseState *pstate, Node *expr, ParseExprKind exprKind)
Definition: parse_expr.c:118
static JsonTablePlan * makeJsonTableSiblingJoin(JsonTablePlan *lplan, JsonTablePlan *rplan)
static char * generateJsonTablePathName(JsonTableParseContext *cxt)
struct JsonTableParseContext JsonTableParseContext
ParseNamespaceItem * transformJsonTable(ParseState *pstate, JsonTable *jt)
static bool LookupPathOrColumnName(JsonTableParseContext *cxt, char *name)
static JsonFuncExpr * transformJsonTableColumn(JsonTableColumn *jtc, Node *contextItemExpr, List *passingArgs)
static JsonTablePlan * makeJsonTablePathScan(JsonTablePathSpec *pathspec, bool errorOnError, int colMin, int colMax, JsonTablePlan *childplan)
static JsonTablePlan * transformJsonTableColumns(JsonTableParseContext *cxt, List *columns, List *passingArgs, JsonTablePathSpec *pathspec)
static bool isCompositeType(Oid typid)
static void CheckDuplicateColumnOrPathNames(JsonTableParseContext *cxt, List *columns)
static JsonTablePlan * transformJsonTableNestedColumns(JsonTableParseContext *cxt, List *passingArgs, List *columns)
int parser_errposition(ParseState *pstate, int location)
Definition: parse_node.c:106
@ EXPR_KIND_FROM_FUNCTION
Definition: parse_node.h:45
ParseNamespaceItem * addRangeTableEntryForTableFunc(ParseState *pstate, TableFunc *tf, Alias *alias, bool lateral, bool inFromCl)
void typenameTypeIdAndMod(ParseState *pstate, const TypeName *typeName, Oid *typeid_p, int32 *typmod_p)
Definition: parse_type.c:310
@ JTC_FORMATTED
Definition: parsenodes.h:1887
@ JTC_FOR_ORDINALITY
Definition: parsenodes.h:1884
@ JTC_NESTED
Definition: parsenodes.h:1888
@ JTC_EXISTS
Definition: parsenodes.h:1886
@ JTC_REGULAR
Definition: parsenodes.h:1885
@ JS_QUOTES_UNSPEC
Definition: parsenodes.h:1819
#define lfirst(lc)
Definition: pg_list.h:172
static int list_length(const List *l)
Definition: pg_list.h:152
#define list_make1(x1)
Definition: pg_list.h:212
#define plan(x)
Definition: pg_regress.c:161
#define snprintf
Definition: port.h:238
static Datum CStringGetDatum(const char *X)
Definition: postgres.h:355
#define InvalidOid
Definition: postgres_ext.h:37
unsigned int Oid
Definition: postgres_ext.h:32
@ JS_FORMAT_DEFAULT
Definition: primnodes.h:1669
@ TFT_JSON_TABLE
Definition: primnodes.h:100
@ JS_ENC_DEFAULT
Definition: primnodes.h:1657
@ JSW_UNSPEC
Definition: primnodes.h:1781
@ JSON_BEHAVIOR_ERROR
Definition: primnodes.h:1797
@ JSON_BEHAVIOR_EMPTY
Definition: primnodes.h:1798
@ JSON_BEHAVIOR_EMPTY_ARRAY
Definition: primnodes.h:1802
@ JSON_QUERY_OP
Definition: primnodes.h:1834
@ JSON_TABLE_OP
Definition: primnodes.h:1836
@ JSON_EXISTS_OP
Definition: primnodes.h:1833
@ JSON_VALUE_OP
Definition: primnodes.h:1835
void appendStringInfoString(StringInfo str, const char *s)
Definition: stringinfo.c:230
void initStringInfo(StringInfo str)
Definition: stringinfo.c:97
ParseLoc location
Definition: primnodes.h:1824
JsonBehaviorType btype
Definition: primnodes.h:1821
List * passing_values
Definition: primnodes.h:1867
char * column_name
Definition: parsenodes.h:1833
JsonExprOp op
Definition: parsenodes.h:1832
List * passing
Definition: parsenodes.h:1837
JsonBehavior * on_empty
Definition: parsenodes.h:1839
ParseLoc location
Definition: parsenodes.h:1843
Node * pathspec
Definition: parsenodes.h:1836
JsonBehavior * on_error
Definition: parsenodes.h:1840
JsonValueExpr * context_item
Definition: parsenodes.h:1835
ParseLoc location
Definition: parsenodes.h:1908
JsonTableColumnType coltype
Definition: parsenodes.h:1898
JsonWrapper wrapper
Definition: parsenodes.h:1903
JsonQuotes quotes
Definition: parsenodes.h:1904
TypeName * typeName
Definition: parsenodes.h:1900
JsonTablePathSpec * pathspec
Definition: parsenodes.h:1901
JsonTablePath * path
Definition: primnodes.h:1929
JsonTablePlan * child
Definition: primnodes.h:1938
JsonTablePlan plan
Definition: primnodes.h:1926
ParseLoc name_location
Definition: parsenodes.h:1857
JsonTablePlan * rplan
Definition: primnodes.h:1959
JsonTablePlan * lplan
Definition: primnodes.h:1958
JsonTablePlan plan
Definition: primnodes.h:1956
JsonBehavior * on_error
Definition: parsenodes.h:1872
List * columns
Definition: parsenodes.h:1871
JsonTablePathSpec * pathspec
Definition: parsenodes.h:1869
Alias * alias
Definition: parsenodes.h:1873
bool lateral
Definition: parsenodes.h:1874
List * passing
Definition: parsenodes.h:1870
JsonValueExpr * context_item
Definition: parsenodes.h:1868
ParseLoc location
Definition: parsenodes.h:1875
Definition: pg_list.h:54
Definition: nodes.h:129
bool p_lateral_active
Definition: parse_node.h:221
ParseLoc location
Definition: primnodes.h:145
Node * docexpr
Definition: primnodes.h:119
TableFuncType functype
Definition: primnodes.h:113
String * makeString(char *str)
Definition: value.c:63
bool contain_vars_of_level(Node *node, int levelsup)
Definition: var.c:444
const char * name