2025年9月25日: PostgreSQL 18 釋出!
支援的版本:當前 (18) / 17 / 16 / 15 / 14 / 13
開發版本:devel
不支援的版本:12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1

58.2. 外表資料包裝器回撥例程 #

FDW 處理函式返回一個 palloc 分配的 FdwRoutine 結構,其中包含指向下文描述的回撥函式的指標。與掃描相關的函式是必需的,其餘是可選的。

型別 FdwRoutinesrc/include/foreign/fdwapi.h 中宣告,有關更多詳細資訊請參見該檔案。

58.2.1. 用於掃描外表的 FDW 例程 #

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

獲取外表的關係大小估計。這在掃描外表的查詢規劃開始時被呼叫。root 是規劃器關於查詢的全域性資訊;baserel 是規劃器關於該表的 C資訊;foreigntableid 是外表的 pg_class OID。(foreigntableid 可以從規劃器資料結構中獲得,但它被顯式傳遞以節省工作量。)

此函式應更新 baserel->rows,使其成為表掃描預期的返回行數,在考慮了限制條件過濾後。 baserel->rows 的初始值只是一個常量預設估計值,如果可能的話應替換它。如果函式可以計算出平均結果行寬度的更好估計值,它也可以選擇更新 baserel->width。(初始值基於列資料型別以及上一次 ANALYZE 測量的列平均寬度值。)此外,如果函式可以計算出外表總行數的更好估計值,它也可以更新 baserel->tuples。(初始值來自 pg_class.reltuples,它代表上一次 ANALYZE 看到的總行數;如果此外表上尚未進行 ANALYZE,則為 -1。)

有關更多資訊,請參見 Section 58.4

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

為掃描外表建立可能的訪問路徑。這在查詢規劃期間被呼叫。引數與 GetForeignRelSize 相同,後者已被呼叫。

此函式必須為外表掃描生成至少一個訪問路徑(ForeignPath 節點),並且必須呼叫 add_path 將每個此類路徑新增到 baserel->pathlist。建議使用 create_foreignscan_path 來構建 ForeignPath 節點。該函式可以生成多個訪問路徑,例如,一個具有有效 pathkeys 來表示預排序結果的路徑。每個訪問路徑都必須包含成本估計,並且可以包含任何 FDW 私有資訊,這些資訊對於識別預期的特定掃描方法是必需的。

有關更多資訊,請參見 Section 58.4

ForeignScan *
GetForeignPlan(PlannerInfo *root,
               RelOptInfo *baserel,
               Oid foreigntableid,
               ForeignPath *best_path,
               List *tlist,
               List *scan_clauses,
               Plan *outer_plan);

從選定的外表訪問路徑建立 ForeignScan 計劃節點。這在查詢規劃結束時被呼叫。引數與 GetForeignRelSize 相同,加上選定的 ForeignPath(之前由 GetForeignPathsGetForeignJoinPathsGetForeignUpperPaths 生成)、將由計劃節點發出的目標列表、將由計劃節點強制執行的限制子句,以及 ForeignScan 的外部子計劃,它用於 RecheckForeignScan 執行的重檢查。(如果路徑是用於連線而不是基本關係,則 foreigntableidInvalidOid。)

此函式必須建立並返回一個 ForeignScan 計劃節點;建議使用 make_foreignscan 來構建 ForeignScan 節點。

有關更多資訊,請參見 Section 58.4

void
BeginForeignScan(ForeignScanState *node,
                 int eflags);

開始執行外表掃描。這在執行器啟動期間被呼叫。它應該在掃描開始之前執行任何必要的初始化,但不能開始執行實際掃描(這應該在第一次呼叫 IterateForeignScan 時完成)。ForeignScanState 節點已經建立,但其 fdw_state 欄位仍為 NULL。有關要掃描的表的資訊可以透過 ForeignScanState 節點訪問(特別是,從底層的 ForeignScan 計劃節點,它包含 GetForeignPlan 提供的任何 FDW 私有資訊)。eflags 包含描述此計劃節點的執行器操作模式的標誌位。

