LCOV - code coverage report
Current view: top level - src/backend/executor - nodeTableFuncscan.c (source / functions) Hit Total Coverage
Test: PostgreSQL 16beta1 Lines: 150 158 94.9 %
Date: 2023-06-06 10:12:12 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-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       22238 : TableFuncNext(TableFuncScanState *node)
      54             : {
      55             :     TupleTableSlot *scanslot;
      56             : 
      57       22238 :     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       22238 :     if (node->tupstore == NULL)
      64         264 :         tfuncFetchRows(node, node->ss.ps.ps_ExprContext);
      65             : 
      66             :     /*
      67             :      * Get the next tuple from tuplestore.
      68             :      */
      69       22220 :     (void) tuplestore_gettupleslot(node->tupstore,
      70             :                                    true,
      71             :                                    false,
      72             :                                    scanslot);
      73       22220 :     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       22184 : ExecTableFuncScan(PlanState *pstate)
      97             : {
      98       22184 :     TableFuncScanState *node = castNode(TableFuncScanState, pstate);
      99             : 
     100       22184 :     return ExecScan(&node->ss,
     101             :                     (ExecScanAccessMtd) TableFuncNext,
     102             :                     (ExecScanRecheckMtd) TableFuncRecheck);
     103             : }
     104             : 
     105             : /* ----------------------------------------------------------------
     106             :  *      ExecInitTableFuncScan
     107             :  * ----------------------------------------------------------------
     108             :  */
     109             : TableFuncScanState *
     110         216 : ExecInitTableFuncScan(TableFuncScan *node, EState *estate, int eflags)
     111             : {
     112             :     TableFuncScanState *scanstate;
     113         216 :     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         216 :     scanstate = makeNode(TableFuncScanState);
     130         216 :     scanstate->ss.ps.plan = (Plan *) node;
     131         216 :     scanstate->ss.ps.state = estate;
     132         216 :     scanstate->ss.ps.ExecProcNode = ExecTableFuncScan;
     133             : 
     134             :     /*
     135             :      * Miscellaneous initialization
     136             :      *
     137             :      * create expression context for node
     138             :      */
     139         216 :     ExecAssignExprContext(estate, &scanstate->ss.ps);
     140             : 
     141             :     /*
     142             :      * initialize source tuple type
     143             :      */
     144         216 :     tupdesc = BuildDescFromLists(tf->colnames,
     145             :                                  tf->coltypes,
     146             :                                  tf->coltypmods,
     147             :                                  tf->colcollations);
     148             :     /* and the corresponding scan slot */
     149         216 :     ExecInitScanTupleSlot(estate, &scanstate->ss, tupdesc,
     150             :                           &TTSOpsMinimalTuple);
     151             : 
     152             :     /*
     153             :      * Initialize result type and projection.
     154             :      */
     155         216 :     ExecInitResultTypeTL(&scanstate->ss.ps);
     156         216 :     ExecAssignScanProjectionInfo(&scanstate->ss);
     157             : 
     158             :     /*
     159             :      * initialize child expressions
     160             :      */
     161         216 :     scanstate->ss.ps.qual =
     162         216 :         ExecInitQual(node->scan.plan.qual, &scanstate->ss.ps);
     163             : 
     164             :     /* Only XMLTABLE is supported currently */
     165         216 :     scanstate->routine = &XmlTableRoutine;
     166             : 
     167         216 :     scanstate->perTableCxt =
     168         216 :         AllocSetContextCreate(CurrentMemoryContext,
     169             :                               "TableFunc per value context",
     170             :                               ALLOCSET_DEFAULT_SIZES);
     171         216 :     scanstate->opaque = NULL;    /* initialized at runtime */
     172             : 
     173         216 :     scanstate->ns_names = tf->ns_names;
     174             : 
     175         216 :     scanstate->ns_uris =
     176         216 :         ExecInitExprList(tf->ns_uris, (PlanState *) scanstate);
     177         216 :     scanstate->docexpr =
     178         216 :         ExecInitExpr((Expr *) tf->docexpr, (PlanState *) scanstate);
     179         216 :     scanstate->rowexpr =
     180         216 :         ExecInitExpr((Expr *) tf->rowexpr, (PlanState *) scanstate);
     181         216 :     scanstate->colexprs =
     182         216 :         ExecInitExprList(tf->colexprs, (PlanState *) scanstate);
     183         216 :     scanstate->coldefexprs =
     184         216 :         ExecInitExprList(tf->coldefexprs, (PlanState *) scanstate);
     185             : 
     186         216 :     scanstate->notnulls = tf->notnulls;
     187             : 
     188             :     /* these are allocated now and initialized later */
     189         216 :     scanstate->in_functions = palloc(sizeof(FmgrInfo) * tupdesc->natts);
     190         216 :     scanstate->typioparams = palloc(sizeof(Oid) * tupdesc->natts);
     191             : 
     192             :     /*
     193             :      * Fill in the necessary fmgr infos.
     194             :      */
     195        1026 :     for (i = 0; i < tupdesc->natts; i++)
     196             :     {
     197             :         Oid         in_funcid;
     198             : 
     199         810 :         getTypeInputInfo(TupleDescAttr(tupdesc, i)->atttypid,
     200         810 :                          &in_funcid, &scanstate->typioparams[i]);
     201         810 :         fmgr_info(in_funcid, &scanstate->in_functions[i]);
     202             :     }
     203             : 
     204         216 :     return scanstate;
     205             : }
     206             : 
     207             : /* ----------------------------------------------------------------
     208             :  *      ExecEndTableFuncScan
     209             :  *
     210             :  *      frees any storage allocated through C routines.
     211             :  * ----------------------------------------------------------------
     212             :  */
     213             : void
     214         198 : ExecEndTableFuncScan(TableFuncScanState *node)
     215             : {
     216             :     /*
     217             :      * Free the exprcontext
     218             :      */
     219         198 :     ExecFreeExprContext(&node->ss.ps);
     220             : 
     221             :     /*
     222             :      * clean out the tuple table
     223             :      */
     224         198 :     if (node->ss.ps.ps_ResultTupleSlot)
     225           0 :         ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
     226         198 :     ExecClearTuple(node->ss.ss_ScanTupleSlot);
     227             : 
     228             :     /*
     229             :      * Release tuplestore resources
     230             :      */
     231         198 :     if (node->tupstore != NULL)
     232         168 :         tuplestore_end(node->tupstore);
     233         198 :     node->tupstore = NULL;
     234         198 : }
     235             : 
     236             : /* ----------------------------------------------------------------
     237             :  *      ExecReScanTableFuncScan
     238             :  *
     239             :  *      Rescans the relation.
     240             :  * ----------------------------------------------------------------
     241             :  */
     242             : void
     243         192 : ExecReScanTableFuncScan(TableFuncScanState *node)
     244             : {
     245         192 :     Bitmapset  *chgparam = node->ss.ps.chgParam;
     246             : 
     247         192 :     if (node->ss.ps.ps_ResultTupleSlot)
     248           0 :         ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
     249         192 :     ExecScanReScan(&node->ss);
     250             : 
     251             :     /*
     252             :      * Recompute when parameters are changed.
     253             :      */
     254         192 :     if (chgparam)
     255             :     {
     256         192 :         if (node->tupstore != NULL)
     257             :         {
     258          78 :             tuplestore_end(node->tupstore);
     259          78 :             node->tupstore = NULL;
     260             :         }
     261             :     }
     262             : 
     263         192 :     if (node->tupstore != NULL)
     264           0 :         tuplestore_rescan(node->tupstore);
     265         192 : }
     266             : 
     267             : /* ----------------------------------------------------------------
     268             :  *      tfuncFetchRows
     269             :  *
     270             :  *      Read rows from a TableFunc producer
     271             :  * ----------------------------------------------------------------
     272             :  */
     273             : static void
     274         264 : tfuncFetchRows(TableFuncScanState *tstate, ExprContext *econtext)
     275             : {
     276         264 :     const TableFuncRoutine *routine = tstate->routine;
     277             :     MemoryContext oldcxt;
     278             :     Datum       value;
     279             :     bool        isnull;
     280             : 
     281             :     Assert(tstate->opaque == NULL);
     282             : 
     283             :     /* build tuplestore for the result */
     284         264 :     oldcxt = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
     285         264 :     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         264 :     MemoryContextSwitchTo(tstate->perTableCxt);
     296             : 
     297         264 :     PG_TRY();
     298             :     {
     299         264 :         routine->InitOpaque(tstate,
     300         264 :                             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         264 :         value = ExecEvalExpr(tstate->docexpr, econtext, &isnull);
     307             : 
     308         264 :         if (!isnull)
     309             :         {
     310             :             /* otherwise, pass the document value to the table builder */
     311         264 :             tfuncInitialize(tstate, econtext, value);
     312             : 
     313             :             /* initialize ordinality counter */
     314         258 :             tstate->ordinal = 1;
     315             : 
     316             :             /* Load all rows into the tuplestore, and we're done */
     317         258 :             tfuncLoadRows(tstate, econtext);
     318             :         }
     319             :     }
     320          18 :     PG_CATCH();
     321             :     {
     322          18 :         if (tstate->opaque != NULL)
     323          18 :             routine->DestroyOpaque(tstate);
     324          18 :         PG_RE_THROW();
     325             :     }
     326         246 :     PG_END_TRY();
     327             : 
     328             :     /* clean up and return to original memory context */
     329             : 
     330         246 :     if (tstate->opaque != NULL)
     331             :     {
     332         246 :         routine->DestroyOpaque(tstate);
     333         246 :         tstate->opaque = NULL;
     334             :     }
     335             : 
     336         246 :     MemoryContextSwitchTo(oldcxt);
     337         246 :     MemoryContextReset(tstate->perTableCxt);
     338         246 : }
     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         264 : tfuncInitialize(TableFuncScanState *tstate, ExprContext *econtext, Datum doc)
     346             : {
     347         264 :     const TableFuncRoutine *routine = tstate->routine;
     348             :     TupleDesc   tupdesc;
     349             :     ListCell   *lc1,
     350             :                *lc2;
     351             :     bool        isnull;
     352             :     int         colno;
     353             :     Datum       value;
     354         264 :     int         ordinalitycol =
     355         264 :         ((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         264 :     routine->SetDocument(tstate, doc);
     362             : 
     363             :     /* Evaluate namespace specifications */
     364         276 :     forboth(lc1, tstate->ns_uris, lc2, tstate->ns_names)
     365             :     {
     366          18 :         ExprState  *expr = (ExprState *) lfirst(lc1);
     367          18 :         String     *ns_node = lfirst_node(String, lc2);
     368             :         char       *ns_uri;
     369             :         char       *ns_name;
     370             : 
     371          18 :         value = ExecEvalExpr((ExprState *) expr, econtext, &isnull);
     372          18 :         if (isnull)
     373           0 :             ereport(ERROR,
     374             :                     (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
     375             :                      errmsg("namespace URI must not be null")));
     376          18 :         ns_uri = TextDatumGetCString(value);
     377             : 
     378             :         /* DEFAULT is passed down to SetNamespace as NULL */
     379          18 :         ns_name = ns_node ? strVal(ns_node) : NULL;
     380             : 
     381          18 :         routine->SetNamespace(tstate, ns_name, ns_uri);
     382             :     }
     383             : 
     384             :     /* Install the row filter expression into the table builder context */
     385         258 :     value = ExecEvalExpr(tstate->rowexpr, econtext, &isnull);
     386         258 :     if (isnull)
     387           0 :         ereport(ERROR,
     388             :                 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
     389             :                  errmsg("row filter expression must not be null")));
     390             : 
     391         258 :     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         258 :     colno = 0;
     399         258 :     tupdesc = tstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor;
     400        1104 :     foreach(lc1, tstate->colexprs)
     401             :     {
     402             :         char       *colfilter;
     403         846 :         Form_pg_attribute att = TupleDescAttr(tupdesc, colno);
     404             : 
     405         846 :         if (colno != ordinalitycol)
     406             :         {
     407         774 :             ExprState  *colexpr = lfirst(lc1);
     408             : 
     409         774 :             if (colexpr != NULL)
     410             :             {
     411         606 :                 value = ExecEvalExpr(colexpr, econtext, &isnull);
     412         606 :                 if (isnull)
     413           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         606 :                 colfilter = TextDatumGetCString(value);
     419             :             }
     420             :             else
     421         168 :                 colfilter = NameStr(att->attname);
     422             : 
     423         774 :             routine->SetColumnFilter(tstate, colfilter, colno);
     424             :         }
     425             : 
     426         846 :         colno++;
     427             :     }
     428         258 : }
     429             : 
     430             : /*
     431             :  * Load all the rows from the TableFunc table builder into a tuplestore.
     432             :  */
     433             : static void
     434         258 : tfuncLoadRows(TableFuncScanState *tstate, ExprContext *econtext)
     435             : {
     436         258 :     const TableFuncRoutine *routine = tstate->routine;
     437         258 :     TupleTableSlot *slot = tstate->ss.ss_ScanTupleSlot;
     438         258 :     TupleDesc   tupdesc = slot->tts_tupleDescriptor;
     439         258 :     Datum      *values = slot->tts_values;
     440         258 :     bool       *nulls = slot->tts_isnull;
     441         258 :     int         natts = tupdesc->natts;
     442             :     MemoryContext oldcxt;
     443             :     int         ordinalitycol;
     444             : 
     445         258 :     ordinalitycol =
     446         258 :         ((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         258 :     oldcxt = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
     455             : 
     456             :     /*
     457             :      * Keep requesting rows from the table builder until there aren't any.
     458             :      */
     459       22232 :     while (routine->FetchRow(tstate))
     460             :     {
     461       21986 :         ListCell   *cell = list_head(tstate->coldefexprs);
     462             :         int         colno;
     463             : 
     464       21986 :         CHECK_FOR_INTERRUPTS();
     465             : 
     466       21986 :         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      153110 :         for (colno = 0; colno < natts; colno++)
     473             :         {
     474      131136 :             Form_pg_attribute att = TupleDescAttr(tupdesc, colno);
     475             : 
     476      131136 :             if (colno == ordinalitycol)
     477             :             {
     478             :                 /* Fast path for ordinality column */
     479         270 :                 values[colno] = Int32GetDatum(tstate->ordinal++);
     480         270 :                 nulls[colno] = false;
     481             :             }
     482             :             else
     483             :             {
     484             :                 bool        isnull;
     485             : 
     486      130866 :                 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      130860 :                 if (isnull && cell != NULL)
     494             :                 {
     495       22192 :                     ExprState  *coldefexpr = (ExprState *) lfirst(cell);
     496             : 
     497       22192 :                     if (coldefexpr != NULL)
     498         222 :                         values[colno] = ExecEvalExpr(coldefexpr, econtext,
     499             :                                                      &isnull);
     500             :                 }
     501             : 
     502             :                 /* Verify a possible NOT NULL constraint */
     503      130860 :                 if (isnull && bms_is_member(colno, tstate->notnulls))
     504           6 :                     ereport(ERROR,
     505             :                             (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
     506             :                              errmsg("null is not allowed in column \"%s\"",
     507             :                                     NameStr(att->attname))));
     508             : 
     509      130854 :                 nulls[colno] = isnull;
     510             :             }
     511             : 
     512             :             /* advance list of default expressions */
     513      131124 :             if (cell != NULL)
     514      131124 :                 cell = lnext(tstate->coldefexprs, cell);
     515             :         }
     516             : 
     517       21974 :         tuplestore_putvalues(tstate->tupstore, tupdesc, values, nulls);
     518             : 
     519       21974 :         MemoryContextReset(econtext->ecxt_per_tuple_memory);
     520             :     }
     521             : 
     522         246 :     MemoryContextSwitchTo(oldcxt);
     523         246 : }

Generated by: LCOV version 1.14