LCOV - code coverage report
Current view: top level - src/backend/parser - parse_jsontable.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 97.8 % 273 267
Test Date: 2026-07-12 13:15:41 Functions: 100.0 % 14 14
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 83.6 % 219 183

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

Generated by: LCOV version 2.0-1