支援的版本:目前 (17) / 16 / 15 / 14 / 13
開發版本:devel
不支援的版本:12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2

57.4. 外部資料包裝器查詢計畫 #

FDW 回呼函式 GetForeignRelSizeGetForeignPathsGetForeignPlanPlanForeignModifyGetForeignJoinPathsGetForeignUpperPathsPlanDirectModify 必須符合 PostgreSQL 計畫器的運作方式。以下是一些關於它們必須做什麼的注意事項。

rootbaserel 中的資訊可用於減少必須從外部資料表擷取的資訊量(因此降低成本)。baserel->baserestrictinfo 特別有趣,因為它包含應該用於篩選要擷取之資料列的限制條件(WHERE 子句)。(FDW 本身不需要強制執行這些條件,因為核心執行器可以改為檢查它們。)baserel->reltarget->exprs 可用於確定需要擷取哪些欄位;但請注意,它僅列出 ForeignScan 計畫節點必須發出的欄位,而不是用於條件評估但不被查詢輸出的欄位。

各種私有欄位可用於 FDW 規劃函式,以保留資訊。通常,無論您儲存在 FDW 私有欄位中的任何內容都應該使用 palloc 配置,以便在規劃結束時回收它。

baserel->fdw_private 是一個 void 指標,FDW 規劃函式可用於儲存與特定外部資料表相關的資訊。核心計畫器除了在建立 RelOptInfo 節點時將其初始化為 NULL 之外,不會觸摸它。它對於將資訊從 GetForeignRelSize 傳遞到 GetForeignPaths 和/或從 GetForeignPaths 傳遞到 GetForeignPlan 很有用,從而避免重新計算。

GetForeignPaths 可以透過將私有資訊儲存在 ForeignPath 節點的 fdw_private 欄位中來識別不同存取路徑的含義。fdw_private 被宣告為 List 指標,但實際上可以包含任何內容,因為核心計畫器不會觸摸它。但是,最佳實踐是使用可以被 nodeToString 轉儲的表示形式,以便與後端中可用的除錯支援一起使用。

GetForeignPlan 可以檢查所選 ForeignPath 節點的 fdw_private 欄位,並且可以產生 fdw_exprsfdw_private 列表以放置在 ForeignScan 計畫節點中,在那裡它們將在執行時可用。這兩個列表都必須以 copyObject 知道如何複製的形式表示。fdw_private 列表沒有其他限制,並且不會被核心後端以任何方式解譯。fdw_exprs 列表(如果不是 NIL)預期包含打算在執行時執行的表達式樹。這些樹將經過計畫器的後處理,使其完全可執行。

GetForeignPlan 中,通常可以將傳入的目標列表按原樣複製到計畫節點中。傳遞的 scan_clauses 列表包含與 baserel->baserestrictinfo 相同的子句,但可以重新排序以提高執行效率。在簡單的情況下,FDW 可以只從 scan_clauses 列表中剝離 RestrictInfo 節點(使用 extract_actual_clauses),並將所有子句放入計畫節點的 qual 列表中,這意味著所有子句都將在執行時由執行器檢查。更複雜的 FDW 可能能夠在內部檢查某些子句,在這種情況下,可以從計畫節點的 qual 列表中刪除這些子句,以便執行器不會浪費時間重新檢查它們。

例如,FDW 可能會識別形式為 foreign_variable = sub_expression 的一些限制子句,它確定可以在遠端伺服器上執行,前提是 sub_expression 的本地評估值。這種子句的實際識別應該在 GetForeignPaths 期間發生,因為它會影響路徑的成本估算。路徑的 fdw_private 欄位可能包含指向已識別子句的 RestrictInfo 節點的指標。然後,GetForeignPlan 將從 scan_clauses 中刪除該子句,但將 sub_expression 新增到 fdw_exprs,以確保它被按摩成可執行形式。它可能還會將控制資訊放入計畫節點的 fdw_private 欄位中,以告知執行函式在執行時該怎麼做。傳輸到遠端伺服器的查詢將涉及類似於 WHERE foreign_variable = $1 的內容,參數值在執行時從評估 fdw_exprs 表達式樹中獲得。

為了確保在 READ COMMITTED 隔離層級的正確行為,任何從計畫節點的 qual 列表中移除的子句,都必須改為新增到 fdw_recheck_quals,或者由 RecheckForeignScan 重新檢查。 當查詢中涉及的其他資料表發生並行更新時,執行器可能需要驗證元組的所有原始 quals 是否仍然滿足,並可能需要針對不同的參數值集進行驗證。 使用 fdw_recheck_quals 通常比在 RecheckForeignScan 內部實作檢查更容易,但是當 outer join 被下推時,此方法將不足夠,因為在這種情況下,join 元組可能會有某些欄位變為 NULL,而不會完全拒絕該元組。

