Подпрограммы обертки сторонних данных
Функция обработки FDW возвращает структуру FdwRoutine
palloc, содержащую указатели на функции обратного вызова, описанные ниже. Функции, связанные со сканированием, обязательны, остальное необязательно
Тип структуры FdwRoutine
объявлен в src/include/foreign/fdwapi.h
, там же можно узнать дополнительные подробности.
Подпрограммы FDW для сканирования сторонних таблиц
void
GetForeignRelSize(PlannerInfo *root,
RelOptInfo *baserel,
Oid foreigntableid);
Получение оценок размера отношения для внешней таблицы. Вызывается в начале планирования запроса, который сканирует внешнюю таблицу. root
- глобальная информация планировщика о запросе; baserel
- информация планировщика об этой таблице; foreigntableid
- OID pg_class
внешней таблицы. (foreigntableid
может быть получен из структур данных планировщика, но для экономии усилий он передается явно).
Эта функция должна записать в baserel->rows
ожидаемое число строк, которое будет получено при сканировании таблицы, с учетом фильтра, заданного ограничением выборки. Начальное значение baserel->rows
- это прост о постоянная оценка по умолчанию, которую следует заменить, если это вообще возможно. Функция также может решить обновить baserel->width
, если она может вычислить лучшую оценку средней ширины строки результата. (Начальное значение основано на типах данных столбцов и на значениях средней ширины столбцов, полученных при последнем ANALYZE
). Кроме того, эта функция может обновить baserel->tuples
, если она может вычислить лучшую оценку общего количества строк внешней таблицы. (Начальное значение берется из pg_class.reltuples
, которое представляет собой общее количество строк, наблюдаемое последним ANALYZE
; оно будет равно -1
, если ANALYZE
не проводился для этой внешней таблицы).
Дополнительную информацию см. в разделе «Планирование запросов с оберткой для сторонних данных».
void
GetForeignPaths(PlannerInfo *root,
RelOptInfo *baserel,
Oid foreigntableid);
Формирует возможные пути доступа для сканирования внешней таблицы. Вызывается во время планирования запроса. Параметры те же, что и для GetForeignRelSize
, который уже был вызван.
Эта функция должна сгенерировать по крайней мере один путь доступа (узел ForeignPath
) для сканирования внешней таблицы и должна вызвать add_path
для добавления каждого такого пути в baserel->pathlist
. Для создания узлов ForeignPath
рекомендуется использовать create_foreignscan_path
. Функция может генерировать несколько путей доступа, например, путь, имеющий допустимые ключи пути для представления предварительно отсортированного результата. Каждый путь доступа должен содержать оценки стоимости и может содержать любую FDW-информацию, необходимую для идентификации конкретного метода сканирования.
Дополнительную информацию см. в разделе «Планирование запросов с оберткой для сторонних данных».
ForeignScan *
GetForeignPlan(PlannerInfo *root,
RelOptInfo *baserel,
Oid foreigntableid,
ForeignPath *best_path,
List *tlist,
List *scan_clauses,
Plan *outer_plan);
Создает узел плана ForeignScan
из выбранного пути внешнего доступа. Вызывается в конце планирования запроса. Параметры такие же, как для GetForeignRelSize
, плюс выбранный ForeignPath
(ранее полученный GetForeignPaths
, GetForeignJoinPaths
или GetForeignUpperPaths
), целевой список, который будет выдан узлом плана, ограничительные условия, которые будут выполняться узлом плана, и внешний подплан ForeignScan
, который используется для перепроверки, выполняемой RecheckForeignScan
. (Если путь предназначен для соединения, а не для базового отношения, foreigntableid
будет InvalidOid
).
Эта функция должна создать и вернуть узел плана ForeignScan
; рекомендуется использовать make_foreignscan
для создания узла ForeignScan
.
Дополнительную информацию см. в разделе «Планирование запросов с оберткой для сторонних данных».
void
BeginForeignScan(ForeignScanState *node,
int eflags);
Начинает выполнение внешнего сканирования. Вызывается во время запуска исполнителя. Он должен выполнить любую инициализацию, необходимую для начала сканирования, но не начинать выполнение самого сканирования (это должно произойти при первом вызове IterateForeignScan
). Узел ForeignScanState
уже создан, но его поле fdw_state
все еще NULL
. Информация о сканируемой таблице доступна через узел ForeignScanState
(в частности, из базового узла плана ForeignScan
, который содержит любую FDW-приватную информацию, предоставленную GetForeignPlan
). eflags
содержит биты флагов, описывающие режим работы исполнителя для данного узла плана.
Обратите внимание, что при значении (eflags & EXEC_FLAG_EXPLAIN_ONLY
) эта функция не должна выполнять никаких видимых извне действий; она должна делать только минимум, необходимый для того, чтобы состояние узла было допустимым для ExplainForeignScan
и EndForeignScan
.
TupleTableSlot *
IterateForeignScan(ForeignScanState *node);
Получает одну строку из внешнего источника, возвращая ее в слот таблицы кортежей (для этого должен использоваться ScanTupleSlot
узла). Возвращает NULL
, если больше нет доступных строк. Инфраструктура слота таблицы кортежей позволяет возвращать либо физический, либо виртуальный кортеж; в большинстве случаев последний вариант предпочтительнее с точки зрения производительности. Обратите внимание, что эта функция вызывается в короткоживущем контексте памяти, который будет сбрасываться между вызовами. Создайте контекст памяти в BeginForeignScan
, если нужно более долговечное хранилище, или используйте es_query_cxt
из EState
узла.
Возвращаемые строки должны соответствовать целевому списку fdw_scan_tlist
, если он был предоставлен, в противном случае они должны соответствовать типу строк сканируемой внешней таблицы. Если хотите оптимизировать отбор столбцов, которые не нужны, необходимо вставить нули в позиции этих столбцов или сгенерировать список fdw_scan_tlist
, в котором эти столбцы опущены.
Обратите внимание, что исполнителю PostgreSQL не важно, нарушают ли возвращаемые строки какие-либо ограничения, определенные для внешней таблицы, но планировщику это важно, и он может неправильно оптимизировать запросы, если во внешней таблице будут видны строки, не удовлетворяющие объявленным ограничениям. Если ограничение нарушается, когда пользователь объявил, что оно должно быть истинным, может быть уместно выдать ошибку (так же, как в случае несоответствия типов данных).
void
ReScanForeignScan(ForeignScanState *node);
Перезапускает сканирование с самого начала. Обратите внимание, что любые параметры, от которых зависит сканирование, могут изменить значение, поэтому новое сканирование не обязательно вернет точно такие же строки.
void
EndForeignScan(ForeignScanState *node);
Завершает сканирование и освобождает ресурсы. Как правило, освобождение памяти не имеет большого значения, но, например, открытые файлы и соедин ения с удаленными серверами должны быть очищены.
Подпрограммы FDW для сканирования сторонних соединений
Если FDW поддерживает выполнение внешних соединений удаленно (а не путем получения данных из обеих таблиц и выполнения соединения локально), он должен предоставлять эту функцию обратного вызова:
void
GetForeignJoinPaths(PlannerInfo *root,
RelOptInfo *joinrel,
RelOptInfo *outerrel,
RelOptInfo *innerrel,
JoinType jointype,
JoinPathExtraData *extra);
Формирует возможные пути доступа для соединения двух (или более) внешних таблиц, которые принадлежат одному внешнему серверу. Эта необязательная функция вызывается во время планирования запроса. Как и GetForeignPaths
, эта функция должна сгенерировать пути ForeignPath
для предоставленного joinrel
(для их создания используйте create_foreign_join_path
) и вызвать add_path
для добавления этих путей к набору путей, выбранных для join. Но, в отличие от GetForeignPaths
, не обязательно, чтобы эта функция успешно создала хотя бы один путь, поскольку всегда возможны пути с локальным присоединением.
Об ратите внимание, что эта функция будет вызываться многократно для одного и того же отношения join, с различными комбинациями внутренних и внешних отношений; ответственность за минимизацию дублирования лежит на FDW.
Если для соединения выбран путь ForeignPath
, он будет представлять весь процесс соединения; пути, созданные для таблиц компонентов и дочерних соединений, не будут использоваться. Последующая обработка пути присоединения происходит так же, как и для пути, сканирующего одну внешнюю таблицу. Одно отличие заключается в том, что scanrelid
результирующего узла плана ForeignScan должен быть установлен в ноль, поскольку не существует единственного отношения, которое он представляет; вместо этого поле fs_relids
узла ForeignScan
представляет набор отношений, которые были объединены. (Последнее поле устанавливается автоматически кодом основного планировщика, и его не нужно заполнять в FDW). Еще одно отличие заключается в том, что, поскольку список столбцов для удаленного соединения не может быть найден из системных каталогов, FDW должен заполнить fdw_scan_tlist
соответствующим списком узлов TargetEntry
, представляющим набор столбцов, которые он будет предоставлять во время выполнения в возвращаемых кортежах.
Дополнительную информацию см. в разделе «Планирование запросов с оберткой для сторонних данных»
Подпрограммы FDW для планирования обработки после сканирования/присоединения
Если FDW поддерживает выполнение удаленной обработки после сканирования/соединения, например, удаленное агрегирование, он должен предоставить эту функцию обратного вызова:
void
GetForeignUpperPaths(PlannerInfo *root,
UpperRelationKind stage,
RelOptInfo *input_rel,
RelOptInfo *output_rel,
void *extra);
Формирует возможные пути доступа для обработки верхнего отношения, которым планировщик называет всю обработку запроса после сканирования/соединения, такую как агрегирование, оконные функции, сортировка и обновление таблиц. Эта необязательная функция вызывается во время планирования запросов. В настоящее время она вызывается только в том случае, если все базовые отношения, участвующие в запросе, принадлежат к одному и тому же FDW. Эта функция должна генерировать пути ForeignPath
для любой обработки после сканирования/соединения, которую FDW умеет выполнять удаленно (используйте create_foreign_upper_path
для их создания), и вызвать add_path
для добавления этих путей к указанному верхнему отношению. Как и в случае с GetForeignJoinPaths
, не обязательно, чтобы эта функция успешно создавала какие-либо пути, поскольку всегда возможны пути, включающие локальную обработку.
Параметр stage определяет, какой этап постсканирования/соединения рассматривается в данный момент. output_rel
- это верхнее отношение, которое должно получать пути, представляющие вычисления этого этапа, а input_rel
- это отношение, представляющее вход для этого этапа. Дополнительный параметр предоставляет дополнительные сведения, в настоящее время он задается только для UPPERREL_PARTIAL_GROUP_AGG
или UPPERREL_GROUP_AGG
, в этом случае он указывает на структуру GroupPathExtraData
; или для UPPERREL_FINAL
, в этом случае он указывает на структуру FinalPathExtraData
. (Обратите внимание, что пути ForeignPath
, доба вленные в output_rel
, обычно не имеют прямой зависимости от путей input_rel
, так как их обработка, как ожидается, будет выполняться извне. Однако изучение путей, ранее сгенерированных для предыдущего шага обработки, может быть полезным, чтобы избежать лишней работы по планированию).
Дополнительную информацию см. в разделе «Планирование запросов с оберткой для сторонних данных»
Подпрограммы FDW для обновления сторонних таблиц
Если FDW поддерживает сторонние таблицы с возможностью записи, он должен предоставлять некоторые или все из следующих функций обратного вызова в зависимости от потребностей и возможностей FDW:
void
AddForeignUpdateTargets(PlannerInfo *root,
Index rtindex,
RangeTblEntry *target_rte,
Relation target_relation);
Операции UPDATE
и DELETE
выполняются над строками, ранее извлеченными функциями сканирования таблицы. FDW может потребоваться дополнительная информация, такая как идентификатор строки или значения столбцов первичных ключей, чтобы гарантировать, что она может определить точную строку для обновления или удаления. Для этого данная функция может добавлять дополнительные скрытые или «нежелательные» целевые столбцы в список столбцов, которые должны быть получены из внешней таблицы при UPDATE
или DELETE
.
Для этого создайте Var
, представляющий нужное дополнительное значение, и передайте его в add_row_identity_var
вместе с именем для столбца с мусором. (Можно сделать это несколько раз, если требуется несколько колонок). Необходимо выбрать отдельное имя колонки для каждой нужной Var
, за исключением того, что Vars
, идентичные за исключением поля varno
, могут и должны иметь общее имя колонки. В основной системе используются имена столбцов tableoid
для столбца tableoid
таблицы, ctid
или ctidN
для ctid
, wholerow
для целой строки Var
, помеченной vartype = RECORD
, и wholerowN
для целой строки Var
с vartype
, равным объявленному типу строки таблицы. По возможности используйте эти имена повторно (планировщик будет объединять дублирующие запросы для одинаковых нежелательных столбцов). Если нужен другой тип нежелательного столбца, кроме этих, возможно, будет разумно выбрать имя с префиксом вашего расширения, чтобы избежать конфликтов с другими FDW.
Если указатель AddForeignUpdateTargets
имеет значение NULL, дополнительные целевые выражения не добавляются. (Это сделает невозможной реализацию операций DELETE
, хотя UPDATE
все еще может быть осуществима, если FDW полагается на неизменный первичный ключ для идентификации строк).
List *
PlanForeignModify(PlannerInfo *root,
ModifyTable *plan,
Index resultRelation,
int subplan_index);
Выполняет любые дополнительные действия планирования, необходимые для вставки, обновления или удаления внешней таблицы. Эта функция генерирует FDW-частную информацию, которая будет прикреплена к узлу плана ModifyTable
, выполняющему действие обновления. Эта частная информация должна иметь форму списка и будет передана BeginForeignModify
на этапе выполнения.
[root]()
- это глобальная информация планировщика о запросе. plan
- это узел плана ModifyTable
, который является полным, за исключением поля fdwPrivLists
. resultRelation
идентифицирует целевую таблицу foreign
по ее индексу таблицы диапазонов. subplan_index
идентифицирует, какая цель узла плана ModifyTable
является данной, считая от нуля; используйте это, если необходимо индексировать подструктуры узла плана для каждой целевой связи.
Дополнительную информацию см. в разделе «Планирование запросов с оберткой для сторонних данных»
Если указатель PlanForeignModify
установлен в NULL, никаких дополнительных действий во время планирования не произв одится, и список fdw_private
, переданный в BeginForeignModify
, будет NIL.
void
BeginForeignModify(ModifyTableState *mtstate,
ResultRelInfo *rinfo,
List *fdw_private,
int subplan_index,
int eflags);
Начинает выполнение операции изменения данных в сторонней таблице. Эта процедура вызывается во время запуска ис полнителя. Она должна выполнить любую инициализацию, необходимую до фактической модификации таблицы. Впоследствии для вставки, обновления или удаления кортежа(ов) будут вызваны ExecForeignInsert
/ExecForeignBatchInsert
, ExecForeignUpdate
или ExecForeignDelete
.
mtstate
- общее состояние выполняемого узла плана ModifyTable
; через эту структуру доступны глобальные данные о плане и состоянии выполнения. rinfo
- структура ResultRelInfo
, описывающая целевую внешнюю таблицу. (Поле ri_FdwState
в ResultRelInfo
доступно для FDW, чтобы хранить любое частное состояние, необходимое для этой операции). fdw_private
содержит частные данные, сгенерированные PlanForeignModify
, если таковые имеются. subplan_index
определяет, какая цель узла плана ModifyTable
является данной. eflags
содержит биты флага, описывающие режим работы исполнителя для этого узла плана.
Обратите внимание, что при значении (eflags & EXEC_FLAG_EXPLAIN_ONLY
) эта функция не должна выполнять никаких видимых извне действий; она должна делать только минимум, необходимый для того, чтобы состояние узла было допустимым для ExplainForeignModify
и EndForeignModify
.
Если указатель BeginForeignModify
установлен в NULL, то никаких действий при запуске исполнителя не производится.
TupleTableSlot *
ExecForeignInsert(EState *estate,
ResultRelInfo *rinfo,
TupleTableSlot *slot,
TupleTableSlot *planSlot);
Вставляет один кортеж в стороннюю таблицу. estate
- глобальное состояние выполнения запроса. rinfo
- структ ура ResultRelInfo
, описывающая целевую внешнюю таблицу. slot
содержит кортеж, который будет вставлен; он будет соответствовать определению типа строки внешней таблицы. planSlot
содержит кортеж, который был сгенерирован подпланом узла плана ModifyTable
; он отличается от slot тем, что может содержать дополнительные «ненужные» столбцы. (PlanSlot
обычно не представляет особого интереса для случаев INSERT
, но приводится для полноты картины).
Возвращаемое значение - это либо слот, содержащий данные, которые были вставлены (они могут отличаться от предоставленных данных, например, в результате действия триггеров), либо NULL, если ни одна строка не была вставлена (опять же, обычно в результате действия триггеров). Переданный слот может быть использован для этой цели повторно.
Данные в возвращаемом слоте используются только в том случае, если оператор INSERT
имеет предложение RETURNING
или включает представление WITH CHECK OPTION
; или если внешняя таблица имеет триггер AFTER ROW
. Триггерам требуются все столбцы, но FDW может решить оптимизировать отказ от возврата некоторых или все х столбцов в зависимости от содержимого предложения RETURNING
или ограничений WITH CHECK OPTION
. Независимо от этого, некоторые слоты должны быть возвращены, чтобы указать на успех, иначе отчетное количество строк в запросе будет неверным.
Если указатель ExecForeignInsert
установлен в NULL, попытки вставить во внешнюю таблицу будут неудачными с сообщением об ошибке.
Обратите внимание, что эта функция также вызывается при вставке маршрутизируемых кортежей в раздел внешней таблицы или при выполнении COPY FROM
для внешней таблицы, и в этом случае она вызывается иначе, чем в случае INSERT
. См. функции обратного вызова, описанные ниже, которые позволяют FDW поддерживать это.
TupleTableSlot **
ExecForeignBatchInsert(EState *estate,
ResultRelInfo *rinfo,
TupleTableSlot **slots,
TupleTableSlot **planSlots,
int *numSlots);
Массовая вставка нескольких кортежей в стороннюю таблицу. Параметры те же, что и для ExecForeignInsert
, за исключением того, что слоты и planSlots
содержат несколько кортежей, а *numSlots
указывает количество кортежей в этих массивах.
Возвращаемое значение - массив слотов, содержащих данные, которые были вставлены (они могут отличаться от предоставленных данных, например, в результате действия триггера). Переданные слоты могут быть повторно использованы для этой цели. Количество успешно вставленных кортежей возвращается в *numSlots
.
Данные в возвращаемом слоте используются только в том случае, если оператор INSERT
включает представление WITH CHECK OPTION
; или если внешняя таблица имеет триггер AFTER ROW
. Триггерам требуются все столбцы, но FDW может решить оптимизировать возврат некоторых или всех столбцов в зависимости от содержимого ограничений WITH CHECK OPTION
.
Если указатель ExecForeignBatchInsert
или GetForeignModifyBatchSize
установлен в NULL, то при попытке вставки в иностранную таблицу будет использоваться ExecForeignInsert
. Эта функция не используется, если INSERT
имеет предложение RETURNING
.
Обратите внимание, что эта функция также вызывается при вставке маршрутизируемых кортежей в раздел внешней таблицы. См. функции обратного вызова, описанные ниже, которые позволяют FDW поддерживать это.
int
GetForeignModifyBatchSize(ResultRelInfo *rinfo);