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

Generated by: LCOV version 1.14