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

Generated by: LCOV version 2.0-1