TRUNCATE
的 FDW 例程EXPLAIN
的 FDW 例程ANALYZE
的 FDW 例程IMPORT FOREIGN SCHEMA
的 FDW 例程FDW 處理函式返回一個 palloc 分配的 FdwRoutine
結構,其中包含指向下文描述的回撥函式的指標。與掃描相關的函式是必需的,其餘是可選的。
型別 FdwRoutine
在 src/include/foreign/fdwapi.h
中宣告,有關更多詳細資訊請參見該檔案。
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
(之前由 GetForeignPaths
、GetForeignJoinPaths
或 GetForeignUpperPaths
生成)、將由計劃節點發出的目標列表、將由計劃節點強制執行的限制子句,以及 ForeignScan
的外部子計劃,它用於 RecheckForeignScan
執行的重檢查。(如果路徑是用於連線而不是基本關係,則 foreigntableid
為 InvalidOid
。)
此函式必須建立並返回一個 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)
為真時,此函式不應執行任何外部可見的操作;它應只執行最低限度的工作,以使節點狀態對 ExplainForeignScan
和 EndForeignScan
有效。
TupleTableSlot * IterateForeignScan(ForeignScanState *node);
從外源獲取一行,並將其返回到元組表槽(節點的 ScanTupleSlot
應用於此目的)。如果沒有更多可用行,則返回 NULL。元組表槽基礎結構允許返回物理或虛擬元組;在大多數情況下,後者從效能角度來看更可取。請注意,這在一個生命週期短的記憶體上下文中呼叫,該上下文在呼叫之間會被重置。如果在 BeginForeignScan
中建立記憶體上下文以獲得更長生命週期的儲存,或者使用節點的 EState
的 es_query_cxt
。
返回的行必須匹配 fdw_scan_tlist
目標列表(如果提供了),否則必須匹配正在掃描的外表的行型別。如果您選擇最佳化掉獲取不需要的列,則應在這些列位置插入 NULL,或者生成一個省略了這些列的 fdw_scan_tlist
列表。
請注意,PostgreSQL 的執行器不關心返回的行是否違反了外表上定義的任何約束——但規劃器關心,如果外表中存在不滿足宣告約束的行,規劃器可能會錯誤地最佳化查詢。如果使用者宣告約束應保持為真而約束被違反,則可能需要引發錯誤(正如在資料型別不匹配的情況下需要做的那樣)。
void ReScanForeignScan(ForeignScanState *node);
從頭重新開始掃描。請注意,掃描依賴的任何引數都可能已更改值,因此新掃描不一定返回完全相同的行。
void EndForeignScan(ForeignScanState *node);
結束掃描並釋放資源。通常不需要釋放 palloc 分配的記憶體,但例如開啟的檔案和到遠端伺服器的連線應被清理。
如果 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。
如果 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_AGG
或 UPPERREL_GROUP_AGG
設定,此時它指向一個 GroupPathExtraData
結構;或者對於 UPPERREL_FINAL
,此時它指向一個 FinalPathExtraData
結構。(注意,新增到 output_rel
的 ForeignPath
路徑通常不會直接依賴於前一個處理步驟的路徑,因為它們的處理預計將在外部完成。然而,檢查前一個處理步驟生成的路徑可能有助於避免冗餘的規劃工作。)
有關更多資訊,請參見 Section 58.4。
如果 FDW 支援可寫的外表,則應根據 FDW 的需求和功能提供以下一個或多個回撥函式。
void AddForeignUpdateTargets(PlannerInfo *root, Index rtindex, RangeTblEntry *target_rte, Relation target_relation);
UPDATE
和 DELETE
操作是針對之前由表掃描函式獲取的行執行的。FDW 可能需要額外資訊,例如行 ID 或主鍵列的值,以確保它能識別要更新或刪除的確切行。為了支援這一點,此函式可以向在 UPDATE
或 DELETE
期間要從外表檢索的列列表中新增額外的隱藏的(或“垃圾”)目標列。
要做到這一點,構造一個表示您需要的額外值的 Var
,並將其與垃圾列的名稱一起傳遞給 add_row_identity_var
。(如果您需要多個列,可以執行此操作多次。)您必須為每個需要的不同 Var
選擇一個唯一的垃圾列名稱,但除了 Var
欄位 varno
不同之外,相同的 Var
可以並且應該共享一個列名。核心系統使用垃圾列名稱 tableoid
來表示表的 tableoid
列,ctid
或 ctid
來表示 N
ctid
,wholerow
來表示標記為 vartype
= RECORD
的整行 Var
,以及 wholerow
來表示 N
vartype
等於表宣告的行型別的整行 Var
。當您能重用這些名稱時(規劃器將合併重複的相同垃圾列請求)。如果您需要除這些之外的其他型別的垃圾列,明智的做法是選擇一個以您的副檔名命名的字首,以避免與其他 FDW 衝突。
如果 AddForeignUpdateTargets
指標設定為 NULL
,則不會新增額外的目標表達式。(這將使得實現 DELETE
操作成為不可能,儘管 UPDATE
仍然可行,如果 FDW 依賴於不變的主鍵來識別行。)
List * PlanForeignModify(PlannerInfo *root, ModifyTable *plan, Index resultRelation, int subplan_index);
為外表上的插入、更新或刪除執行任何額外的規劃操作。此函式生成將附加到執行更新操作的 ModifyTable
計劃節點上的 FDW 私有資訊。此私有資訊必須採用 List
的形式,並在執行階段傳遞給 BeginForeignModify
。
root
是規劃器關於查詢的全域性資訊。plan
是 ModifyTable
計劃節點,除了 fdwPrivLists
欄位外,它是完整的。resultRelation
透過其範圍表索引標識目標外表。subplan_index
標識這是 ModifyTable
計劃節點的哪個目標,從零開始計數;如果您想索引到 plan
節點的目標關係結構中,請使用此欄位。
有關更多資訊,請參見 Section 58.4。
如果 PlanForeignModify
指標設定為 NULL
,則不執行額外的計劃時操作,並且傳遞給 BeginForeignModify
的 fdw_private
列表將是 NIL。
void BeginForeignModify(ModifyTableState *mtstate, ResultRelInfo *rinfo, List *fdw_private, int subplan_index, int eflags);
開始執行外表修改操作。此例程在執行器啟動期間被呼叫。它應該在實際的表修改之前執行任何必要的初始化。隨後,將為要插入、更新或刪除的元組呼叫 ExecForeignInsert/ExecForeignBatchInsert
、ExecForeignUpdate
或 ExecForeignDelete
。
mtstate
是正在執行的 ModifyTable
計劃節點的整體狀態;有關計劃和執行狀態的全域性資料可以透過此結構訪問。rinfo
是描述目標外表的 ResultRelInfo
結構。(ResultRelInfo
的 ri_FdwState
欄位可供 FDW 儲存其對該操作所需的任何私有狀態。)fdw_private
包含 PlanForeignModify
生成的私有資料(如果有)。subplan_index
標識這是 ModifyTable
計劃節點的哪個目標。eflags
包含描述此計劃節點的執行器操作模式的標誌位。
請注意,當 (eflags & EXEC_FLAG_EXPLAIN_ONLY)
為真時,此函式不應執行任何外部可見的操作;它應只執行最低限度的工作,以使節點狀態對 ExplainForeignModify
和 EndForeignModify
有效。
如果 BeginForeignModify
指標設定為 NULL
,則在執行器啟動期間不執行任何操作。
TupleTableSlot * ExecForeignInsert(EState *estate, ResultRelInfo *rinfo, TupleTableSlot *slot, TupleTableSlot *planSlot);
將一個元組插入外表。estate
是查詢的全域性執行狀態。rinfo
是描述目標外表的 ResultRelInfo
結構。slot
包含要插入的元組;它將匹配外表的行型別定義。planSlot
包含由 ModifyTable
計劃節點的子計劃生成的元組;它可能包含額外的“垃圾”列,這與 slot
不同。(planSlot
對於 INSERT
情況通常作用不大,但為了完整性而提供。)
返回值是包含實際插入資料的槽,或者如果沒有實際插入行,則返回 NULL(同樣,通常是觸發器的結果)。傳入的 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
相同,只是 slots
和 planSlots
包含多個元組,並且 *numSlots
指定了這些陣列中的元組數。
返回值是一個包含實際插入資料的槽陣列(這可能與提供的資料不同,例如作為觸發器操作的結果)。傳入的 slots
可用於此目的。成功插入的元組數在 *numSlots
中返回。
返回槽中的資料僅在 INSERT
語句涉及檢視 WITH CHECK OPTION
時使用;或者當外表具有 AFTER ROW
觸發器時使用。觸發器需要所有列,但 FDW 可以根據 WITH CHECK OPTION
約束的內容選擇最佳化掉返回某些或所有列。
如果 ExecForeignBatchInsert
或 GetForeignModifyBatchSize
指標設定為 NULL
,則嘗試插入外表將使用 ExecForeignInsert
。如果 INSERT
具有 RETURNING
子句,則不使用此函式。
請注意,當插入路由到分割槽表的分割槽或在外表上執行 COPY FROM
時,也會呼叫此函式,此時的呼叫方式與 INSERT
情況不同。請參閱下面描述的回撥函式,這些函式允許 FDW 支援這一點。
int GetForeignModifyBatchSize(ResultRelInfo *rinfo);
報告單個 ExecForeignBatchInsert
呼叫可處理的指定外表的最大元組數。執行器最多將給定的元組數傳遞給 ExecForeignBatchInsert
。rinfo
是描述目標外表的 ResultRelInfo
結構。期望 FDW 提供一個外伺服器和/或外表選項供使用者設定此值,或者使用某個硬編碼值。
如果 ExecForeignBatchInsert
或 GetForeignModifyBatchSize
指標設定為 NULL
,則嘗試插入外表將使用 ExecForeignInsert
。
TupleTableSlot * ExecForeignUpdate(EState *estate, ResultRelInfo *rinfo, TupleTableSlot *slot, TupleTableSlot *planSlot);
更新外表中的一個元組。estate
是查詢的全域性執行狀態。rinfo
是描述目標外表的 ResultRelInfo
結構。slot
包含元組的新資料;它將匹配外表的行型別定義。planSlot
包含由 ModifyTable
計劃節點的子計劃生成的元組。與 slot
不同,此元組僅包含查詢更改的列的新值,因此不要依賴外表的屬性編號來索引 planSlot
。此外,planSlot
通常包含額外的“垃圾”列。特別是,AddForeignUpdateTargets
請求的任何垃圾列都將從此槽中可用。
返回值是包含實際更新後的行的槽,或者如果沒有實際更新行,則返回 NULL(同樣,通常是觸發器的結果)。傳入的 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
,則在執行器關閉期間不執行任何操作。
透過 INSERT
或 COPY FROM
插入到分割槽表中的元組會被路由到分割槽。如果 FDW 支援可路由的外表分割槽,則還應提供以下回調函式。當在外表上執行 COPY FROM
時,也會呼叫這些函式。
void BeginForeignInsert(ModifyTableState *mtstate, ResultRelInfo *rinfo);
開始執行外表上的插入操作。此例程在第一個元組被插入到外表之前呼叫,這兩種情況都適用:當它是在表路由選擇的分割槽時,以及在 COPY FROM
命令中指定的目標時。它應在實際插入之前執行任何必要的初始化。隨後,將為要插入到外表中的元組呼叫 ExecForeignInsert
或 ExecForeignBatchInsert
。
mtstate
是正在執行的 ModifyTable
計劃節點的整體狀態;有關計劃和執行狀態的全域性資料可以透過此結構訪問。rinfo
是描述目標外表的 ResultRelInfo
結構。(ResultRelInfo
的 ri_FdwState
欄位可供 FDW 儲存其對該操作所需的任何私有狀態。)
當此函式由 COPY FROM
命令呼叫時,mtstate
中的計劃相關全域性資料將不提供,並且隨後為每個插入的元組呼叫的 ExecForeignInsert
的 planSlot
引數為 NULL
,無論外表是用於路由元組的分割槽還是命令中指定的目標。
如果 BeginForeignInsert
指標設定為 NULL
,則在初始化期間不執行任何操作。
請注意,如果 FDW 不支援可路由的外表分割槽和/或在外表上執行 COPY FROM
,則此函式或隨後呼叫的 ExecForeignInsert/ExecForeignBatchInsert
必須根據需要丟擲錯誤。
void EndForeignInsert(EState *estate, ResultRelInfo *rinfo);
結束插入操作並釋放資源。通常不需要釋放 palloc 分配的記憶體,但例如開啟的檔案和到遠端伺服器的連線應被清理。
如果 EndForeignInsert
指標設定為 NULL
,則在終止期間不執行任何操作。
int IsForeignRelUpdatable(Relation rel);
報告指定外表支援哪些更新操作。返回值應為表示外表支援哪些操作的規則事件編號的位掩碼,使用 CmdType
列舉;即,(1 << CMD_UPDATE) = 4
表示 UPDATE
,(1 << CMD_INSERT) = 8
表示 INSERT
,(1 << CMD_DELETE) = 16
表示 DELETE
。
如果 IsForeignRelUpdatable
指標設定為 NULL
,則假定外表是可插入、可更新或可刪除的,如果 FDW 分別提供 ExecForeignInsert
、ExecForeignUpdate
或 ExecForeignDelete
。此函式僅在 FDW 支援某些表可更新而另一些表不可更新時才需要。(即使如此,也可以在執行例程中丟擲錯誤而不是在此函式中檢查。但是,此函式用於確定可更新性以顯示在 information_schema
檢視中。)
對某些外表的插入、更新和刪除可以透過實現一組替代介面進行最佳化。用於插入、更新和刪除的常規介面會獲取遠端伺服器的行,然後逐個修改這些行。在某些情況下,這種逐行方法是必需的,但它可能效率低下。如果外伺服器可以在不實際檢索它們的情況下確定應修改哪些行,並且沒有會影響操作的本地結構(行級本地觸發器、儲存的生成列或父檢視中的 WITH CHECK OPTION
約束),則可以安排使整個操作在外伺服器上執行。下面描述的介面使之成為可能。
bool PlanDirectModify(PlannerInfo *root, ModifyTable *plan, Index resultRelation, int subplan_index);
決定是否可以安全地在外伺服器上執行直接修改。如果可以,則在執行所需規劃操作後返回 true
。否則,返回 false
。此可選函式在查詢規劃期間被呼叫。如果此函式成功,則在執行階段將呼叫 BeginDirectModify
、IterateDirectModify
和 EndDirectModify
。否則,表修改將使用上面描述的表更新函式執行。引數與 PlanForeignModify
相同。
為了在外伺服器上執行直接修改,此函式必須用一個 ForeignScan
計劃節點重寫目標子計劃,該節點在外伺服器上執行直接修改。ForeignScan
的 operation
和 resultRelation
欄位必須相應設定。operation
必須設定為與語句型別對應的 CmdType
列舉(即,UPDATE
為 CMD_UPDATE
,INSERT
為 CMD_INSERT
,DELETE
為 CMD_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)
為真時,此函式不應執行任何外部可見的操作;它應只執行最低限度的工作,以使節點狀態對 ExplainDirectModify
和 EndDirectModify
有效。
如果 BeginDirectModify
指標設定為 NULL
,則不嘗試在外伺服器上執行直接修改。
TupleTableSlot * IterateDirectModify(ForeignScanState *node);
當 INSERT
、UPDATE
或 DELETE
查詢沒有 RETURNING
子句時,在外伺服器上直接修改後只需返回 NULL。當查詢有該子句時,獲取一行包含計算 RETURNING
所需資料的結果,並將其返回到元組表槽(節點的 ScanTupleSlot
應用於此目的)。實際插入、更新或刪除的資料必須儲存在 node->resultRelInfo->ri_projectReturning->pi_exprContext->ecxt_scantuple
中。如果沒有更多可用行,則返回 NULL。請注意,這在一個生命週期短的記憶體上下文中呼叫,該上下文在呼叫之間會被重置。如果在 BeginDirectModify
中建立記憶體上下文以獲得更長生命週期的儲存,或者使用節點的 EState
的 es_query_cxt
。
返回的行必須匹配 fdw_scan_tlist
目標列表(如果提供了),否則必須匹配正在更新的外表的行型別。如果您選擇最佳化掉獲取 RETURNING
計算不需要的列,則應在這些列位置插入 NULL,或者生成一個省略了這些列的 fdw_scan_tlist
列表。
無論查詢是否有該子句,查詢報告的行數必須由 FDW 本身增加。當查詢沒有該子句時,FDW 還必須在 EXPLAIN ANALYZE
的情況下為 ForeignScanState
節點增加行計數。
如果 IterateDirectModify
指標設定為 NULL
,則不嘗試在外伺服器上執行直接修改。
void EndDirectModify(ForeignScanState *node);
在直接修改外伺服器之後清理資源。通常不需要釋放 palloc 分配的記憶體,但例如開啟的檔案和到遠端伺服器的連線應被清理。
如果 EndDirectModify
指標設定為 NULL
,則不嘗試在外伺服器上執行直接修改。
TRUNCATE
的 FDW 例程 #void ExecForeignTruncate(List *rels, DropBehavior behavior, bool restart_seqs);
截斷外表。當在 TRUNCATE 外表上執行時呼叫此函式。rels
是要截斷的外表 Relation
資料結構的列表。
behavior
是 DROP_RESTRICT
或 DROP_CASCADE
,分別指示在原始 TRUNCATE
命令中請求了 RESTRICT
或 CASCADE
選項。
如果 restart_seqs
為 true
,則原始 TRUNCATE
命令請求了 RESTART IDENTITY
行為,否則請求的是 CONTINUE IDENTITY
行為。
請注意,原始 TRUNCATE
命令中指定的 ONLY
選項不會傳遞給 ExecForeignTruncate
。此行為類似於在外表上對 SELECT
、UPDATE
和 DELETE
的回撥函式。
ExecForeignTruncate
為要截斷外表的外伺服器呼叫一次。這意味著 rels
中包含的所有外表必須屬於同一個伺服器。
如果 ExecForeignTruncate
指標設定為 NULL
,則嘗試截斷外表將以錯誤訊息失敗。
如果 FDW 希望支援延遲行鎖定(如 Section 58.5 所述),則必須提供以下回調函式。
RowMarkType GetForeignRowMarkType(RangeTblEntry *rte, LockClauseStrength strength);
報告要對外表使用的行標記選項。rte
是表的 RangeTblEntry
節點,strength
描述了相關 FOR UPDATE/SHARE
子句請求的鎖強度(如果有)。結果必須是 RowMarkType
列舉型別的一個成員。
此函式在查詢規劃期間為出現在 UPDATE
、DELETE
或 SELECT FOR UPDATE/SHARE
查詢中且不是 UPDATE
或 DELETE
目標的每個外表呼叫。
如果 GetForeignRowMarkType
指標設定為 NULL
,則始終使用 ROW_MARK_COPY
選項。(這意味著 RefetchForeignRow
將永遠不會被呼叫,因此也不需要提供它。)
有關更多資訊,請參見 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,在這種情況下,外表資料包裝器可以自己構建本地路徑,或者可以選擇不為該連線建立訪問路徑。)
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
期間不列印額外資訊。
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
陣列中。必須返回實際收集的行數。此外,將表中活動行和死行的估計總數儲存到輸出引數 totalrows
和 totaldeadrows
中。(如果 FDW 沒有死行的概念,則將 totaldeadrows
設定為零。)
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 可以忽略 ImportForeignSchemaStmt
的 local_schema
欄位,因為核心伺服器將自動將該名稱插入到解析的 CREATE FOREIGN TABLE
命令中。
FDW 也不必關心實現 list_type
和 table_list
指定的過濾,因為核心伺服器將自動跳過根據這些選項排除的表的任何返回命令。然而,避免為被排除的表建立命令的工作通常是有用的。函式 IsImportableForeignTable()
可能有助於測試給定的外表名稱是否會透過過濾器。
如果 FDW 不支援匯入表定義,則 ImportForeignSchema
指標可以設定為 NULL
。
一個 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 段消失之前採取某些操作的外表資料包裝器應實現此方法。
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
。建議使用 ExecAsyncRequestDone
或 ExecAsyncRequestPending
來設定 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
中的輸出引數。
List * ReparameterizeForeignPathByChild(PlannerInfo *root, List *fdw_private, RelOptInfo *child_rel);
當將由給定子關係 child_rel
的最頂層父級引數化的路徑轉換為由子關係引數化時,會呼叫此函式。該函式用於重新引數化 ForeignPath
的 fdw_private
成員中儲存的任何路徑或轉換任何表示式節點。回撥可以使用 reparameterize_path_by_child
、adjust_appendrel_attrs
或 adjust_appendrel_attrs_multilevel
,視情況而定。
如果您在文件中看到任何不正確、不符合您對特定功能的經驗或需要進一步澄清的內容,請使用 此表單 報告文件問題。