Перейти к основному содержимому

Подпрограммы обертки сторонних данных

Функция обработки 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);

Сообщает максимальное количество кортежей, которое может обработать один вызов ExecForeignBatchInsert для указанной внешней таблицы. Исполнитель передает в ExecForeignBatchInsert не более заданного количества кортежей. rinfo - структура ResultRelInfo, описывающая целевую иностранную таблицу. Ожидается, что FDW предоставит опцию иностранного сервера и/или иностранной таблицы, чтобы пользователь мог установить это значение, или какое-то жестко закодированное значение.

Если указатель ExecForeignBatchInsert или GetForeignModifyBatchSize установлен на NULL, при попытке вставки во внешнюю таблицу будет использоваться ExecForeignInsert.

TupleTableSlot *
ExecForeignUpdate(EState *estate,
ResultRelInfo *rinfo,
TupleTableSlot *slot,
TupleTableSlot *planSlot);

Обновление одного кортежа в сторонней таблице. estate - глобальное состояние выполнения запроса. rinfo - структура ResultRelInfo, описывающая целевую внешнюю таблицу. slot содержит новые данные для кортежа; они будут соответствовать определению типа строки во внешней таблице. planSlot содержит кортеж, который был сгенерирован подпланом узла плана ModifyTable. В отличие от slot, этот кортеж содержит только новые значения для столбцов, измененных запросом, поэтому не следует полагаться на номера атрибутов внешней таблицы для индексации в planSlot. Кроме того, planSlot обычно содержит дополнительные «нежелательные» столбцы. В частности, из этого слота будут доступны все нежелательные столбцы, которые были запрошены AddForeignUpdateTargets.

Возвращаемое значение - это либо слот, содержащий строку, которая была фактически обновлена (она может отличаться от предоставленных данных, например, в результате действия триггеров), либо NULL, если ни одна строка не была обновлена (опять же, обычно в результате действия триггеров). Переданный слот можно повторно использовать для этой цели.

Данные в возвращаемом слоте используются только в том случае, если оператор UPDATE имеет предложение RETURNING или включает представление WITH CHECK OPTION; или если внешняя таблица имеет триггер AFTER ROW. Триггерам требуются все столбцы, но FDW может решить оптимизировать отказ от возврата некоторых или всех столбцов в зависимости от содержания предложения RETURNING или ограничений WITH CHECK OPTION. Независимо от этого, некоторые слоты должны быть возвращены, чтобы указать на успех, иначе отчетное количество строк в запросе будет неверным.

Если указатель ExecForeignUpdate установлен в NULL, попытки обновить внешнюю таблицу завершатся неудачей с сообщением об ошибке.

TupleTableSlot *
ExecForeignDelete(EState *estate,
ResultRelInfo *rinfo,
TupleTableSlot *slot,
TupleTableSlot *planSlot);

Удаление одного кортежа из внешней таблицы. estate - глобальное состояние выполнения запроса. rinfo - структура ResultRelInfo, описывающая целевую внешнюю таблицу. slot не содержит ничего полезного при вызове, но может использоваться для хранения возвращаемого кортежа. planSlot содержит кортеж, который был сгенерирован подпланом узла плана ModifyTable; в частности, он будет содержать любые junk-столбцы, которые были запрошены AddForeignUpdateTargets. Нежелательные столбцы должны использоваться для идентификации удаляемого кортежа.

Возвращаемое значение - это либо слот, содержащий удаленный ряд, либо NULL, если ряд не был удален (обычно в результате работы триггеров). Переданный слот может быть использован для хранения возвращаемого кортежа.

Данные в возвращаемом слоте используются только в том случае, если в запросе DELETE есть предложение RETURNING или во внешней таблице установлен триггер AFTER ROW. Триггерам требуются все столбцы, но FDW может решить оптимизировать возврат некоторых или всех столбцов в зависимости от содержания условия RETURNING. Независимо от этого, некоторые слоты должны быть возвращены, чтобы указать на успех, иначе отчетное количество строк в запросе будет неверным.

