Написание обёртки сторонних данных

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

Обёртки сторонних данных, включенные в дистрибутив, являются хорошими примерами для написания собственных обёрток (см. каталог contrib дерева исходного кода). Справочная страница CREATE FOREIGN DATA WRAPPER также содержит некоторые полезные сведения.

Примечание!!!
Стандарт SQL определяет интерфейс для написания обёрток сторонних данных. Однако QHB не реализует этот API, потому что усилия по его размещению в QHB были бы большими, а стандартизованный API все равно не получил широкого распространения.

Функции обёрток сторонних данных

Автор FDW1 должен реализовать функцию обработчика и (опционально) функцию валидатора. Обе функции должны быть написаны на компилируемом языке, таком как C/RUST, с использованием интерфейса версии 1. Дополнительные сведения о соглашениях о вызовах языка C и динамической загрузке см. в разделе Функции на нативном языке.

Функция обработчика просто возвращает структуру указателей функций для функций обратного вызова, которые будут вызваны планировщиком, исполнителем и различными командами обслуживания. Большая часть усилий по написанию FDW1 заключается в реализации этих функций обратного вызова. Функция обработчика должна быть зарегистрирована в QHB как не имеющая аргументов и возвращающая специальный псевдотип fdw_handler. Функции обратного вызова являются простыми функциями C/RUST и не видны или не вызываются на уровне SQL. Функции обратного вызова описаны в разделе Процедуры обратного вызова сторонних данных.

Функция validator отвечает за проверку параметров, указанных в командах CREATE и ALTER для обёртки сторонних данных, а также сторонних серверов, сопоставлений пользователей и сторонних таблиц, использующих обёртку. Функция validator должна быть зарегистрирована как принимающая два аргумента, текстовый массив, содержащий параметры для проверки, и OID, представляющий тип объекта, с которым связаны параметры (в виде OID системного каталога объект будет сохранен либо ForeignDataWrapperRelationId, ForeignServerRelationId, UserMappingRelationId, или ForeignTableRelationId). Если функция валидатора не задана, параметры не проверяются во время создания объекта или во время изменения объекта.

Процедуры обратного вызова сторонних данных

Функция обработчика FDW1 возвращает структуру palloc’D FdwRoutine, содержащая указатели на функции обратного вызова, описанные ниже. Функции, связанные со сканированием, являются обязательными, остальные-необязательными.

Тип структуры FdwRoutine объявлен в src/include/foreign/fdwapi.h, который см. для получения дополнительных сведений.

Процедуры FDW для сканирования сторонних таблиц

void
GetForeignRelSize(PlannerInfo *root,
                  RelOptInfo *baserel,
                  Oid foreigntableid);

Получение оценки размера отношения для сторонней таблицы. Функция вызывается в начале планирования запроса, который сканирует стороннюю таблицу. root - глобальная информация планировщика о запросе; baserel - информация планировщика об этой таблице; и foreigntableid это pg_class OID из сторонней таблицы. (foreigntableid может быть получен из структур данных планировщика, но он передается явно, чтобы сэкономить усилия).

Эта функция должна выставлять baserel->rows в ожидаемое количество строк, возвращаемых сканированием таблицы, с учётом фильтра, заданного ограничением выборки. Начальное значение baserel->rows — некая константа по умолчанию, которую следует заменить, если это вообще возможно. Эта функция также может изменять baserel->width, если есть возможность улучшить оценку ширины строки результата.

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

void
GetForeignPaths(PlannerInfo *root,
                RelOptInfo *baserel,
                Oid foreigntableid);

Создаёт возможные пути доступа для сканирования сторонней таблицы. Вызывается во время планирования запросов. Параметры такие же, как и для GetForeignRelSize (см. выше).

Эта функция должна генерировать по крайней мере один путь доступа (ForeignPath узел) для сканирования сторонней таблицы и должна вызвать add_path, чтобы добавить каждый такой путь к baserel->pathlist. Рекомендуется использовать create_foreignscan_path для того, чтобы построить ForeignPath узлы. Функция может генерировать несколько путей доступа, например, путь, который имеет допустимый pathkeys для представления предварительно отсортированного результата. Каждый путь доступа должен содержать смету расходов и может содержать любую конфиденциальную информацию FDW1, необходимую для идентификации конкретного метода сканирования.

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

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

