1/*-------------------------------------------------------------------------
4 * parsing of JSON_TABLE
6 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * src/backend/parser/parse_jsontable.c
13 *-------------------------------------------------------------------------
27#include "utils/fmgrprotos.h"
31/* Context for transformJsonTableColumns() */
49 Node *contextItemExpr,
54 int colMin,
int colMax,
64 * transformJsonTable -
65 * Transform a raw JsonTable into TableFunc
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.
92 errmsg(
"invalid %s behavior",
"ON ERROR"),
93 errdetail(
"Only EMPTY [ ARRAY ] or ERROR is allowed in the top-level ON ERROR clause."),
97 if (rootPathSpec->
name == NULL)
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
108 * (LATERAL can't nest within a single pstate level, so we don't need
109 * save/restore logic here.)
118 * Transform JsonFuncExpr representing the top JSON_TABLE context_item and
119 * pathspec into a dummy JSON_TABLE_OP JsonExpr.
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.
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
151 tf->ordinalitycol = -1;
/* undefine ordinality column number */
157 * Mark the RTE as LATERAL if the user said LATERAL explicitly, or if
158 * there are any lateral cross-references in it.
163 tf, jt->
alias, is_lateral,
true);
167 * Check if a column / path name is duplicated in the given shared list of
176 foreach(lc1, columns)
186 errcode(ERRCODE_DUPLICATE_ALIAS),
187 errmsg(
"duplicate JSON_TABLE column or path name: %s",
200 errcode(ERRCODE_DUPLICATE_ALIAS),
201 errmsg(
"duplicate JSON_TABLE column or path name: %s",
210 * Lookup a column/path name in the given name list, returning true if already
220 if (strcmp(
name, (
const char *)
lfirst(lc)) == 0)
227/* Generate a new unique JSON_TABLE path name. */
232 char *
name = namebuf;
234 snprintf(namebuf,
sizeof(namebuf),
"json_table_path_%d",
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.
257 bool ordinality_found =
false;
265 /* Start of column range */
268 foreach(col, columns)
279 tf->colnames =
lappend(tf->colnames,
284 * Determine the type and typmod for the new column. FOR ORDINALITY
285 * columns are INTEGER by standard; the others are user-specified.
290 if (ordinality_found)
292 (
errcode(ERRCODE_SYNTAX_ERROR),
293 errmsg(
"only one FOR ORDINALITY column is allowed"),
295 ordinality_found =
true;
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
323 param->
typeId = contextItemTypid;
348 tf->coltypmods =
lappend_int(tf->coltypmods, typmod);
349 tf->colcollations =
lappend_oid(tf->colcollations, typcoll);
350 tf->colvalexprs =
lappend(tf->colvalexprs, colexpr);
353 /* End of column range. */
356 /* No columns in this Scan beside the nested ones. */
357 colMax = colMin = -1;
362 /* Recursively transform nested columns */
365 /* Create a "parent" scan responsible for all columns handled above. */
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.
379 return typid == JSONOID ||
381 typid == RECORDOID ||
383 typtype == TYPTYPE_COMPOSITE ||
384 /* domain over one of the above? */
385 (typtype == TYPTYPE_DOMAIN &&
390 * Transform JSON_TABLE column definition into a JsonFuncExpr
392 * - regular column into JSON_VALUE()
393 * - FORMAT JSON column into JSON_QUERY()
394 * - EXISTS column into JSON_EXISTS()
410 /* Pass the column name so any runtime JsonExpr errors can print it. */
422 /* Construct default path as '$."column_name"' */
432 jfexpr->pathspec = pathspec;
433 jfexpr->passing = passingArgs;
435 jfexpr->output->typeName = jtc->typeName;
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;
448 * Recursively transform nested columns and create child plan(s) that will be
449 * used to evaluate their row patterns.
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
489 * Create a JsonTablePlan for given path and ON ERROR behavior.
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'.
498 int colMin,
int colMax,
512 scan->
plan.type = T_JsonTablePathScan;
516 scan->
child = childplan;
525 * Create a JsonTablePlan that will perform a join of the rows coming from
526 * 'lplan' and 'rplan'.
528 * The default way of "joining" the rows is to perform a UNION between the
529 * sets of rows from 'lplan' and 'rplan'.
536 join->
plan.type = T_JsonTableSiblingJoin;
int errdetail(const char *fmt,...)
int errcode(int sqlerrcode)
int errmsg(const char *fmt,...)
#define ereport(elevel,...)
#define DirectFunctionCall1(func, arg1)
Assert(PointerIsAligned(start, uint64))
void escape_json(StringInfo buf, const char *str)
Datum jsonpath_in(PG_FUNCTION_ARGS)
List * lappend(List *list, void *datum)
List * lappend_int(List *list, int datum)
List * lappend_oid(List *list, Oid datum)
char get_typtype(Oid typid)
Oid getBaseType(Oid typid)
#define type_is_array(typid)
JsonTablePath * makeJsonTablePath(Const *pathvalue, char *pathname)
Node * makeStringConst(char *str, int location)
JsonValueExpr * makeJsonValueExpr(Expr *raw_expr, Expr *formatted_expr, JsonFormat *format)
JsonFormat * makeJsonFormat(JsonFormatType type, JsonEncoding encoding, int location)
Const * makeConst(Oid consttype, int32 consttypmod, Oid constcollid, int constlen, Datum constvalue, bool constisnull, bool constbyval)
char * pstrdup(const char *in)
Oid exprType(const Node *expr)
int32 exprTypmod(const Node *expr)
Oid exprCollation(const Node *expr)
#define IsA(nodeptr, _type_)
#define castNode(_type_, nodeptr)
void assign_expr_collations(ParseState *pstate, Node *expr)
Node * transformExpr(ParseState *pstate, Node *expr, ParseExprKind exprKind)
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)
@ EXPR_KIND_FROM_FUNCTION
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)
static int list_length(const List *l)
static Datum CStringGetDatum(const char *X)
@ JSON_BEHAVIOR_EMPTY_ARRAY
void appendStringInfoString(StringInfo str, const char *s)
void initStringInfo(StringInfo str)
JsonValueExpr * context_item
JsonTableColumnType coltype
JsonTablePathSpec * pathspec
JsonTablePathSpec * pathspec
JsonValueExpr * context_item
String * makeString(char *str)
bool contain_vars_of_level(Node *node, int levelsup)