Если указатель ExecForeignDelete установлен в NULL, попытки удаления из внешней таблицы будут неудачными с сообщением об ошибке.

void
EndForeignModify(EState *estate,
ResultRelInfo *rinfo);

Завершение обновления таблицы и освобождение ресурсов. Как правило, освобождение памяти не имеет большого значения, но, например, открытые файлы и соединения с удаленными серверами должны быть очищены.

Если указатель EndForeignModify установлен в NULL, то при завершении работы исполнителя никаких действий не происходит.

Кортежи, вставленные в таблицу с разделами с помощью INSERT или COPY FROM, направляются в соответствующие секции. Если FDW поддерживает маршрутизируемые разделы внешней таблицы, он также должен предоставлять следующие функции обратного вызова. Эти функции также вызываются при выполнении COPY FROM для внешней таблицы.

void
BeginForeignInsert(ModifyTableState *mtstate,
ResultRelInfo *rinfo);

Начало выполнения операции вставки во внешнюю таблицу. Эта процедура вызывается непосредственно перед вставкой первого кортежа в постороннюю таблицу в обоих случаях, когда она является разделом, выбранным для маршрутизации кортежей, и целью, указанной в команде COPY FROM. Он должен выполнить любую инициализацию, необходимую перед фактической вставкой. Впоследствии ExecForeignInsert или ExecForeignBatchInsert будет вызываться для кортежа(ов), который(ые) должен(ы) быть вставлен(ы) в стороннюю таблицу.

mtstate - общее состояние выполняемого узла плана ModifyTable; через эту структуру доступны глобальные данные о плане и состоянии выполнения. rinfo - структура ResultRelInfo описывающий целевую внешнюю таблицу. (Поле ri_FdwState в ResultRelInfo доступно для FDW, чтобы хранить любое частное состояние, необходимое ему для этой операции).

Когда эта команда вызывается командой COPY FROM, связанные с планом глобальные данные в mtstate не предоставляются, а параметр planSlot команды ExecForeignInsert, вызываемой впоследствии для каждого вставленного кортежа, равен NULL, независимо от того, является ли внешняя таблица разделом, выбранным для маршрутизации кортежей, или целью, указанной в команде.

Если указатель BeginForeignInsert установлен в NULL, то инициализация не выполняется. Обратите внимание, что если FDW не поддерживает маршрутизируемые разделы внешних таблиц и/или выполнение COPY FROM на внешних таблицах, эта функция или вызываемые впоследствии ExecForeignInsert/ExecForeignBatchInsert должны при необходимости выдавать ошибку.

void
EndForeignInsert(EState *estate,
ResultRelInfo *rinfo);

Завершение операции вставки и освобождение ресурсов. Как правило, освобождение palloc-памяти не имеет большого значения, но, например, открытые файлы и соединения с удаленными серверами должны быть очищены.

Если указатель EndForeignInsert установлен в NULL, то никаких действий при завершении не предпринимается.

int
IsForeignRelUpdatable(Relation rel);

Сообщает, какие операции обновления поддерживает указанная внешняя таблица. Возвращаемое значение должно представлять собой битовую маску номеров событий правил, указывающих, какие операции поддерживаются внешней таблицей, с использованием перечисления CmdType; то есть (1 << CMD_UPDATE) = 4 для UPDATE, (1 << CMD_INSERT)= 8 для INSERT, и (1 << CMD_DELETE) = 16 для DELETE.

Если указатель IsForeignRelUpdatable установлен в NULL, предполагается, что сторонние таблицы позволяют добавлять, изменять и удалять строки, если FDW обеспечивает ExecForeignInsert, ExecForeignUpdate или ExecForeignDelete соответственно. Эта функция нужна только в том случае, если FDW поддерживает некоторые таблицы, которые можно обновлять, а некоторые - нет. (Даже в этом случае вместо проверки в этой функции допускается выброс ошибки в процедуре выполнения. Однако эта функция используется для определения обновляемости для отображения в представлениях information_schema).

