56.1. Опорные функции метода извлечения выборки #
Функция-обработчик TSM возвращает структуру TsmRoutine
(выделенную вызовом palloc) с указателями на опорные функции, описанные ниже. Большинство этих функций обязательные, но некоторые — нет, и их указатели могут быть равны NULL.
void SampleScanGetSampleSize (PlannerInfo *root, RelOptInfo *baserel, List *paramexprs, BlockNumber *pages, double *tuples);
Эта функция вызывается во время планирования. Она должна рассчитать число страниц отношения, которые будут прочитаны при сканировании выборки, и число кортежей, выбираемых при сканировании. (Например, эти числа можно получить, оценив процент выбираемых данных, а затем умножив baserel->pages
и baserel->tuples
на это значение и округлив результат до целых.) Список paramexprs
содержит выражения, переданные в параметрах предложению TABLESAMPLE
. Если для целей оценивания нужны их значения, рекомендуется воспользоваться estimate_expression_value()
, чтобы попытаться свести эти выражения к константам; но данная функция должна выдавать оценку размера, даже если это не удастся, и не должна выдавать ошибку, даже если считает переданные значения неверными (помните, что это только приблизительные оценки чисел, которые будут получены во время выполнения). Параметры pages
и tuples
являются выходными.
void InitSampleScan (SampleScanState *node, int eflags);
Выполняет инициализацию перед выполнением узла плана SampleScan. Эта функция вызывается при запуске исполнителя. Она должна выполнить все подготовительные действия, необходимые для начала обработки. Узел SampleScanState
уже был создан, но его поле tsm_state
содержит NULL. Функция InitSampleScan
может выделить через palloc область для любых внутренних данных, нужных методу извлечения выборки, и сохранить указатель на неё в node->tsm_state
. Информацию о сканируемой таблице можно получить через другие поля узла SampleScanState
(но заметьте, что дескриптор сканирования node->ss.ss_currentScanDesc
ещё не настроен). Параметр eflags
содержит битовые флаги, описывающие режим работы исполнителя для этого узла плана.
Когда (eflags & EXEC_FLAG_EXPLAIN_ONLY)
не равно нулю, собственно сканирование не будет выполняться, поэтому эта функция должна сделать только то, что необходимо для получения состояния узла, подходящего для EXPLAIN
и EndSampleScan
.
Эту функцию можно опустить (присвоить указателю NULL), тогда вся инициализация, необходимая для метода извлечения выборки, должна иметь место в BeginSampleScan
.
void BeginSampleScan (SampleScanState *node, Datum *params, int nparams, uint32 seed);
Начинает выполнение сканирования выборки. Эта функция вызывается непосредственно перед первой попыткой выбрать кортеж и может вызываться повторно, если потребуется перезапустить сканирование. Информацию о сканируемой таблице можно получить через поля узла SampleScanState
(но заметьте, что дескриптор сканирования node->ss.ss_currentScanDesc
ещё не настроен). Массив params
, длины nparams
, содержит значения параметров, переданных в предложении TABLESAMPLE
. Их количество и типы задаются в списке parameterTypes
метода выборки, и они гарантированно не равны NULL. Параметр seed
содержит значение затравки, которое этот метод должен учитывать при генерации любых случайных чисел; это либо хеш, полученный из значения REPEATABLE
, если оно было передано, либо результат random()
в противном случае.
Эта функция может скорректировать поля node->use_bulkread
и node->use_pagemode
. Если поле node->use_bulkread
равно true
(это значение по умолчанию), при сканировании будет использоваться стратегия доступа к буферу, ориентированная на переработку буферов после использования. Может быть разумным присвоить ему false
, если при сканировании будет просматриваться только небольшой процент страниц. Если поле node->use_pagemode
равно true
(это значение по умолчанию), при сканировании проверка видимости будет выполняться в один проход для всех кортежей на каждой просматриваемой странице. Может иметь смысл присвоить ему false
, если при сканировании выбирается только небольшой процент кортежей на странице. В результате будет выполняться меньше проверок видимости кортежей, хотя каждая проверка будет дороже, так как потребует расширенную блокировку.
Если метод выборки помечен как repeatable_across_scans
, он должен быть способен выбирать при повторном сканировании тот же набор кортежей, что был выбран в первый раз, то есть новый вызов BeginSampleScan
должен приводить к выборке тех же кортежей, что и предыдущий (если параметры TABLESAMPLE
и значение затравки не меняются).
BlockNumber NextSampleBlock (SampleScanState *node, BlockNumber nblocks);
Возвращает номер блока следующей сканируемой страницы либо InvalidBlockNumber
, если страниц для сканирования не осталось.
Эту функцию можно опустить (присвоить её указателю NULL), в этом случае код ядра произведёт последовательное сканирование всего отношения. Такое сканирование может быть синхронизированным, так что метод выборки не должен полагать, что страницы отношения каждый раз просматриваются в одном и том же порядке.
OffsetNumber NextSampleTuple (SampleScanState *node, BlockNumber blockno, OffsetNumber maxoffset);
Возвращает номер смещения следующего кортежа, выбираемого с указанной страницы, либо InvalidOffsetNumber
, если кортежей для выборки не осталось. В maxoffset
задаётся максимальный номер смещения, допустимый на этой странице.
Примечание
NextSampleTuple
не говорит явно, для каких из номеров смещений в диапазоне 1 .. maxoffset
действительно содержатся актуальные кортежи. Это обычно не проблема, так как код ядра игнорирует запросы на выборку несуществующих или невидимых кортежей; это не должно приводить к отклонениям в выборке. Однако при необходимости функция может прочитать в поле node->donetuples
количество кортежей, которые оказались актуальными и видимыми, из числа тех, что она выдала.
Примечание
Функция NextSampleTuple
не должна полагать, что в blockno
будет получен тот же номер страницы, что был выдан при последнем вызове NextSampleBlock
. Этот номер определённо был выдан при каком-то предыдущем вызове NextSampleBlock
, но код ядра может вызывать NextSampleBlock
перед тем, как собственно сканировать страницы, для поддержки упреждающего чтения. Однако можно рассчитывать на то, что как только начнётся выборка кортежей с одной данной страницы, все последующие вызовы NextSampleTuple
будут обращаться к этой странице, пока не будет возвращено значение InvalidOffsetNumber
.
void EndSampleScan (SampleScanState *node);
Завершает сканирование и освобождает ресурсы. Обычно при этом не нужно освобождать память, выделенную через palloc, но все видимые извне ресурсы должны быть очищены. Эту функцию чаще всего можно опустить (присвоить её указателю NULL), если таких ресурсов нет.