請注意,當 (eflags & EXEC_FLAG_EXPLAIN_ONLY) 為真時,此函式不應執行任何外部可見的操作;它應只執行最低限度的工作,以使節點狀態對 ExplainForeignScanEndForeignScan 有效。

TupleTableSlot *
IterateForeignScan(ForeignScanState *node);

從外源獲取一行,並將其返回到元組表槽(節點的 ScanTupleSlot 應用於此目的)。如果沒有更多可用行,則返回 NULL。元組表槽基礎結構允許返回物理或虛擬元組;在大多數情況下,後者從效能角度來看更可取。請注意,這在一個生命週期短的記憶體上下文中呼叫,該上下文在呼叫之間會被重置。如果在 BeginForeignScan 中建立記憶體上下文以獲得更長生命週期的儲存,或者使用節點的 EStatees_query_cxt

返回的行必須匹配 fdw_scan_tlist 目標列表(如果提供了),否則必須匹配正在掃描的外表的行型別。如果您選擇最佳化掉獲取不需要的列,則應在這些列位置插入 NULL,或者生成一個省略了這些列的 fdw_scan_tlist 列表。

請注意,PostgreSQL 的執行器不關心返回的行是否違反了外表上定義的任何約束——但規劃器關心,如果外表中存在不滿足宣告約束的行,規劃器可能會錯誤地最佳化查詢。如果使用者宣告約束應保持為真而約束被違反,則可能需要引發錯誤(正如在資料型別不匹配的情況下需要做的那樣)。

void
ReScanForeignScan(ForeignScanState *node);

從頭重新開始掃描。請注意,掃描依賴的任何引數都可能已更改值,因此新掃描不一定返回完全相同的行。

void
EndForeignScan(ForeignScanState *node);

結束掃描並釋放資源。通常不需要釋放 palloc 分配的記憶體,但例如開啟的檔案和到遠端伺服器的連線應被清理。

58.2.2. 用於掃描外連線的 FDW 例程 #

如果 FDW 支援遠端執行外連線(而不是透過獲取兩個表的 C資料並本地執行連線),則應提供此回撥函式。

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

為屬於同一外伺服器的兩個(或更多)外表的連線建立可能的訪問路徑。這個可選函式在查詢規劃期間被呼叫。與 GetForeignPaths 一樣,此函式應為提供的 joinrel 生成 ForeignPath 路徑(使用 create_foreign_join_path 構建它們),並呼叫 add_path 將這些路徑新增到要考慮的連線路徑集中。但與 GetForeignPaths 不同的是,此函式不一定需要成功建立至少一個路徑,因為本地連線的路徑總是可能的。

請注意,此函式將針對相同的連線關係被反覆呼叫,具有不同的內部和外部關係組合;FDW 負責最小化重複工作。

另請注意,要應用於連線的連線子句集,以 extra->restrictlist 的形式傳遞,取決於內部和外部關係的組合。為 joinrel 生成的 ForeignPath 路徑必須包含其使用的連線子句集,如果該路徑被規劃器選為 joinrel 的最佳路徑,則規劃器將使用它來轉換為計劃。

如果為連線選擇了 ForeignPath 路徑,它將代表整個連線過程;為元件表和附屬連線生成的路徑將不被使用。連線路徑的後續處理與掃描單個外表的路徑的處理非常相似。一個區別是,生成的 ForeignScan 計劃節點的 scanrelid 應設定為零,因為它不代表任何單個關係;相反,ForeignScan 節點的 fs_relids 欄位代表連線的關係集。(後一個欄位由核心規劃器程式碼自動設定,FDW 無需填充。)另一個區別是,因為系統目錄中找不到遠端連線的列列表,所以 FDW 必須用 TargetEntry 節點的一個適當列表填充 fdw_scan_tlist,該列表代表其在執行時返回的元組中將提供的列集。

注意

PostgreSQL 16 開始,fs_relids 包括外部連線的範圍表索引(如果此連線涉及)。新欄位 fs_base_relids 僅包括基本關係索引,因此模仿 fs_relids 的舊語義。