Некоторые вставки, обновления и удаления в сторонних таблицах можно оптимизировать, реализовав альтернативный набор интерфейсов. Обычные интерфейсы для вставок, обновлений и удалений получают строки с удаленного сервера и затем изменяют эти строки по одной. В некоторых случаях такой подход необходим, но он может быть неэффективным. Если внешний сервер может определить, какие строки должны быть изменены, не получая их, и если нет локальных структур, которые могли бы повлиять на операцию (локальные триггеры на уровне строк, хранимые генерируемые столбцы или ограничения WITH CHECK OPTION из родительских представлений), то можно организовать все так, чтобы вся операция выполнялась на удаленном сервере. Описанные ниже интерфейсы позволяют это сделать.

bool
PlanDirectModify(PlannerInfo *root,
ModifyTable *plan,
Index resultRelation,
int subplan_index);

Определяет, безопасно ли выполнять прямую модификацию на удаленном сервере. Если да, то возвращает true после выполнения необходимых для этого плановых действий. В противном случае возвращает false. Эта необязательная функция вызывается во время планирования запроса. Если результат этой функции положительный, то вместо нее на этапе выполнения будут вызваны BeginDirectModify, IterateDirectModify и EndDirectModify. Иначе, модификация таблицы будет выполнена с помощью функций обновления таблицы, описанных выше. Парамеры те же, что и для PlanForeignModify.

Чтобы выполнить прямую модификацию на удаленном сервере, эта функция должна переписать целевой подплан с узлом плана ForeignScan, который выполняет прямую модификацию на удаленном сервере. Поля operation и resultRelation в ForeignScan должны быть установлены соответствующим образом. В operation должно быть установлено соответствующее значение перечисления CmdType, соответствующее типу оператора (то есть CMD_UPDATE для UPDATE, CMD_INSERT для INSERT и CMD_DELETE для DELETE), а аргумент resultRelation должен быть скопирован в поле resultRelation.

Дополнительную информацию см. в разделе «Планирование запросов с оберткой для сторонних данных»

Если указатель PlanDirectModify установлен в NULL, попытки выполнить прямую модификацию на удаленном сервере не предпринимаются.

void
BeginDirectModify(ForeignScanState *node,
int eflags);

Подготовка к выполнению прямой модификации на удаленном сервере. Вызывается во время запуска исполнителя. Он должен выполнить любую инициализацию, необходимую перед прямой модификацией (она должна быть выполнена при первом вызове IterateDirectModify). Узел ForeignScanState уже создан, но его поле fdw_state все еще NULL. Информация о модифицируемой таблице доступна через узел ForeignScanState (в частности, из нижележащего узла плана ForeignScan, который содержит любую FDW-приватную информацию, предоставленную PlanDirectModify). eflags содержит биты флага, описывающие режим работы исполнителя для данного узла плана.

Обратите внимание, что при значении (eflags & EXEC_FLAG_EXPLAIN_ONLY) эта функция не должна выполнять никаких видимых извне действий; она должна делать только минимум, необходимый для того, чтобы состояние узла было действительным для ExplainDirectModify и EndDirectModify.

Если указатель BeginDirectModify установлен в NULL, попытки выполнить прямую модификацию на удаленном сервере не предпринимаются.

TupleTableSlot *
IterateDirectModify(ForeignScanState *node);

Если запрос INSERT, UPDATE или DELETE не содержит условия RETURNING, просто верните NULL после прямой модификации на удаленном сервере. Если в запросе есть условие, получите один результат, содержащий данные, необходимые для вычисления RETURNING, и верните его в слот таблицы кортежей (для этого следует использовать ScanTupleSlot узла). Данные, которые были фактически вставлены, обновлены или удалены, должны храниться в node->resultRelInfo->ri_projectReturning->pi_exprContext->ecxt_scantuple. Возвращает NULL, если больше нет доступных строк. Обратите внимание, что эта функция вызывается в недолговечном контексте памяти, который будет сбрасываться между вызовами. Создайте контекст памяти в BeginDirectModify, если нужно более долговечное хранилище, или используйте es_query_cxt из EState-узла.

