58.1. Sampling Method Support Functions
The TSM handler function returns a palloc'd
TsmRoutine
struct
containing pointers to the support functions described below. Most of
the functions are required, but some are optional, and those pointers can
be NULL.
void SampleScanGetSampleSize (PlannerInfo *root, RelOptInfo *baserel, List *paramexprs, BlockNumber *pages, double *tuples);
This function is called during planning. It must estimate the number of
relation pages that will be read during a sample scan, and the number of
tuples that will be selected by the scan. (For example, these might be
determined by estimating the sampling fraction, and then multiplying
the
baserel->pages
and
baserel->tuples
numbers by that, being sure to round the results to integral values.)
The
paramexprs
list holds the expression(s) that are
parameters to the
TABLESAMPLE
clause. It is recommended to
use
estimate_expression_value()
to try to reduce these
expressions to constants, if their values are needed for estimation
purposes; but the function must provide size estimates even if they cannot
be reduced, and it should not fail even if the values appear invalid
(remember that they're only estimates of what the run-time values will be).
The
pages
and
tuples
parameters are outputs.
void InitSampleScan (SampleScanState *node, int eflags);
Initialize for execution of a SampleScan plan node.
This is called during executor startup.
It should perform any initialization needed before processing can start.
The
SampleScanState
node has already been created, but
its
tsm_state
field is NULL.
The
InitSampleScan
function can palloc whatever internal
state data is needed by the sampling method, and store a pointer to
it in
node->tsm_state
.
Information about the table to scan is accessible through other fields
of the
SampleScanState
node (but note that the
node->ss.ss_currentScanDesc
scan descriptor is not set
up yet).
eflags
contains flag bits describing the executor's
operating mode for this plan node.
When
(eflags & EXEC_FLAG_EXPLAIN_ONLY)
is true,
the scan will not actually be performed, so this function should only do
the minimum required to make the node state valid for
EXPLAIN
and
EndSampleScan
.
This function can be omitted (set the pointer to NULL), in which case
BeginSampleScan
must perform all initialization needed
by the sampling method.
void BeginSampleScan (SampleScanState *node, Datum *params, int nparams, uint32 seed);
Begin execution of a sampling scan.
This is called just before the first attempt to fetch a tuple, and
may be called again if the scan needs to be restarted.
Information about the table to scan is accessible through fields
of the
SampleScanState
node (but note that the
node->ss.ss_currentScanDesc
scan descriptor is not set
up yet).
The
params
array, of length
nparams
, contains the
values of the parameters supplied in the
TABLESAMPLE
clause.
These will have the number and types specified in the sampling
method's
parameterTypes
list, and have been checked
to not be null.
seed
contains a seed to use for any random numbers generated
within the sampling method; it is either a hash derived from the
REPEATABLE
value if one was given, or the result
of
random()
if not.
This function may adjust the fields
node->use_bulkread
and
node->use_pagemode
.
If
node->use_bulkread
is
true
, which it is by
default, the scan will use a buffer access strategy that encourages
recycling buffers after use. It might be reasonable to set this
to
false
if the scan will visit only a small fraction of the
table's pages.
If
node->use_pagemode
is
true
, which it is by
default, the scan will perform visibility checking in a single pass for
all tuples on each visited page. It might be reasonable to set this
to
false
if the scan will select only a small fraction of the
tuples on each visited page. That will result in fewer tuple visibility
checks being performed, though each one will be more expensive because it
will require more locking.
If the sampling method is
marked
repeatable_across_scans
, it must be able to
select the same set of tuples during a rescan as it did originally, that is
a fresh call of
BeginSampleScan
must lead to selecting the
same tuples as before (if the
TABLESAMPLE
parameters
and seed don't change).
BlockNumber NextSampleBlock (SampleScanState *node);
Returns the block number of the next page to be scanned, or
InvalidBlockNumber
if no pages remain to be scanned.
This function can be omitted (set the pointer to NULL), in which case the core code will perform a sequential scan of the entire relation. Such a scan can use synchronized scanning, so that the sampling method cannot assume that the relation pages are visited in the same order on each scan.
OffsetNumber NextSampleTuple (SampleScanState *node, BlockNumber blockno, OffsetNumber maxoffset);
Returns the offset number of the next tuple to be sampled on the
specified page, or
InvalidOffsetNumber
if no tuples remain to
be sampled.
maxoffset
is the largest offset number in use
on the page.
Note
NextSampleTuple
is not explicitly told which of the offset
numbers in the range
1 .. maxoffset
actually contain valid
tuples. This is not normally a problem since the core code ignores
requests to sample missing or invisible tuples; that should not result in
any bias in the sample. However, if necessary, the function can
examine
node->ss.ss_currentScanDesc->rs_vistuples[]
to identify which tuples are valid and visible. (This
requires
node->use_pagemode
to be
true
.)
Note
NextSampleTuple
must
not
assume
that
blockno
is the same page number returned by the most
recent
NextSampleBlock
call. It was returned by some
previous
NextSampleBlock
call, but the core code is allowed
to call
NextSampleBlock
in advance of actually scanning
pages, so as to support prefetching. It is OK to assume that once
sampling of a given page begins, successive
NextSampleTuple
calls all refer to the same page until
InvalidOffsetNumber
is
returned.
void EndSampleScan (SampleScanState *node);
End the scan and release resources. It is normally not important to release palloc'd memory, but any externally-visible resources should be cleaned up. This function can be omitted (set the pointer to NULL) in the common case where no such resources exist.