Методы извлечения выборки из таблицы

В дополнение к методам BERNOULLI и SYSTEM, требуемыми стандартом SQL, реализация предложения TABLESAMPLE в QHB поддерживает пользовательские методы извлечения выборки из таблицы. Метод извлечения выборки определяет, какие строки таблицы будут выбраны, при использовании предложения TABLESAMPLE.

На уровне SQL, метод извлечения выборки представлен единственной функцией (как правило, реализованной на C или Rust) с сигнатурой

имя_метода(internal) RETURNS tsm_handler

Имя функции совпадает с именем метода, используемым в предложении TABLESAMPLE. Аргумент internal является фиктивным (всегда принимающим нулевое значение) — он предотвращает вызов этой функции непосредственно из команды SQL. Результатом функции должна быть аллоцированная palloc структура, имеющая тип TsmRoutine, который содержит указатели на опорные функции для метода извлечения выборки. Опорные функции описаны в разделе Опорные функции метода извлечения выборки.

Кроме указателей на функций, структура TsmRoutine должна содержать следующие поля:

  • List *parameterTypes

    Это список содержит OID типов данных параметров, принимаемых предложением TABLESAMPLE при использовании данного метода извлечения выборки. Например, у встроенных методов этот список содержит один элемент со значением FLOAT4OID, представлющий собой процент выборки. Пользовательские методы могут иметь дополнительные или иные параметры.

  • bool repeatable_across_queries

    Значение true означает, что метод извлечения выборки возвращает идентичные выборки в последовательных запросах, если в каждом запросе предоставляются одни и те же параметры и начальное значение REPEATABLE, а содержимое таблицы не изменяется. Если же значение равно false, предложение REPEATABLE не принимается для использования с методом выборки.

  • bool repeatable_across

    Если true, то метод извлечения выборки способен возвращать идентичные выборки в последовательных сканирования внутри одного и того же запроса (при неизменных параметрах, начальном значении и снимке). Если же значение равно false, то планировщик не будет выбирать планы запроса, требующие более одного сканирования семплированной таблицы, так как они может приводить к неконсистентным результатам запроса.

Для дополнительной информации, см. тип структуры TsmRoutine объявленный в src/include/access/tsmapi.h.

Методы извлечения выборки, входящие в стандартный дистрибутив, могут послужить хорошим примером при реализации собственных методов выборки. Код встроенных методов находится в подкаталоге src/backend/access/tablesample дерева исходного кода, а в подкаталоге contrib можно найти методы извлечения выборки расширений.

Опорные функции метода извлечения выборки

Функция обработчика TSM возвращает аллоцированную palloc структуру TsmRoutine, содержащую указатели на опорные функции, описанные ниже. Большинство этих функций являются обязательными, однако некоторые являются опциональными, и соответствующие им указатели могут быть 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);

Начать выполнение сканирования выборки. Это функция вызывается непосредственно перед первой попыткой извлечения кортежа и может быть вызвана снова, если сканирование необходимо перезапустить. Информация о таблице, подлежащей сканированию, доступна через поля структуры node (но обратите внимание, что node->ss.ss_currentScanDesc в этот момент ещё не инициализирован). Массив params, длины nparams, содержит значения параметров переданных предложению TABLESAMPLE. Они будут иметь номер и тип, определённые списком patameterTypes метода извлечения выборки, и будут проверены на неравенство NULL. seed содержит начальное значение для всех псевдослучайных чисел, сгенерированных внутри метода. Оно представляет собой или хеш на основе значения REPEATABLE, или результат вызова random().

Эта функция может менять значения полей node->use_bulkread и node->use_pagemode. Если node->use_pagemode истино, что по-умолчанию так, то сканирование будет использовать стратегию доступа к буферу, которая поощряет переиспользование буферов. Может иметь смысл установить это поле в false, если сканирование будет выбирать лишь малую часть всех кортежей в каждой посещённой таблице. Это приведет к меньшему количеству выполняемых проверок видимости кортежей, хотя каждая из них будет стоить дороже, так как потребует больше блокировок.

Если метод извлечения выборки помечен как repeatable_across_scans, он обязан выбирать один и тот же набор кортежей во время повторного сканирования, как и во время первоначального, то есть вызов BeginSampleScan должен приводить к выбору тех же кортежей, что и раньше (если параметры TABLESPACE и начальное значение не изменились).

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) в обычном случае, когда подобных ресурсов нет.