有關更多資訊,請參見 Section 58.4

58.2.3. 用於規劃掃描/連線後處理的 FDW 例程 #

如果 FDW 支援遠端執行掃描/連線後處理,例如遠端聚合,則應提供此回撥函式。

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

上層關係處理建立可能的訪問路徑,這是規劃器對所有掃描/連線後查詢處理(如聚合、視窗函式、排序和表更新)的術語。此可選函式在查詢規劃期間被呼叫。當前,僅當查詢中涉及的所有基本關係屬於同一個 FDW 時才會呼叫它。此函式應為 FDW 知道如何遠端執行的任何掃描/連線後處理生成 ForeignPath 路徑(使用 create_foreign_upper_path 構建它們),並呼叫 add_path 將這些路徑新增到指定的上層關係中。與 GetForeignJoinPaths 一樣,此函式不一定需要成功建立任何路徑,因為本地處理的路徑總是可能的。

引數 stage 標識當前正在考慮哪個掃描/連線後步驟。output_rel 是應接收表示此步驟計算的路徑的上層關係,input_rel 是代表此步驟輸入的 C關係。extra 引數提供其他詳細資訊,當前僅針對 UPPERREL_PARTIAL_GROUP_AGGUPPERREL_GROUP_AGG 設定,此時它指向一個 GroupPathExtraData 結構;或者對於 UPPERREL_FINAL,此時它指向一個 FinalPathExtraData 結構。(注意,新增到 output_relForeignPath 路徑通常不會直接依賴於前一個處理步驟的路徑,因為它們的處理預計將在外部完成。然而,檢查前一個處理步驟生成的路徑可能有助於避免冗餘的規劃工作。)

有關更多資訊,請參見 Section 58.4

58.2.4. 用於更新外表的 FDW 例程 #

如果 FDW 支援可寫的外表,則應根據 FDW 的需求和功能提供以下一個或多個回撥函式。

void
AddForeignUpdateTargets(PlannerInfo *root,
                        Index rtindex,
                        RangeTblEntry *target_rte,
                        Relation target_relation);

UPDATEDELETE 操作是針對之前由表掃描函式獲取的行執行的。FDW 可能需要額外資訊,例如行 ID 或主鍵列的值,以確保它能識別要更新或刪除的確切行。為了支援這一點,此函式可以向在 UPDATEDELETE 期間要從外表檢索的列列表中新增額外的隱藏的(或“垃圾”)目標列。

要做到這一點,構造一個表示您需要的額外值的 Var,並將其與垃圾列的名稱一起傳遞給 add_row_identity_var。(如果您需要多個列,可以執行此操作多次。)您必須為每個需要的不同 Var 選擇一個唯一的垃圾列名稱,但除了 Var 欄位 varno 不同之外,相同的 Var 可以並且應該共享一個列名。核心系統使用垃圾列名稱 tableoid 來表示表的 tableoid 列,ctidctidN 來表示 ctidwholerow 來表示標記為 vartype = RECORD 的整行 Var,以及 wholerowN 來表示 vartype 等於表宣告的行型別的整行 Var。當您能重用這些名稱時(規劃器將合併重複的相同垃圾列請求)。如果您需要除這些之外的其他型別的垃圾列,明智的做法是選擇一個以您的副檔名命名的字首,以避免與其他 FDW 衝突。

如果 AddForeignUpdateTargets 指標設定為 NULL,則不會新增額外的目標表達式。(這將使得實現 DELETE 操作成為不可能,儘管 UPDATE 仍然可行,如果 FDW 依賴於不變的主鍵來識別行。)

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

為外表上的插入、更新或刪除執行任何額外的規劃操作。此函式生成將附加到執行更新操作的 ModifyTable 計劃節點上的 FDW 私有資訊。此私有資訊必須採用 List 的形式,並在執行階段傳遞給 BeginForeignModify

