LCOV - code coverage report
Current view: top level - src/backend/parser - parse_jsontable.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 98.1 % 266 261
Test Date: 2026-08-15 09:15:43 Functions: 100.0 % 14 14
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 84.8 % 211 179

             Branch data     Line data    Source code
       1                 :             : /*-------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * parse_jsontable.c
       4                 :             :  *    parsing of JSON_TABLE
       5                 :             :  *
       6                 :             :  * Portions Copyright (c) 1996-2026, 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"
      23                 :             : #include "parser/parse_collate.h"
      24                 :             : #include "parser/parse_expr.h"
      25                 :             : #include "parser/parse_relation.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() */
      32                 :             : typedef struct JsonTableParseContext
      33                 :             : {
      34                 :             :     ParseState *pstate;
      35                 :             :     JsonTable  *jt;
      36                 :             :     TableFunc  *tf;
      37                 :             :     List       *pathNames;      /* list of all path and columns names */
      38                 :             :     int         pathNameId;     /* path name id counter */
      39                 :             : } JsonTableParseContext;
      40                 :             : 
      41                 :             : static JsonTablePlan *transformJsonTableColumns(JsonTableParseContext *cxt,
      42                 :             :                                                 JsonTablePlanSpec *planspec,
      43                 :             :                                                 List *columns,
      44                 :             :                                                 List *passingArgs,
      45                 :             :                                                 JsonTablePathSpec *pathspec);
      46                 :             : static JsonTablePlan *transformJsonTableNestedColumns(JsonTableParseContext *cxt,
      47                 :             :                                                       JsonTablePlanSpec *plan,
      48                 :             :                                                       List *passingArgs,
      49                 :             :                                                       List *columns);
      50                 :             : static JsonFuncExpr *transformJsonTableColumn(JsonTableColumn *jtc,
      51                 :             :                                               Node *contextItemExpr,
      52                 :             :                                               List *passingArgs);
      53                 :             : static bool isCompositeType(Oid typid);
      54                 :             : static JsonTablePlan *makeJsonTablePathScan(JsonTableParseContext *cxt,
      55                 :             :                                             JsonTablePathSpec *pathspec,
      56                 :             :                                             JsonTablePlanSpec *planspec,
      57                 :             :                                             bool errorOnError,
      58                 :             :                                             int colMin, int colMax,
      59                 :             :                                             JsonTablePlan *childplan);
      60                 :             : static void CheckDuplicateColumnOrPathNames(JsonTableParseContext *cxt,
      61                 :             :                                             List *columns);
      62                 :             : static bool LookupPathOrColumnName(JsonTableParseContext *cxt, char *name);
      63                 :             : static char *generateJsonTablePathName(JsonTableParseContext *cxt);
      64                 :             : static void validateJsonTableChildPlan(JsonTableParseContext *cxt,
      65                 :             :                                        JsonTablePlanSpec *plan,
      66                 :             :                                        List *columns);
      67                 :             : static JsonTablePlan *makeJsonTableSiblingJoin(bool cross,
      68                 :             :                                                JsonTablePlan *lplan,
      69                 :             :                                                JsonTablePlan *rplan);
      70                 :             : static void
      71                 :             :             appendJsonTableColumns(JsonTableParseContext *cxt, List *columns, List *passingArgs);
      72                 :             : 
      73                 :             : /*
      74                 :             :  * transformJsonTable -
      75                 :             :  *          Transform a raw JsonTable into TableFunc
      76                 :             :  *
      77                 :             :  * Mainly, this transforms the JSON_TABLE() document-generating expression
      78                 :             :  * (jt->context_item) and the column-generating expressions (jt->columns) to
      79                 :             :  * populate TableFunc.docexpr and TableFunc.colvalexprs, respectively. Also,
      80                 :             :  * the PASSING values (jt->passing) are transformed and added into
      81                 :             :  * TableFunc.passingvalexprs.
      82                 :             :  */
      83                 :             : ParseNamespaceItem *
      84                 :         480 : transformJsonTable(ParseState *pstate, JsonTable *jt)
      85                 :             : {
      86                 :             :     TableFunc  *tf;
      87                 :             :     JsonFuncExpr *jfe;
      88                 :             :     JsonExpr   *je;
      89                 :         480 :     JsonTablePlanSpec *plan = jt->planspec;
      90                 :         480 :     JsonTablePathSpec *rootPathSpec = jt->pathspec;
      91                 :             :     bool        is_lateral;
      92                 :         480 :     JsonTableParseContext cxt = {pstate};
      93                 :             : 
      94                 :             :     Assert(IsA(rootPathSpec->string, A_Const) &&
      95                 :             :            castNode(A_Const, rootPathSpec->string)->val.node.type == T_String);
      96                 :             : 
      97         [ +  + ]:         480 :     if (jt->on_error &&
      98         [ +  + ]:          48 :         jt->on_error->btype != JSON_BEHAVIOR_ERROR &&
      99         [ +  - ]:          24 :         jt->on_error->btype != JSON_BEHAVIOR_EMPTY &&
     100         [ +  + ]:          24 :         jt->on_error->btype != JSON_BEHAVIOR_EMPTY_ARRAY)
     101         [ +  - ]:          12 :         ereport(ERROR,
     102                 :             :                 errcode(ERRCODE_SYNTAX_ERROR),
     103                 :             :                 errmsg("invalid %s behavior", "ON ERROR"),
     104                 :             :                 errdetail("Only EMPTY [ ARRAY ] or ERROR is allowed in the top-level ON ERROR clause."),
     105                 :             :                 parser_errposition(pstate, jt->on_error->location));
     106                 :             : 
     107                 :         468 :     cxt.pathNameId = 0;
     108                 :             : 
     109                 :             :     /*
     110                 :             :      * Collect the user-supplied path and column names, checking that they are
     111                 :             :      * distinct.  If the row pattern path has an explicit name, it shares this
     112                 :             :      * namespace, so seed the list with it.
     113                 :             :      */
     114         [ +  + ]:         468 :     if (rootPathSpec->name != NULL)
     115                 :         144 :         cxt.pathNames = list_make1(rootPathSpec->name);
     116                 :         468 :     CheckDuplicateColumnOrPathNames(&cxt, jt->columns);
     117                 :             : 
     118                 :             :     /*
     119                 :             :      * Generate a name for the row pattern path if it was not given one.  Path
     120                 :             :      * names are optional for every path, including when a PLAN clause is
     121                 :             :      * present; a specific PLAN() can only reference named paths, so an
     122                 :             :      * unnamed path that the plan must mention is caught later as a path name
     123                 :             :      * mismatch or a path not covered by the plan.  We generate the name only
     124                 :             :      * after collecting the user-supplied names above, so that it cannot
     125                 :             :      * collide with any of them.
     126                 :             :      */
     127         [ +  + ]:         448 :     if (rootPathSpec->name == NULL)
     128                 :         312 :         rootPathSpec->name = generateJsonTablePathName(&cxt);
     129                 :             : 
     130                 :             :     /*
     131                 :             :      * We make lateral_only names of this level visible, whether or not the
     132                 :             :      * RangeTableFunc is explicitly marked LATERAL.  This is needed for SQL
     133                 :             :      * spec compliance and seems useful on convenience grounds for all
     134                 :             :      * functions in FROM.
     135                 :             :      *
     136                 :             :      * (LATERAL can't nest within a single pstate level, so we don't need
     137                 :             :      * save/restore logic here.)
     138                 :             :      */
     139                 :             :     Assert(!pstate->p_lateral_active);
     140                 :         448 :     pstate->p_lateral_active = true;
     141                 :             : 
     142                 :         448 :     tf = makeNode(TableFunc);
     143                 :         448 :     tf->functype = TFT_JSON_TABLE;
     144                 :             : 
     145                 :             :     /*
     146                 :             :      * Transform JsonFuncExpr representing the top JSON_TABLE context_item and
     147                 :             :      * pathspec into a dummy JSON_TABLE_OP JsonExpr.
     148                 :             :      */
     149                 :         448 :     jfe = makeNode(JsonFuncExpr);
     150                 :         448 :     jfe->op = JSON_TABLE_OP;
     151                 :         448 :     jfe->context_item = jt->context_item;
     152                 :         448 :     jfe->pathspec = (Node *) rootPathSpec->string;
     153                 :         448 :     jfe->passing = jt->passing;
     154                 :         448 :     jfe->on_empty = NULL;
     155                 :         448 :     jfe->on_error = jt->on_error;
     156                 :         448 :     jfe->location = jt->location;
     157                 :         448 :     tf->docexpr = transformExpr(pstate, (Node *) jfe, EXPR_KIND_FROM_FUNCTION);
     158                 :             : 
     159                 :             :     /*
     160                 :             :      * Create a JsonTablePlan that will generate row pattern that becomes
     161                 :             :      * source data for JSON path expressions in jt->columns.  This also adds
     162                 :             :      * the columns' transformed JsonExpr nodes into tf->colvalexprs.
     163                 :             :      */
     164                 :         448 :     cxt.jt = jt;
     165                 :         448 :     cxt.tf = tf;
     166                 :         448 :     tf->plan = (Node *) transformJsonTableColumns(&cxt, plan, jt->columns,
     167                 :             :                                                   jt->passing,
     168                 :             :                                                   rootPathSpec);
     169                 :             : 
     170                 :             :     /*
     171                 :             :      * Copy the transformed PASSING arguments into the TableFunc node, because
     172                 :             :      * they are evaluated separately from the JsonExpr that we just put in
     173                 :             :      * TableFunc.docexpr.  JsonExpr.passing_values is still kept around for
     174                 :             :      * get_json_table().
     175                 :             :      */
     176                 :         380 :     je = (JsonExpr *) tf->docexpr;
     177                 :         380 :     tf->passingvalexprs = copyObject(je->passing_values);
     178                 :             : 
     179                 :         380 :     tf->ordinalitycol = -1;      /* undefine ordinality column number */
     180                 :         380 :     tf->location = jt->location;
     181                 :             : 
     182                 :         380 :     pstate->p_lateral_active = false;
     183                 :             : 
     184                 :             :     /*
     185                 :             :      * Mark the RTE as LATERAL if the user said LATERAL explicitly, or if
     186                 :             :      * there are any lateral cross-references in it.
     187                 :             :      */
     188   [ +  -  +  + ]:         380 :     is_lateral = jt->lateral || contain_vars_of_level((Node *) tf, 0);
     189                 :             : 
     190                 :         380 :     return addRangeTableEntryForTableFunc(pstate,
     191                 :             :                                           tf, jt->alias, is_lateral, true);
     192                 :             : }
     193                 :             : 
     194                 :             : /*
     195                 :             :  * Check if a column / path name is duplicated in the given shared list of
     196                 :             :  * names.
     197                 :             :  */
     198                 :             : static void
     199                 :        1000 : CheckDuplicateColumnOrPathNames(JsonTableParseContext *cxt,
     200                 :             :                                 List *columns)
     201                 :             : {
     202                 :             :     ListCell   *lc1;
     203                 :             : 
     204   [ +  -  +  +  :        2664 :     foreach(lc1, columns)
                   +  + ]
     205                 :             :     {
     206                 :        1688 :         JsonTableColumn *jtc = castNode(JsonTableColumn, lfirst(lc1));
     207                 :             : 
     208         [ +  + ]:        1688 :         if (jtc->coltype == JTC_NESTED)
     209                 :             :         {
     210         [ +  + ]:         544 :             if (jtc->pathspec->name)
     211                 :             :             {
     212         [ +  + ]:         436 :                 if (LookupPathOrColumnName(cxt, jtc->pathspec->name))
     213         [ +  - ]:          12 :                     ereport(ERROR,
     214                 :             :                             errcode(ERRCODE_DUPLICATE_ALIAS),
     215                 :             :                             errmsg("duplicate JSON_TABLE column or path name: %s",
     216                 :             :                                    jtc->pathspec->name),
     217                 :             :                             parser_errposition(cxt->pstate,
     218                 :             :                                                jtc->pathspec->name_location));
     219                 :         424 :                 cxt->pathNames = lappend(cxt->pathNames, jtc->pathspec->name);
     220                 :             :             }
     221                 :             : 
     222                 :         532 :             CheckDuplicateColumnOrPathNames(cxt, jtc->columns);
     223                 :             :         }
     224                 :             :         else
     225                 :             :         {
     226         [ +  + ]:        1144 :             if (LookupPathOrColumnName(cxt, jtc->name))
     227         [ +  - ]:           8 :                 ereport(ERROR,
     228                 :             :                         errcode(ERRCODE_DUPLICATE_ALIAS),
     229                 :             :                         errmsg("duplicate JSON_TABLE column or path name: %s",
     230                 :             :                                jtc->name),
     231                 :             :                         parser_errposition(cxt->pstate, jtc->location));
     232                 :        1136 :             cxt->pathNames = lappend(cxt->pathNames, jtc->name);
     233                 :             :         }
     234                 :             :     }
     235                 :         976 : }
     236                 :             : 
     237                 :             : /*
     238                 :             :  * Lookup a column/path name in the given name list, returning true if already
     239                 :             :  * there.
     240                 :             :  */
     241                 :             : static bool
     242                 :        2004 : LookupPathOrColumnName(JsonTableParseContext *cxt, char *name)
     243                 :             : {
     244                 :             :     ListCell   *lc;
     245                 :             : 
     246   [ +  +  +  +  :       10158 :     foreach(lc, cxt->pathNames)
                   +  + ]
     247                 :             :     {
     248         [ +  + ]:        8182 :         if (strcmp(name, (const char *) lfirst(lc)) == 0)
     249                 :          28 :             return true;
     250                 :             :     }
     251                 :             : 
     252                 :        1976 :     return false;
     253                 :             : }
     254                 :             : 
     255                 :             : /* Generate a new unique JSON_TABLE path name. */
     256                 :             : static char *
     257                 :         416 : generateJsonTablePathName(JsonTableParseContext *cxt)
     258                 :             : {
     259                 :             :     char        namebuf[32];
     260                 :             :     char       *name;
     261                 :             : 
     262                 :             :     /*
     263                 :             :      * Bump the counter until we produce a name that is not already used as a
     264                 :             :      * path or column name.  Otherwise a generated name could coincide with a
     265                 :             :      * user-supplied one, which would confuse PLAN clause matching and could
     266                 :             :      * silently drop a column.
     267                 :             :      */
     268                 :             :     do
     269                 :             :     {
     270                 :         424 :         snprintf(namebuf, sizeof(namebuf), "json_table_path_%d",
     271                 :         424 :                  cxt->pathNameId++);
     272         [ +  + ]:         424 :     } while (LookupPathOrColumnName(cxt, namebuf));
     273                 :             : 
     274                 :         416 :     name = pstrdup(namebuf);
     275                 :         416 :     cxt->pathNames = lappend(cxt->pathNames, name);
     276                 :             : 
     277                 :         416 :     return name;
     278                 :             : }
     279                 :             : 
     280                 :             : /*
     281                 :             :  * Create a JsonTablePlan that will supply the source row for 'columns'
     282                 :             :  * using 'pathspec' and append the columns' transformed JsonExpr nodes and
     283                 :             :  * their type/collation information to cxt->tf.
     284                 :             :  */
     285                 :             : static JsonTablePlan *
     286                 :         784 : transformJsonTableColumns(JsonTableParseContext *cxt,
     287                 :             :                           JsonTablePlanSpec *planspec,
     288                 :             :                           List *columns,
     289                 :             :                           List *passingArgs,
     290                 :             :                           JsonTablePathSpec *pathspec)
     291                 :             : {
     292                 :         784 :     JsonTable  *jt = cxt->jt;
     293                 :         784 :     TableFunc  *tf = cxt->tf;
     294                 :             :     JsonTablePathScan *scan;
     295                 :             :     JsonTablePlanSpec *childPlanSpec;
     296         [ +  + ]:        1056 :     bool        defaultPlan = planspec == NULL ||
     297         [ +  + ]:         272 :         planspec->plan_type == JSTP_DEFAULT;
     298         [ +  + ]:         820 :     bool        errorOnError = jt->on_error &&
     299         [ +  + ]:          36 :         jt->on_error->btype == JSON_BEHAVIOR_ERROR;
     300                 :             :     int         colMin,
     301                 :             :                 colMax;
     302                 :         784 :     JsonTablePlan *childplan = NULL;
     303                 :             : 
     304                 :             :     /* Start of column range */
     305                 :         784 :     colMin = list_length(tf->colvalexprs);
     306                 :             : 
     307         [ +  + ]:         784 :     if (defaultPlan)
     308                 :         572 :         childPlanSpec = planspec;
     309                 :             :     else
     310                 :             :     {
     311                 :             :         /* validate parent and child plans */
     312                 :             :         JsonTablePlanSpec *parentPlanSpec;
     313                 :             : 
     314         [ +  + ]:         212 :         if (planspec->plan_type == JSTP_JOINED)
     315                 :             :         {
     316         [ +  + ]:         108 :             if (planspec->join_type != JSTP_JOIN_INNER &&
     317         [ +  + ]:          76 :                 planspec->join_type != JSTP_JOIN_OUTER)
     318         [ +  - ]:           8 :                 ereport(ERROR,
     319                 :             :                         (errcode(ERRCODE_SYNTAX_ERROR),
     320                 :             :                          errmsg("invalid JSON_TABLE plan clause"),
     321                 :             :                          errdetail("Expected INNER or OUTER."),
     322                 :             :                          parser_errposition(cxt->pstate, planspec->location)));
     323                 :             : 
     324                 :         100 :             parentPlanSpec = planspec->plan1;
     325                 :         100 :             childPlanSpec = planspec->plan2;
     326                 :             : 
     327                 :             :             Assert(parentPlanSpec->plan_type != JSTP_JOINED);
     328                 :             :             Assert(parentPlanSpec->pathname);
     329                 :             :         }
     330                 :             :         else
     331                 :             :         {
     332                 :         104 :             parentPlanSpec = planspec;
     333                 :         104 :             childPlanSpec = NULL;
     334                 :             :         }
     335                 :             : 
     336         [ +  + ]:         204 :         if (strcmp(parentPlanSpec->pathname, pathspec->name) != 0)
     337         [ +  - ]:           4 :             ereport(ERROR,
     338                 :             :                     (errcode(ERRCODE_SYNTAX_ERROR),
     339                 :             :                      errmsg("invalid JSON_TABLE plan"),
     340                 :             :                      errdetail("PATH name mismatch: expected %s but %s is given.",
     341                 :             :                                pathspec->name, parentPlanSpec->pathname),
     342                 :             :                      parser_errposition(cxt->pstate, planspec->location)));
     343                 :             : 
     344                 :         200 :         validateJsonTableChildPlan(cxt, childPlanSpec, columns);
     345                 :             :     }
     346                 :             : 
     347                 :         736 :     appendJsonTableColumns(cxt, columns, passingArgs);
     348                 :             : 
     349                 :             :     /* End of column range. */
     350         [ +  + ]:         716 :     if (list_length(tf->colvalexprs) == colMin)
     351                 :             :     {
     352                 :             :         /* No columns in this Scan beside the nested ones. */
     353                 :         112 :         colMax = colMin = -1;
     354                 :             :     }
     355                 :             :     else
     356                 :         604 :         colMax = list_length(tf->colvalexprs) - 1;
     357                 :             : 
     358   [ +  +  +  + ]:         716 :     if (childPlanSpec || defaultPlan)
     359                 :             :     {
     360                 :             :         /* transform recursively nested columns */
     361                 :         632 :         childplan = transformJsonTableNestedColumns(cxt, childPlanSpec,
     362                 :             :                                                     columns, passingArgs);
     363                 :             :     }
     364                 :             : 
     365                 :             :     /* transform only non-nested columns */
     366                 :         704 :     scan = (JsonTablePathScan *) makeJsonTablePathScan(cxt, pathspec,
     367                 :             :                                                        planspec,
     368                 :             :                                                        errorOnError,
     369                 :             :                                                        colMin,
     370                 :             :                                                        colMax,
     371                 :             :                                                        childplan);
     372                 :             : 
     373                 :         704 :     return (JsonTablePlan *) scan;
     374                 :             : }
     375                 :             : 
     376                 :             : /* Append transformed non-nested JSON_TABLE columns to the TableFunc node */
     377                 :             : static void
     378                 :         736 : appendJsonTableColumns(JsonTableParseContext *cxt, List *columns, List *passingArgs)
     379                 :             : {
     380                 :             :     ListCell   *col;
     381                 :         736 :     ParseState *pstate = cxt->pstate;
     382                 :         736 :     TableFunc  *tf = cxt->tf;
     383                 :         736 :     bool        ordinality_found = false;
     384                 :         736 :     Oid         contextItemTypid = exprType(tf->docexpr);
     385                 :             : 
     386   [ +  -  +  +  :        2052 :     foreach(col, columns)
                   +  + ]
     387                 :             :     {
     388                 :        1336 :         JsonTableColumn *rawc = castNode(JsonTableColumn, lfirst(col));
     389                 :             :         Oid         typid;
     390                 :             :         int32       typmod;
     391                 :        1336 :         Oid         typcoll = InvalidOid;
     392                 :             :         Node       *colexpr;
     393                 :             : 
     394         [ +  + ]:        1336 :         if (rawc->name)
     395                 :         992 :             tf->colnames = lappend(tf->colnames,
     396                 :         992 :                                    makeString(pstrdup(rawc->name)));
     397                 :             : 
     398                 :             :         /*
     399                 :             :          * Determine the type and typmod for the new column. FOR ORDINALITY
     400                 :             :          * columns are INTEGER by standard; the others are user-specified.
     401                 :             :          */
     402   [ +  +  +  +  :        1336 :         switch (rawc->coltype)
                      - ]
     403                 :             :         {
     404                 :         100 :             case JTC_FOR_ORDINALITY:
     405         [ +  + ]:         100 :                 if (ordinality_found)
     406         [ +  - ]:           4 :                     ereport(ERROR,
     407                 :             :                             (errcode(ERRCODE_SYNTAX_ERROR),
     408                 :             :                              errmsg("only one FOR ORDINALITY column is allowed"),
     409                 :             :                              parser_errposition(pstate, rawc->location)));
     410                 :          96 :                 ordinality_found = true;
     411                 :          96 :                 colexpr = NULL;
     412                 :          96 :                 typid = INT4OID;
     413                 :          96 :                 typmod = -1;
     414                 :          96 :                 break;
     415                 :             : 
     416                 :         696 :             case JTC_REGULAR:
     417                 :         696 :                 typenameTypeIdAndMod(pstate, rawc->typeName, &typid, &typmod);
     418                 :             : 
     419                 :             :                 /*
     420                 :             :                  * Use JTC_FORMATTED so as to use JSON_QUERY for this column
     421                 :             :                  * if the specified type is one that's better handled using
     422                 :             :                  * JSON_QUERY() or if non-default WRAPPER or QUOTES behavior
     423                 :             :                  * is specified.
     424                 :             :                  */
     425         [ +  + ]:         696 :                 if (isCompositeType(typid) ||
     426         [ +  + ]:         568 :                     rawc->quotes != JS_QUOTES_UNSPEC ||
     427         [ -  + ]:         540 :                     rawc->wrapper != JSW_UNSPEC)
     428                 :         156 :                     rawc->coltype = JTC_FORMATTED;
     429                 :             : 
     430                 :             :                 pg_fallthrough;
     431                 :             :             case JTC_FORMATTED:
     432                 :             :             case JTC_EXISTS:
     433                 :             :                 {
     434                 :             :                     JsonFuncExpr *jfe;
     435                 :         892 :                     CaseTestExpr *param = makeNode(CaseTestExpr);
     436                 :             : 
     437                 :         892 :                     param->collation = InvalidOid;
     438                 :         892 :                     param->typeId = contextItemTypid;
     439                 :         892 :                     param->typeMod = -1;
     440                 :             : 
     441                 :         892 :                     jfe = transformJsonTableColumn(rawc, (Node *) param,
     442                 :             :                                                    passingArgs);
     443                 :             : 
     444                 :         892 :                     colexpr = transformExpr(pstate, (Node *) jfe,
     445                 :             :                                             EXPR_KIND_FROM_FUNCTION);
     446                 :         876 :                     assign_expr_collations(pstate, colexpr);
     447                 :             : 
     448                 :         876 :                     typid = exprType(colexpr);
     449                 :         876 :                     typmod = exprTypmod(colexpr);
     450                 :         876 :                     typcoll = exprCollation(colexpr);
     451                 :         876 :                     break;
     452                 :             :                 }
     453                 :             : 
     454                 :         344 :             case JTC_NESTED:
     455                 :         344 :                 continue;
     456                 :             : 
     457                 :           0 :             default:
     458         [ #  # ]:           0 :                 elog(ERROR, "unknown JSON_TABLE column type: %d", (int) rawc->coltype);
     459                 :             :                 break;
     460                 :             :         }
     461                 :             : 
     462                 :         972 :         tf->coltypes = lappend_oid(tf->coltypes, typid);
     463                 :         972 :         tf->coltypmods = lappend_int(tf->coltypmods, typmod);
     464                 :         972 :         tf->colcollations = lappend_oid(tf->colcollations, typcoll);
     465                 :         972 :         tf->colvalexprs = lappend(tf->colvalexprs, colexpr);
     466                 :             :     }
     467                 :         716 : }
     468                 :             : 
     469                 :             : /*
     470                 :             :  * Check if the type is "composite" for the purpose of checking whether to use
     471                 :             :  * JSON_VALUE() or JSON_QUERY() for a given JsonTableColumn.
     472                 :             :  */
     473                 :             : static bool
     474                 :         724 : isCompositeType(Oid typid)
     475                 :             : {
     476                 :         724 :     char        typtype = get_typtype(typid);
     477                 :             : 
     478         [ +  + ]:         688 :     return typid == JSONOID ||
     479         [ +  - ]:         644 :         typid == JSONBOID ||
     480         [ +  + ]:         644 :         typid == RECORDOID ||
     481         [ +  + ]:        1244 :         type_is_array(typid) ||
     482   [ +  +  +  + ]:        1440 :         typtype == TYPTYPE_COMPOSITE ||
     483                 :             :     /* domain over one of the above? */
     484         [ -  + ]:          28 :         (typtype == TYPTYPE_DOMAIN &&
     485                 :          28 :          isCompositeType(getBaseType(typid)));
     486                 :             : }
     487                 :             : 
     488                 :             : /*
     489                 :             :  * Transform JSON_TABLE column definition into a JsonFuncExpr
     490                 :             :  * This turns:
     491                 :             :  *   - regular column into JSON_VALUE()
     492                 :             :  *   - FORMAT JSON column into JSON_QUERY()
     493                 :             :  *   - EXISTS column into JSON_EXISTS()
     494                 :             :  */
     495                 :             : static JsonFuncExpr *
     496                 :         892 : transformJsonTableColumn(JsonTableColumn *jtc, Node *contextItemExpr,
     497                 :             :                          List *passingArgs)
     498                 :             : {
     499                 :             :     Node       *pathspec;
     500                 :         892 :     JsonFuncExpr *jfexpr = makeNode(JsonFuncExpr);
     501                 :             : 
     502         [ +  + ]:         892 :     if (jtc->coltype == JTC_REGULAR)
     503                 :         540 :         jfexpr->op = JSON_VALUE_OP;
     504         [ +  + ]:         352 :     else if (jtc->coltype == JTC_EXISTS)
     505                 :         104 :         jfexpr->op = JSON_EXISTS_OP;
     506                 :             :     else
     507                 :         248 :         jfexpr->op = JSON_QUERY_OP;
     508                 :             : 
     509                 :             :     /* Pass the column name so any runtime JsonExpr errors can print it. */
     510                 :             :     Assert(jtc->name != NULL);
     511                 :         892 :     jfexpr->column_name = pstrdup(jtc->name);
     512                 :             : 
     513                 :         892 :     jfexpr->context_item = makeJsonValueExpr((Expr *) contextItemExpr, NULL,
     514                 :             :                                              makeJsonFormat(JS_FORMAT_DEFAULT,
     515                 :             :                                                             JS_ENC_DEFAULT,
     516                 :             :                                                             -1));
     517         [ +  + ]:         892 :     if (jtc->pathspec)
     518                 :         808 :         pathspec = (Node *) jtc->pathspec->string;
     519                 :             :     else
     520                 :             :     {
     521                 :             :         /* Construct default path as '$."column_name"' */
     522                 :             :         StringInfoData path;
     523                 :             : 
     524                 :          84 :         initStringInfo(&path);
     525                 :             : 
     526                 :          84 :         appendStringInfoString(&path, "$.");
     527                 :          84 :         escape_json(&path, jtc->name);
     528                 :             : 
     529                 :          84 :         pathspec = makeStringConst(path.data, -1);
     530                 :             :     }
     531                 :         892 :     jfexpr->pathspec = pathspec;
     532                 :         892 :     jfexpr->passing = passingArgs;
     533                 :         892 :     jfexpr->output = makeNode(JsonOutput);
     534                 :         892 :     jfexpr->output->typeName = jtc->typeName;
     535                 :         892 :     jfexpr->output->returning = makeNode(JsonReturning);
     536                 :         892 :     jfexpr->output->returning->format = jtc->format;
     537                 :         892 :     jfexpr->on_empty = jtc->on_empty;
     538                 :         892 :     jfexpr->on_error = jtc->on_error;
     539                 :         892 :     jfexpr->quotes = jtc->quotes;
     540                 :         892 :     jfexpr->wrapper = jtc->wrapper;
     541                 :         892 :     jfexpr->location = jtc->location;
     542                 :             : 
     543                 :         892 :     return jfexpr;
     544                 :             : }
     545                 :             : 
     546                 :             : static JsonTableColumn *
     547                 :         128 : findNestedJsonTableColumn(List *columns, const char *pathname)
     548                 :             : {
     549                 :             :     ListCell   *lc;
     550                 :             : 
     551   [ +  -  +  -  :         488 :     foreach(lc, columns)
                   +  - ]
     552                 :             :     {
     553                 :         488 :         JsonTableColumn *jtc = castNode(JsonTableColumn, lfirst(lc));
     554                 :             : 
     555         [ +  + ]:         488 :         if (jtc->coltype == JTC_NESTED &&
     556         [ +  - ]:         176 :             jtc->pathspec->name &&
     557         [ +  + ]:         176 :             !strcmp(jtc->pathspec->name, pathname))
     558                 :         128 :             return jtc;
     559                 :             :     }
     560                 :             : 
     561                 :           0 :     return NULL;
     562                 :             : }
     563                 :             : 
     564                 :             : /*
     565                 :             :  * Recursively transform nested columns and create child plan(s) that will be
     566                 :             :  * used to evaluate their row patterns.
     567                 :             :  *
     568                 :             :  * Default plan is transformed into a cross/union join of its nested columns.
     569                 :             :  * Simple and outer/inner plans are transformed into a JsonTablePlan by
     570                 :             :  * finding and transforming corresponding nested column.
     571                 :             :  * Sibling plans are recursively transformed into a JsonTableSiblingJoin.
     572                 :             :  */
     573                 :             : static JsonTablePlan *
     574                 :         736 : transformJsonTableNestedColumns(JsonTableParseContext *cxt,
     575                 :             :                                 JsonTablePlanSpec *planspec,
     576                 :             :                                 List *columns,
     577                 :             :                                 List *passingArgs)
     578                 :             : {
     579                 :         736 :     JsonTableColumn *jtc = NULL;
     580                 :             : 
     581   [ +  +  +  + ]:         736 :     if (!planspec || planspec->plan_type == JSTP_DEFAULT)
     582                 :             :     {
     583                 :             :         /* unspecified or default plan */
     584                 :         552 :         JsonTablePlan *plan = NULL;
     585                 :             :         ListCell   *lc;
     586   [ +  +  +  + ]:         552 :         bool        cross = planspec && (planspec->join_type & JSTP_JOIN_CROSS);
     587                 :             : 
     588                 :             :         /*
     589                 :             :          * If there are multiple NESTED COLUMNS clauses in 'columns', their
     590                 :             :          * respective plans will be combined using a "sibling join" plan,
     591                 :             :          * which effectively does a UNION of the sets of rows coming from each
     592                 :             :          * nested plan.
     593                 :             :          */
     594   [ +  -  +  +  :        1480 :         foreach(lc, columns)
                   +  + ]
     595                 :             :         {
     596                 :         928 :             JsonTableColumn *col = castNode(JsonTableColumn, lfirst(lc));
     597                 :             :             JsonTablePlan *nested;
     598                 :             : 
     599         [ +  + ]:         928 :             if (col->coltype != JTC_NESTED)
     600                 :         720 :                 continue;
     601                 :             : 
     602         [ +  + ]:         208 :             if (col->pathspec->name == NULL)
     603                 :             :             {
     604                 :          96 :                 col->pathspec->name = generateJsonTablePathName(cxt);
     605                 :             :             }
     606                 :             : 
     607                 :         208 :             nested = transformJsonTableColumns(cxt, planspec, col->columns,
     608                 :             :                                                passingArgs,
     609                 :             :                                                col->pathspec);
     610                 :             : 
     611                 :             :             /* Join nested plan with previous sibling nested plans. */
     612         [ +  + ]:         208 :             if (plan)
     613                 :          76 :                 plan = makeJsonTableSiblingJoin(cross, plan, nested);
     614                 :             :             else
     615                 :         132 :                 plan = nested;
     616                 :             :         }
     617                 :             : 
     618                 :         552 :         return plan;
     619                 :             :     }
     620         [ +  + ]:         184 :     else if (planspec->plan_type == JSTP_SIMPLE)
     621                 :             :     {
     622                 :          92 :         jtc = findNestedJsonTableColumn(columns, planspec->pathname);
     623                 :             :     }
     624         [ +  - ]:          92 :     else if (planspec->plan_type == JSTP_JOINED)
     625                 :             :     {
     626         [ +  + ]:          92 :         if (planspec->join_type == JSTP_JOIN_INNER ||
     627         [ +  + ]:          68 :             planspec->join_type == JSTP_JOIN_OUTER)
     628                 :             :         {
     629                 :             :             Assert(planspec->plan1->plan_type == JSTP_SIMPLE);
     630                 :          36 :             jtc = findNestedJsonTableColumn(columns, planspec->plan1->pathname);
     631                 :             :         }
     632                 :             :         else
     633                 :             :         {
     634                 :          56 :             JsonTablePlan *lplan = transformJsonTableNestedColumns(cxt,
     635                 :          56 :                                                                    planspec->plan1,
     636                 :             :                                                                    columns,
     637                 :             :                                                                    passingArgs);
     638                 :          48 :             JsonTablePlan *rplan = transformJsonTableNestedColumns(cxt,
     639                 :          48 :                                                                    planspec->plan2,
     640                 :             :                                                                    columns,
     641                 :             :                                                                    passingArgs);
     642                 :             : 
     643                 :          44 :             return makeJsonTableSiblingJoin(planspec->join_type == JSTP_JOIN_CROSS,
     644                 :             :                                             lplan, rplan);
     645                 :             :         }
     646                 :             :     }
     647                 :             :     else
     648         [ #  # ]:           0 :         elog(ERROR, "invalid JSON_TABLE plan type %d", planspec->plan_type);
     649                 :             : 
     650                 :             :     /*
     651                 :             :      * The plan's path names were already matched one-to-one against the
     652                 :             :      * nested columns by validateJsonTableChildPlan(), so a nested column with
     653                 :             :      * this path name must exist.
     654                 :             :      */
     655                 :             :     Assert(jtc != NULL);
     656                 :             : 
     657                 :         128 :     return transformJsonTableColumns(cxt, planspec, jtc->columns,
     658                 :             :                                      passingArgs,
     659                 :             :                                      jtc->pathspec);
     660                 :             : }
     661                 :             : 
     662                 :             : /*
     663                 :             :  * Create transformed JSON_TABLE parent plan node by appending all non-nested
     664                 :             :  * columns to the TableFunc node and remembering their indices in the
     665                 :             :  * colvalexprs list.
     666                 :             :  *
     667                 :             :  * colMin and colMax give the range of columns computed by this scan in the
     668                 :             :  * global flat list of column expressions that will be passed to the
     669                 :             :  * JSON_TABLE's TableFunc.  Both are -1 when all of columns are nested and
     670                 :             :  * thus computed by 'childplan'.
     671                 :             :  */
     672                 :             : static JsonTablePlan *
     673                 :         704 : makeJsonTablePathScan(JsonTableParseContext *cxt, JsonTablePathSpec *pathspec,
     674                 :             :                       JsonTablePlanSpec *planspec,
     675                 :             :                       bool errorOnError,
     676                 :             :                       int colMin, int colMax,
     677                 :             :                       JsonTablePlan *childplan)
     678                 :             : {
     679                 :         704 :     JsonTablePathScan *scan = makeNode(JsonTablePathScan);
     680                 :             :     char       *pathstring;
     681                 :             :     Const      *value;
     682                 :             : 
     683                 :             :     Assert(IsA(pathspec->string, A_Const));
     684                 :         704 :     pathstring = castNode(A_Const, pathspec->string)->val.sval.sval;
     685                 :         704 :     value = makeConst(JSONPATHOID, -1, InvalidOid, -1,
     686                 :             :                       DirectFunctionCall1(jsonpath_in,
     687                 :             :                                           CStringGetDatum(pathstring)),
     688                 :             :                       false, false);
     689                 :             : 
     690                 :         704 :     scan->plan.type = T_JsonTablePathScan;
     691                 :         704 :     scan->path = makeJsonTablePath(value, pathspec->name);
     692                 :         704 :     scan->errorOnError = errorOnError;
     693                 :             : 
     694                 :         704 :     scan->child = childplan;
     695                 :             : 
     696                 :         704 :     scan->colMin = colMin;
     697                 :         704 :     scan->colMax = colMax;
     698                 :             : 
     699         [ +  + ]:         704 :     if (scan->child)
     700         [ +  + ]:         288 :         scan->outerJoin = planspec == NULL ||
     701         [ +  + ]:         288 :             (planspec->join_type & JSTP_JOIN_OUTER);
     702                 :             :     /* else: default plan case, no children found */
     703                 :             : 
     704                 :         704 :     return (JsonTablePlan *) scan;
     705                 :             : }
     706                 :             : 
     707                 :             : /*
     708                 :             :  * Create a JsonTablePlan that will perform a join of the rows coming from
     709                 :             :  * 'lplan' and 'rplan'.
     710                 :             :  *
     711                 :             :  * The default way of "joining" the rows is to perform a UNION between the
     712                 :             :  * sets of rows from 'lplan' and 'rplan'.
     713                 :             :  */
     714                 :             : static JsonTablePlan *
     715                 :         120 : makeJsonTableSiblingJoin(bool cross, JsonTablePlan *lplan, JsonTablePlan *rplan)
     716                 :             : {
     717                 :         120 :     JsonTableSiblingJoin *join = makeNode(JsonTableSiblingJoin);
     718                 :             : 
     719                 :         120 :     join->plan.type = T_JsonTableSiblingJoin;
     720                 :         120 :     join->lplan = lplan;
     721                 :         120 :     join->rplan = rplan;
     722                 :         120 :     join->cross = cross;
     723                 :             : 
     724                 :         120 :     return (JsonTablePlan *) join;
     725                 :             : }
     726                 :             : 
     727                 :             : /* Collect sibling path names from plan to the specified list. */
     728                 :             : static void
     729                 :         236 : collectSiblingPathsInJsonTablePlan(JsonTablePlanSpec *plan, List **paths)
     730                 :             : {
     731         [ +  + ]:         236 :     if (plan->plan_type == JSTP_SIMPLE)
     732                 :         132 :         *paths = lappend(*paths, plan->pathname);
     733         [ +  - ]:         104 :     else if (plan->plan_type == JSTP_JOINED)
     734                 :             :     {
     735         [ +  + ]:         104 :         if (plan->join_type == JSTP_JOIN_INNER ||
     736         [ +  + ]:          80 :             plan->join_type == JSTP_JOIN_OUTER)
     737                 :             :         {
     738                 :             :             Assert(plan->plan1->plan_type == JSTP_SIMPLE);
     739                 :          36 :             *paths = lappend(*paths, plan->plan1->pathname);
     740                 :             :         }
     741         [ +  + ]:          68 :         else if (plan->join_type == JSTP_JOIN_CROSS ||
     742         [ +  - ]:          24 :                  plan->join_type == JSTP_JOIN_UNION)
     743                 :             :         {
     744                 :          68 :             collectSiblingPathsInJsonTablePlan(plan->plan1, paths);
     745                 :          68 :             collectSiblingPathsInJsonTablePlan(plan->plan2, paths);
     746                 :             :         }
     747                 :             :         else
     748         [ #  # ]:           0 :             elog(ERROR, "invalid JSON_TABLE join type %d",
     749                 :             :                  plan->join_type);
     750                 :             :     }
     751                 :         236 : }
     752                 :             : 
     753                 :             : /*
     754                 :             :  * Validate child JSON_TABLE plan by checking that:
     755                 :             :  *  - all nested columns have path names specified
     756                 :             :  *  - all nested columns have corresponding node in the sibling plan
     757                 :             :  *  - plan does not contain duplicate or extra nodes
     758                 :             :  */
     759                 :             : static void
     760                 :         200 : validateJsonTableChildPlan(JsonTableParseContext *cxt, JsonTablePlanSpec *plan,
     761                 :             :                            List *columns)
     762                 :             : {
     763                 :         200 :     ParseState *pstate = cxt->pstate;
     764                 :             :     ListCell   *lc1;
     765                 :         200 :     List       *siblings = NIL;
     766                 :         200 :     int         nchildren = 0;
     767                 :             : 
     768         [ +  + ]:         200 :     if (plan)
     769                 :         100 :         collectSiblingPathsInJsonTablePlan(plan, &siblings);
     770                 :             : 
     771   [ +  -  +  +  :         604 :     foreach(lc1, columns)
                   +  + ]
     772                 :             :     {
     773                 :         436 :         JsonTableColumn *jtc = castNode(JsonTableColumn, lfirst(lc1));
     774                 :             : 
     775         [ +  + ]:         436 :         if (jtc->coltype == JTC_NESTED)
     776                 :             :         {
     777                 :             :             ListCell   *lc2;
     778                 :         188 :             bool        found = false;
     779                 :             : 
     780                 :             :             /*
     781                 :             :              * A NESTED path need not be named; generate a name if it was left
     782                 :             :              * unnamed.  A generated name cannot be referenced by a specific
     783                 :             :              * PLAN(), so such a path is reported just below as a nested path
     784                 :             :              * not covered by the plan.
     785                 :             :              */
     786         [ +  + ]:         188 :             if (jtc->pathspec->name == NULL)
     787                 :           8 :                 jtc->pathspec->name = generateJsonTablePathName(cxt);
     788                 :             : 
     789                 :             :             /* find nested path name in the list of sibling path names */
     790   [ +  +  +  +  :         272 :             foreach(lc2, siblings)
                   +  + ]
     791                 :             :             {
     792         [ +  + ]:         240 :                 if ((found = !strcmp(jtc->pathspec->name, lfirst(lc2))))
     793                 :         156 :                     break;
     794                 :             :             }
     795                 :             : 
     796         [ +  + ]:         188 :             if (!found)
     797         [ +  - ]:          32 :                 ereport(ERROR,
     798                 :             :                         errcode(ERRCODE_SYNTAX_ERROR),
     799                 :             :                         errmsg("invalid JSON_TABLE specification"),
     800                 :             :                         errdetail("PLAN clause for nested path %s was not found.",
     801                 :             :                                   jtc->pathspec->name),
     802                 :             :                         parser_errposition(pstate, jtc->location));
     803                 :             : 
     804                 :         156 :             nchildren++;
     805                 :             :         }
     806                 :             :     }
     807                 :             : 
     808         [ +  + ]:         168 :     if (list_length(siblings) > nchildren)
     809   [ +  -  +  - ]:           4 :         ereport(ERROR,
     810                 :             :                 errcode(ERRCODE_SYNTAX_ERROR),
     811                 :             :                 errmsg("invalid JSON_TABLE plan clause"),
     812                 :             :                 errdetail("PLAN clause contains some extra or duplicate sibling nodes."),
     813                 :             :                 parser_errposition(pstate, plan ? plan->location : -1));
     814                 :         164 : }
        

Generated by: LCOV version 2.0-1