Возвращаемые строки должны соответствовать целевому списку fdw_scan_tlist, если он был предоставлен, в противном случае они должны соответствовать типу строк обновляемой внешней таблицы. Если необходимо оптимизировать отбор столбцов, которые не нужны для вычисления RETURNING, необходимо вставить нули в позиции этих столбцов или сгенерировать список fdw_scan_tlist, в котором эти столбцы опущены.

Независимо от того, содержит ли запрос условие или нет, отчетное количество строк запроса должно быть увеличено самим FDW. Если запрос не содержит оговорку, FDW должен также увеличить количество строк для узла ForeignScanState в случае EXPLAIN ANALYZE.

Если указатель IterateDirectModify установлен в NULL, попытки выполнить прямую модификацию на удаленном сервере не предпринимаются.

void
EndDirectModify(ForeignScanState *node);

Очистка после прямой модификации на удаленном сервере. Как правило, освобождение памяти palloc не имеет большого значения, но, например, открытые файлы и соединения с удаленным сервером должны быть очищены.

Если указатель EndDirectModify установлен в NULL, попытки выполнить прямую модификацию на удаленном сервере не предпринимаются.

Подпрограммы FDW для TRUNCATE

void
ExecForeignTruncate(List *rels,
DropBehavior behavior,
bool restart_seqs);

Опустошает сторонние таблицы. Эта функция вызывается при выполнении TRUNCATE для внешней таблицы. rels - это список структур данных Relation сторонних таблиц, которые необходимо опустошить.

Значением behavior может быть либо DROP_RESTRICT, либо DROP_CASCADE, в зависимости от того, какое указание (RESTRICT или CASCADE, соответственно) присутствовало в исходной команде TRUNCATE.

Если restart_seqs равен true, то первоначальная команда TRUNCATE запрашивала поведение RESTART IDENTITY, в противном случае было запрошено поведение CONTINUE IDENTITY.

Обратите внимание, что параметры ONLY, указанные в исходной команде TRUNCATE, не передаются в ExecForeignTruncate. Такое поведение аналогично функциям обратного вызова SELECT, UPDATE и DELETE для внешней таблицы.

ExecForeignTruncate вызывается один раз для каждого стороннего сервера, для которого должны быть усечены сторонние таблицы. Это означает, что все сторонние таблицы, включенные в rels, должны принадлежать одному и тому же серверу.

Если указатель ExecForeignTruncate установлен в NULL, попытки усечения внешних таблиц будут неудачными с сообщением об ошибке.

Подпрограммы FDW для блокировки строк

Если FDW хочет поддерживать позднюю блокировку строк (как описано в разделе «Блокировка строк в обертках сторонних данных»), он должен предоставить следующие функции обратного вызова:

RowMarkType
GetForeignRowMarkType(RangeTblEntry *rte,
LockClauseStrength strength);

Сообщает, какой вариант маркировки строк следует использовать для внешней таблицы. rte - узел RangeTblEntry для таблицы, а strength описывает силу блокировки, запрашиваемую соответствующим предложением FOR UPDATE/SHARE, если таковое имеется. Результат должен быть членом перечислительного типа RowMarkType.

Эта функция вызывается во время планирования запроса для каждой внешней таблицы, которая появляется в запросе UPDATE, DELETE или SELECT FOR UPDATE/SHARE и не является целью UPDATE или DELETE.

Если указатель GetForeignRowMarkType установлен в NULL, всегда используется параметр ROW_MARK_COPY. (Это подразумевает, что RefetchForeignRow никогда не будет вызван, поэтому его также не нужно предоставлять).

Дополнительные сведения см. в разделе «Блокировка строк в обертках сторонних данных»