root 是規劃器關於查詢的全域性資訊。planModifyTable 計劃節點,除了 fdwPrivLists 欄位外,它是完整的。resultRelation 透過其範圍表索引標識目標外表。subplan_index 標識這是 ModifyTable 計劃節點的哪個目標,從零開始計數;如果您想索引到 plan 節點的目標關係結構中,請使用此欄位。

有關更多資訊,請參見 Section 58.4

如果 PlanForeignModify 指標設定為 NULL,則不執行額外的計劃時操作,並且傳遞給 BeginForeignModifyfdw_private 列表將是 NIL。

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

開始執行外表修改操作。此例程在執行器啟動期間被呼叫。它應該在實際的表修改之前執行任何必要的初始化。隨後,將為要插入、更新或刪除的元組呼叫 ExecForeignInsert/ExecForeignBatchInsertExecForeignUpdateExecForeignDelete

mtstate 是正在執行的 ModifyTable 計劃節點的整體狀態;有關計劃和執行狀態的全域性資料可以透過此結構訪問。rinfo 是描述目標外表的 ResultRelInfo 結構。(ResultRelInfori_FdwState 欄位可供 FDW 儲存其對該操作所需的任何私有狀態。)fdw_private 包含 PlanForeignModify 生成的私有資料(如果有)。subplan_index 標識這是 ModifyTable 計劃節點的哪個目標。eflags 包含描述此計劃節點的執行器操作模式的標誌位。

請注意,當 (eflags & EXEC_FLAG_EXPLAIN_ONLY) 為真時,此函式不應執行任何外部可見的操作;它應只執行最低限度的工作,以使節點狀態對 ExplainForeignModifyEndForeignModify 有效。

如果 BeginForeignModify 指標設定為 NULL,則在執行器啟動期間不執行任何操作。

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

將一個元組插入外表。estate 是查詢的全域性執行狀態。rinfo 是描述目標外表的 ResultRelInfo 結構。slot 包含要插入的元組;它將匹配外表的行型別定義。planSlot 包含由 ModifyTable 計劃節點的子計劃生成的元組;它可能包含額外的“垃圾”列,這與 slot 不同。(planSlot 對於 INSERT 情況通常作用不大,但為了完整性而提供。)

返回值是包含實際插入資料的槽,或者如果沒有實際插入行,則返回 NULL(同樣,通常是觸發器的結果)。傳入的 slot 可用於此目的。

返回槽中的資料僅在 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 相同,只是 slotsplanSlots 包含多個元組,並且 *numSlots 指定了這些陣列中的元組數。

返回值是一個包含實際插入資料的槽陣列(這可能與提供的資料不同,例如作為觸發器操作的結果)。傳入的 slots 可用於此目的。成功插入的元組數在 *numSlots 中返回。

返回槽中的資料僅在 INSERT 語句涉及檢視 WITH CHECK OPTION 時使用;或者當外表具有 AFTER ROW 觸發器時使用。觸發器需要所有列,但 FDW 可以根據 WITH CHECK OPTION 約束的內容選擇最佳化掉返回某些或所有列。

如果 ExecForeignBatchInsertGetForeignModifyBatchSize 指標設定為 NULL,則嘗試插入外表將使用 ExecForeignInsert。如果 INSERT 具有 RETURNING 子句,則不使用此函式。

請注意,當插入路由到分割槽表的分割槽或在外表上執行 COPY FROM 時,也會呼叫此函式,此時的呼叫方式與 INSERT 情況不同。請參閱下面描述的回撥函式,這些函式允許 FDW 支援這一點。

int
GetForeignModifyBatchSize(ResultRelInfo *rinfo);

報告單個 ExecForeignBatchInsert 呼叫可處理的指定外表的最大元組數。執行器最多將給定的元組數傳遞給 ExecForeignBatchInsertrinfo 是描述目標外表的 ResultRelInfo 結構。期望 FDW 提供一個外伺服器和/或外表選項供使用者設定此值,或者使用某個硬編碼值。

如果 ExecForeignBatchInsertGetForeignModifyBatchSize 指標設定為 NULL,則嘗試插入外表將使用 ExecForeignInsert

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

