LCOV - differential code coverage report
Current view: top level - src/backend/executor - nodeTableFuncscan.c (source / functions) Coverage Total Hit UBC CBC
Current: Differential Code Coverage HEAD vs 15 Lines: 95.1 % 162 154 8 154
Current Date: 2023-04-08 15:15:32 Functions: 88.9 % 9 8 1 8
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           TLA  Line data    Source code
       1                 : /*-------------------------------------------------------------------------
       2                 :  *
       3                 :  * nodeTableFuncscan.c
       4                 :  *    Support routines for scanning RangeTableFunc (XMLTABLE like functions).
       5                 :  *
       6                 :  * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
       7                 :  * Portions Copyright (c) 1994, Regents of the University of California
       8                 :  *
       9                 :  *
      10                 :  * IDENTIFICATION
      11                 :  *    src/backend/executor/nodeTableFuncscan.c
      12                 :  *
      13                 :  *-------------------------------------------------------------------------
      14                 :  */
      15                 : /*
      16                 :  * INTERFACE ROUTINES
      17                 :  *      ExecTableFuncScan       scans a function.
      18                 :  *      ExecFunctionNext        retrieve next tuple in sequential order.
      19                 :  *      ExecInitTableFuncScan   creates and initializes a TableFuncscan node.
      20                 :  *      ExecEndTableFuncScan        releases any storage allocated.
      21                 :  *      ExecReScanTableFuncScan rescans the function
      22                 :  */
      23                 : #include "postgres.h"
      24                 : 
      25                 : #include "executor/executor.h"
      26                 : #include "executor/nodeTableFuncscan.h"
      27                 : #include "executor/tablefunc.h"
      28                 : #include "miscadmin.h"
      29                 : #include "nodes/execnodes.h"
      30                 : #include "utils/builtins.h"
      31                 : #include "utils/lsyscache.h"
      32                 : #include "utils/memutils.h"
      33                 : #include "utils/xml.h"
      34                 : 
      35                 : static TupleTableSlot *TableFuncNext(TableFuncScanState *node);
      36                 : static bool TableFuncRecheck(TableFuncScanState *node, TupleTableSlot *slot);
      37                 : 
      38                 : static void tfuncFetchRows(TableFuncScanState *tstate, ExprContext *econtext);
      39                 : static void tfuncInitialize(TableFuncScanState *tstate, ExprContext *econtext, Datum doc);
      40                 : static void tfuncLoadRows(TableFuncScanState *tstate, ExprContext *econtext);
      41                 : 
      42                 : /* ----------------------------------------------------------------
      43                 :  *                      Scan Support
      44                 :  * ----------------------------------------------------------------
      45                 :  */
      46                 : /* ----------------------------------------------------------------
      47                 :  *      TableFuncNext
      48                 :  *
      49                 :  *      This is a workhorse for ExecTableFuncScan
      50                 :  * ----------------------------------------------------------------
      51                 :  */
      52                 : static TupleTableSlot *
      53 CBC       11124 : TableFuncNext(TableFuncScanState *node)
      54                 : {
      55                 :     TupleTableSlot *scanslot;
      56                 : 
      57           11124 :     scanslot = node->ss.ss_ScanTupleSlot;
      58                 : 
      59                 :     /*
      60                 :      * If first time through, read all tuples from function and put them in a
      61                 :      * tuplestore. Subsequent calls just fetch tuples from tuplestore.
      62                 :      */
      63           11124 :     if (node->tupstore == NULL)
      64             132 :         tfuncFetchRows(node, node->ss.ps.ps_ExprContext);
      65                 : 
      66                 :     /*
      67                 :      * Get the next tuple from tuplestore.
      68                 :      */
      69           11115 :     (void) tuplestore_gettupleslot(node->tupstore,
      70                 :                                    true,
      71                 :                                    false,
      72                 :                                    scanslot);
      73           11115 :     return scanslot;
      74                 : }
      75                 : 
      76                 : /*
      77                 :  * TableFuncRecheck -- access method routine to recheck a tuple in EvalPlanQual
      78                 :  */
      79                 : static bool
      80 UBC           0 : TableFuncRecheck(TableFuncScanState *node, TupleTableSlot *slot)
      81                 : {
      82                 :     /* nothing to check */
      83               0 :     return true;
      84                 : }
      85                 : 
      86                 : /* ----------------------------------------------------------------
      87                 :  *      ExecTableFuncScan(node)
      88                 :  *
      89                 :  *      Scans the function sequentially and returns the next qualifying
      90                 :  *      tuple.
      91                 :  *      We call the ExecScan() routine and pass it the appropriate
      92                 :  *      access method functions.
      93                 :  * ----------------------------------------------------------------
      94                 :  */
      95                 : static TupleTableSlot *
      96 CBC       11097 : ExecTableFuncScan(PlanState *pstate)
      97                 : {
      98           11097 :     TableFuncScanState *node = castNode(TableFuncScanState, pstate);
      99                 : 
     100           11097 :     return ExecScan(&node->ss,
     101                 :                     (ExecScanAccessMtd) TableFuncNext,
     102                 :                     (ExecScanRecheckMtd) TableFuncRecheck);
     103                 : }
     104                 : 
     105                 : /* ----------------------------------------------------------------
     106                 :  *      ExecInitTableFuncScan
     107                 :  * ----------------------------------------------------------------
     108                 :  */
     109                 : TableFuncScanState *
     110             108 : ExecInitTableFuncScan(TableFuncScan *node, EState *estate, int eflags)
     111                 : {
     112                 :     TableFuncScanState *scanstate;
     113             108 :     TableFunc  *tf = node->tablefunc;
     114                 :     TupleDesc   tupdesc;
     115                 :     int         i;
     116                 : 
     117                 :     /* check for unsupported flags */
     118             108 :     Assert(!(eflags & EXEC_FLAG_MARK));
     119                 : 
     120                 :     /*
     121                 :      * TableFuncscan should not have any children.
     122                 :      */
     123             108 :     Assert(outerPlan(node) == NULL);
     124             108 :     Assert(innerPlan(node) == NULL);
     125                 : 
     126                 :     /*
     127                 :      * create new ScanState for node
     128                 :      */
     129             108 :     scanstate = makeNode(TableFuncScanState);
     130             108 :     scanstate->ss.ps.plan = (Plan *) node;
     131             108 :     scanstate->ss.ps.state = estate;
     132             108 :     scanstate->ss.ps.ExecProcNode = ExecTableFuncScan;
     133                 : 
     134                 :     /*
     135                 :      * Miscellaneous initialization
     136                 :      *
     137                 :      * create expression context for node
     138                 :      */
     139             108 :     ExecAssignExprContext(estate, &scanstate->ss.ps);
     140                 : 
     141                 :     /*
     142                 :      * initialize source tuple type
     143                 :      */
     144             108 :     tupdesc = BuildDescFromLists(tf->colnames,
     145                 :                                  tf->coltypes,
     146                 :                                  tf->coltypmods,
     147                 :                                  tf->colcollations);
     148                 :     /* and the corresponding scan slot */
     149             108 :     ExecInitScanTupleSlot(estate, &scanstate->ss, tupdesc,
     150                 :                           &TTSOpsMinimalTuple);
     151                 : 
     152                 :     /*
     153                 :      * Initialize result type and projection.
     154                 :      */
     155             108 :     ExecInitResultTypeTL(&scanstate->ss.ps);
     156             108 :     ExecAssignScanProjectionInfo(&scanstate->ss);
     157                 : 
     158                 :     /*
     159                 :      * initialize child expressions
     160                 :      */
     161             108 :     scanstate->ss.ps.qual =
     162             108 :         ExecInitQual(node->scan.plan.qual, &scanstate->ss.ps);
     163                 : 
     164                 :     /* Only XMLTABLE is supported currently */
     165             108 :     scanstate->routine = &XmlTableRoutine;
     166                 : 
     167             108 :     scanstate->perTableCxt =
     168             108 :         AllocSetContextCreate(CurrentMemoryContext,
     169                 :                               "TableFunc per value context",
     170                 :                               ALLOCSET_DEFAULT_SIZES);
     171             108 :     scanstate->opaque = NULL;    /* initialized at runtime */
     172                 : 
     173             108 :     scanstate->ns_names = tf->ns_names;
     174                 : 
     175             108 :     scanstate->ns_uris =
     176             108 :         ExecInitExprList(tf->ns_uris, (PlanState *) scanstate);
     177             108 :     scanstate->docexpr =
     178             108 :         ExecInitExpr((Expr *) tf->docexpr, (PlanState *) scanstate);
     179             108 :     scanstate->rowexpr =
     180             108 :         ExecInitExpr((Expr *) tf->rowexpr, (PlanState *) scanstate);
     181             108 :     scanstate->colexprs =
     182             108 :         ExecInitExprList(tf->colexprs, (PlanState *) scanstate);
     183             108 :     scanstate->coldefexprs =
     184             108 :         ExecInitExprList(tf->coldefexprs, (PlanState *) scanstate);
     185                 : 
     186             108 :     scanstate->notnulls = tf->notnulls;
     187                 : 
     188                 :     /* these are allocated now and initialized later */
     189             108 :     scanstate->in_functions = palloc(sizeof(FmgrInfo) * tupdesc->natts);
     190             108 :     scanstate->typioparams = palloc(sizeof(Oid) * tupdesc->natts);
     191                 : 
     192                 :     /*
     193                 :      * Fill in the necessary fmgr infos.
     194                 :      */
     195             513 :     for (i = 0; i < tupdesc->natts; i++)
     196                 :     {
     197                 :         Oid         in_funcid;
     198                 : 
     199             405 :         getTypeInputInfo(TupleDescAttr(tupdesc, i)->atttypid,
     200             405 :                          &in_funcid, &scanstate->typioparams[i]);
     201             405 :         fmgr_info(in_funcid, &scanstate->in_functions[i]);
     202                 :     }
     203                 : 
     204             108 :     return scanstate;
     205                 : }
     206                 : 
     207                 : /* ----------------------------------------------------------------
     208                 :  *      ExecEndTableFuncScan
     209                 :  *
     210                 :  *      frees any storage allocated through C routines.
     211                 :  * ----------------------------------------------------------------
     212                 :  */
     213                 : void
     214              99 : ExecEndTableFuncScan(TableFuncScanState *node)
     215                 : {
     216                 :     /*
     217                 :      * Free the exprcontext
     218                 :      */
     219              99 :     ExecFreeExprContext(&node->ss.ps);
     220                 : 
     221                 :     /*
     222                 :      * clean out the tuple table
     223                 :      */
     224              99 :     if (node->ss.ps.ps_ResultTupleSlot)
     225 UBC           0 :         ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
     226 CBC          99 :     ExecClearTuple(node->ss.ss_ScanTupleSlot);
     227                 : 
     228                 :     /*
     229                 :      * Release tuplestore resources
     230                 :      */
     231              99 :     if (node->tupstore != NULL)
     232              84 :         tuplestore_end(node->tupstore);
     233              99 :     node->tupstore = NULL;
     234              99 : }
     235                 : 
     236                 : /* ----------------------------------------------------------------
     237                 :  *      ExecReScanTableFuncScan
     238                 :  *
     239                 :  *      Rescans the relation.
     240                 :  * ----------------------------------------------------------------
     241                 :  */
     242                 : void
     243              96 : ExecReScanTableFuncScan(TableFuncScanState *node)
     244                 : {
     245              96 :     Bitmapset  *chgparam = node->ss.ps.chgParam;
     246                 : 
     247              96 :     if (node->ss.ps.ps_ResultTupleSlot)
     248 UBC           0 :         ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
     249 CBC          96 :     ExecScanReScan(&node->ss);
     250                 : 
     251                 :     /*
     252                 :      * Recompute when parameters are changed.
     253                 :      */
     254              96 :     if (chgparam)
     255                 :     {
     256              96 :         if (node->tupstore != NULL)
     257                 :         {
     258              39 :             tuplestore_end(node->tupstore);
     259              39 :             node->tupstore = NULL;
     260                 :         }
     261                 :     }
     262                 : 
     263              96 :     if (node->tupstore != NULL)
     264 UBC           0 :         tuplestore_rescan(node->tupstore);
     265 CBC          96 : }
     266                 : 
     267                 : /* ----------------------------------------------------------------
     268                 :  *      tfuncFetchRows
     269                 :  *
     270                 :  *      Read rows from a TableFunc producer
     271                 :  * ----------------------------------------------------------------
     272                 :  */
     273                 : static void
     274             132 : tfuncFetchRows(TableFuncScanState *tstate, ExprContext *econtext)
     275                 : {
     276             132 :     const TableFuncRoutine *routine = tstate->routine;
     277                 :     MemoryContext oldcxt;
     278                 :     Datum       value;
     279                 :     bool        isnull;
     280                 : 
     281             132 :     Assert(tstate->opaque == NULL);
     282                 : 
     283                 :     /* build tuplestore for the result */
     284             132 :     oldcxt = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
     285             132 :     tstate->tupstore = tuplestore_begin_heap(false, false, work_mem);
     286                 : 
     287                 :     /*
     288                 :      * Each call to fetch a new set of rows - of which there may be very many
     289                 :      * if XMLTABLE is being used in a lateral join - will allocate a possibly
     290                 :      * substantial amount of memory, so we cannot use the per-query context
     291                 :      * here. perTableCxt now serves the same function as "argcontext" does in
     292                 :      * FunctionScan - a place to store per-one-call (i.e. one result table)
     293                 :      * lifetime data (as opposed to per-query or per-result-tuple).
     294                 :      */
     295             132 :     MemoryContextSwitchTo(tstate->perTableCxt);
     296                 : 
     297             132 :     PG_TRY();
     298                 :     {
     299             132 :         routine->InitOpaque(tstate,
     300             132 :                             tstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor->natts);
     301                 : 
     302                 :         /*
     303                 :          * If evaluating the document expression returns NULL, the table
     304                 :          * expression is empty and we return immediately.
     305                 :          */
     306             132 :         value = ExecEvalExpr(tstate->docexpr, econtext, &isnull);
     307                 : 
     308             132 :         if (!isnull)
     309                 :         {
     310                 :             /* otherwise, pass the document value to the table builder */
     311             132 :             tfuncInitialize(tstate, econtext, value);
     312                 : 
     313                 :             /* initialize ordinality counter */
     314             129 :             tstate->ordinal = 1;
     315                 : 
     316                 :             /* Load all rows into the tuplestore, and we're done */
     317             129 :             tfuncLoadRows(tstate, econtext);
     318                 :         }
     319                 :     }
     320               9 :     PG_CATCH();
     321                 :     {
     322               9 :         if (tstate->opaque != NULL)
     323               9 :             routine->DestroyOpaque(tstate);
     324               9 :         PG_RE_THROW();
     325                 :     }
     326             123 :     PG_END_TRY();
     327                 : 
     328                 :     /* clean up and return to original memory context */
     329                 : 
     330             123 :     if (tstate->opaque != NULL)
     331                 :     {
     332             123 :         routine->DestroyOpaque(tstate);
     333             123 :         tstate->opaque = NULL;
     334                 :     }
     335                 : 
     336             123 :     MemoryContextSwitchTo(oldcxt);
     337             123 :     MemoryContextReset(tstate->perTableCxt);
     338             123 : }
     339                 : 
     340                 : /*
     341                 :  * Fill in namespace declarations, the row filter, and column filters in a
     342                 :  * table expression builder context.
     343                 :  */
     344                 : static void
     345             132 : tfuncInitialize(TableFuncScanState *tstate, ExprContext *econtext, Datum doc)
     346                 : {
     347             132 :     const TableFuncRoutine *routine = tstate->routine;
     348                 :     TupleDesc   tupdesc;
     349                 :     ListCell   *lc1,
     350                 :                *lc2;
     351                 :     bool        isnull;
     352                 :     int         colno;
     353                 :     Datum       value;
     354             132 :     int         ordinalitycol =
     355             132 :     ((TableFuncScan *) (tstate->ss.ps.plan))->tablefunc->ordinalitycol;
     356                 : 
     357                 :     /*
     358                 :      * Install the document as a possibly-toasted Datum into the tablefunc
     359                 :      * context.
     360                 :      */
     361             132 :     routine->SetDocument(tstate, doc);
     362                 : 
     363                 :     /* Evaluate namespace specifications */
     364             138 :     forboth(lc1, tstate->ns_uris, lc2, tstate->ns_names)
     365                 :     {
     366               9 :         ExprState  *expr = (ExprState *) lfirst(lc1);
     367               9 :         String     *ns_node = lfirst_node(String, lc2);
     368                 :         char       *ns_uri;
     369                 :         char       *ns_name;
     370                 : 
     371               9 :         value = ExecEvalExpr((ExprState *) expr, econtext, &isnull);
     372               9 :         if (isnull)
     373 UBC           0 :             ereport(ERROR,
     374                 :                     (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
     375                 :                      errmsg("namespace URI must not be null")));
     376 CBC           9 :         ns_uri = TextDatumGetCString(value);
     377                 : 
     378                 :         /* DEFAULT is passed down to SetNamespace as NULL */
     379               9 :         ns_name = ns_node ? strVal(ns_node) : NULL;
     380                 : 
     381               9 :         routine->SetNamespace(tstate, ns_name, ns_uri);
     382                 :     }
     383                 : 
     384                 :     /* Install the row filter expression into the table builder context */
     385             129 :     value = ExecEvalExpr(tstate->rowexpr, econtext, &isnull);
     386             129 :     if (isnull)
     387 UBC           0 :         ereport(ERROR,
     388                 :                 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
     389                 :                  errmsg("row filter expression must not be null")));
     390                 : 
     391 CBC         129 :     routine->SetRowFilter(tstate, TextDatumGetCString(value));
     392                 : 
     393                 :     /*
     394                 :      * Install the column filter expressions into the table builder context.
     395                 :      * If an expression is given, use that; otherwise the column name itself
     396                 :      * is the column filter.
     397                 :      */
     398             129 :     colno = 0;
     399             129 :     tupdesc = tstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor;
     400             552 :     foreach(lc1, tstate->colexprs)
     401                 :     {
     402                 :         char       *colfilter;
     403             423 :         Form_pg_attribute att = TupleDescAttr(tupdesc, colno);
     404                 : 
     405             423 :         if (colno != ordinalitycol)
     406                 :         {
     407             387 :             ExprState  *colexpr = lfirst(lc1);
     408                 : 
     409             387 :             if (colexpr != NULL)
     410                 :             {
     411             303 :                 value = ExecEvalExpr(colexpr, econtext, &isnull);
     412             303 :                 if (isnull)
     413 UBC           0 :                     ereport(ERROR,
     414                 :                             (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
     415                 :                              errmsg("column filter expression must not be null"),
     416                 :                              errdetail("Filter for column \"%s\" is null.",
     417                 :                                        NameStr(att->attname))));
     418 CBC         303 :                 colfilter = TextDatumGetCString(value);
     419                 :             }
     420                 :             else
     421              84 :                 colfilter = NameStr(att->attname);
     422                 : 
     423             387 :             routine->SetColumnFilter(tstate, colfilter, colno);
     424                 :         }
     425                 : 
     426             423 :         colno++;
     427                 :     }
     428             129 : }
     429                 : 
     430                 : /*
     431                 :  * Load all the rows from the TableFunc table builder into a tuplestore.
     432                 :  */
     433                 : static void
     434             129 : tfuncLoadRows(TableFuncScanState *tstate, ExprContext *econtext)
     435                 : {
     436             129 :     const TableFuncRoutine *routine = tstate->routine;
     437             129 :     TupleTableSlot *slot = tstate->ss.ss_ScanTupleSlot;
     438             129 :     TupleDesc   tupdesc = slot->tts_tupleDescriptor;
     439             129 :     Datum      *values = slot->tts_values;
     440             129 :     bool       *nulls = slot->tts_isnull;
     441             129 :     int         natts = tupdesc->natts;
     442                 :     MemoryContext oldcxt;
     443                 :     int         ordinalitycol;
     444                 : 
     445             129 :     ordinalitycol =
     446             129 :         ((TableFuncScan *) (tstate->ss.ps.plan))->tablefunc->ordinalitycol;
     447                 : 
     448                 :     /*
     449                 :      * We need a short-lived memory context that we can clean up each time
     450                 :      * around the loop, to avoid wasting space. Our default per-tuple context
     451                 :      * is fine for the job, since we won't have used it for anything yet in
     452                 :      * this tuple cycle.
     453                 :      */
     454             129 :     oldcxt = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
     455                 : 
     456                 :     /*
     457                 :      * Keep requesting rows from the table builder until there aren't any.
     458                 :      */
     459           11121 :     while (routine->FetchRow(tstate))
     460                 :     {
     461           10998 :         ListCell   *cell = list_head(tstate->coldefexprs);
     462                 :         int         colno;
     463                 : 
     464           10998 :         CHECK_FOR_INTERRUPTS();
     465                 : 
     466           10998 :         ExecClearTuple(tstate->ss.ss_ScanTupleSlot);
     467                 : 
     468                 :         /*
     469                 :          * Obtain the value of each column for this row, installing them into
     470                 :          * the slot; then add the tuple to the tuplestore.
     471                 :          */
     472           76590 :         for (colno = 0; colno < natts; colno++)
     473                 :         {
     474           65598 :             Form_pg_attribute att = TupleDescAttr(tupdesc, colno);
     475                 : 
     476           65598 :             if (colno == ordinalitycol)
     477                 :             {
     478                 :                 /* Fast path for ordinality column */
     479             135 :                 values[colno] = Int32GetDatum(tstate->ordinal++);
     480             135 :                 nulls[colno] = false;
     481                 :             }
     482                 :             else
     483                 :             {
     484                 :                 bool        isnull;
     485                 : 
     486           65463 :                 values[colno] = routine->GetValue(tstate,
     487                 :                                                   colno,
     488                 :                                                   att->atttypid,
     489                 :                                                   att->atttypmod,
     490                 :                                                   &isnull);
     491                 : 
     492                 :                 /* No value?  Evaluate and apply the default, if any */
     493           65460 :                 if (isnull && cell != NULL)
     494                 :                 {
     495           11122 :                     ExprState  *coldefexpr = (ExprState *) lfirst(cell);
     496                 : 
     497           11122 :                     if (coldefexpr != NULL)
     498             111 :                         values[colno] = ExecEvalExpr(coldefexpr, econtext,
     499                 :                                                      &isnull);
     500                 :                 }
     501                 : 
     502                 :                 /* Verify a possible NOT NULL constraint */
     503           65460 :                 if (isnull && bms_is_member(colno, tstate->notnulls))
     504               3 :                     ereport(ERROR,
     505                 :                             (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
     506                 :                              errmsg("null is not allowed in column \"%s\"",
     507                 :                                     NameStr(att->attname))));
     508                 : 
     509           65457 :                 nulls[colno] = isnull;
     510                 :             }
     511                 : 
     512                 :             /* advance list of default expressions */
     513           65592 :             if (cell != NULL)
     514           65592 :                 cell = lnext(tstate->coldefexprs, cell);
     515                 :         }
     516                 : 
     517           10992 :         tuplestore_putvalues(tstate->tupstore, tupdesc, values, nulls);
     518                 : 
     519           10992 :         MemoryContextReset(econtext->ecxt_per_tuple_memory);
     520                 :     }
     521                 : 
     522             123 :     MemoryContextSwitchTo(oldcxt);
     523             123 : }
        

Generated by: LCOV version v1.16-55-g56c0a2a