另一個 ForeignScan 欄位可以由 FDW 填寫的是 fdw_scan_tlist,它描述了 FDW 為此計畫節點傳回的元組。 對於簡單的外部資料表掃描,可以將其設定為 NIL,表示傳回的元組具有為外部資料表宣告的列類型。 非 NIL 值必須是目標列表(TargetEntry 的列表),其中包含表示傳回欄位的 Vars 和/或表達式。 例如,這可以用於表明 FDW 省略了它認為查詢不需要的某些欄位。 此外,如果 FDW 可以比本地計算更便宜地計算查詢使用的表達式,則它可以將這些表達式新增到 fdw_scan_tlist。 請注意,join 計畫(由 GetForeignJoinPaths 建立的路徑)必須始終提供 fdw_scan_tlist,以描述它們將傳回的欄位集。

FDW 應始終至少建構一個僅依賴於資料表的限制子句的路徑。 在 join 查詢中,它也可以選擇建構依賴於 join 子句的路徑,例如 foreign_variable = local_variable。 這些子句不會在 baserel->baserestrictinfo 中找到,但必須在關聯的 join 列表中尋找。 使用此類子句的路徑稱為「參數化路徑」。 它必須使用 param_info 的適當值來識別所選 join 子句中使用的其他關聯; 使用 get_baserel_parampathinfo 來計算該值。 在 GetForeignPlan 中,join 子句的 local_variable 部分將新增到 fdw_exprs,然後在執行時,該案例的工作方式與普通限制子句相同。

如果 FDW 支援遠端 join,則 GetForeignJoinPaths 應以與 GetForeignPaths 對於基本資料表的工作方式非常相似的方式,為潛在的遠端 join 產生 ForeignPath。 有關預期 join 的資訊可以以與上述描述相同的方式傳遞到 GetForeignPlan。 但是,baserestrictinfo 與 join 關聯無關;相反,特定 join 的相關 join 子句作為單獨的參數 (extra->restrictlist) 傳遞給 GetForeignJoinPaths

FDW 也可以支援直接執行一些高於掃描和 join 層級的計畫動作,例如 grouping 或 aggregation。 為了提供這些選項,FDW 應該產生路徑並將它們插入到適當的上層關聯中。 例如,表示遠端 aggregation 的路徑應使用 add_path 插入到 UPPERREL_GROUP_AGG 關聯中。 此路徑將根據成本與透過讀取外部關聯的簡單掃描路徑執行的本地 aggregation 進行比較(請注意,還必須提供此類路徑,否則在計畫時間會出現錯誤)。 如果遠端 aggregation 路徑獲勝(通常會這樣),則會以通常的方式將其轉換為計畫,方法是呼叫 GetForeignPlan。 產生這些路徑的建議位置是在 GetForeignUpperPaths 回呼函數中,如果查詢的所有基本關聯都來自同一個 FDW,則會針對每個上層關聯(即,每個掃描/join 後處理步驟)呼叫該函數。

PlanForeignModify第 57.2.4 節 中描述的其他回呼是圍繞著外部關聯將以通常的方式掃描,然後個別的列更新將由本地 ModifyTable 計畫節點驅動的假設來設計的。 對於需要讀取本地資料表以及外部資料表的一般情況,此方法是必要的。 但是,如果該操作可以完全由外部伺服器執行,則 FDW 可以產生表示該操作的路徑,並將其插入到 UPPERREL_FINAL 上層關聯中,它將與 ModifyTable 方法競爭。 這種方法也可以用於實作遠端 SELECT FOR UPDATE,而不是使用 第 57.2.6 節 中描述的列鎖定回呼。 請記住,插入到 UPPERREL_FINAL 中的路徑負責實作查詢的所有行為。

在規劃 UPDATEDELETE 時,PlanForeignModifyPlanDirectModify 可以查閱外部資料表的 RelOptInfo 結構,並使用先前由掃描規劃函數建立的 baserel->fdw_private 資料。 但是,在 INSERT 中,目標資料表不會被掃描,因此沒有它的 RelOptInfoPlanForeignModify 傳回的 List 具有與 ForeignScan 計畫節點的 fdw_private 列表相同的限制,也就是說,它必須僅包含 copyObject 知道如何複製的結構。

帶有 ON CONFLICT 子句的 INSERT 不支援指定衝突目標,因為遠端資料表上的唯一約束或排除約束在本地是未知的。 這反過來表示不支援 ON CONFLICT DO UPDATE,因為規格在那裡是強制性的。

提交更正

如果您在文件中發現任何不正確、與您對特定功能的體驗不符或需要進一步澄清的地方,請使用此表格回報文件問題。