Эта функция должна создавать и возвращать a ForeignScan узел планирования; рекомендуется использовать его make_foreignscan для того чтобы построить the ForeignScan узел.

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

void
BeginForeignScan(ForeignScanState *node,
                 int eflags);

Начните выполнять внешнее сканирование. Это называется во время запуска исполнителя. Он должен выполнить любую инициализацию, необходимую перед началом сканирования, но не начать выполнение фактического сканирования (это должно быть сделано при первом вызове к IterateForeignScan). То ForeignScanState узел уже был создан, но его fdw_state поле по-прежнему имеет значение NULL. Информация о таблице, подлежащей сканированию, доступна через ForeignScanState узел (в частности, от нижележащего ForeignScan узел плана, который содержит любую FDW1-частную информацию, предоставляемую компанией GetForeignPlan). eflags содержит флаговые биты, описывающие рабочий режим исполнителя для данного узла плана.

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

TupleTableSlot *
IterateForeignScan(ForeignScanState *node);

Извлеките одну строку из внешнего источника, возвращая ее в слот таблицы кортежей (узел ScanTupleSlot следует использовать для этой цели). Возвращайте значение NULL, если больше нет доступных строк. Инфраструктура слотов таблиц кортежей позволяет возвращать либо физический, либо виртуальный Кортеж; в большинстве случаев последний вариант предпочтительнее с точки зрения производительности. Обратите внимание, что это вызывается в контексте кратковременной памяти, который будет сброшен между вызовами. Создайте контекст памяти в BeginForeignScan если вам нужно более долговечное хранение, или используйте es_query_cxt узлы EState.

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

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

void
ReScanForeignScan(ForeignScanState *node);

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

void
EndForeignScan(ForeignScanState *node);

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

Процедуры FDW для сканирования сторонних соединений

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

void
GetForeignJoinPaths(PlannerInfo *root,
                    RelOptInfo *joinrel,
                    RelOptInfo *outerrel,
                    RelOptInfo *innerrel,
                    JoinType jointype,
                    JoinPathExtraData *extra);

Создайте возможные пути доступа для объединения двух (или более) внешних таблиц, принадлежащих одному и тому же внешнему серверу. Эта необязательная функция вызывается во время планирования запроса. Как и в случае с GetForeignPaths, эта функция должна генерировать ForeignPath путь (или пути) для поставляемого устройства joinrel (использовать create_foreign_join_path чтобы построить их), и вызовите add_path чтобы добавить эти пути к набору путей, рассматриваемых для объединения. Но в отличие от него GetForeignPaths, нет необходимости, чтобы эта функция успешно создавала хотя бы один путь, поскольку пути, включающие локальное присоединение, всегда возможны.

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

Если a ForeignPath путь выбирается для соединения, он будет представлять весь процесс соединения; пути, созданные для таблиц компонентов и дочерних соединений, использоваться не будут. Последующая обработка пути соединения выполняется во многом так же, как и для пути сканирования одной внешней таблицы. Одно отличие заключается в том, что scanrelid из полученных результатов: ForeignScan узел плана должен быть установлен в ноль, так как нет ни одного отношения, которое он представляет; вместо этого, узел плана должен быть установлен в ноль. fs_relids область применения: ForeignScan узел представляет собой набор отношений, которые были соединены. (Последнее поле автоматически настраивается кодом основного планировщика и не должно заполняться FDW1). Другое отличие состоит в том, что поскольку список столбцов для удаленного соединения не может быть найден из системных каталогов, FDW1 должен заполнить fdw_scan_tlist с соответствующим перечнем TargetEntry узлы, представляющие набор столбцов, которые он будет предоставлять во время выполнения в кортежах, которые он возвращает.

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

Процедуры FDW для планирования обработки после сканирования / соединения

Если FDW1 поддерживает выполнение удаленной обработки после сканирования/соединения, такой как удаленная агрегация, он должен предоставить эту функцию обратного вызова:

void
GetForeignUpperPaths(PlannerInfo *root,
                     UpperRelationKind stage,
                     RelOptInfo *input_rel,
                     RelOptInfo *output_rel,
                     void *extra);

