TRUNCATE
的 FDW 常式EXPLAIN
的 FDW 常式ANALYZE
的 FDW 常式IMPORT FOREIGN SCHEMA
的 FDW 常式FDW 處理常式函式會傳回一個 palloc'd FdwRoutine
結構,其中包含指向以下描述的回呼函式的指標。 掃描相關的函式是必要的,其餘的是可選的。
FdwRoutine
結構類型在 src/include/foreign/fdwapi.h
中宣告,請參閱該檔案以取得更多詳細資訊。
void GetForeignRelSize(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid);
取得外部表格的關係大小估計值。 這會在查詢的規劃開始時呼叫,該查詢會掃描外部表格。 root
是規劃器關於查詢的全域資訊;baserel
是規劃器關於此表格的資訊;而 foreigntableid
是外部表格的 pg_class
OID。(foreigntableid
可以從規劃器資料結構中取得,但為了節省精力,會明確傳遞它。)
此函式應更新 baserel->rows
,使其成為表格掃描傳回的預期資料列數,並考量限制條件限定詞完成的篩選。 baserel->rows
的初始值只是一個常數預設估計值,如果可能,應替換它。 如果函式可以計算出更好的平均結果資料列寬度估計值,它也可以選擇更新 baserel->width
。(初始值基於欄位資料類型和上次 ANALYZE
測量的欄位平均寬度值。) 此外,如果函式可以計算出更好的外部表格總資料列計數估計值,則此函式可以更新 baserel->tuples
。(初始值來自 pg_class
。reltuples
,它代表上次 ANALYZE
看到的總資料列計數;如果未在此外部表格上執行 ANALYZE
,則它將為 -1
。)
有關其他資訊,請參閱第 57.4 節。
void GetForeignPaths(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid);
為掃描外部表格建立可能的存取路徑。 這會在查詢規劃期間呼叫。 參數與 GetForeignRelSize
相同,該函數已被呼叫。
此函式必須為掃描外部表格產生至少一個存取路徑(ForeignPath
節點),並且必須呼叫 add_path
以將每個此類路徑新增到 baserel->pathlist
。 建議使用 create_foreignscan_path
來建置 ForeignPath
節點。 該函式可以產生多個存取路徑,例如,一個具有有效 pathkeys
的路徑來表示預先排序的結果。 每個存取路徑都必須包含成本估計值,並且可以包含識別預期特定掃描方法所需的任何 FDW 私有資訊。
有關其他資訊,請參閱第 57.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
節點。
有關其他資訊,請參閱第 57.4 節。
void BeginForeignScan(ForeignScanState *node, int eflags);
開始執行外部掃描。這個函式會在執行器啟動時被呼叫。它應該執行掃描開始前所需的任何初始化動作,但不應該開始執行實際的掃描(實際掃描應該在第一次呼叫 IterateForeignScan
時執行)。ForeignScanState
節點已經被建立,但它的 fdw_state
欄位仍然是 NULL。關於要掃描的表格的資訊可以透過 ForeignScanState
節點存取(特別是從底層的 ForeignScan
計劃節點,該節點包含由 GetForeignPlan
提供的任何 FDW 私有資訊)。eflags
包含描述此計劃節點的執行器操作模式的旗標位元。
請注意,當 (eflags & EXEC_FLAG_EXPLAIN_ONLY)
為 true 時,此函式不應執行任何外部可見的動作;它只應執行使節點狀態對 ExplainForeignScan
和 EndForeignScan
有效所需的最小值。
TupleTableSlot * IterateForeignScan(ForeignScanState *node);
從外部來源提取一行,並將其返回到元組表格槽 (tuple table slot) 中(應該使用節點的 ScanTupleSlot
來完成此目的)。如果沒有更多列可用,則返回 NULL。元組表格槽基礎架構允許返回物理或虛擬元組;在大多數情況下,從效能的角度來看,後者是更佳的選擇。請注意,此函式是在短暫的記憶體上下文中被呼叫的,該記憶體上下文將在每次呼叫之間被重置。如果您需要更長時間的儲存空間,請在 BeginForeignScan
中建立記憶體上下文,或使用節點的 EState
的 es_query_cxt
。
如果提供了 fdw_scan_tlist
目標列表,則返回的列必須與之匹配,否則它們必須與正在掃描的外部表格的列類型匹配。如果您選擇最佳化掉對不需要的列的提取,則應在這些列的位置插入 null,或者生成一個省略了這些列的 fdw_scan_tlist
列表。
請注意,PostgreSQL 的執行器並不關心返回的列是否違反了外部表格上定義的任何約束 — 但規劃器很關心,如果外部表格中存在不滿足已宣告約束的列,則可能會錯誤地最佳化查詢。如果使用者宣告了約束應該為真,但約束卻被違反了,那麼就應該引發一個錯誤(就像在資料類型不匹配的情況下需要做的那樣)。
void ReScanForeignScan(ForeignScanState *node);
從頭開始重新啟動掃描。請注意,掃描所依賴的任何參數可能已經更改了值,因此新的掃描不一定會返回完全相同的列。
void EndForeignScan(ForeignScanState *node);
結束掃描並釋放資源。通常釋放 palloc'd 記憶體並不重要,但例如,打開的檔案和到遠端伺服器的連線應該被清理乾淨。
如果 FDW 支援遠端執行外部聯結(而不是透過提取兩個表格的資料並在本地執行聯結),則它應該提供此回呼函式。
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
路徑轉換為計劃。
如果為聯結選擇了 ForeignPath
路徑,它將代表整個聯結過程;為組成表格和輔助聯結生成的路徑將不會被使用。聯結路徑的後續處理與掃描單個外部表格的路徑的處理方式非常相似。一個不同之處在於,結果 ForeignScan
計劃節點的 scanrelid
應設定為零,因為沒有單個關係代表它;相反,ForeignScan
節點的 fs_relids
欄位代表已聯結的關係集合。(後者欄位由核心規劃器程式碼自動設定,無需由 FDW 填寫。)另一個不同之處在於,由於無法從系統目錄中找到遠端聯結的列列表,因此 FDW 必須使用適當的 TargetEntry
節點列表填寫 fdw_scan_tlist
,表示它在執行時將在返回的元組中提供的列集合。
從 PostgreSQL 16 開始,fs_relids
包括外部聯結的 rangetable 索引(如果有的話)。新欄位 fs_base_relids
僅包含基本關係索引,因此模擬 fs_relids
的舊語意。
有關其他資訊,請參閱第 57.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
則是代表此步驟輸入的關聯。extra
參數提供額外的詳細資訊,目前僅針對 UPPERREL_PARTIAL_GROUP_AGG
或 UPPERREL_GROUP_AGG
進行設定,在這種情況下,它指向一個 GroupPathExtraData
結構;或者針對 UPPERREL_FINAL
進行設定,在這種情況下,它指向一個 FinalPathExtraData
結構。(請注意,添加到 output_rel
的 ForeignPath
路徑通常不會直接依賴於 input_rel
的路徑,因為它們的處理預計會在外部完成。然而,檢查先前為上一個處理步驟生成的路徑,有助於避免冗餘的規劃工作。)
有關其他資訊,請參閱第 57.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
,您必須選擇一個不同的無用列名稱,但除了 varno
欄位之外相同的 Var
可以並且應該共享一個列名稱。核心系統將無用列名稱 tableoid
用於表格的 tableoid
列,ctid
或 ctid
用於 N
ctid
,wholerow
用於標記為 vartype
= RECORD
的整列 Var
,以及 wholerow
用於 N
vartype
等於表格宣告列類型的整列 Var
。在可以的情況下重複使用這些名稱(規劃器會合併對相同無用列的重複請求)。如果您需要除這些之外的其他種類的無用列,明智的做法可能是選擇以您的擴充名稱為前綴的名稱,以避免與其他 FDW 發生衝突。
如果 AddForeignUpdateTargets
指標設定為 NULL
,則不會添加額外的目標表達式。(這將使 DELETE
操作無法實現,但如果 FDW 依賴於不變的主鍵來識別列,則 UPDATE
可能仍然可行。)
List * PlanForeignModify(PlannerInfo *root, ModifyTable *plan, Index resultRelation, int subplan_index);
對外部表格上的插入、更新或刪除執行所需的任何其他規劃操作。此函式會產生 FDW 私有資訊,這些資訊將附加到執行更新操作的 ModifyTable
計劃節點。此私有資訊必須採用 List
的形式,並將在執行階段傳遞給 BeginForeignModify
。
root
是規劃器關於查詢的整體資訊。plan
是 ModifyTable
計劃節點,除了 fdwPrivLists
欄位之外是完整的。resultRelation
按其範圍表格索引識別目標外部表格。subplan_index
識別此 ModifyTable
計劃節點的哪個目標,從零開始計數;如果您想索引到 plan
節點的每個目標關係子結構中,請使用此索引。
有關其他資訊,請參閱第 57.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)
為 true 時,此函式不應執行任何外部可見的動作;它應該只執行使節點狀態對於 ExplainForeignModify
和 EndForeignModify
有效所需的最小值。
如果 BeginForeignModify
指標設定為 NULL
,則在執行器啟動期間不會採取任何動作。
TupleTableSlot * ExecForeignInsert(EState *estate, ResultRelInfo *rinfo, TupleTableSlot *slot, TupleTableSlot *planSlot);
將一個元組插入到外部表格中。estate
是查詢的全域執行狀態。rinfo
是描述目標外部表格的 ResultRelInfo
結構。slot
包含要插入的元組;它將匹配外部表格的列類型定義。planSlot
包含由 ModifyTable
計劃節點的子計劃產生的元組;它與 slot
的不同之處在於可能包含額外的「無用」列。(planSlot
通常對於 INSERT
情況不太重要,但為了完整性而提供。)
傳回值是一個包含實際插入資料的 slot(這可能與提供的資料不同,例如由於觸發程式動作的結果),或者如果實際上沒有插入任何列則傳回 NULL(同樣,通常是由於觸發程式的結果)。傳入的 slot
可以重複用於此目的。
僅當 INSERT
語句具有 RETURNING
子句或涉及 WITH CHECK OPTION
的檢視表;或者如果外部表格具有 AFTER ROW
觸發程式時,才使用傳回的 slot 中的資料。觸發程式需要所有列,但 FDW 可以選擇優化掉傳回某些或所有列,具體取決於 RETURNING
子句或 WITH CHECK OPTION
約束的內容。無論如何,必須傳回一些 slot 以表明成功,否則查詢報告的列計數將錯誤。
如果 ExecForeignInsert
指標設定為 NULL
,則嘗試插入到外部表格中將會失敗並顯示錯誤訊息。
請注意,當將路由過的元組插入到外部資料表分割區,或在外部資料表上執行 COPY FROM
時,也會呼叫此函式。在這些情況下,它的呼叫方式與 INSERT
情況不同。請參閱下方描述的回呼函式,這些函式允許 FDW 支援這種情況。
TupleTableSlot ** ExecForeignBatchInsert(EState *estate, ResultRelInfo *rinfo, TupleTableSlot **slots, TupleTableSlot **planSlots, int *numSlots);
將多個元組批量插入到外部資料表中。參數與 ExecForeignInsert
相同,除了 slots
和 planSlots
包含多個元組,且 *numSlots
指定這些陣列中的元組數量。
回傳值是一個包含實際插入資料的 slots 陣列(這可能與提供的資料不同,例如由於觸發程序動作的結果)。傳入的 slots
可以重複使用於此目的。成功插入的元組數量將回傳於 *numSlots
中。
只有在 INSERT
語句涉及具有 WITH CHECK OPTION
的視窗,或者外部資料表具有 AFTER ROW
觸發程序時,才會使用回傳的 slot 中的資料。觸發程序需要所有欄位,但 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
請求的任何垃圾欄位都將可從此 slot 取得。
回傳值是一個包含實際更新列的 slot(這可能與提供的資料不同,例如由於觸發程序動作的結果),如果沒有實際更新列,則為 NULL(同樣,通常是由於觸發程序)。傳入的 slot
可以重複使用於此目的。
只有在 UPDATE
語句具有 RETURNING
子句或涉及具有 WITH CHECK OPTION
的視窗,或者外部資料表具有 AFTER ROW
觸發程序時,才會使用回傳的 slot 中的資料。觸發程序需要所有欄位,但 FDW 可以選擇最佳化,根據 RETURNING
子句或 WITH CHECK OPTION
約束的內容,省略回傳某些或所有欄位。無論如何,必須回傳一些 slot 以指示成功,否則查詢回報的列計數將會錯誤。
如果 ExecForeignUpdate
指標設定為 NULL
,則嘗試更新外部資料表將會失敗並顯示錯誤訊息。
TupleTableSlot * ExecForeignDelete(EState *estate, ResultRelInfo *rinfo, TupleTableSlot *slot, TupleTableSlot *planSlot);
從外部資料表中刪除一個元組。estate
是查詢的全局執行狀態。rinfo
是描述目標外部資料表的 ResultRelInfo
結構。slot
在呼叫時不包含任何有用的內容,但可用於保存回傳的元組。planSlot
包含由 ModifyTable
計劃節點的子計劃產生的元組;特別是,它將攜帶 AddForeignUpdateTargets
請求的任何垃圾欄位。垃圾欄位必須用於識別要刪除的元組。
回傳值是一個包含已刪除列的 slot,如果沒有刪除列,則為 NULL(通常是由於觸發程序)。傳入的 slot
可用於保存要回傳的元組。
只有在 DELETE
查詢具有 RETURNING
子句,或者外部資料表具有 AFTER ROW
觸發程序時,才會使用回傳的 slot 中的資料。觸發程序需要所有欄位,但 FDW 可以選擇最佳化,根據 RETURNING
子句的內容,省略回傳某些或所有欄位。無論如何,必須回傳一些 slot 以指示成功,否則查詢回報的列計數將會錯誤。
如果 ExecForeignDelete
指標設定為 NULL
,則嘗試從外部資料表刪除將會失敗並顯示錯誤訊息。
void EndForeignModify(EState *estate, ResultRelInfo *rinfo);
結束資料表更新並釋放資源。通常釋放 palloc'd 記憶體並不重要,但例如,應該清理開啟的檔案和與遠端伺服器的連線。
如果 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
列舉(也就是說,CMD_UPDATE
代表 UPDATE
,CMD_INSERT
代表 INSERT
,而 CMD_DELETE
代表 DELETE
),並且 resultRelation
參數必須複製到 resultRelation
欄位。
有關其他資訊,請參閱第 57.4 節。
如果 PlanDirectModify
指標設定為 NULL
,則不會嘗試在遠端伺服器上執行直接修改。
void BeginDirectModify(ForeignScanState *node, int eflags);
準備在遠端伺服器上執行直接修改。這在執行器啟動期間呼叫。它應該執行直接修改之前所需的任何初始化(該初始化應在首次呼叫 IterateDirectModify
時完成)。ForeignScanState
節點已經建立,但其 fdw_state
欄位仍然為 NULL。有關要修改的資料表的資訊可透過 ForeignScanState
節點(特別是從底層 ForeignScan
計畫節點,其中包含 PlanDirectModify
提供的任何 FDW 私有資訊)存取。eflags
包含描述執行器此計畫節點的操作模式的旗標位元。
請注意,當 (eflags & EXEC_FLAG_EXPLAIN_ONLY)
為 true 時,此函數不應執行任何外部可見的動作;它應該只做讓節點狀態對 ExplainDirectModify
和 EndDirectModify
有效所需的最小值。
如果 BeginDirectModify
指標設定為 NULL
,則不會嘗試在遠端伺服器上執行直接修改。
TupleTableSlot * IterateDirectModify(ForeignScanState *node);
當 INSERT
、UPDATE
或 DELETE
查詢沒有 RETURNING
子句時,在遠端伺服器上直接修改後,僅返回 NULL。當查詢具有此子句時,提取一個包含 RETURNING
計算所需資料的結果,並將其以元組表格槽 (tuple table slot) 的形式返回 (應使用節點的 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 希望支援延遲列鎖定 (如第 57.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
,因此也不需要提供它。)
有關更多資訊,請參閱 第 57.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
,則重新提取列的嘗試將失敗並顯示錯誤訊息。
有關更多資訊,請參閱 第 57.5 節。
bool RecheckForeignScan(ForeignScanState *node, TupleTableSlot *slot);
重新檢查先前返回的元組是否仍然匹配相關的掃描和連接限定詞,並可能提供元組的修改版本。對於不執行連接下推的外部資料包裝器,通常將此設定為 NULL
並適當地設定 fdw_recheck_quals
會更方便。但是,當下推外部連接時,即使所有需要的屬性都存在,僅僅將與所有基本表格相關的檢查重新應用於結果元組是不夠的,因為無法匹配某些限定詞可能會導致某些屬性變為 NULL,而不是不返回元組。RecheckForeignScan
可以重新檢查限定詞,如果它們仍然滿足則返回 true,否則返回 false,但它也可以將替換元組儲存到提供的槽中。
為了實作 join 下推(join pushdown),外部資料包裝器通常會建立一個替代的本地 join 計畫,該計畫僅用於重新檢查;這會變成 ForeignScan
的外部子計畫。當需要重新檢查時,可以執行此子計畫,並且將產生的元組儲存在插槽中。這個計畫不需要非常有效率,因為沒有基本資料表會傳回超過一個的列;例如,它可以將所有 join 實作為巢狀迴圈。可以使用函數 GetExistingLocalJoinPath
來搜尋現有的路徑,以找到合適的本地 join 路徑,該路徑可以用作替代的本地 join 計畫。GetExistingLocalJoinPath
在指定 join 關聯的路徑清單中搜尋未參數化的路徑。(如果它沒有找到這樣的路徑,則傳回 NULL,在這種情況下,外部資料包裝器可以自行建立本地路徑,或者選擇不為該 join 建立存取路徑。)
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
是一個 AsyncRequest
結構,描述了 ForeignScan
節點和從它請求元組的父 Append
節點。這個函式應該將元組儲存到 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
標記時,才會呼叫此函式,並且應將事件新增到 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
。
如果您在文件中發現任何不正確、與您使用特定功能的經驗不符或需要進一步澄清的地方,請使用此表格來回報文件問題。