void
RefetchForeignRow(EState *estate,
ExecRowMark *erm,
Datum rowid,
TupleTableSlot *slot,
bool *updated);

Повторная выборка одного кортежа из внешней таблицы, после блокировки, если требуется. estate - глобальное состояние выполнения запроса. erm - структура ExecRowMark, описывающая целевую внешнюю таблицу и тип блокировки строки (если есть), которую необходимо получить. rowid идентифицирует кортеж, который необходимо извлечь. slot не содержит ничего полезного при вызове, но может быть использован для хранения возвращаемого кортежа. updated - выходной параметр.

Эта функция должна сохранить кортеж в предоставленный слот или очистить его, если блокировка ряда не была получена. Тип блокировки ряда, который необходимо получить, определяется erm->markType, который является значением, ранее возвращенным GetForeignRowMarkType. (ROW_MARK_REFERENCE означает просто повторную выборку кортежа без приобретения какой-либо блокировки, и ROW_MARK_COPY никогда не будет встречаться этой процедуре).

Кроме того, параметр *updated должен быть установлен в true, если полученная информация является обновленной версией кортежа, а не той, которая была получена ранее. (Если FDW не может быть уверен в этом, рекомендуется всегда возвращать true).

Обратите внимание, что по умолчанию неспособность получить блокировку строки должна приводить к возникновению ошибки; возврат с пустым слотом уместен только в том случае, если в erm->waitPolicy указана опция SKIP LOCKED.

rowid - это значение ctid, ранее считанное для строки, которую нужно повторно извлечь. Хотя значение rowid передается как Datum, в настоящее время оно может быть только tid. Функция API выбрана в надежде, что в будущем можно будет разрешить другие типы данных для идентификаторов рядов.

Если указатель RefetchForeignRow установлен в NULL, попытки повторной выборки строк будут неудачными с сообщением об ошибке.

Дополнительные сведения см. в разделе «Блокировка строк в обертках сторонних данных»

bool
RecheckForeignScan(ForeignScanState *node,
TupleTableSlot *slot);

Перепроверяет, что ранее возвращенный кортеж все еще соответствует соответствующим квалификаторам сканирования и присоединения, и, возможно, предоставляет модифицированную версию кортежа. Для оберток сторонних данных, которые не выполняют join pushdown, обычно удобнее установить это значение в NULL и вместо этого задать fdw_recheck_quals соответствующим образом. Однако, когда внешние соединения сдвигаются вниз, недостаточно повторно применять проверки, относящиеся ко всем базовым таблицам, к кортежу результатов, даже если все необходимые атрибуты присутствуют, потому что несоответствие некоторого квалификатора может привести к тому, что некоторые атрибуты станут NULL, а не к тому, что кортеж не будет возвращен. RecheckForeignScan может перепроверить квалификаторы и вернуть true, если они по-прежнему удовлетворены, и false в противном случае, а также сохранить заменяющий кортеж в предоставленный слот.

Для реализации join pushdown обертка сторонних данных обычно строит альтернативный локальный план join, который используется только для перепроверки; он становится внешним подпланом ForeignScan. Когда требуется перепроверка, этот подплан может быть выполнен, и полученный кортеж может быть сохранен в слоте. Этот план не обязательно должен быть эффективным, поскольку ни одна базовая таблица не будет возвращать более одной строки; например, можно реализовать все соединения в виде вложенных циклов. Функция GetExistingLocalJoinPath может использоваться для поиска в существующих путях подходящего пути локального соединения, который может быть использован в качестве альтернативного плана локального соединения. GetExistingLocalJoinPath ищет непараметрический путь в списке путей указанного отношения присоединения. (Если такой путь не найден, возвращается NULL, в этом случае обертка внешних данных может сама построить локальный путь или не создавать пути доступа для этого соединения).

Подпрограммы FDW для EXPLAIN

void
ExplainForeignScan(ForeignScanState *node,
ExplainState *es);