Создайте возможные пути доступа для обработки верхних отношений, которая является термином планировщика для всех процессов обработки запросов после сканирования/объединения, таких как агрегация, функции окон, сортировка и обновления таблиц. Эта необязательная функция вызывается во время планирования запроса. В настоящее время он вызывается только в том случае, если все базовые отношения, участвующие в запросе, принадлежат одному и тому же FDW1. Эта функция должна генерировать ForeignPath путь(или пути) для любой обработки post-scan/join, которую FDW1 умеет выполнять удаленно (используйте create_foreign_upper_path чтобы построить их), и вызовите add_path чтобы добавить эти пути к указанному верхнему отношению. Как и в случае с GetForeignJoinPaths, нет необходимости, чтобы эта функция успешно создавала какие-либо пути, поскольку пути, включающие локальную обработку, всегда возможны.

Параметр stage определяет, какой шаг post-scan/join в настоящее время рассматривается. output_rel является ли верхнее отношение, которое должно принимать пути, представляющие вычисление этого шага, и input_rel является отношением, представляющим входные данные для этого шага. То дополнительный параметр предоставляет дополнительную информацию, в настоящее время он установлен только для UPPERREL_PARTIAL_GROUP_AGG или UPPERREL_GROUP_AGG, и в этом случае он указывает на GroupPathExtraData структура; или для UPPERREL_FINAL, и в этом случае он указывает на a FinalPathExtraData структура. (Заметить что ForeignPath пути, добавленные в output_rel как правило, не будет иметь никакой прямой зависимости от путей input_rel, поскольку их обработка, как ожидается, будет осуществляться извне. Однако изучение путей, ранее созданных для предыдущего этапа обработки, может быть полезно во избежание избыточной работы по планированию).

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

Процедуры FDW для обновления сторонних таблиц

Если FDW1 поддерживает записываемые внешние таблицы, он должен предоставлять некоторые или все из следующих функций обратного вызова в зависимости от потребностей и возможностей FDW1:

void
AddForeignUpdateTargets(Query *parsetree,
                        RangeTblEntry *target_rte,
                        Relation target_relation);

Операции UPDATE и DELETE выполняются для строк, ранее извлеченных функциями сканирования таблиц. FDW1 может потребоваться дополнительная информация, такая как идентификатор строки или значения столбцов первичного ключа, чтобы гарантировать, что он может идентифицировать точную строку для обновления или удаления. Чтобы поддержать это, эта функция может добавить дополнительные скрытые или “мусорные” целевые столбцы в список столбцов, которые должны быть получены из внешней таблицы во время UPDATE или DELETE .

Чтобы сделать это, добавьте TargetEntry пункты к parsetree - >targetList, содержащий выражения для получения дополнительных значений. Каждая такая запись должна быть отмечена resjunk = true, и должны иметь различное resname это позволит идентифицировать его во время выполнения. Избегайте использования сопоставления имен ctidN, wholerow, или wholerowN, поскольку основная система может генерировать ненужные столбцы этих имен. Если дополнительные выражения являются более сложными, чем простые Vars, они должны быть выполнены через eval_const_expressions перед добавлением их в targetlist.

Хотя эта функция вызывается во время планирования, предоставленная информация немного отличается от той, которая доступна для других процедур планирования. parsetree является ли дерево синтаксического анализа для команды UPDATE или DELETE, в то время как target_rte и target_relation опишите целевую внешнюю таблицу.

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

List *
PlanForeignModify(PlannerInfo *root,
                  ModifyTable *plan,
                  Index resultRelation,
                  int subplan_index);

Выполните все дополнительные действия планирования, необходимые для вставки, обновления или удаления во внешней таблице. Эта функция генерирует FDW1-закрытую информацию, которая будет присоединена к ModifyTable запланируйте узел, который выполняет действие обновления. Эта частная информация должна иметь вид: Список, и будет поставлен к BeginForeignModify во время стадии исполнения.

root это глобальная информация планировщика о запросе. plan это ModifyTable узел плана, который является полным за исключением fdwPrivLists поле. resultRelation определяет целевую внешнюю таблицу по индексу таблицы диапазонов. subplan_index определяет, какая это цель ModifyTable узла плана, считая от нуля; используйте это, если вы хотите индексировать в намеченные планы или другой субструктуры план узел.

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

Если PlanForeignModify указатель установлен в значение NULL, никакие дополнительные действия план-времени не приняты, и fdw_private список доставленных в BeginForeignModify будет ноль.

