LCOV - code coverage report
Current view: top level - src/backend/executor - nodeSamplescan.c (source / functions) Hit Total Coverage
Test: PostgreSQL 16beta1 Lines: 103 107 96.3 %
Date: 2023-06-06 09:15:10 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             :      * Free the exprcontext
     193             :      */
     194         212 :     ExecFreeExprContext(&node->ss.ps);
     195             : 
     196             :     /*
     197             :      * clean out the tuple table
     198             :      */
     199         212 :     if (node->ss.ps.ps_ResultTupleSlot)
     200         114 :         ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
     201         212 :     ExecClearTuple(node->ss.ss_ScanTupleSlot);
     202             : 
     203             :     /*
     204             :      * close heap scan
     205             :      */
     206         212 :     if (node->ss.ss_currentScanDesc)
     207         146 :         table_endscan(node->ss.ss_currentScanDesc);
     208         212 : }
     209             : 
     210             : /* ----------------------------------------------------------------
     211             :  *      ExecReScanSampleScan
     212             :  *
     213             :  *      Rescans the relation.
     214             :  *
     215             :  * ----------------------------------------------------------------
     216             :  */
     217             : void
     218          58 : ExecReScanSampleScan(SampleScanState *node)
     219             : {
     220             :     /* Remember we need to do BeginSampleScan again (if we did it at all) */
     221          58 :     node->begun = false;
     222          58 :     node->done = false;
     223          58 :     node->haveblock = false;
     224          58 :     node->donetuples = 0;
     225             : 
     226          58 :     ExecScanReScan(&node->ss);
     227          58 : }
     228             : 
     229             : 
     230             : /*
     231             :  * Initialize the TABLESAMPLE method: evaluate params and call BeginSampleScan.
     232             :  */
     233             : static void
     234         216 : tablesample_init(SampleScanState *scanstate)
     235             : {
     236         216 :     TsmRoutine *tsm = scanstate->tsmroutine;
     237         216 :     ExprContext *econtext = scanstate->ss.ps.ps_ExprContext;
     238             :     Datum      *params;
     239             :     Datum       datum;
     240             :     bool        isnull;
     241             :     uint32      seed;
     242             :     bool        allow_sync;
     243             :     int         i;
     244             :     ListCell   *arg;
     245             : 
     246         216 :     scanstate->donetuples = 0;
     247         216 :     params = (Datum *) palloc(list_length(scanstate->args) * sizeof(Datum));
     248             : 
     249         216 :     i = 0;
     250         426 :     foreach(arg, scanstate->args)
     251             :     {
     252         216 :         ExprState  *argstate = (ExprState *) lfirst(arg);
     253             : 
     254         216 :         params[i] = ExecEvalExprSwitchContext(argstate,
     255             :                                               econtext,
     256             :                                               &isnull);
     257         216 :         if (isnull)
     258           6 :             ereport(ERROR,
     259             :                     (errcode(ERRCODE_INVALID_TABLESAMPLE_ARGUMENT),
     260             :                      errmsg("TABLESAMPLE parameter cannot be null")));
     261         210 :         i++;
     262             :     }
     263             : 
     264         210 :     if (scanstate->repeatable)
     265             :     {
     266          72 :         datum = ExecEvalExprSwitchContext(scanstate->repeatable,
     267             :                                           econtext,
     268             :                                           &isnull);
     269          72 :         if (isnull)
     270           6 :             ereport(ERROR,
     271             :                     (errcode(ERRCODE_INVALID_TABLESAMPLE_REPEAT),
     272             :                      errmsg("TABLESAMPLE REPEATABLE parameter cannot be null")));
     273             : 
     274             :         /*
     275             :          * The REPEATABLE parameter has been coerced to float8 by the parser.
     276             :          * The reason for using float8 at the SQL level is that it will
     277             :          * produce unsurprising results both for users used to databases that
     278             :          * accept only integers in the REPEATABLE clause and for those who
     279             :          * might expect that REPEATABLE works like setseed() (a float in the
     280             :          * range from -1 to 1).
     281             :          *
     282             :          * We use hashfloat8() to convert the supplied value into a suitable
     283             :          * seed.  For regression-testing purposes, that has the convenient
     284             :          * property that REPEATABLE(0) gives a machine-independent result.
     285             :          */
     286          66 :         seed = DatumGetUInt32(DirectFunctionCall1(hashfloat8, datum));
     287             :     }
     288             :     else
     289             :     {
     290             :         /* Use the seed selected by ExecInitSampleScan */
     291         138 :         seed = scanstate->seed;
     292             :     }
     293             : 
     294             :     /* Set default values for params that BeginSampleScan can adjust */
     295         204 :     scanstate->use_bulkread = true;
     296         204 :     scanstate->use_pagemode = true;
     297             : 
     298             :     /* Let tablesample method do its thing */
     299         204 :     tsm->BeginSampleScan(scanstate,
     300             :                          params,
     301         204 :                          list_length(scanstate->args),
     302             :                          seed);
     303             : 
     304             :     /* We'll use syncscan if there's no NextSampleBlock function */
     305         176 :     allow_sync = (tsm->NextSampleBlock == NULL);
     306             : 
     307             :     /* Now we can create or reset the HeapScanDesc */
     308         176 :     if (scanstate->ss.ss_currentScanDesc == NULL)
     309             :     {
     310         146 :         scanstate->ss.ss_currentScanDesc =
     311         146 :             table_beginscan_sampling(scanstate->ss.ss_currentRelation,
     312         146 :                                      scanstate->ss.ps.state->es_snapshot,
     313             :                                      0, NULL,
     314         146 :                                      scanstate->use_bulkread,
     315             :                                      allow_sync,
     316         146 :                                      scanstate->use_pagemode);
     317             :     }
     318             :     else
     319             :     {
     320          30 :         table_rescan_set_params(scanstate->ss.ss_currentScanDesc, NULL,
     321          30 :                                 scanstate->use_bulkread,
     322             :                                 allow_sync,
     323          30 :                                 scanstate->use_pagemode);
     324             :     }
     325             : 
     326         176 :     pfree(params);
     327             : 
     328             :     /* And we're initialized. */
     329         176 :     scanstate->begun = true;
     330         176 : }
     331             : 
     332             : /*
     333             :  * Get next tuple from TABLESAMPLE method.
     334             :  */
     335             : static TupleTableSlot *
     336      241330 : tablesample_getnext(SampleScanState *scanstate)
     337             : {
     338      241330 :     TableScanDesc scan = scanstate->ss.ss_currentScanDesc;
     339      241330 :     TupleTableSlot *slot = scanstate->ss.ss_ScanTupleSlot;
     340             : 
     341      241330 :     ExecClearTuple(slot);
     342             : 
     343      241330 :     if (scanstate->done)
     344           0 :         return NULL;
     345             : 
     346             :     for (;;)
     347             :     {
     348      254062 :         if (!scanstate->haveblock)
     349             :         {
     350       12908 :             if (!table_scan_sample_next_block(scan, scanstate))
     351             :             {
     352         170 :                 scanstate->haveblock = false;
     353         170 :                 scanstate->done = true;
     354             : 
     355             :                 /* exhausted relation */
     356         170 :                 return NULL;
     357             :             }
     358             : 
     359       12738 :             scanstate->haveblock = true;
     360             :         }
     361             : 
     362      253892 :         if (!table_scan_sample_next_tuple(scan, scanstate, slot))
     363             :         {
     364             :             /*
     365             :              * If we get here, it means we've exhausted the items on this page
     366             :              * and it's time to move to the next.
     367             :              */
     368       12732 :             scanstate->haveblock = false;
     369       12732 :             continue;
     370             :         }
     371             : 
     372             :         /* Found visible tuple, return it. */
     373      241160 :         break;
     374             :     }
     375             : 
     376      241160 :     scanstate->donetuples++;
     377             : 
     378      241160 :     return slot;
     379             : }

Generated by: LCOV version 1.14