=== Applying patches on top of PostgreSQL commit ID bcb15f48786df1c4c3c0428645b5b0c6da0f3cb7 === /etc/rc.d/jail: WARNING: Per-jail configuration via jail_* variables is obsolete. Please consider migrating to /etc/jail.conf. Thu Aug 13 04:58:25 UTC 2026 On branch cf/6260 nothing to commit, working tree clean === using 'git am' to apply patch ./v2-0001-extend-JSON_TABLE-top-level-path-expression.patch === Applying: extend JSON_TABLE top level path expression Using index info to reconstruct a base tree... M src/backend/nodes/makefuncs.c M src/backend/nodes/nodeFuncs.c M src/backend/parser/gram.y M src/backend/parser/parse_jsontable.c M src/backend/utils/adt/jsonpath_exec.c M src/backend/utils/adt/ruleutils.c M src/include/nodes/parsenodes.h M src/test/regress/expected/sqljson_jsontable.out M src/test/regress/sql/sqljson_jsontable.sql Falling back to patching base and 3-way merge... Auto-merging src/test/regress/sql/sqljson_jsontable.sql Auto-merging src/test/regress/expected/sqljson_jsontable.out Auto-merging src/include/nodes/parsenodes.h Auto-merging src/backend/utils/adt/ruleutils.c Auto-merging src/backend/utils/adt/jsonpath_exec.c CONFLICT (content): Merge conflict in src/backend/utils/adt/jsonpath_exec.c Auto-merging src/backend/parser/parse_jsontable.c CONFLICT (content): Merge conflict in src/backend/parser/parse_jsontable.c Auto-merging src/backend/parser/gram.y CONFLICT (content): Merge conflict in src/backend/parser/gram.y Auto-merging src/backend/nodes/nodeFuncs.c Auto-merging src/backend/nodes/makefuncs.c error: Failed to merge in the changes. hint: Use 'git am --show-current-patch=diff' to see the failed patch Patch failed at 0001 extend JSON_TABLE top level path expression When you have resolved this problem, run "git am --continue". If you prefer to skip this patch, run "git am --skip" instead. To restore the original branch and stop patching, run "git am --abort". === using patch(1) to apply patch ./v2-0001-extend-JSON_TABLE-top-level-path-expression.patch === patching file src/backend/nodes/makefuncs.c Hunk #1 succeeded at 1065 (offset 57 lines). patching file src/backend/nodes/nodeFuncs.c Hunk #1 succeeded at 4321 (offset 119 lines). patching file src/backend/parser/gram.y Hunk #1 FAILED at 14435. 1 out of 1 hunk FAILED -- saving rejects to file src/backend/parser/gram.y.rej patching file src/backend/parser/parse_jsontable.c Hunk #2 succeeded at 44 with fuzz 2 (offset 1 line). Hunk #3 FAILED at 53. Hunk #4 succeeded at 81 (offset 10 lines). Hunk #5 succeeded at 95 (offset 11 lines). Hunk #6 succeeded at 150 (offset 28 lines). Hunk #7 succeeded at 166 with fuzz 2 (offset 28 lines). Hunk #8 succeeded at 293 with fuzz 2 (offset 39 lines). Hunk #9 FAILED at 370. Hunk #10 succeeded at 522 (offset 99 lines). Hunk #11 FAILED at 481. Hunk #12 FAILED at 501. 4 out of 12 hunks FAILED -- saving rejects to file src/backend/parser/parse_jsontable.c.rej patching file src/backend/utils/adt/jsonpath_exec.c Hunk #1 succeeded at 392 (offset 37 lines). Hunk #2 succeeded at 4422 (offset 316 lines). Hunk #3 FAILED at 4119. Hunk #4 succeeded at 4479 (offset 315 lines). Hunk #5 succeeded at 4509 with fuzz 1 (offset 315 lines). Hunk #6 FAILED at 4207. Hunk #7 FAILED at 4220. Hunk #8 succeeded at 4585 (offset 319 lines). 3 out of 8 hunks FAILED -- saving rejects to file src/backend/utils/adt/jsonpath_exec.c.rej patching file src/backend/utils/adt/ruleutils.c Hunk #1 succeeded at 12919 (offset 690 lines). patching file src/include/nodes/parsenodes.h Hunk #1 succeeded at 1975 (offset 101 lines). patching file src/test/regress/expected/sqljson_jsontable.out Hunk #1 succeeded at 325 (offset 16 lines). Hunk #2 succeeded at 346 (offset 16 lines). Hunk #3 succeeded at 365 (offset 16 lines). Hunk #4 succeeded at 382 (offset 16 lines). Hunk #5 succeeded at 401 (offset 16 lines). Hunk #6 succeeded at 415 (offset 16 lines). Hunk #7 succeeded at 464 (offset 16 lines). Hunk #8 succeeded at 480 (offset 16 lines). Hunk #9 succeeded at 746 (offset 54 lines). Hunk #10 succeeded at 757 (offset 54 lines). Hunk #11 succeeded at 1510 with fuzz 1 (offset 636 lines). Hunk #12 succeeded at 1583 (offset 637 lines). Hunk #13 succeeded at 1781 (offset 732 lines). Hunk #14 succeeded at 1865 (offset 732 lines). Hunk #15 succeeded at 1934 (offset 732 lines). Hunk #16 succeeded at 1944 (offset 732 lines). Hunk #17 succeeded at 1956 (offset 732 lines). Hunk #18 succeeded at 1966 (offset 732 lines). patching file src/test/regress/sql/sqljson_jsontable.sql Hunk #1 succeeded at 341 (offset 29 lines). Hunk #2 succeeded at 814 with fuzz 1 (offset 392 lines). Hunk #3 succeeded at 988 (offset 481 lines). Unstaged changes after reset: M src/backend/nodes/makefuncs.c M src/backend/nodes/nodeFuncs.c M src/backend/parser/parse_jsontable.c M src/backend/utils/adt/jsonpath_exec.c M src/backend/utils/adt/ruleutils.c M src/include/nodes/parsenodes.h M src/test/regress/expected/sqljson_jsontable.out M src/test/regress/sql/sqljson_jsontable.sql Removing src/backend/parser/gram.y.rej Removing src/backend/parser/parse_jsontable.c.rej Removing src/backend/utils/adt/jsonpath_exec.c.rej === using 'git apply' to apply patch ./v2-0001-extend-JSON_TABLE-top-level-path-expression.patch === Applied patch to 'src/backend/nodes/makefuncs.c' cleanly. Applied patch to 'src/backend/nodes/nodeFuncs.c' cleanly. Applied patch to 'src/backend/parser/gram.y' with conflicts. Applied patch to 'src/backend/parser/parse_jsontable.c' with conflicts. Applied patch to 'src/backend/utils/adt/jsonpath_exec.c' with conflicts. Applied patch to 'src/backend/utils/adt/ruleutils.c' cleanly. Applied patch to 'src/include/nodes/parsenodes.h' cleanly. Applied patch to 'src/test/regress/expected/sqljson_jsontable.out' cleanly. Applied patch to 'src/test/regress/sql/sqljson_jsontable.sql' cleanly. U src/backend/parser/gram.y U src/backend/parser/parse_jsontable.c U src/backend/utils/adt/jsonpath_exec.c diff --cc src/backend/parser/gram.y index 17035fb4d15,09810419683..00000000000 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@@ -15191,22 -14435,19 +15191,32 @@@ json_table ')' { JsonTable *n = makeNode(JsonTable); - char *pathstring; + JsonTablePathSpec *rootPathSpec = makeNode(JsonTablePathSpec); n->context_item = (JsonValueExpr *) $3; ++<<<<<<< ours + if (!IsA($5, A_Const) || + castNode(A_Const, $5)->val.node.type != T_String) + ereport(ERROR, + errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("only string constants are supported in" + " JSON_TABLE path specification"), + parser_errposition(@5)); + pathstring = castNode(A_Const, $5)->val.sval.sval; + n->pathspec = makeJsonTablePathSpec(pathstring, $6, @5, @6); ++======= + + rootPathSpec->expr = $5; + rootPathSpec->name = $6; + rootPathSpec->name_location = @6; + rootPathSpec->location = @5; + n->pathspec = rootPathSpec; + ++>>>>>>> theirs n->passing = $7; n->columns = $10; - n->on_error = (JsonBehavior *) $12; + n->planspec = (JsonTablePlanSpec *) $12; + n->on_error = (JsonBehavior *) $13; n->location = @1; $$ = (Node *) n; } diff --cc src/backend/parser/parse_jsontable.c index d86c6946ee9,6ffb9a46353..00000000000 --- a/src/backend/parser/parse_jsontable.c +++ b/src/backend/parser/parse_jsontable.c @@@ -39,21 -41,19 +41,27 @@@ typedef struct JsonTableParseContex } JsonTableParseContext; static JsonTablePlan *transformJsonTableColumns(JsonTableParseContext *cxt, + JsonTablePlanSpec *planspec, List *columns, List *passingArgs, - JsonTablePathSpec *pathspec); + JsonTablePathSpec *pathspec, + bool isTopLevel); static JsonTablePlan *transformJsonTableNestedColumns(JsonTableParseContext *cxt, + JsonTablePlanSpec *plan, List *passingArgs, List *columns); static JsonFuncExpr *transformJsonTableColumn(JsonTableColumn *jtc, Node *contextItemExpr, List *passingArgs); static bool isCompositeType(Oid typid); ++<<<<<<< ours +static JsonTablePlan *makeJsonTablePathScan(JsonTableParseContext *cxt, + JsonTablePathSpec *pathspec, + JsonTablePlanSpec *planspec, ++======= + static JsonTablePlan *makeJsonTablePathScan(JsonTablePathSpec *pathspec, + bool isTopLevel, ++>>>>>>> theirs bool errorOnError, int colMin, int colMax, JsonTablePlan *childplan); @@@ -163,9 -137,10 +169,10 @@@ transformJsonTable(ParseState *pstate, */ cxt.jt = jt; cxt.tf = tf; - tf->plan = (Node *) transformJsonTableColumns(&cxt, jt->columns, + tf->plan = (Node *) transformJsonTableColumns(&cxt, plan, jt->columns, jt->passing, - rootPathSpec); + rootPathSpec, + true); /* * Copy the transformed PASSING arguments into the TableFunc node, because @@@ -283,23 -253,22 +294,24 @@@ generateJsonTablePathName(JsonTablePars * their type/collation information to cxt->tf. */ static JsonTablePlan * -transformJsonTableColumns(JsonTableParseContext *cxt, List *columns, +transformJsonTableColumns(JsonTableParseContext *cxt, + JsonTablePlanSpec *planspec, + List *columns, List *passingArgs, - JsonTablePathSpec *pathspec) + JsonTablePathSpec *pathspec, + bool isTopLevel) { - ParseState *pstate = cxt->pstate; JsonTable *jt = cxt->jt; TableFunc *tf = cxt->tf; - ListCell *col; - bool ordinality_found = false; + JsonTablePathScan *scan; + JsonTablePlanSpec *childPlanSpec; + bool defaultPlan = planspec == NULL || + planspec->plan_type == JSTP_DEFAULT; bool errorOnError = jt->on_error && jt->on_error->btype == JSON_BEHAVIOR_ERROR; - Oid contextItemTypid = exprType(tf->docexpr); int colMin, colMax; - JsonTablePlan *childplan; + JsonTablePlan *childplan = NULL; /* Start of column range */ colMin = list_length(tf->colvalexprs); @@@ -464,6 -357,22 +476,25 @@@ appendJsonTableColumns(JsonTableParseCo tf->colcollations = lappend_oid(tf->colcollations, typcoll); tf->colvalexprs = lappend(tf->colvalexprs, colexpr); } ++<<<<<<< ours ++======= + + /* End of column range. */ + if (list_length(tf->colvalexprs) == colMin) + { + /* No columns in this Scan beside the nested ones. */ + colMax = colMin = -1; + } + else + colMax = list_length(tf->colvalexprs) - 1; + + /* Recursively transform nested columns */ + childplan = transformJsonTableNestedColumns(cxt, passingArgs, columns); + + /* Create a "parent" scan responsible for all columns handled above. */ + return makeJsonTablePathScan(pathspec, isTopLevel, errorOnError, colMin, colMax, + childplan); ++>>>>>>> theirs } /* @@@ -572,91 -458,39 +603,96 @@@ findNestedJsonTableColumn(List *columns */ static JsonTablePlan * transformJsonTableNestedColumns(JsonTableParseContext *cxt, - List *passingArgs, - List *columns) + JsonTablePlanSpec *planspec, + List *columns, + List *passingArgs) { - JsonTablePlan *plan = NULL; - ListCell *lc; + JsonTableColumn *jtc = NULL; - /* - * If there are multiple NESTED COLUMNS clauses in 'columns', their - * respective plans will be combined using a "sibling join" plan, which - * effectively does a UNION of the sets of rows coming from each nested - * plan. - */ - foreach(lc, columns) + if (!planspec || planspec->plan_type == JSTP_DEFAULT) { - JsonTableColumn *jtc = castNode(JsonTableColumn, lfirst(lc)); - JsonTablePlan *nested; + /* unspecified or default plan */ + JsonTablePlan *plan = NULL; + ListCell *lc; + bool cross = planspec && (planspec->join_type & JSTP_JOIN_CROSS); - if (jtc->coltype != JTC_NESTED) - continue; + /* + * If there are multiple NESTED COLUMNS clauses in 'columns', their + * respective plans will be combined using a "sibling join" plan, + * which effectively does a UNION of the sets of rows coming from each + * nested plan. + */ + foreach(lc, columns) + { + JsonTableColumn *col = castNode(JsonTableColumn, lfirst(lc)); + JsonTablePlan *nested; - if (jtc->pathspec->name == NULL) - jtc->pathspec->name = generateJsonTablePathName(cxt); + if (col->coltype != JTC_NESTED) + continue; ++<<<<<<< ours + if (col->pathspec->name == NULL) + { + col->pathspec->name = generateJsonTablePathName(cxt); + } ++======= + nested = transformJsonTableColumns(cxt, jtc->columns, passingArgs, + jtc->pathspec, false); ++>>>>>>> theirs + + nested = transformJsonTableColumns(cxt, planspec, col->columns, + passingArgs, + col->pathspec); + + /* Join nested plan with previous sibling nested plans. */ + if (plan) + plan = makeJsonTableSiblingJoin(cross, plan, nested); + else + plan = nested; + } - if (plan) - plan = makeJsonTableSiblingJoin(plan, nested); + return plan; + } + else if (planspec->plan_type == JSTP_SIMPLE) + { + jtc = findNestedJsonTableColumn(columns, planspec->pathname); + } + else if (planspec->plan_type == JSTP_JOINED) + { + if (planspec->join_type == JSTP_JOIN_INNER || + planspec->join_type == JSTP_JOIN_OUTER) + { + Assert(planspec->plan1->plan_type == JSTP_SIMPLE); + jtc = findNestedJsonTableColumn(columns, planspec->plan1->pathname); + } else - plan = nested; + { + JsonTablePlan *lplan = transformJsonTableNestedColumns(cxt, + planspec->plan1, + columns, + passingArgs); + JsonTablePlan *rplan = transformJsonTableNestedColumns(cxt, + planspec->plan2, + columns, + passingArgs); + + return makeJsonTableSiblingJoin(planspec->join_type == JSTP_JOIN_CROSS, + lplan, rplan); + } } + else + elog(ERROR, "invalid JSON_TABLE plan type %d", planspec->plan_type); - return plan; + /* + * The plan's path names were already matched one-to-one against the + * nested columns by validateJsonTableChildPlan(), so a nested column with + * this path name must exist. + */ + Assert(jtc != NULL); + + return transformJsonTableColumns(cxt, planspec, jtc->columns, + passingArgs, + jtc->pathspec); } /* @@@ -670,9 -502,7 +706,13 @@@ * thus computed by 'childplan'. */ static JsonTablePlan * ++<<<<<<< ours +makeJsonTablePathScan(JsonTableParseContext *cxt, JsonTablePathSpec *pathspec, + JsonTablePlanSpec *planspec, + bool errorOnError, ++======= + makeJsonTablePathScan(JsonTablePathSpec *pathspec, bool isTopLevel, bool errorOnError, ++>>>>>>> theirs int colMin, int colMax, JsonTablePlan *childplan) { diff --cc src/backend/utils/adt/jsonpath_exec.c index 9a37e6fe663,85efbc13796..00000000000 --- a/src/backend/utils/adt/jsonpath_exec.c +++ b/src/backend/utils/adt/jsonpath_exec.c @@@ -4434,10 -4119,23 +4435,23 @@@ JsonTableInitOpaque(TableFuncScanState JsonTablePlan *rootplan = (JsonTablePlan *) tf->plan; JsonExpr *je = castNode(JsonExpr, tf->docexpr); List *args = NIL; + NullableDatum pathexpr; - cxt = palloc0(sizeof(JsonTableExecContext)); + cxt = palloc0_object(JsonTableExecContext); cxt->magic = JSON_TABLE_EXEC_CONTEXT_MAGIC; + /* + * Evaluate JSON_TABLE() top-level path expression and save the value to + * pathexpr + */ + if (state->rowexpr != NULL) + { + ExprState *pathexprstate = state->rowexpr; + + pathexpr.value = ExecEvalExpr(pathexprstate, ps->ps_ExprContext, + &pathexpr.isnull); + } + /* * Evaluate JSON_TABLE() PASSING arguments to be passed to the jsonpath * executor via JsonPathVariables. @@@ -4508,9 -4207,10 +4522,10 @@@ JsonTableDestroyOpaque(TableFuncScanSta static JsonTablePlanState * JsonTableInitPlan(JsonTableExecContext *cxt, JsonTablePlan *plan, JsonTablePlanState *parentstate, + NullableDatum *pathexpr, List *args, MemoryContext mcxt) { - JsonTablePlanState *planstate = palloc0(sizeof(*planstate)); + JsonTablePlanState *planstate = palloc0_object(JsonTablePlanState); planstate->plan = plan; planstate->parent = parentstate; @@@ -4521,8 -4220,20 +4536,25 @@@ JsonTablePathScan *scan = (JsonTablePathScan *) plan; int i; ++<<<<<<< ours + planstate->outerJoin = scan->outerJoin; + planstate->path = DatumGetJsonPathP(scan->path->value->constvalue); ++======= + /* + * The top-level path expression has already been evaluated and stored + * in pathexpr; see makeJsonTablePathScan and JsonTableInitOpaque also. + */ + if (pathexpr) + { + if (pathexpr->isnull) + planstate->path = NULL; + else + planstate->path = DatumGetJsonPathP(pathexpr->value); + } + else + planstate->path = DatumGetJsonPathP(scan->path->value->constvalue); + ++>>>>>>> theirs planstate->args = args; planstate->mcxt = AllocSetContextCreate(mcxt, "JsonTableExecContext", ALLOCSET_DEFAULT_SIZES); @@@ -4541,11 -4252,9 +4573,15 @@@ { JsonTableSiblingJoin *join = (JsonTableSiblingJoin *) plan; ++<<<<<<< ours + planstate->cross = join->cross; + + planstate->left = JsonTableInitPlan(cxt, join->lplan, parentstate, ++======= + planstate->left = JsonTableInitPlan(cxt, join->lplan, parentstate, NULL, ++>>>>>>> theirs args, mcxt); - planstate->right = JsonTableInitPlan(cxt, join->rplan, parentstate, + planstate->right = JsonTableInitPlan(cxt, join->rplan, parentstate, NULL, args, mcxt); }