LCOV - code coverage report
Current view: top level - src/backend/executor - nodeTableFuncscan.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 150 157 95.5 %
Date: 2024-02-28 23:11:08 Functions: 8 9 88.9 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14