mirror of
https://github.com/postgres/postgres.git
synced 2026-02-03 20:40:14 -05:00
Compiling with -Wshadow=compatible-local yields quite a few warnings about
local variables being shadowed by compatible local variables in an inner
scope. Of course, this is perfectly valid in C, but we have had bugs in
the past as a result of developers failing to notice this. af7d270dd is a
recent example.
Here we do a cleanup of warnings we receive from -Wshadow=compatible-local
for code which is new to PostgreSQL 15. We've yet to have the discussion
about if we actually ever want to run that as a standard compilation flag.
We'll need to at least get the number of warnings down to something easier
to manage before we can realistically consider if we want this or not.
This commit is the first step towards reducing the warnings.
The changes being made here are all fairly trivial. Because of that, and
the fact that v15 is still in beta, this is being back-patched into 15.
It seems more risky not to do this as the risk of future bugs is increased
by the additional conflicts that this commit could cause for any future
bug fixes touching the same areas as this commit.
Author: Justin Pryzby
Discussion: https://postgr.es/m/20220817145434.GC26426%40telsasoft.com
Backpatch-through: 15
732 lines
19 KiB
C
732 lines
19 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* parse_jsontable.c
|
|
* parsing of JSON_TABLE
|
|
*
|
|
* Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* src/backend/parser/parse_jsontable.c
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
|
|
#include "catalog/pg_collation.h"
|
|
#include "catalog/pg_type.h"
|
|
#include "miscadmin.h"
|
|
#include "nodes/makefuncs.h"
|
|
#include "nodes/nodeFuncs.h"
|
|
#include "optimizer/optimizer.h"
|
|
#include "parser/parse_clause.h"
|
|
#include "parser/parse_collate.h"
|
|
#include "parser/parse_expr.h"
|
|
#include "parser/parse_relation.h"
|
|
#include "parser/parse_type.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/json.h"
|
|
#include "utils/lsyscache.h"
|
|
|
|
/* Context for JSON_TABLE transformation */
|
|
typedef struct JsonTableContext
|
|
{
|
|
ParseState *pstate; /* parsing state */
|
|
JsonTable *table; /* untransformed node */
|
|
TableFunc *tablefunc; /* transformed node */
|
|
List *pathNames; /* list of all path and columns names */
|
|
int pathNameId; /* path name id counter */
|
|
Oid contextItemTypid; /* type oid of context item (json/jsonb) */
|
|
} JsonTableContext;
|
|
|
|
static JsonTableParent *transformJsonTableColumns(JsonTableContext *cxt,
|
|
JsonTablePlan *plan,
|
|
List *columns,
|
|
char *pathSpec,
|
|
char **pathName,
|
|
int location);
|
|
|
|
static Node *
|
|
makeStringConst(char *str, int location)
|
|
{
|
|
A_Const *n = makeNode(A_Const);
|
|
|
|
n->val.node.type = T_String;
|
|
n->val.sval.sval = str;
|
|
n->location = location;
|
|
|
|
return (Node *) n;
|
|
}
|
|
|
|
/*
|
|
* Transform JSON_TABLE column
|
|
* - regular column into JSON_VALUE()
|
|
* - FORMAT JSON column into JSON_QUERY()
|
|
* - EXISTS column into JSON_EXISTS()
|
|
*/
|
|
static Node *
|
|
transformJsonTableColumn(JsonTableColumn *jtc, Node *contextItemExpr,
|
|
List *passingArgs, bool errorOnError)
|
|
{
|
|
JsonFuncExpr *jfexpr = makeNode(JsonFuncExpr);
|
|
JsonCommon *common = makeNode(JsonCommon);
|
|
JsonOutput *output = makeNode(JsonOutput);
|
|
char *pathspec;
|
|
JsonFormat *default_format;
|
|
|
|
jfexpr->op =
|
|
jtc->coltype == JTC_REGULAR ? JSON_VALUE_OP :
|
|
jtc->coltype == JTC_EXISTS ? JSON_EXISTS_OP : JSON_QUERY_OP;
|
|
jfexpr->common = common;
|
|
jfexpr->output = output;
|
|
jfexpr->on_empty = jtc->on_empty;
|
|
jfexpr->on_error = jtc->on_error;
|
|
if (!jfexpr->on_error && errorOnError)
|
|
jfexpr->on_error = makeJsonBehavior(JSON_BEHAVIOR_ERROR, NULL);
|
|
jfexpr->omit_quotes = jtc->omit_quotes;
|
|
jfexpr->wrapper = jtc->wrapper;
|
|
jfexpr->location = jtc->location;
|
|
|
|
output->typeName = jtc->typeName;
|
|
output->returning = makeNode(JsonReturning);
|
|
output->returning->format = jtc->format;
|
|
|
|
default_format = makeJsonFormat(JS_FORMAT_DEFAULT, JS_ENC_DEFAULT, -1);
|
|
|
|
common->pathname = NULL;
|
|
common->expr = makeJsonValueExpr((Expr *) contextItemExpr, default_format);
|
|
common->passing = passingArgs;
|
|
|
|
if (jtc->pathspec)
|
|
pathspec = jtc->pathspec;
|
|
else
|
|
{
|
|
/* Construct default path as '$."column_name"' */
|
|
StringInfoData path;
|
|
|
|
initStringInfo(&path);
|
|
|
|
appendStringInfoString(&path, "$.");
|
|
escape_json(&path, jtc->name);
|
|
|
|
pathspec = path.data;
|
|
}
|
|
|
|
common->pathspec = makeStringConst(pathspec, -1);
|
|
|
|
return (Node *) jfexpr;
|
|
}
|
|
|
|
static bool
|
|
isJsonTablePathNameDuplicate(JsonTableContext *cxt, const char *pathname)
|
|
{
|
|
ListCell *lc;
|
|
|
|
foreach(lc, cxt->pathNames)
|
|
{
|
|
if (!strcmp(pathname, (const char *) lfirst(lc)))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/* Register the column name in the path name list. */
|
|
static void
|
|
registerJsonTableColumn(JsonTableContext *cxt, char *colname)
|
|
{
|
|
if (isJsonTablePathNameDuplicate(cxt, colname))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_DUPLICATE_ALIAS),
|
|
errmsg("duplicate JSON_TABLE column name: %s", colname),
|
|
errhint("JSON_TABLE column names must be distinct from one another.")));
|
|
|
|
cxt->pathNames = lappend(cxt->pathNames, colname);
|
|
}
|
|
|
|
/* Recursively register all nested column names in the path name list. */
|
|
static void
|
|
registerAllJsonTableColumns(JsonTableContext *cxt, List *columns)
|
|
{
|
|
ListCell *lc;
|
|
|
|
foreach(lc, columns)
|
|
{
|
|
JsonTableColumn *jtc = castNode(JsonTableColumn, lfirst(lc));
|
|
|
|
if (jtc->coltype == JTC_NESTED)
|
|
{
|
|
if (jtc->pathname)
|
|
registerJsonTableColumn(cxt, jtc->pathname);
|
|
|
|
registerAllJsonTableColumns(cxt, jtc->columns);
|
|
}
|
|
else
|
|
{
|
|
registerJsonTableColumn(cxt, jtc->name);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Generate a new unique JSON_TABLE path name. */
|
|
static char *
|
|
generateJsonTablePathName(JsonTableContext *cxt)
|
|
{
|
|
char namebuf[32];
|
|
char *name = namebuf;
|
|
|
|
do
|
|
{
|
|
snprintf(namebuf, sizeof(namebuf), "json_table_path_%d",
|
|
++cxt->pathNameId);
|
|
} while (isJsonTablePathNameDuplicate(cxt, name));
|
|
|
|
name = pstrdup(name);
|
|
cxt->pathNames = lappend(cxt->pathNames, name);
|
|
|
|
return name;
|
|
}
|
|
|
|
/* Collect sibling path names from plan to the specified list. */
|
|
static void
|
|
collectSiblingPathsInJsonTablePlan(JsonTablePlan *plan, List **paths)
|
|
{
|
|
if (plan->plan_type == JSTP_SIMPLE)
|
|
*paths = lappend(*paths, plan->pathname);
|
|
else if (plan->plan_type == JSTP_JOINED)
|
|
{
|
|
if (plan->join_type == JSTPJ_INNER ||
|
|
plan->join_type == JSTPJ_OUTER)
|
|
{
|
|
Assert(plan->plan1->plan_type == JSTP_SIMPLE);
|
|
*paths = lappend(*paths, plan->plan1->pathname);
|
|
}
|
|
else if (plan->join_type == JSTPJ_CROSS ||
|
|
plan->join_type == JSTPJ_UNION)
|
|
{
|
|
collectSiblingPathsInJsonTablePlan(plan->plan1, paths);
|
|
collectSiblingPathsInJsonTablePlan(plan->plan2, paths);
|
|
}
|
|
else
|
|
elog(ERROR, "invalid JSON_TABLE join type %d",
|
|
plan->join_type);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Validate child JSON_TABLE plan by checking that:
|
|
* - all nested columns have path names specified
|
|
* - all nested columns have corresponding node in the sibling plan
|
|
* - plan does not contain duplicate or extra nodes
|
|
*/
|
|
static void
|
|
validateJsonTableChildPlan(ParseState *pstate, JsonTablePlan *plan,
|
|
List *columns)
|
|
{
|
|
ListCell *lc1;
|
|
List *siblings = NIL;
|
|
int nchildren = 0;
|
|
|
|
if (plan)
|
|
collectSiblingPathsInJsonTablePlan(plan, &siblings);
|
|
|
|
foreach(lc1, columns)
|
|
{
|
|
JsonTableColumn *jtc = castNode(JsonTableColumn, lfirst(lc1));
|
|
|
|
if (jtc->coltype == JTC_NESTED)
|
|
{
|
|
ListCell *lc2;
|
|
bool found = false;
|
|
|
|
if (!jtc->pathname)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
errmsg("nested JSON_TABLE columns must contain an explicit AS pathname specification if an explicit PLAN clause is used"),
|
|
parser_errposition(pstate, jtc->location)));
|
|
|
|
/* find nested path name in the list of sibling path names */
|
|
foreach(lc2, siblings)
|
|
{
|
|
if ((found = !strcmp(jtc->pathname, lfirst(lc2))))
|
|
break;
|
|
}
|
|
|
|
if (!found)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
errmsg("invalid JSON_TABLE plan"),
|
|
errdetail("Plan node for nested path %s was not found in plan.", jtc->pathname),
|
|
parser_errposition(pstate, jtc->location)));
|
|
|
|
nchildren++;
|
|
}
|
|
}
|
|
|
|
if (list_length(siblings) > nchildren)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
errmsg("invalid JSON_TABLE plan"),
|
|
errdetail("Plan node contains some extra or duplicate sibling nodes."),
|
|
parser_errposition(pstate, plan ? plan->location : -1)));
|
|
}
|
|
|
|
static JsonTableColumn *
|
|
findNestedJsonTableColumn(List *columns, const char *pathname)
|
|
{
|
|
ListCell *lc;
|
|
|
|
foreach(lc, columns)
|
|
{
|
|
JsonTableColumn *jtc = castNode(JsonTableColumn, lfirst(lc));
|
|
|
|
if (jtc->coltype == JTC_NESTED &&
|
|
jtc->pathname &&
|
|
!strcmp(jtc->pathname, pathname))
|
|
return jtc;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static Node *
|
|
transformNestedJsonTableColumn(JsonTableContext *cxt, JsonTableColumn *jtc,
|
|
JsonTablePlan *plan)
|
|
{
|
|
JsonTableParent *node;
|
|
char *pathname = jtc->pathname;
|
|
|
|
node = transformJsonTableColumns(cxt, plan, jtc->columns, jtc->pathspec,
|
|
&pathname, jtc->location);
|
|
node->name = pstrdup(pathname);
|
|
|
|
return (Node *) node;
|
|
}
|
|
|
|
static Node *
|
|
makeJsonTableSiblingJoin(bool cross, Node *lnode, Node *rnode)
|
|
{
|
|
JsonTableSibling *join = makeNode(JsonTableSibling);
|
|
|
|
join->larg = lnode;
|
|
join->rarg = rnode;
|
|
join->cross = cross;
|
|
|
|
return (Node *) join;
|
|
}
|
|
|
|
/*
|
|
* Recursively transform child JSON_TABLE plan.
|
|
*
|
|
* Default plan is transformed into a cross/union join of its nested columns.
|
|
* Simple and outer/inner plans are transformed into a JsonTableParent by
|
|
* finding and transforming corresponding nested column.
|
|
* Sibling plans are recursively transformed into a JsonTableSibling.
|
|
*/
|
|
static Node *
|
|
transformJsonTableChildPlan(JsonTableContext *cxt, JsonTablePlan *plan,
|
|
List *columns)
|
|
{
|
|
JsonTableColumn *jtc = NULL;
|
|
|
|
if (!plan || plan->plan_type == JSTP_DEFAULT)
|
|
{
|
|
/* unspecified or default plan */
|
|
Node *res = NULL;
|
|
ListCell *lc;
|
|
bool cross = plan && (plan->join_type & JSTPJ_CROSS);
|
|
|
|
/* transform all nested columns into cross/union join */
|
|
foreach(lc, columns)
|
|
{
|
|
JsonTableColumn *col = castNode(JsonTableColumn, lfirst(lc));
|
|
Node *node;
|
|
|
|
if (col->coltype != JTC_NESTED)
|
|
continue;
|
|
|
|
node = transformNestedJsonTableColumn(cxt, col, plan);
|
|
|
|
/* join transformed node with previous sibling nodes */
|
|
res = res ? makeJsonTableSiblingJoin(cross, res, node) : node;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
else if (plan->plan_type == JSTP_SIMPLE)
|
|
{
|
|
jtc = findNestedJsonTableColumn(columns, plan->pathname);
|
|
}
|
|
else if (plan->plan_type == JSTP_JOINED)
|
|
{
|
|
if (plan->join_type == JSTPJ_INNER ||
|
|
plan->join_type == JSTPJ_OUTER)
|
|
{
|
|
Assert(plan->plan1->plan_type == JSTP_SIMPLE);
|
|
jtc = findNestedJsonTableColumn(columns, plan->plan1->pathname);
|
|
}
|
|
else
|
|
{
|
|
Node *node1 = transformJsonTableChildPlan(cxt, plan->plan1,
|
|
columns);
|
|
Node *node2 = transformJsonTableChildPlan(cxt, plan->plan2,
|
|
columns);
|
|
|
|
return makeJsonTableSiblingJoin(plan->join_type == JSTPJ_CROSS,
|
|
node1, node2);
|
|
}
|
|
}
|
|
else
|
|
elog(ERROR, "invalid JSON_TABLE plan type %d", plan->plan_type);
|
|
|
|
if (!jtc)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
errmsg("invalid JSON_TABLE plan"),
|
|
errdetail("Path name was %s not found in nested columns list.",
|
|
plan->pathname),
|
|
parser_errposition(cxt->pstate, plan->location)));
|
|
|
|
return transformNestedJsonTableColumn(cxt, jtc, plan);
|
|
}
|
|
|
|
/* Check whether type is json/jsonb, array, or record. */
|
|
static bool
|
|
typeIsComposite(Oid typid)
|
|
{
|
|
char typtype;
|
|
|
|
if (typid == JSONOID ||
|
|
typid == JSONBOID ||
|
|
typid == RECORDOID ||
|
|
type_is_array(typid))
|
|
return true;
|
|
|
|
typtype = get_typtype(typid);
|
|
|
|
if (typtype == TYPTYPE_COMPOSITE)
|
|
return true;
|
|
|
|
if (typtype == TYPTYPE_DOMAIN)
|
|
return typeIsComposite(getBaseType(typid));
|
|
|
|
return false;
|
|
}
|
|
|
|
/* Append transformed non-nested JSON_TABLE columns to the TableFunc node */
|
|
static void
|
|
appendJsonTableColumns(JsonTableContext *cxt, List *columns)
|
|
{
|
|
ListCell *col;
|
|
ParseState *pstate = cxt->pstate;
|
|
JsonTable *jt = cxt->table;
|
|
TableFunc *tf = cxt->tablefunc;
|
|
bool errorOnError = jt->on_error &&
|
|
jt->on_error->btype == JSON_BEHAVIOR_ERROR;
|
|
|
|
foreach(col, columns)
|
|
{
|
|
JsonTableColumn *rawc = castNode(JsonTableColumn, lfirst(col));
|
|
Oid typid;
|
|
int32 typmod;
|
|
Node *colexpr;
|
|
|
|
if (rawc->name)
|
|
{
|
|
/* make sure column names are unique */
|
|
ListCell *colname;
|
|
|
|
foreach(colname, tf->colnames)
|
|
if (!strcmp((const char *) colname, rawc->name))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
errmsg("column name \"%s\" is not unique",
|
|
rawc->name),
|
|
parser_errposition(pstate, rawc->location)));
|
|
|
|
tf->colnames = lappend(tf->colnames,
|
|
makeString(pstrdup(rawc->name)));
|
|
}
|
|
|
|
/*
|
|
* Determine the type and typmod for the new column. FOR ORDINALITY
|
|
* columns are INTEGER by standard; the others are user-specified.
|
|
*/
|
|
switch (rawc->coltype)
|
|
{
|
|
case JTC_FOR_ORDINALITY:
|
|
colexpr = NULL;
|
|
typid = INT4OID;
|
|
typmod = -1;
|
|
break;
|
|
|
|
case JTC_REGULAR:
|
|
typenameTypeIdAndMod(pstate, rawc->typeName, &typid, &typmod);
|
|
|
|
/*
|
|
* Use implicit FORMAT JSON for composite types (arrays and
|
|
* records)
|
|
*/
|
|
if (typeIsComposite(typid))
|
|
rawc->coltype = JTC_FORMATTED;
|
|
else if (rawc->wrapper != JSW_NONE)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
errmsg("cannot use WITH WRAPPER clause with scalar columns"),
|
|
parser_errposition(pstate, rawc->location)));
|
|
else if (rawc->omit_quotes)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
errmsg("cannot use OMIT QUOTES clause with scalar columns"),
|
|
parser_errposition(pstate, rawc->location)));
|
|
|
|
/* FALLTHROUGH */
|
|
case JTC_EXISTS:
|
|
case JTC_FORMATTED:
|
|
{
|
|
Node *je;
|
|
CaseTestExpr *param = makeNode(CaseTestExpr);
|
|
|
|
param->collation = InvalidOid;
|
|
param->typeId = cxt->contextItemTypid;
|
|
param->typeMod = -1;
|
|
|
|
je = transformJsonTableColumn(rawc, (Node *) param,
|
|
NIL, errorOnError);
|
|
|
|
colexpr = transformExpr(pstate, je, EXPR_KIND_FROM_FUNCTION);
|
|
assign_expr_collations(pstate, colexpr);
|
|
|
|
typid = exprType(colexpr);
|
|
typmod = exprTypmod(colexpr);
|
|
break;
|
|
}
|
|
|
|
case JTC_NESTED:
|
|
continue;
|
|
|
|
default:
|
|
elog(ERROR, "unknown JSON_TABLE column type: %d", rawc->coltype);
|
|
break;
|
|
}
|
|
|
|
tf->coltypes = lappend_oid(tf->coltypes, typid);
|
|
tf->coltypmods = lappend_int(tf->coltypmods, typmod);
|
|
tf->colcollations = lappend_oid(tf->colcollations, get_typcollation(typid));
|
|
tf->colvalexprs = lappend(tf->colvalexprs, colexpr);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Create transformed JSON_TABLE parent plan node by appending all non-nested
|
|
* columns to the TableFunc node and remembering their indices in the
|
|
* colvalexprs list.
|
|
*/
|
|
static JsonTableParent *
|
|
makeParentJsonTableNode(JsonTableContext *cxt, char *pathSpec, List *columns)
|
|
{
|
|
JsonTableParent *node = makeNode(JsonTableParent);
|
|
|
|
node->path = makeConst(JSONPATHOID, -1, InvalidOid, -1,
|
|
DirectFunctionCall1(jsonpath_in,
|
|
CStringGetDatum(pathSpec)),
|
|
false, false);
|
|
|
|
/* save start of column range */
|
|
node->colMin = list_length(cxt->tablefunc->colvalexprs);
|
|
|
|
appendJsonTableColumns(cxt, columns);
|
|
|
|
/* save end of column range */
|
|
node->colMax = list_length(cxt->tablefunc->colvalexprs) - 1;
|
|
|
|
node->errorOnError =
|
|
cxt->table->on_error &&
|
|
cxt->table->on_error->btype == JSON_BEHAVIOR_ERROR;
|
|
|
|
return node;
|
|
}
|
|
|
|
static JsonTableParent *
|
|
transformJsonTableColumns(JsonTableContext *cxt, JsonTablePlan *plan,
|
|
List *columns, char *pathSpec, char **pathName,
|
|
int location)
|
|
{
|
|
JsonTableParent *node;
|
|
JsonTablePlan *childPlan;
|
|
bool defaultPlan = !plan || plan->plan_type == JSTP_DEFAULT;
|
|
|
|
if (!*pathName)
|
|
{
|
|
if (cxt->table->plan)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
errmsg("invalid JSON_TABLE expression"),
|
|
errdetail("JSON_TABLE columns must contain "
|
|
"explicit AS pathname specification if "
|
|
"explicit PLAN clause is used"),
|
|
parser_errposition(cxt->pstate, location)));
|
|
|
|
*pathName = generateJsonTablePathName(cxt);
|
|
}
|
|
|
|
if (defaultPlan)
|
|
childPlan = plan;
|
|
else
|
|
{
|
|
/* validate parent and child plans */
|
|
JsonTablePlan *parentPlan;
|
|
|
|
if (plan->plan_type == JSTP_JOINED)
|
|
{
|
|
if (plan->join_type != JSTPJ_INNER &&
|
|
plan->join_type != JSTPJ_OUTER)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
errmsg("invalid JSON_TABLE plan"),
|
|
errdetail("Expected INNER or OUTER JSON_TABLE plan node."),
|
|
parser_errposition(cxt->pstate, plan->location)));
|
|
|
|
parentPlan = plan->plan1;
|
|
childPlan = plan->plan2;
|
|
|
|
Assert(parentPlan->plan_type != JSTP_JOINED);
|
|
Assert(parentPlan->pathname);
|
|
}
|
|
else
|
|
{
|
|
parentPlan = plan;
|
|
childPlan = NULL;
|
|
}
|
|
|
|
if (strcmp(parentPlan->pathname, *pathName))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
errmsg("invalid JSON_TABLE plan"),
|
|
errdetail("Path name mismatch: expected %s but %s is given.",
|
|
*pathName, parentPlan->pathname),
|
|
parser_errposition(cxt->pstate, plan->location)));
|
|
|
|
validateJsonTableChildPlan(cxt->pstate, childPlan, columns);
|
|
}
|
|
|
|
/* transform only non-nested columns */
|
|
node = makeParentJsonTableNode(cxt, pathSpec, columns);
|
|
node->name = pstrdup(*pathName);
|
|
|
|
if (childPlan || defaultPlan)
|
|
{
|
|
/* transform recursively nested columns */
|
|
node->child = transformJsonTableChildPlan(cxt, childPlan, columns);
|
|
if (node->child)
|
|
node->outerJoin = !plan || (plan->join_type & JSTPJ_OUTER);
|
|
/* else: default plan case, no children found */
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
/*
|
|
* transformJsonTable -
|
|
* Transform a raw JsonTable into TableFunc.
|
|
*
|
|
* Transform the document-generating expression, the row-generating expression,
|
|
* the column-generating expressions, and the default value expressions.
|
|
*/
|
|
ParseNamespaceItem *
|
|
transformJsonTable(ParseState *pstate, JsonTable *jt)
|
|
{
|
|
JsonTableContext cxt;
|
|
TableFunc *tf = makeNode(TableFunc);
|
|
JsonFuncExpr *jfe = makeNode(JsonFuncExpr);
|
|
JsonTablePlan *plan = jt->plan;
|
|
JsonCommon *jscommon;
|
|
char *rootPathName = jt->common->pathname;
|
|
char *rootPath;
|
|
bool is_lateral;
|
|
|
|
cxt.pstate = pstate;
|
|
cxt.table = jt;
|
|
cxt.tablefunc = tf;
|
|
cxt.pathNames = NIL;
|
|
cxt.pathNameId = 0;
|
|
|
|
if (rootPathName)
|
|
registerJsonTableColumn(&cxt, rootPathName);
|
|
|
|
registerAllJsonTableColumns(&cxt, jt->columns);
|
|
|
|
#if 0 /* XXX it' unclear from the standard whether
|
|
* root path name is mandatory or not */
|
|
if (plan && plan->plan_type != JSTP_DEFAULT && !rootPathName)
|
|
{
|
|
/* Assign root path name and create corresponding plan node */
|
|
JsonTablePlan *rootNode = makeNode(JsonTablePlan);
|
|
JsonTablePlan *rootPlan = (JsonTablePlan *)
|
|
makeJsonTableJoinedPlan(JSTPJ_OUTER, (Node *) rootNode,
|
|
(Node *) plan, jt->location);
|
|
|
|
rootPathName = generateJsonTablePathName(&cxt);
|
|
|
|
rootNode->plan_type = JSTP_SIMPLE;
|
|
rootNode->pathname = rootPathName;
|
|
|
|
plan = rootPlan;
|
|
}
|
|
#endif
|
|
|
|
jscommon = copyObject(jt->common);
|
|
jscommon->pathspec = makeStringConst(pstrdup("$"), -1);
|
|
|
|
jfe->op = JSON_TABLE_OP;
|
|
jfe->common = jscommon;
|
|
jfe->on_error = jt->on_error;
|
|
jfe->location = jt->common->location;
|
|
|
|
/*
|
|
* We make lateral_only names of this level visible, whether or not the
|
|
* RangeTableFunc is explicitly marked LATERAL. This is needed for SQL
|
|
* spec compliance and seems useful on convenience grounds for all
|
|
* functions in FROM.
|
|
*
|
|
* (LATERAL can't nest within a single pstate level, so we don't need
|
|
* save/restore logic here.)
|
|
*/
|
|
Assert(!pstate->p_lateral_active);
|
|
pstate->p_lateral_active = true;
|
|
|
|
tf->functype = TFT_JSON_TABLE;
|
|
tf->docexpr = transformExpr(pstate, (Node *) jfe, EXPR_KIND_FROM_FUNCTION);
|
|
|
|
cxt.contextItemTypid = exprType(tf->docexpr);
|
|
|
|
if (!IsA(jt->common->pathspec, A_Const) ||
|
|
castNode(A_Const, jt->common->pathspec)->val.node.type != T_String)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
errmsg("only string constants supported in JSON_TABLE path specification"),
|
|
parser_errposition(pstate,
|
|
exprLocation(jt->common->pathspec))));
|
|
|
|
rootPath = castNode(A_Const, jt->common->pathspec)->val.sval.sval;
|
|
|
|
tf->plan = (Node *) transformJsonTableColumns(&cxt, plan, jt->columns,
|
|
rootPath, &rootPathName,
|
|
jt->common->location);
|
|
|
|
tf->ordinalitycol = -1; /* undefine ordinality column number */
|
|
tf->location = jt->location;
|
|
|
|
pstate->p_lateral_active = false;
|
|
|
|
/*
|
|
* Mark the RTE as LATERAL if the user said LATERAL explicitly, or if
|
|
* there are any lateral cross-references in it.
|
|
*/
|
|
is_lateral = jt->lateral || contain_vars_of_level((Node *) tf, 0);
|
|
|
|
return addRangeTableEntryForTableFunc(pstate,
|
|
tf, jt->alias, is_lateral, true);
|
|
}
|