更新外表中的一個元組。estate 是查詢的全域性執行狀態。rinfo 是描述目標外表的 ResultRelInfo 結構。slot 包含元組的新資料;它將匹配外表的行型別定義。planSlot 包含由 ModifyTable 計劃節點的子計劃生成的元組。與 slot 不同,此元組僅包含查詢更改的列的新值,因此不要依賴外表的屬性編號來索引 planSlot。此外,planSlot 通常包含額外的“垃圾”列。特別是,AddForeignUpdateTargets 請求的任何垃圾列都將從此槽中可用。

返回值是包含實際更新後的行的槽,或者如果沒有實際更新行,則返回 NULL(同樣,通常是觸發器的結果)。傳入的 slot 可用於此目的。

返回的槽中的資料僅在 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 計劃節點的子計劃生成的元組;特別是,它將攜帶 AddForeignUpdateTargets 請求的任何垃圾列。垃圾列必須用於識別要刪除的元組。

返回值是包含已刪除行的槽,或者如果沒有刪除行,則返回 NULL(通常是觸發器的結果)。傳入的 slot 可用於儲存要返回的元組。

返回的槽中的資料僅在 DELETE 查詢具有 RETURNING 子句或外表具有 AFTER ROW 觸發器時使用。觸發器需要所有列,但 FDW 可以根據 RETURNING 子句的內容選擇最佳化掉返回某些或所有列。無論如何,必須返回某個槽以指示成功,否則查詢報告的行數將是錯誤的。

如果 ExecForeignDelete 指標設定為 NULL,則嘗試從外表中刪除將以錯誤訊息失敗。

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

結束表更新並釋放資源。通常不需要釋放 palloc 分配的記憶體,但例如開啟的檔案和到遠端伺服器的連線應被清理。

如果 EndForeignModify 指標設定為 NULL,則在執行器關閉期間不執行任何操作。

透過 INSERTCOPY FROM 插入到分割槽表中的元組會被路由到分割槽。如果 FDW 支援可路由的外表分割槽,則還應提供以下回調函式。當在外表上執行 COPY FROM 時,也會呼叫這些函式。

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

開始執行外表上的插入操作。此例程在第一個元組被插入到外表之前呼叫,這兩種情況都適用:當它是在表路由選擇的分割槽時,以及在 COPY FROM 命令中指定的目標時。它應在實際插入之前執行任何必要的初始化。隨後,將為要插入到外表中的元組呼叫 ExecForeignInsertExecForeignBatchInsert

mtstate 是正在執行的 ModifyTable 計劃節點的整體狀態;有關計劃和執行狀態的全域性資料可以透過此結構訪問。rinfo 是描述目標外表的 ResultRelInfo 結構。(ResultRelInfori_FdwState 欄位可供 FDW 儲存其對該操作所需的任何私有狀態。)

當此函式由 COPY FROM 命令呼叫時,mtstate 中的計劃相關全域性資料將不提供,並且隨後為每個插入的元組呼叫的 ExecForeignInsertplanSlot 引數為 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 分別提供 ExecForeignInsertExecForeignUpdateExecForeignDelete。此函式僅在 FDW 支援某些表可更新而另一些表不可更新時才需要。(即使如此,也可以在執行例程中丟擲錯誤而不是在此函式中檢查。但是,此函式用於確定可更新性以顯示在 information_schema 檢視中。)

對某些外表的插入、更新和刪除可以透過實現一組替代介面進行最佳化。用於插入、更新和刪除的常規介面會獲取遠端伺服器的行,然後逐個修改這些行。在某些情況下,這種逐行方法是必需的,但它可能效率低下。如果外伺服器可以在不實際檢索它們的情況下確定應修改哪些行,並且沒有會影響操作的本地結構(行級本地觸發器、儲存的生成列或父檢視中的 WITH CHECK OPTION 約束),則可以安排使整個操作在外伺服器上執行。下面描述的介面使之成為可能。

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