Печать дополнительного вывода EXPLAIN для сканирования сторонней таблицы. Эта функция может вызывать ExplainPropertyText и связанные с ней функции для добавления полей в вывод EXPLAIN. Поля флагов в es могут быть использованы для определения того, что печатать, а состояние узла ForeignScanState может быть проверено для получения статистики во время выполнения в случае EXPLAIN ANALYZE.

Если указатель ExplainForeignScan установлен в NULL, то никакая дополнительная информация не будет выведена во время EXPLAIN.

void
ExplainForeignModify(ModifyTableState *mtstate,
ResultRelInfo *rinfo,
List *fdw_private,
int subplan_index,
struct ExplainState *es);

Печать дополнительного вывода EXPLAIN для обновления сторонней таблицы. Эта функция может вызывать ExplainPropertyText и связанные с ней функции для добавления полей в вывод EXPLAIN. Поля флагов в es могут быть использованы для определения того, что печатать, а состояние узла ModifyTableState может быть проверено для получения статистики во время выполнения в случае EXPLAIN ANALYZE. Первые четыре аргумента те же, что и для BeginForeignModify.

Если указатель ExplainForeignModify установлен в NULL, то никакая дополнительная информация не выводится во время EXPLAIN.

void
ExplainDirectModify(ForeignScanState *node,
ExplainState *es);

Печать дополнительного вывода EXPLAIN для прямой модификации на удаленном сервере. Эта функция может вызывать ExplainPropertyText и связанные с ней функции для добавления полей в вывод EXPLAIN. Поля флагов в es могут быть использованы для определения того, что печатать, а состояние узла ForeignScanState может быть проверено для получения статистики во время выполнения в случае EXPLAIN ANALYZE.

Если указатель ExplainDirectModify установлен в NULL, то никакая дополнительная информация не выводится во время EXPLAIN.

Подпрограммы FDW для ANALYZE

bool
AnalyzeForeignTable(Relation relation,
AcquireSampleRowsFunc *func,
BlockNumber *totalpages);

Эта функция вызывается при выполнении ANALYZE для внешней таблицы. Если FDW может собирать статистику для этой внешней таблицы, она должна вернуть true и предоставить указатель на функцию, которая соберет выборку строк из таблицы в func, плюс предполагаемый размер таблицы в страницах в totalpages. В противном случае возвращается false.

Если FDW не поддерживает сбор статистики для каких-либо таблиц, параметр AnalyzeForeignTable может быть установлен в NULL.

Если указано, то функция сбора образцов должна иметь сигнатуру.

int
AcquireSampleRowsFunc(Relation relation,
int elevel,
HeapTuple *rows,
int targrows,
double *totalrows,
double *totaldeadrows);

Случайная выборка до targrows строк должна быть собрана из таблицы и сохранена в массиве rows, предоставленном вызывающей стороной. Фактическое количество собранных строк должно быть возвращено. Кроме того, в выходные параметры totalrows и totaldeadrows следует записать оценки общего количества живых и мертвых строк в таблице. (Установите totaldeadrows в ноль, если в FDW нет понятия мертвых строк).

Подпрограммы FDW для IMPORT FOREIGN SCHEMA

List *
ImportForeignSchema(ImportForeignSchemaStmt *stmt, Oid serverOid);

Получение списка команд создания внешней таблицы. Эта функция вызывается при выполнении IMPORT FOREIGN SCHEMA, ей передается дерево разбора для этого оператора, а также OID стороннего сервера, который необходимо использовать. Она должна вернуть список строк C, каждая из которых должна содержать команду CREATE FOREIGN TABLE. Эти строки будут разобраны и выполнены основным сервером.