void
BeginForeignModify(ModifyTableState *mtstate,
                   ResultRelInfo *rinfo,
                   List *fdw_private,
                   int subplan_index,
                   int eflags);

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

mtstate является ли общее состояние ModifyTable выполняется узел плана; глобальные данные о состоянии плана и выполнения доступны через эту структуру. rinfo это ResultRelInfo структура, описывающая целевую внешнюю таблицу. (Этот ri_FdwState область применения: ResultRelInfo доступен для FDW1, чтобы хранить любое частное государство, которое ему нужно для этой операции). 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 обычно представляет небольшой интерес для случаев вставки, но предоставляется для полноты).

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

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

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

Обратите внимание, что эта функция также вызывается при вставке маршрутизируемых кортежей в раздел внешней таблицы или выполнении COPY FROM внешней таблицы, и в этом случае она вызывается иначе, чем в случае вставки. Смотрите функции обратного вызова, описанные ниже, которые позволяют FDW1 поддерживать это.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

int
IsForeignRelUpdatable(Relation rel);

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

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

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

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

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

Чтобы выполнить прямое изменение на удаленном сервере, эта функция должна переписать целевой подплан с ForeignScan узлом планирования, который выполняет прямое изменение на удаленном сервере. То операция область применения: ForeignScan должно быть установлено на CmdType перечисление соответственно; то есть, CMD_UPDATE для UPDATE, CMD_INSERT для INSERT, и CMD_DELETE для DELETE .

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

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

void
BeginDirectModify(ForeignScanState *node,
                  int eflags);

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

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

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

TupleTableSlot *
IterateDirectModify(ForeignScanState *node);

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

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

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

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

void
EndDirectModify(ForeignScanState *node);

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

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

FDW-процедуры для блокировки строк

Если FDW1 желает поддерживать late row locking (как описано в разделе Блокировка строк в обёртках сторонних данных), он должен предоставить следующие функции обратного вызова:

RowMarkType
GetForeignRowMarkType(RangeTblEntry *rte,
                      LockClauseStrength strength);

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

Эта функция вызывается во время планирования запроса для каждой внешней таблицы, которая появляется в запросе 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 если то, что было извлечено, было обновленной версией кортежа, а не той же самой версией, полученной ранее. (Если FDW1 не может быть уверен в этом, всегда рекомендовано возвращать true).

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

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

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

Дополнительную информацию смотрите в разделе Блокировка строк в обёртках сторонних данных.

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

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

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

Процедуры FDW для EXPLAIN

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

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

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

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

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

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

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

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

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

Процедуры FDW для ANALYZE

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

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

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

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

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

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

Процедуры FDW для IMPORT FOREIGN SCHEMA

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

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

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

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

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

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

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

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

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

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

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

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

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