決定是否可以安全地在外伺服器上執行直接修改。如果可以,則在執行所需規劃操作後返回 true。否則,返回 false。此可選函式在查詢規劃期間被呼叫。如果此函式成功,則在執行階段將呼叫 BeginDirectModifyIterateDirectModifyEndDirectModify。否則,表修改將使用上面描述的表更新函式執行。引數與 PlanForeignModify 相同。

為了在外伺服器上執行直接修改,此函式必須用一個 ForeignScan 計劃節點重寫目標子計劃,該節點在外伺服器上執行直接修改。ForeignScanoperationresultRelation 欄位必須相應設定。operation 必須設定為與語句型別對應的 CmdType 列舉(即,UPDATECMD_UPDATEINSERTCMD_INSERTDELETECMD_DELETE),並且 resultRelation 引數必須複製到 resultRelation 欄位。

有關更多資訊,請參見 Section 58.4

如果 PlanDirectModify 指標設定為 NULL,則不嘗試在外伺服器上執行直接修改。

void
BeginDirectModify(ForeignScanState *node,
                  int eflags);

準備在外伺服器上執行直接修改。這在執行器啟動期間被呼叫。它應該在直接修改之前執行任何必要的初始化(這應該在第一次呼叫 IterateDirectModify 時完成)。ForeignScanState 節點已經建立,但其 fdw_state 欄位仍為 NULL。有關要修改的表的資訊可以透過 ForeignScanState 節點訪問(特別是,從底層的 ForeignScan 計劃節點,它包含 PlanDirectModify 提供的任何 FDW 私有資訊)。eflags 包含描述此計劃節點的執行器操作模式的標誌位。

請注意,當 (eflags & EXEC_FLAG_EXPLAIN_ONLY) 為真時,此函式不應執行任何外部可見的操作;它應只執行最低限度的工作,以使節點狀態對 ExplainDirectModifyEndDirectModify 有效。

如果 BeginDirectModify 指標設定為 NULL,則不嘗試在外伺服器上執行直接修改。

TupleTableSlot *
IterateDirectModify(ForeignScanState *node);

INSERTUPDATEDELETE 查詢沒有 RETURNING 子句時,在外伺服器上直接修改後只需返回 NULL。當查詢有該子句時,獲取一行包含計算 RETURNING 所需資料的結果,並將其返回到元組表槽(節點的 ScanTupleSlot 應用於此目的)。實際插入、更新或刪除的資料必須儲存在 node->resultRelInfo->ri_projectReturning->pi_exprContext->ecxt_scantuple 中。如果沒有更多可用行,則返回 NULL。請注意,這在一個生命週期短的記憶體上下文中呼叫,該上下文在呼叫之間會被重置。如果在 BeginDirectModify 中建立記憶體上下文以獲得更長生命週期的儲存,或者使用節點的 EStatees_query_cxt

返回的行必須匹配 fdw_scan_tlist 目標列表(如果提供了),否則必須匹配正在更新的外表的行型別。如果您選擇最佳化掉獲取 RETURNING 計算不需要的列,則應在這些列位置插入 NULL,或者生成一個省略了這些列的 fdw_scan_tlist 列表。

無論查詢是否有該子句,查詢報告的行數必須由 FDW 本身增加。當查詢沒有該子句時,FDW 還必須在 EXPLAIN ANALYZE 的情況下為 ForeignScanState 節點增加行計數。

如果 IterateDirectModify 指標設定為 NULL,則不嘗試在外伺服器上執行直接修改。

void
EndDirectModify(ForeignScanState *node);

在直接修改外伺服器之後清理資源。通常不需要釋放 palloc 分配的記憶體,但例如開啟的檔案和到遠端伺服器的連線應被清理。

如果 EndDirectModify 指標設定為 NULL,則不嘗試在外伺服器上執行直接修改。

58.2.5. 用於 TRUNCATE 的 FDW 例程 #

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

截斷外表。當在 TRUNCATE 外表上執行時呼叫此函式。rels 是要截斷的外表 Relation 資料結構的列表。

behaviorDROP_RESTRICTDROP_CASCADE,分別指示在原始 TRUNCATE 命令中請求了 RESTRICTCASCADE 選項。