В структуре ImportForeignSchemaStmt remote_schema - это имя удаленной схемы, из которой должны быть импортированы таблицы. list_type определяет способ фильтрации имен таблиц: FDW_IMPORT_SCHEMA_ALL означает, что все таблицы удаленной схемы должны быть импортированы (в этом случае table_list пуст), FDW_IMPORT_SCHEMA_LIMIT_TO означает включить только таблицы, перечисленные в списке_таблиц, и FDW_IMPORT_SCHEMA_EXCEPT означает исключить таблицы, перечисленные в списке_таблиц. options - список опций, используемых для процесса импорта. Значение опций зависит от FDW. Например, FDW может использовать опцию для определения того, следует ли импортировать атрибуты NOT NULL столбцов. Эти опции не должны иметь ничего общего с теми, которые поддерживаются FDW в качестве опций объектов базы данных.

FDW может игнорировать поле local_schema в ImportForeignSchemaStmt, поскольку основной сервер автоматически вставит это имя в разобранные команды CREATE FOREIGN TABLE.

FDW также не нужно заботиться о реализации фильтрации, заданной list_type и table_list, поскольку основной сервер автоматически пропустит все возвращаемые команды для таблиц, исключенных в соответствии с этими параметрами. Однако часто бывает полезно избежать работы по созданию команд для исключенных таблиц. Функция IsImportableForeignTable() может быть полезна для проверки того, пройдет ли данное имя сторонней таблицы через фильтр.

Если FDW не поддерживает импорт определений таблиц, указатель ImportForeignSchema может быть установлен в NULL.

Подпрограммы FDW для параллельного выполнения

Узел ForeignScan может, по желанию, поддерживать параллельное выполнение. Параллельный ForeignScan будет выполняться в нескольких процессах и должен возвращать каждую строку ровно один раз во всех взаимодействующих процессах. Для этого процессы могут координировать свои действия через динамическую общую память фиксированного размера. Не гарантируется, что эта общая память будет отображена по одному и тому же адресу в каждом процессе, поэтому она не должна содержать указателей. Следующие функции являются необязательными, но большинство из них необходимы для поддержки параллельного выполнения.

bool
IsForeignScanParallelSafe(PlannerInfo *root, RelOptInfo *rel,
RangeTblEntry *rte);

Проверяет, можно ли выполнить сканирование в параллельном рабочем. Эта функция вызывается только тогда, когда планировщик считает, что параллельный план возможен, и должна возвращать true, если сканирование безопасно для выполнения в параллельном рабочем. Обычно это не так, если удаленный источник данных имеет семантику транзакций, если только соединение рабочего с данными не может быть каким-то образом сделано так, чтобы разделять тот же контекст транзакции, что и лидер.

Если эта функция не определена, предполагается, что сканирование должно происходить внутри параллельного лидера. Обратите внимание, что возврат true не означает, что само сканирование может выполняться параллельно, а только то, что оно может быть выполнено в параллельном рабочем. Поэтому может быть полезно определить этот метод, даже если параллельное выполнение не поддерживается.

Size
EstimateDSMForeignScan(ForeignScanState *node, ParallelContext *pcxt);

Оценивает объем динамической общей памяти, который потребуется для параллельной работы. Это значение может быть больше, чем объем, который будет реально использоваться, но не должно быть меньше. Возвращаемое значение - в байтах. Эта функция необязательна, и ее можно пропустить, если она не нужна; но если ее пропустить, то следующие три функции также должны быть пропущены, потому что для использования FDW не будет выделена общая память.

void
InitializeDSMForeignScan(ForeignScanState *node, ParallelContext *pcxt,
void *coordinate);

Инициализирует динамическую разделяемую память, которая потребуется для параллельной работы. coordinate указывает на область общей памяти размером, равным возвращаемому значению EstimateDSMForeignScan. Эта функция необязательна и может быть опущена, если в ней нет необходимости.

void
ReInitializeDSMForeignScan(ForeignScanState *node, ParallelContext *pcxt,
void *coordinate);

Переинициализирует динамическую разделяемую память, необходимую для параллельной работы, перед тем как будет повторно просканирован узел чтения сторонних данных. Эта функция является необязательной и может быть опущена, если в ней нет необходимости. Рекомендуется, чтобы эта функция сбрасывала только общее состояние, а функция ReScanForeignScan сбрасывала только локальное состояние. В настоящее время эта функция будет вызываться перед ReScanForeignScan, но лучше не полагаться на этот порядок.

