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