58.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);

Возвращает номер блока следующей сканируемой страницы либо InvalidBlockNumber, если страниц для сканирования не осталось.

Эту функцию можно опустить (присвоить её указателю NULL), в этом случае код ядра произведёт последовательное сканирование всего отношения. Такое сканирование может быть синхронизированным, так что метод выборки не должен полагать, что страницы отношения каждый раз просматриваются в одном и том же порядке.

OffsetNumber
NextSampleTuple (SampleScanState *node,
                 BlockNumber blockno,
                 OffsetNumber maxoffset);

Возвращает номер смещения следующего кортежа, выбираемого с указанной страницы, либо InvalidOffsetNumber, если кортежей для выборки не осталось. В maxoffset задаётся максимальный номер смещения, допустимый на этой странице.

Примечание

NextSampleTuple не говорит явно, для каких из номеров смещений в диапазоне 1 .. maxoffset действительно содержатся актуальные кортежи. Это обычно не проблема, так как код ядра игнорирует запросы на выборку несуществующих или невидимых кортежей; это не должно приводить к отклонениям в выборке. Однако при необходимости функция может проверить node->ss.ss_currentScanDesc->rs_vistuples[] и понять, какие кортежи актуальны и видимы. (Для этого требуется, чтобы признак node->use_pagemode равнялся true.)

Примечание

Функция NextSampleTuple не должна полагать, что в blockno будет получен тот же номер страницы, что был выдан при последнем вызове NextSampleBlock. Этот номер определённо был выдан при каком-то предыдущем вызове NextSampleBlock, но код ядра может вызывать NextSampleBlock перед тем, как собственно сканировать страницы, для поддержки упреждающего чтения. Однако можно рассчитывать на то, что как только начнётся выборка кортежей с одной данной страницы, все последующие вызовы NextSampleTuple будут обращаться к этой странице, пока не будет возвращено значение InvalidOffsetNumber.

void
EndSampleScan (SampleScanState *node);

Завершает сканирование и освобождает ресурсы. Обычно при этом не нужно освобождать память, выделенную через palloc, но все видимые извне ресурсы должны быть очищены. Эту функцию чаще всего можно опустить (присвоить её указателю NULL), если таких ресурсов нет.