void
InitializeWorkerForeignScan(ForeignScanState *node, shm_toc *toc,
void *coordinate);

Инициализирует локальное состояние параллельного исполнителя на основе общего состояния, установленного лидером во время InitializeDSMForeignScan. Эта функция является необязательной и может быть опущена, если в ней нет необходимости.

void
ShutdownForeignScan(ForeignScanState *node);

Освобождает ресурсы, когда предполагается, что узел не будет выполнен до конца. Эта функция вызывается не во всех случаях; иногда может вызываться только EndForeignScan. Поскольку сегмент DSM, используемый параллельным запросом, уничтожается сразу после вызова этого обратного вызова, обертки внешних данных, которые хотят предпринять какие-то действия до того, как сегмент DSM исчезнет, должны реализовать этот метод.

Подпрограммы FDW для асинхронного выполнения

Узел ForeignScan может, по желанию, поддерживать асинхронное выполнение, как описано в src/backend/executor/README. Следующие функции являются необязательными, но все они необходимы, если поддерживается асинхронное выполнение.

bool
IsForeignPathAsyncCapable(ForeignPath *path);

Проверяет, может ли данный путь ForeignPath асинхронно сканировать базовое внешнее отношение. Эта функция будет вызываться только в конце планирования запроса, когда данный путь является прямым дочерним путем AppendPath и когда планировщик считает, что асинхронное выполнение улучшает производительность, и должна возвращать true, если данный путь способен асинхронно сканировать внешнее отношение.

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

void
ForeignAsyncRequest(AsyncRequest *areq);

Асинхронно выдает один кортеж из узла ForeignScan. areq - это структура AsyncRequest, описывающая узел ForeignScan и родительский узел Append, который запросил у него кортеж. Эта функция должна сохранить кортеж в слот, указанный areq->result, и установить areq->request_complete в true; или если ему нужно подождать какого-либо события, внешнего по отношению к основному серверу, например, сетевого ввода-вывода, и он не может выдать кортеж немедленно, установите флаг в false и установите areq->callback_pending в true, чтобы узел ForeignScan получил обратный вызов от функций обратного вызова, описанных ниже. Если кортежей больше нет, установите слот в NULL или пустой слот, а флаг areq->request_complete в true. Для установки выходных параметров в areq рекомендуется использовать ExecAsyncRequestDone или ExecAsyncRequestPending.

void
ForeignAsyncConfigureWait(AsyncRequest *areq);

Настраивает событие дескриптора файла, которого хочет дождаться узел ForeignScan. Эта функция будет вызвана только тогда, когда у узла ForeignScan установлен флаг areq->callback_pending, и должна добавить событие в as_eventset родительского узла Append, описываемого areq. Дополнительную информацию смотрите в комментариях к ExecAsyncConfigureWait в src/backend/executor/execAsync.c. Когда произойдет событие дескриптора файла, будет вызван ForeignAsyncNotify.

void
ForeignAsyncNotify(AsyncRequest *areq);

Обрабатывает произошедшее релевантное событие, а затем асинхронно выдает один кортеж из узла ForeignScan. Эта функция должна установить выходные параметры в areq таким же образом, как и ForeignAsyncRequest.

Подпрограммы FDW для перепараметризации путей

List *
ReparameterizeForeignPathByChild(PlannerInfo *root, List *fdw_private,
RelOptInfo *child_rel);

Эта функция вызывается при преобразовании пути, параметризованного самым верхним родителем данного дочернего отношения child_rel, в путь, параметризованный дочерним отношением. Функция используется для репараметризации любых путей или трансляции любых узлов выражений, сохраненных в данном fdw_private структуры ForeignPath. Обратный вызов может использовать reparameterize_path_by_child, adjust_appendrel_attrs или adjust_appendrel_attrs_multilevel по мере необходимости.