如果 restart_seqstrue,則原始 TRUNCATE 命令請求了 RESTART IDENTITY 行為,否則請求的是 CONTINUE IDENTITY 行為。

請注意,原始 TRUNCATE 命令中指定的 ONLY 選項不會傳遞給 ExecForeignTruncate。此行為類似於在外表上對 SELECTUPDATEDELETE 的回撥函式。

ExecForeignTruncate 為要截斷外表的外伺服器呼叫一次。這意味著 rels 中包含的所有外表必須屬於同一個伺服器。

如果 ExecForeignTruncate 指標設定為 NULL,則嘗試截斷外表將以錯誤訊息失敗。

58.2.6. 用於行鎖定的 FDW 例程 #

如果 FDW 希望支援延遲行鎖定(如 Section 58.5 所述),則必須提供以下回調函式。

RowMarkType
GetForeignRowMarkType(RangeTblEntry *rte,
                      LockClauseStrength strength);

報告要對外表使用的行標記選項。rte 是表的 RangeTblEntry 節點,strength 描述了相關 FOR UPDATE/SHARE 子句請求的鎖強度(如果有)。結果必須是 RowMarkType 列舉型別的一個成員。

此函式在查詢規劃期間為出現在 UPDATEDELETESELECT FOR UPDATE/SHARE 查詢中且不是 UPDATEDELETE 目標的每個外表呼叫。

如果 GetForeignRowMarkType 指標設定為 NULL,則始終使用 ROW_MARK_COPY 選項。(這意味著 RefetchForeignRow 將永遠不會被呼叫,因此也不需要提供它。)

有關更多資訊,請參見 Section 58.5

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 的選擇是希望將來允許其他資料型別的行 ID。

如果 RefetchForeignRow 指標設定為 NULL,則嘗試重新獲取行將以錯誤訊息失敗。

有關更多資訊,請參見 Section 58.5

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

重新檢查先前返回的元組是否仍滿足相關的掃描和連線限定條件,並可能提供該元組的修改版本。對於不執行連線下推的外表資料包裝器,通常更方便將其設定為 NULL,而是相應地設定 fdw_recheck_quals。但是,當外部連線被推下時,即使所有需要的屬性都存在,僅重新應用與所有基本表相關的檢查也不足以滿足要求,因為未能滿足某些限定條件可能會導致某些屬性變為 NULL,而不是不返回元組。RecheckForeignScan 可以重新檢查限定條件並返回 true(如果它們仍然被滿足)或 false(否則),但它也可以將替換元組儲存到提供的槽中。

為了實現連線下推,外表資料包裝器通常會構建一個替代的本地連線計劃,該計劃僅用於重檢查;這將成為 ForeignScan 的外部子計劃。當需要重檢查時,可以執行此子計劃並將生成的元組儲存在槽中。此計劃不必高效,因為沒有基本表會返回多於一行;例如,它可以實現所有連線為巢狀迴圈。GetExistingLocalJoinPath 函式可用於搜尋現有路徑以找到合適的本地連線路徑,該路徑可用作替代本地連線計劃。GetExistingLocalJoinPath 搜尋指定連線關係路徑列表中的非引數化路徑。(如果找不到這樣的路徑,它將返回 NULL,在這種情況下,外表資料包裝器可以自己構建本地路徑,或者可以選擇不為該連線建立訪問路徑。)

58.2.7. 用於 EXPLAIN 的 FDW 例程 #

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 期間不列印額外資訊。

58.2.8. 用於 ANALYZE 的 FDW 例程 #

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 陣列中。必須返回實際收集的行數。此外,將表中活動行和死行的估計總數儲存到輸出引數 totalrowstotaldeadrows 中。(如果 FDW 沒有死行的概念,則將 totaldeadrows 設定為零。)