void
InitializeDSMForeignScan(ForeignScanState *node, ParallelContext *pcxt,
                         void *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 для репараметризации путей

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 как и положено.

Вспомогательные функции для обёртки сторонних данных

Несколько вспомогательных функций экспортируются с главного сервера, чтобы авторы обёрток сторонних данных могли легко получить доступ к атрибутам объектов, связанных с FDW1, таким как параметры FDW1. Чтобы использовать любую из этих функций, необходимо включить файл заголовка foreign/foreign.h в вашем исходном файле. Этот заголовок также определяет типы структур, возвращаемые этими функциями.

ForeignDataWrapper *
GetForeignDataWrapperExtended(Oid fdwid, bits16 flags);

Эта функция возвращает объект ForeignDataWrapper для обёртки сторонних данных с заданным OID. ForeignDataWrapper объект содержит свойства FDW1 (см. foreign/foreign.h относительно деталей). флаги это побитовая или битовая маска, указывающая на дополнительный набор опций. Он может принять значение FDW_MISSING_OK, и в этом случае a NULL результат возвращается вызывающему объекту вместо ошибки для неопределенного объекта.

ForeignDataWrapper *
GetForeignDataWrapper(Oid fdwid);

Эта функция возвращает a объект ForeignDataWrapper для обёртки сторонних данных с заданным OID. ForeignDataWrapper объект содержит свойства FDW1 (см. foreign/foreign.h относительно деталей).

ForeignServer *
GetForeignServerExtended(Oid serverid, bits16 flags);

Эта функция возвращает ForeignServer объект для внешнего сервера с заданным OID. ForeignServer объект содержит свойства сервера (см. foreign/foreign.h относительно деталей). флаги это побитовая или битовая маска, указывающая на дополнительный набор опций. Он может принять значение FSV_MISSING_OK, и в этом случае a NULL результат возвращается вызывающему объекту вместо ошибки для неопределенного объекта.

ForeignServer *
GetForeignServer(Oid serverid);

Эта функция возвращает ForeignServer объект для внешнего сервера с заданным OID. Один ForeignServer объект содержит свойства сервера (см. foreign/foreign.h относительно деталей).

UserMapping *
GetUserMapping(Oid userid, Oid serverid);

Эта функция возвращает объект UserMapping для отображения пользователя данной роли на данном сервере. (Если нет никакого сопоставления для конкретного пользователя, он вернет сопоставление для PUBLIC, или бросьте ошибку, если ее нет). UserMapping объект содержит свойства пользовательского сопоставления (см. foreign/foreign.h относительно деталей).

ForeignTable *
GetForeignTable(Oid relid);

Эта функция возвращает объект ForeignTable для внешней таблицы с заданным OID. ForeignTable объект содержит свойства внешней таблицы (см. foreign/foreign.h относительно деталей).

List *
GetForeignColumnOptions(Oid relid, AttrNumber attnum);

Эта функция возвращает параметры FDW1 для каждого столбца для столбца с заданным внешним OID таблицы и номером атрибута, в виде списка DefElem. Значение NIL возвращается, если столбец не имеет параметров.

Некоторые типы объектов имеют функции поиска на основе имен в дополнение к основанным на OID функциям:

ForeignDataWrapper *
GetForeignDataWrapperByName(const char *name, bool missing_ok);

Эта функция возвращает объект ForeignDataWrapper для обёртки сторонних данных с заданным именем. Если обёртка не найдена, верните значение NULL, если missing_ok имеет значение true, иначе возникнет ошибка.

ForeignServer *
GetForeignServerByName(const char *name, bool missing_ok);

Эта функция возвращает ForeignServer объект для внешнего сервера с заданным именем. Если сервер не найден, верните значение NULL, если missing_ok имеет значение true, иначе возникнет ошибка.

Планирование запросов на обработку сторонних данных

Функции обратного вызова FDW1 GetForeignRelSize, GetForeignPaths, GetForeignPlan, PlanForeignModify, GetForeignJoinPaths, GetForeignUpperPaths, и PlanDirectModify должны вписываться в работу планировщика QHB. Вот некоторые заметки о том, что они должны делать.

Информация внутри корень и подложка может использоваться для уменьшения объема информации, которая должна быть извлечена из внешней таблицы (и, следовательно, снизить стоимость). baserel - >baserestrictinfo это особенно интересно, так как содержит ограничительные кавычки (WHERE предложения), которые должны использоваться для фильтрации строк, подлежащих извлечению. (Сам FDW1 не требуется для принудительного применения этих тестов, поскольку основной исполнитель может проверить их вместо этого). baserel- > reltarget - >>exprs можно использовать, чтобы определить, какие столбцы должны быть извлечены; но обратите внимание, что он только перечисляет столбцы, которые должны быть испущены ForeignScan узел плана, а не столбцы, которые используются при квалификационной оценке, но не выводятся запросом.

Различные частные поля доступны для функций планирования FDW1, чтобы сохранить информацию. Как правило, все, что вы храните в частных полях FDW1, должно быть убрано, чтобы оно было исправлено в конце планирования.

baserel - >fdw_private это void указатель, доступный для функций планирования FDW1 для хранения информации, относящейся к конкретной внешней таблице. Основной планировщик не трогает его, кроме как для инициализации его в ноль, когда RelOptInfo узел будет создан. Это полезно для передачи информации вперед от GetForeignRelSize Для GetForeignPaths и-или GetForeignPaths Для GetForeignPlan, тем самым избегая пересчета.

GetForeignPaths можно определить значение различных путей доступа, храня личную информацию в fdw_private область применения: ForeignPath узлы. fdw_private объявляется как a Список указатель, но на самом деле может содержать что-либо, так как основной планировщик не касается его. Тем не менее, рекомендуется использовать представление, которое может быть выгружено nodeToString, для использования с поддержкой отладки, доступной в бэкенде.

GetForeignPlan может исследовать fdw_private поле выбранного ForeignPath узла, и может произвести fdw_exprs и fdw_private списки, подлежащие размещению в ForeignScan запланируйте узел, где они будут доступны во время выполнения. Оба эти списка должны быть представлены в такой форме, чтобы copyObject умеет копировать. fdw_private список не имеет никаких других ограничений и никоим образом не интерпретируется ядром backend. То fdw_exprs список, если он не равен NIL, должен содержать деревья выражений, предназначенные для выполнения во время выполнения. Эти деревья будут проходить пост-обработку планировщиком, чтобы сделать их полностью исполняемыми.

В GetForeignPlan, как правило, переданный целевой список можно скопировать в узел плана как есть. Переданный scan_clauses список содержит те же пункты, что и baserel - >baserestrictinfo, но может быть переупорядочен для для лучшей эффективности выполнения. В простых случаях FDW1 может просто удалить RestrictInfo узлы из списка scan_clauses (с помощью extract_actual_clauses) и поместить все предложения в список квалификации узла плана, что означает, что все предложения будут проверены исполнителем во время выполнения. Более сложные FDW1 могут быть в состоянии проверить некоторые пункты внутренне, и в этом случае эти пункты могут быть удалены из списка квалификации узла плана, чтобы исполнитель не тратил время на повторную проверку их.

Например, FDW1 может идентифицировать некоторые положения об ограничении формы foreign_variable = sub_expression, который он определяет, может быть выполнен на удаленном сервере с учетом локально оцененного значения sub_expression. Фактическая идентификация такой оговорки должна произойти во время GetForeignPaths, так как это повлияло бы на смету расходов для пути. Путь fdw_private поле, вероятно, будет содержать указатель на идентифицированное предложение RestrictInfo узел. Затем GetForeignPlan удалил бы это положение из scan_clauses, но добавить то sub_expression Для fdw_exprs чтобы убедиться, что он получает массаж в исполняемую форму. Это, вероятно, также поместит управляющую информацию в узел плана fdw_private поле, указывающее функциям выполнения, что следует делать во время выполнения. Запрос, передаваемый на удаленный сервер, будет включать что-то вроде Где foreign_variable = $1, при этом значение параметра получено во время выполнения из оценки fdw_exprs дерево выражения.

Все предложения, удаленные из списка квалификации узла плана, должны быть добавлены вместо этого fdw_recheck_quals или перепроверить с помощью RecheckForeignScan для того чтобы обеспечить правильное поведение на READ COMMITTED уровень изоляции. Когда параллельное обновление происходит для некоторой другой таблицы, вовлеченной в запрос, исполнителю может потребоваться проверить, что все исходные тесты все еще удовлетворены для кортежа, возможно, против другого набора значений параметров. С помощью fdw_recheck_quals обычно это проще, чем реализовать проверки внутри RecheckForeignScan, но этот метод будет недостаточным, когда внешние соединения были вытеснены, так как кортежи соединения в этом случае могут иметь некоторые поля, переходящие в NULL, не отвергая кортеж полностью.

Другой ForeignScan поле, которое может быть заполнено FDWs1 является fdw_scan_tlist, который описывает кортежи, возвращенные FDW1 для этого узла плана. Для простых сканирований внешней таблицы это может быть установлено в NULL, подразумевая, что возвращенные кортежи имеют тип строки, объявленный для внешней таблицы. non-NIL значение должно быть целевым списком (списком TargetEntry) содержащие Vars и / или выражения, представляющие возвращаемые столбцы. Это может быть использовано, например, чтобы показать, что FDW1 пропустил некоторые столбцы, которые он заметил, не будут нужны для запроса. Кроме того, если FDW1 может вычислять выражения, используемые запросом, дешевле, чем это можно сделать локально, он может добавить эти выражения в fdw_scan_tlist. Обратите внимание, что планы объединения (созданные из путей, сделанных GetForeignJoinPaths) должны всегда поставлять fdw_scan_tlist чтобы описать набор столбцов, которые они вернут.

FDW1 всегда должен создавать по крайней мере один путь, который зависит только от предложений ограничения таблицы. В запросах join он также может создавать пути, которые зависят от предложений join, например foreign_variable = локальная переменная. Такие пункты не будут найдены в baserel - >baserestrictinfo но нужно искать в списках присоединения к отношениям. Путь, использующий такое предложение, называется "параметризованным путем". Он должен идентифицировать другие отношения, используемые в выбранном предложении соединения с подходящим значением param_info; использовать get_baserel_parampathinfo чтобы вычислить это значение. В GetForeignPlan, этот локальная переменная часть предложения соединения будет добавлена к fdw_exprs, и тогда во время выполнения дело работает так же, как и для обычного предложения об ограничении.

Если FDW1 поддерживает удаленные соединения, GetForeignJoinPaths должен производить ForeignPath для потенциальных удаленных соединений во многом таким же образом, как GetForeignPaths работает для базовых таблиц. Информацию о предполагаемом присоединении можно передать вперед GetForeignPlan точно так же, как описано выше. Однако, baserestrictinfo не относится к отношениям соединения; вместо этого соответствующие условия соединения для конкретного соединения передаются в GetForeignJoinPaths как отдельный параметр (extra - >restrictlist).

FDW1 может дополнительно поддерживать прямое выполнение некоторых действий плана, которые находятся выше уровня сканирования и объединения, таких как группирование или агрегирование. Чтобы предложить такие варианты, FDW1 должен генерировать пути и вставлять их в соответствующее верхнее отношение. Например, путь, представляющий удаленную агрегацию, должен быть вставлен в поле UPPERREL_GROUP_AGG связь, используя add_path. Этот путь будет сравниваться по стоимости с локальным агрегированием, выполненным путем считывания простого пути сканирования для внешней связи (обратите внимание, что такой путь также должен быть указан, иначе будет ошибка во время планирования). Если путь удаленной агрегации выигрывает, что обычно и происходит, то он будет преобразован в план обычным способом, путем вызова GetForeignPlan. Рекомендуемое место для создания таких путей находится в GetForeignUpperPaths функция обратного вызова, которая вызывается для каждого верхнего отношения (т. е. для каждого шага обработки после сканирования/соединения), если все базовые отношения запроса исходят из одного и того же FDW1.

PlanForeignModify и другие обратные вызовы, описанные в разделе Процедуры FDW для обновления сторонних таблиц, разработаны вокруг предположения, что внешнее отношение будет сканироваться обычным способом, а затем отдельные обновления строк будут управляться локальным ModifyTable план узла. Этот подход необходим для общего случая, когда обновление требует чтения локальных таблиц, а также внешних таблиц. Тем не менее, если операция может быть выполнена полностью внешним сервером, FDW1 может создать путь, представляющий это, и вставить его в поле UPPERREL_FINAL верхнее отношение, где он будет конкурировать с ModifyTable подход. Этот подход также может быть использован для реализации удаленного доступа SELECT FOR UPDATE, а не использовать обратные вызовы блокировки строк, описанные в разделе FDW-процедуры для блокировки строк. Имейте в виду, что путь вставляется в UPPERREL_FINAL отвечает за реализацию всего поведения запроса.

При планировании UPDATE или DELETE, PlanForeignModify и PlanDirectModify может искать RelOptInfo структуру для внешней таблицы и использовать baserel - >fdw_private данные, ранее созданные функциями планирования сканирования. Однако в INSERT целевая таблица не сканируется, поэтому ее нет RelOptInfo для него. То List возвращаемый PlanForeignModify имеет те же ограничения, что и fdw_private список a ForeignScan узел плана, то есть он должен содержать только структуры, которые copyObject умеет копировать.

INSERT с ON CONFLICT предложением не поддерживает указание целевого объекта конфликта, поскольку уникальные ограничения или ограничения исключения для удаленных таблиц не известны локально. Это в свою очередь подразумевает, что ON CONFLICT DO UPDATE не поддерживается, так как спецификация там обязательна.

Блокировка строк в обёртках сторонних данных

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

Одним из ключевых решений, которые необходимо принять, является ли выполнять раннюю блокировку (early locking) или позднюю блокировку (late locking). В ранней блокировке строка блокируется, когда она впервые извлекается из базового хранилища, в то время как в поздней блокировке строка блокируется только тогда, когда известно, что она должна быть заблокирована. (Разница возникает потому, что некоторые строки могут быть отброшены локально проверенным ограничением или условиями соединения). Ранняя блокировка намного проще и позволяет избежать дополнительных обходов в удаленный магазин, но она может привести к блокировке строк, которые не должны были быть заблокированы, что приводит к снижению параллелизма или даже неожиданным тупикам. Кроме того, поздняя блокировка возможна только в том случае, если строка, подлежащая блокировке, может быть однозначно повторно идентифицирована позже. Предпочтительно идентификатор строки должен идентифицировать конкретную версию строки, как это делают TIDs QHB.

По умолчанию QHB игнорирует соображения блокировки при взаимодействии с FDW1, но FDW1 может выполнять раннюю блокировку без какой-либо явной поддержки со стороны основного кода. Функции API, описанные в разделе FDW-процедуры для блокировки строк, позволяют FDW1 использовать позднюю блокировку, если она пожелает.

Дополнительным соображением является то, что в READ COMMITTED режим изоляции, QHB, возможно, потребуется повторно проверить ограничение и условия соединения с обновленной версией некоторого целевого кортежа. Повторная проверка условий соединения требует повторного получения копий нецелевых строк, которые ранее были присоединены к целевому кортежу. При работе со стандартными таблицами QHB это делается путем включения tid нецелевых таблиц в список столбцов, проецируемых через соединение, а затем повторной выборки нецелевых строк, когда это требуется. Этот подход сохраняет компактность набора данных соединения, но требует недорогой возможности повторного извлечения, а также TID, который может однозначно определить версию строки, подлежащую повторному извлечению. Поэтому по умолчанию метод, используемый с внешними таблицами, заключается в том, чтобы включить копию всей строки, извлеченной из внешней таблицы, в список столбцов, проецируемых через соединение. Это не ставит никаких особых требований к FDW1, но может привести к снижению производительности слияния и хэш-соединений. FDW1, способный удовлетворять требованиям повторной выборки, может выбрать для этого первый способ.

Для UPDATE или DELETE на внешней таблице рекомендуется, чтобы ForeignScan операция над целевой таблицей выполняет раннюю блокировку строк, которые она извлекает, возможно, через эквивалент SELECT FOR UPDATE. FDW1 может определить, является ли таблица целью UPDATE/DELETE во время плана, сравнивая ее relid с root- > parse - >>resultRelation, или во время выполнения с помощью ExecRelationIsTargetRelation(). Альтернативной возможностью является выполнение поздней блокировки в пределах ExecForeignUpdate или ExecForeignDelete обратный вызов, но никакой специальной поддержки для этого не предусмотрено.

Для внешних таблиц, которые должны быть заблокированы с помощью команды SELECT FOR UPDATE/SHARE, ForeignScan операция может снова выполнить раннюю блокировку путем выборки кортежей с эквивалентом SELECT FOR UPDATE/SHARE. Для выполнения поздней блокировки вместо этого, предоставьте функции обратного вызова, определенные в разделе FDW-процедуры для блокировки строк. В GetForeignRowMarkType, выберите параметр rowmark ROW_MARK_EXCLUSIVE, ROW_MARK_NOKEYEXCLUSIVE, ROW_MARK_SHARE, или ROW_MARK_KEYSHARE в зависимости от требуемого режима блокировки. (Основной код будет действовать одинаково независимо от того, какой из этих четырех вариантов вы выберете). В других местах вы можете определить, была ли внешняя таблица заблокирована этим типом команды с помощью get_plan_rowmark в запланированное время, или ExecFindRowMark во время выполнения; вы должны проверить не только то, возвращается ли ненулевая структура rowmark, но и то, что нет ее strength поля LCS_NONE.

Наконец, для внешних таблиц, которые используются в команде UPDATE, DELETE или SELECT FOR UPDATE/SHARE, но не заданы для блокировки строк, можно переопределить выбор по умолчанию для копирования целых строк, имея GetForeignRowMarkType выбранный вариант ROW_MARK_REFERENCE когда он видит режима блокировки LCS_NONE. Это приведет к тому, что RefetchForeignRow быть вызванным с этим значением для markType; затем он должен повторно получить строку, не приобретая никакого нового замка. (Если у вас есть a GetForeignRowMarkType функция, но не хотите, чтобы повторно получить разблокированные строки, выберите опцию ROW_MARK_COPY для LCS_NONE).

Смотрите src/include/nodes/lockoptions.h, комментарии для RowMarkType и PlanRowMark в src/include/nodes/plannodes.h, и комментарии для ExecRowMark в src/include/nodes/execnodes.h для дополнительной информации.

1

Foreign Data Wrapper, обёртка сторонних данных