LCOV - code coverage report
Current view: top level - src/backend/executor - nodeSamplescan.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 99 103 96.1 %
Date: 2024-03-28 10:11:15 Functions: 7 8 87.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * nodeSamplescan.c
       4             :  *    Support routines for sample scans of relations (table sampling).
       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/nodeSamplescan.c
      12             :  *
      13             :  *-------------------------------------------------------------------------
      14             :  */
      15             : #include "postgres.h"
      16             : 
      17             : #include "access/relscan.h"
      18             : #include "access/tableam.h"
      19             : #include "access/tsmapi.h"
      20             : #include "common/pg_prng.h"
      21             : #include "executor/executor.h"
      22             : #include "executor/nodeSamplescan.h"
      23             : #include "utils/fmgrprotos.h"
      24             : #include "utils/rel.h"
      25             : 
      26             : static TupleTableSlot *SampleNext(SampleScanState *node);
      27             : static void tablesample_init(SampleScanState *scanstate);
      28             : static TupleTableSlot *tablesample_getnext(SampleScanState *scanstate);
      29             : 
      30             : /* ----------------------------------------------------------------
      31             :  *                      Scan Support
      32             :  * ----------------------------------------------------------------
      33             :  */
      34             : 
      35             : /* ----------------------------------------------------------------
      36             :  *      SampleNext
      37             :  *
      38             :  *      This is a workhorse for ExecSampleScan
      39             :  * ----------------------------------------------------------------
      40             :  */
      41             : static TupleTableSlot *
      42      241370 : SampleNext(SampleScanState *node)
      43             : {
      44             :     /*
      45             :      * if this is first call within a scan, initialize
      46             :      */
      47      241370 :     if (!node->begun)
      48         216 :         tablesample_init(node);
      49             : 
      50             :     /*
      51             :      * get the next tuple, and store it in our result slot
      52             :      */
      53      241330 :     return tablesample_getnext(node);
      54             : }
      55             : 
      56             : /*
      57             :  * SampleRecheck -- access method routine to recheck a tuple in EvalPlanQual
      58             :  */
      59             : static bool
      60           0 : SampleRecheck(SampleScanState *node, TupleTableSlot *slot)
      61             : {
      62             :     /*
      63             :      * No need to recheck for SampleScan, since like SeqScan we don't pass any
      64             :      * checkable keys to heap_beginscan.
      65             :      */
      66           0 :     return true;
      67             : }
      68             : 
      69             : /* ----------------------------------------------------------------
      70             :  *      ExecSampleScan(node)
      71             :  *
      72             :  *      Scans the relation using the sampling method and returns
      73             :  *      the next qualifying tuple.
      74             :  *      We call the ExecScan() routine and pass it the appropriate
      75             :  *      access method functions.
      76             :  * ----------------------------------------------------------------
      77             :  */
      78             : static TupleTableSlot *
      79      241364 : ExecSampleScan(PlanState *pstate)
      80             : {
      81      241364 :     SampleScanState *node = castNode(SampleScanState, pstate);
      82             : 
      83      241364 :     return ExecScan(&node->ss,
      84             :                     (ExecScanAccessMtd) SampleNext,
      85             :                     (ExecScanRecheckMtd) SampleRecheck);
      86             : }
      87             : 
      88             : /* ----------------------------------------------------------------
      89             :  *      ExecInitSampleScan
      90             :  * ----------------------------------------------------------------
      91             :  */
      92             : SampleScanState *
      93         300 : ExecInitSampleScan(SampleScan *node, EState *estate, int eflags)
      94             : {
      95             :     SampleScanState *scanstate;
      96         300 :     TableSampleClause *tsc = node->tablesample;
      97             :     TsmRoutine *tsm;
      98             : 
      99             :     Assert(outerPlan(node) == NULL);
     100             :     Assert(innerPlan(node) == NULL);
     101             : 
     102             :     /*
     103             :      * create state structure
     104             :      */
     105         300 :     scanstate = makeNode(SampleScanState);
     106         300 :     scanstate->ss.ps.plan = (Plan *) node;
     107         300 :     scanstate->ss.ps.state = estate;
     108         300 :     scanstate->ss.ps.ExecProcNode = ExecSampleScan;
     109             : 
     110             :     /*
     111             :      * Miscellaneous initialization
     112             :      *
     113             :      * create expression context for node
     114             :      */
     115         300 :     ExecAssignExprContext(estate, &scanstate->ss.ps);
     116             : 
     117             :     /*
     118             :      * open the scan relation
     119             :      */
     120         300 :     scanstate->ss.ss_currentRelation =
     121         300 :         ExecOpenScanRelation(estate,
     122             :                              node->scan.scanrelid,
     123             :                              eflags);
     124             : 
     125             :     /* we won't set up the HeapScanDesc till later */
     126         300 :     scanstate->ss.ss_currentScanDesc = NULL;
     127             : 
     128             :     /* and create slot with appropriate rowtype */
     129         300 :     ExecInitScanTupleSlot(estate, &scanstate->ss,
     130         300 :                           RelationGetDescr(scanstate->ss.ss_currentRelation),
     131             :                           table_slot_callbacks(scanstate->ss.ss_currentRelation));
     132             : 
     133             :     /*
     134             :      * Initialize result type and projection.
     135             :      */
     136         300 :     ExecInitResultTypeTL(&scanstate->ss.ps);
     137         300 :     ExecAssignScanProjectionInfo(&scanstate->ss);
     138             : 
     139             :     /*
     140             :      * initialize child expressions
     141             :      */
     142         300 :     scanstate->ss.ps.qual =
     143         300 :         ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
     144             : 
     145         300 :     scanstate->args = ExecInitExprList(tsc->args, (PlanState *) scanstate);
     146         300 :     scanstate->repeatable =
     147         300 :         ExecInitExpr(tsc->repeatable, (PlanState *) scanstate);
     148             : 
     149             :     /*
     150             :      * If we don't have a REPEATABLE clause, select a random seed.  We want to
     151             :      * do this just once, since the seed shouldn't change over rescans.
     152             :      */
     153         300 :     if (tsc->repeatable == NULL)
     154         168 :         scanstate->seed = pg_prng_uint32(&pg_global_prng_state);
     155             : 
     156             :     /*
     157             :      * Finally, initialize the TABLESAMPLE method handler.
     158             :      */
     159         300 :     tsm = GetTsmRoutine(tsc->tsmhandler);
     160         300 :     scanstate->tsmroutine = tsm;
     161         300 :     scanstate->tsm_state = NULL;
     162             : 
     163         300 :     if (tsm->InitSampleScan)
     164         300 :         tsm->InitSampleScan(scanstate, eflags);
     165             : 
     166             :     /* We'll do BeginSampleScan later; we can't evaluate params yet */
     167         300 :     scanstate->begun = false;
     168             : 
     169         300 :     return scanstate;
     170             : }
     171             : 
     172             : /* ----------------------------------------------------------------
     173             :  *      ExecEndSampleScan
     174             :  *
     175             :  *      frees any storage allocated through C routines.
     176             :  * ----------------------------------------------------------------
     177             :  */
     178             : void
     179         260 : ExecEndSampleScan(SampleScanState *node)
     180             : {
     181             :     /*
     182             :      * Tell sampling function that we finished the scan.
     183             :      */
     184         260 :     if (node->tsmroutine->EndSampleScan)
     185           0 :         node->tsmroutine->EndSampleScan(node);
     186             : 
     187             :     /*
     188             :      * close heap scan
     189             :      */
     190         260 :     if (node->ss.ss_currentScanDesc)
     191         146 :         table_endscan(node->ss.ss_currentScanDesc);
     192         260 : }
     193             : 
     194             : /* ----------------------------------------------------------------
     195             :  *      ExecReScanSampleScan
     196             :  *
     197             :  *      Rescans the relation.
     198             :  *
     199             :  * ----------------------------------------------------------------
     200             :  */
     201             : void
     202          58 : ExecReScanSampleScan(SampleScanState *node)
     203             : {
     204             :     /* Remember we need to do BeginSampleScan again (if we did it at all) */
     205          58 :     node->begun = false;
     206          58 :     node->done = false;
     207          58 :     node->haveblock = false;
     208          58 :     node->donetuples = 0;
     209             : 
     210          58 :     ExecScanReScan(&node->ss);
     211          58 : }
     212             : 
     213             : 
     214             : /*
     215             :  * Initialize the TABLESAMPLE method: evaluate params and call BeginSampleScan.
     216             :  */
     217             : static void
     218         216 : tablesample_init(SampleScanState *scanstate)
     219             : {
     220         216 :     TsmRoutine *tsm = scanstate->tsmroutine;
     221         216 :     ExprContext *econtext = scanstate->ss.ps.ps_ExprContext;
     222             :     Datum      *params;
     223             :     Datum       datum;
     224             :     bool        isnull;
     225             :     uint32      seed;
     226             :     bool        allow_sync;
     227             :     int         i;
     228             :     ListCell   *arg;
     229             : 
     230         216 :     scanstate->donetuples = 0;
     231         216 :     params = (Datum *) palloc(list_length(scanstate->args) * sizeof(Datum));
     232             : 
     233         216 :     i = 0;
     234         426 :     foreach(arg, scanstate->args)
     235             :     {
     236         216 :         ExprState  *argstate = (ExprState *) lfirst(arg);
     237             : 
     238         216 :         params[i] = ExecEvalExprSwitchContext(argstate,
     239             :                                               econtext,
     240             :                                               &isnull);
     241         216 :         if (isnull)
     242           6 :             ereport(ERROR,
     243             :                     (errcode(ERRCODE_INVALID_TABLESAMPLE_ARGUMENT),
     244             :                      errmsg("TABLESAMPLE parameter cannot be null")));
     245         210 :         i++;
     246             :     }
     247             : 
     248         210 :     if (scanstate->repeatable)
     249             :     {
     250          72 :         datum = ExecEvalExprSwitchContext(scanstate->repeatable,
     251             :                                           econtext,
     252             :                                           &isnull);
     253          72 :         if (isnull)
     254           6 :             ereport(ERROR,
     255             :                     (errcode(ERRCODE_INVALID_TABLESAMPLE_REPEAT),
     256             :                      errmsg("TABLESAMPLE REPEATABLE parameter cannot be null")));
     257             : 
     258             :         /*
     259             :          * The REPEATABLE parameter has been coerced to float8 by the parser.
     260             :          * The reason for using float8 at the SQL level is that it will
     261             :          * produce unsurprising results both for users used to databases that
     262             :          * accept only integers in the REPEATABLE clause and for those who
     263             :          * might expect that REPEATABLE works like setseed() (a float in the
     264             :          * range from -1 to 1).
     265             :          *
     266             :          * We use hashfloat8() to convert the supplied value into a suitable
     267             :          * seed.  For regression-testing purposes, that has the convenient
     268             :          * property that REPEATABLE(0) gives a machine-independent result.
     269             :          */
     270          66 :         seed = DatumGetUInt32(DirectFunctionCall1(hashfloat8, datum));
     271             :     }
     272             :     else
     273             :     {
     274             :         /* Use the seed selected by ExecInitSampleScan */
     275         138 :         seed = scanstate->seed;
     276             :     }
     277             : 
     278             :     /* Set default values for params that BeginSampleScan can adjust */
     279         204 :     scanstate->use_bulkread = true;
     280         204 :     scanstate->use_pagemode = true;
     281             : 
     282             :     /* Let tablesample method do its thing */
     283         204 :     tsm->BeginSampleScan(scanstate,
     284             :                          params,
     285         204 :                          list_length(scanstate->args),
     286             :                          seed);
     287             : 
     288             :     /* We'll use syncscan if there's no NextSampleBlock function */
     289         176 :     allow_sync = (tsm->NextSampleBlock == NULL);
     290             : 
     291             :     /* Now we can create or reset the HeapScanDesc */
     292         176 :     if (scanstate->ss.ss_currentScanDesc == NULL)
     293             :     {
     294         146 :         scanstate->ss.ss_currentScanDesc =
     295         146 :             table_beginscan_sampling(scanstate->ss.ss_currentRelation,
     296         146 :                                      scanstate->ss.ps.state->es_snapshot,
     297             :                                      0, NULL,
     298         146 :                                      scanstate->use_bulkread,
     299             :                                      allow_sync,
     300         146 :                                      scanstate->use_pagemode);
     301             :     }
     302             :     else
     303             :     {
     304          30 :         table_rescan_set_params(scanstate->ss.ss_currentScanDesc, NULL,
     305          30 :                                 scanstate->use_bulkread,
     306             :                                 allow_sync,
     307          30 :                                 scanstate->use_pagemode);
     308             :     }
     309             : 
     310         176 :     pfree(params);
     311             : 
     312             :     /* And we're initialized. */
     313         176 :     scanstate->begun = true;
     314         176 : }
     315             : 
     316             : /*
     317             :  * Get next tuple from TABLESAMPLE method.
     318             :  */
     319             : static TupleTableSlot *
     320      241330 : tablesample_getnext(SampleScanState *scanstate)
     321             : {
     322      241330 :     TableScanDesc scan = scanstate->ss.ss_currentScanDesc;
     323      241330 :     TupleTableSlot *slot = scanstate->ss.ss_ScanTupleSlot;
     324             : 
     325      241330 :     ExecClearTuple(slot);
     326             : 
     327      241330 :     if (scanstate->done)
     328           0 :         return NULL;
     329             : 
     330             :     for (;;)
     331             :     {
     332      254062 :         if (!scanstate->haveblock)
     333             :         {
     334       12908 :             if (!table_scan_sample_next_block(scan, scanstate))
     335             :             {
     336         170 :                 scanstate->haveblock = false;
     337         170 :                 scanstate->done = true;
     338             : 
     339             :                 /* exhausted relation */
     340         170 :                 return NULL;
     341             :             }
     342             : 
     343       12738 :             scanstate->haveblock = true;
     344             :         }
     345             : 
     346      253892 :         if (!table_scan_sample_next_tuple(scan, scanstate, slot))
     347             :         {
     348             :             /*
     349             :              * If we get here, it means we've exhausted the items on this page
     350             :              * and it's time to move to the next.
     351             :              */
     352       12732 :             scanstate->haveblock = false;
     353       12732 :             continue;
     354             :         }
     355             : 
     356             :         /* Found visible tuple, return it. */
     357      241160 :         break;
     358             :     }
     359             : 
     360      241160 :     scanstate->donetuples++;
     361             : 
     362      241160 :     return slot;
     363             : }

Generated by: LCOV version 1.14