58.2.9. 用於 IMPORT FOREIGN SCHEMA 的 FDW 例程 #

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 表示僅包含 table_list 中列出的表,FDW_IMPORT_SCHEMA_EXCEPT 表示排除 table_list 中列出的表。options 是用於匯入過程的選項列表。選項的含義由 FDW 決定。例如,FDW 可以使用一個選項來定義是否應匯入列的 NOT NULL 屬性。這些選項不必與 FDW 作為資料庫物件選項支援的選項相關。

FDW 可以忽略 ImportForeignSchemaStmtlocal_schema 欄位,因為核心伺服器將自動將該名稱插入到解析的 CREATE FOREIGN TABLE 命令中。

FDW 也不必關心實現 list_typetable_list 指定的過濾,因為核心伺服器將自動跳過根據這些選項排除的表的任何返回命令。然而,避免為被排除的表建立命令的工作通常是有用的。函式 IsImportableForeignTable() 可能有助於測試給定的外表名稱是否會透過過濾器。

如果 FDW 不支援匯入表定義,則 ImportForeignSchema 指標可以設定為 NULL

58.2.10. 用於並行執行的 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 段消失之前採取某些操作的外表資料包裝器應實現此方法。

58.2.11. 用於非同步執行的 FDW 例程 #

ForeignScan 節點可以(可選地)支援非同步執行,如 src/backend/executor/README 中所述。以下所有函式都是可選的,但如果支援非同步執行,則所有函式都是必需的。

bool
IsForeignPathAsyncCapable(ForeignPath *path);

測試給定的 ForeignPath 路徑是否可以非同步掃描底層外表。此函式僅在查詢規劃結束時呼叫,當給定路徑是 AppendPath 路徑的直接子項,並且規劃器認為非同步執行可以提高效能時。它應返回 true,表示給定路徑能夠非同步掃描外表。

如果未定義此函式,則假定給定路徑使用 IterateForeignScan 掃描外表。(這意味著以下回調函式永遠不會被呼叫,因此也不需要提供它們。)

void
ForeignAsyncRequest(AsyncRequest *areq);

非同步從 ForeignScan 節點生成一個元組。areq 是描述 ForeignScan 節點和請求其元組的父 Append 節點的 AsyncRequest 結構。此函式應將元組儲存在 areq->result 指定的槽中,並將 areq->request_complete 設定為 true;或者,如果它需要等待核心伺服器外部的事件(如網路 I/O),並且無法立即生成任何元組,則將標誌設定為 false,並將 areq->callback_pending 設定為 true,以便 ForeignScan 節點從下面描述的回撥函式獲得回撥。如果沒有更多可用元組,則將槽設定為 NULL 或空槽,並將 areq->request_complete 標誌設定為 true。建議使用 ExecAsyncRequestDoneExecAsyncRequestPending 來設定 areq 中的輸出引數。

void
ForeignAsyncConfigureWait(AsyncRequest *areq);

配置一個檔案描述符事件,ForeignScan 節點希望等待該事件。僅當 ForeignScan 節點將 areq->callback_pending 標誌設定為 true 時,才會呼叫此函式,並且應將該事件新增到 areq 描述的父 Append 節點的 as_eventset 中。有關更多資訊,請參見 src/backend/executor/execAsync.c 中的 ExecAsyncConfigureWait 的註釋。當檔案描述符事件發生時,將呼叫 ForeignAsyncNotify

void
ForeignAsyncNotify(AsyncRequest *areq);

處理已發生的相關事件,然後非同步從 ForeignScan 節點生成一個元組。此函式應以與 ForeignAsyncRequest 相同的方式設定 areq 中的輸出引數。

58.2.12. 用於路徑重引數化的 FDW 例程 #

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

當將由給定子關係 child_rel 的最頂層父級引數化的路徑轉換為由子關係引數化時,會呼叫此函式。該函式用於重新引數化 ForeignPathfdw_private 成員中儲存的任何路徑或轉換任何表示式節點。回撥可以使用 reparameterize_path_by_childadjust_appendrel_attrsadjust_appendrel_attrs_multilevel,視情況而定。

提交更正

如果您在文件中看到任何不正確、不符合您對特定功能的經驗或需要進一步澄清的內容,請使用 此表單 報告文件問題。