索引存取方法必須在 IndexAmRoutine
中提供的索引建構和維護函數如下:
IndexBuildResult * ambuild (Relation heapRelation, Relation indexRelation, IndexInfo *indexInfo);
建立新的索引。索引關聯已實際建立,但為空。它必須填入存取方法所需的任何固定資料,以及表中已存在的所有元組的條目。通常,ambuild
函數將呼叫 table_index_build_scan()
來掃描表中的現有元組,並計算需要插入索引中的鍵。該函數必須傳回一個 palloc'd 結構,其中包含有關新索引的統計資訊。amcanbuildparallel
標誌指示存取方法是否支援平行索引建構。如果設定為 true
,系統將嘗試為建構分配平行工作者。僅支援非平行索引建構的存取方法應將此標誌設定為 false
。
void ambuildempty (Relation indexRelation);
建立一個空的索引,並將其寫入給定關聯的初始化分支(INIT_FORKNUM
)。此方法僅針對未記錄索引呼叫;寫入初始化分支的空索引將在每次伺服器重新啟動時複製到主關聯分支上。
bool aminsert (Relation indexRelation, Datum *values, bool *isnull, ItemPointer heap_tid, Relation heapRelation, IndexUniqueCheck checkUnique, bool indexUnchanged, IndexInfo *indexInfo);
將一個新的元組插入現有的索引中。values
和 isnull
陣列提供要索引的鍵值,而 heap_tid
是要索引的 TID。如果存取方法支援唯一索引(其 amcanunique
標誌為 true),則 checkUnique
指示要執行的唯一性檢查類型。這取決於唯一性約束是否可延遲;有關詳細資訊,請參閱第 62.5 節。通常,存取方法僅在執行唯一性檢查時才需要 heapRelation
參數(因為那時它必須查看堆以驗證元組的存活性)。
Boolean 值 indexUnchanged
提供了關於要索引的元組性質的提示。當它為真時,該元組是索引中某些現有元組的副本。新元組是邏輯上未更改的後續 MVCC 元組版本。當發生 UPDATE
時,如果未修改索引涵蓋的任何欄位,但仍然需要在索引中建立新版本,則會發生這種情況。索引 AM 可以使用此提示來決定在同一邏輯列累積多個版本的部分索引中應用自下而上的索引刪除。請注意,更新非鍵欄位或僅出現在部分索引謂詞中的欄位不會影響 indexUnchanged
的值。核心程式碼使用低開銷的方法來確定每個元組的 indexUnchanged
值,該方法允許誤報和誤判。索引 AM 不得將 indexUnchanged
視為關於元組可見性或版本控制的權威資訊來源。
該函數的 Boolean 結果值僅在 checkUnique
為 UNIQUE_CHECK_PARTIAL
時才重要。在這種情況下,true 結果表示已知新條目是唯一的,而 false 表示它可能不是唯一的(並且必須排程延遲的唯一性檢查)。對於其他情況,建議使用常數 false 結果。
有些索引可能不會索引所有元組。如果該元組不應被索引,aminsert
應該直接返回而不做任何事情。
如果索引 AM 希望在 SQL 陳述式中跨連續的索引插入快取資料,它可以在 indexInfo->ii_Context
中分配空間,並在 indexInfo->ii_AmCache
中儲存指向該資料的指標(最初將為 NULL)。如果在索引插入後必須釋放記憶體以外的資源,則可以提供 aminsertcleanup
,它將在釋放記憶體之前被呼叫。
void aminsertcleanup (Relation indexRelation, IndexInfo *indexInfo);
清除在 indexInfo->ii_AmCache
中跨連續插入維護的狀態。如果資料需要額外的清除步驟(例如,釋放釘選的緩衝區),並且僅僅釋放記憶體是不夠的,這非常有用。
IndexBulkDeleteResult * ambulkdelete (IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, void *callback_state);
從索引中刪除 tuple。這是一個「"大量刪除"」操作,其目的是掃描整個索引,並檢查每個條目以確定是否應該刪除它。傳入的 callback
函數必須以 callback(
的形式被呼叫,以確定任何特定的索引條目(由其引用的 TID 識別)是否要被刪除。必須返回 NULL 或一個 palloc'd 結構,其中包含有關刪除操作效果的統計資訊。如果沒有資訊需要傳遞給 TID
, callback_state) returns boolamvacuumcleanup
,則返回 NULL 也是可以的。
由於 maintenance_work_mem
的限制,當需要刪除大量 tuple 時,ambulkdelete
可能需要被呼叫多次。stats
參數是此索引先前呼叫的結果(在 VACUUM
操作中的第一次呼叫時為 NULL)。這允許 AM 在整個操作中累積統計資訊。通常,如果傳遞的 stats
不為 null,ambulkdelete
將修改並返回相同的結構。
IndexBulkDeleteResult * amvacuumcleanup (IndexVacuumInfo *info, IndexBulkDeleteResult *stats);
在 VACUUM
操作後(零或多個 ambulkdelete
呼叫)進行清理。除了返回索引統計資訊外,這不一定需要做任何事情,但它可能會執行批量清理,例如回收空的索引頁面。stats
是上次 ambulkdelete
呼叫返回的任何內容,如果由於沒有 tuple 需要刪除而沒有呼叫 ambulkdelete
,則為 NULL。如果結果不為 NULL,則它必須是一個 palloc'd 結構。它包含的統計資訊將用於更新 pg_class
,並且如果給出 VERBOSE
,將由 VACUUM
報告。如果在 VACUUM
操作期間索引根本沒有更改,則返回 NULL 是可以的,但否則應返回正確的統計資訊。
在 ANALYZE
操作完成時,也會呼叫 amvacuumcleanup
。在這種情況下,stats
始終為 NULL,並且任何返回值都將被忽略。可以通過檢查 info->analyze_only
來區分這種情況。建議存取方法在此類呼叫中除了插入後的清理之外不做任何事情,並且僅在 autovacuum worker process 中執行。
bool amcanreturn (Relation indexRelation, int attno);
通過返回該欄位的原始索引值,檢查索引是否可以在給定的欄位上支援僅索引掃描。屬性編號從 1 開始,也就是說,第一個欄位的 attno 為 1。如果支援,則返回 true,否則返回 false。此函數應始終為包含的欄位(如果支援這些欄位)返回 true,因為一個無法檢索的包含欄位沒有意義。如果存取方法根本不支援僅索引掃描,則可以在其 IndexAmRoutine
結構中將 amcanreturn
欄位設定為 NULL。
void amcostestimate (PlannerInfo *root, IndexPath *path, double loop_count, Cost *indexStartupCost, Cost *indexTotalCost, Selectivity *indexSelectivity, double *indexCorrelation, double *indexPages);
估計索引掃描的成本。此函數在下面的第 62.6 節中完整描述。
bytea * amoptions (ArrayType *reloptions, bool validate);
解析並驗證索引的 reloptions 陣列。僅當索引存在非 NULL 的 reloptions 陣列時才會呼叫它。reloptions
是一個 text
陣列,包含 name
=
value
形式的條目。該函數應建構一個 bytea
值,該值將被複製到索引的 relcache 條目的 rd_options
欄位中。bytea
值中的資料內容可由存取方法定義;大多數標準存取方法使用 struct StdRdOptions
。當 validate
為 true 時,如果任何選項無法識別或具有無效值,則該函數應報告適當的錯誤訊息;當 validate
為 false 時,應以靜默方式忽略無效的條目。(當載入已儲存在 pg_catalog
中的選項時,validate
為 false;僅當存取方法已更改其選項規則時,才會找到無效的條目,在這種情況下,忽略過時的條目是合適的。)如果需要預設行為,則返回 NULL 是可以的。
bool amproperty (Oid index_oid, int attno, IndexAMProperty prop, const char *propname, bool *res, bool *isnull);
amproperty
方法允許索引存取方法覆蓋 pg_index_column_has_property
和相關函數的預設行為。如果存取方法對於索引屬性查詢沒有任何特殊行為,則可以在其 IndexAmRoutine
結構中將 amproperty
欄位設定為 NULL。否則,對於 pg_indexam_has_property
呼叫,將使用 index_oid
和 attno
均為零來呼叫 amproperty
方法,或者對於 pg_index_has_property
呼叫,將使用 index_oid
有效且 attno
為零來呼叫,或者對於 pg_index_column_has_property
呼叫,將使用 index_oid
有效且 attno
大於零來呼叫。prop
是一個枚舉值,用於識別正在測試的屬性,而 propname
是原始屬性名稱字串。如果核心程式碼無法識別該屬性名稱,則 prop
為 AMPROP_UNKNOWN
。存取方法可以通過檢查 propname
是否匹配來定義自定義屬性名稱(使用 pg_strcasecmp
來匹配,以與核心程式碼保持一致);對於核心程式碼已知的名稱,最好檢查 prop
。如果 amproperty
方法返回 true
,則它已確定屬性測試結果:它必須將 *res
設定為要返回的布林值,或者將 *isnull
設定為 true
以返回 NULL。(在呼叫之前,兩個引用的變數都初始化為 false
。)如果 amproperty
方法返回 false
,則核心程式碼將繼續使用其正常邏輯來確定屬性測試結果。
支援排序運算符的存取方法應實作 AMPROP_DISTANCE_ORDERABLE
屬性測試,因為核心程式碼不知道如何執行此操作,並且將返回 NULL。如果可以比打開索引並呼叫 amcanreturn
更便宜地完成此操作,則實作 AMPROP_RETURNABLE
測試也可能是有利的,這是核心程式碼的預設行為。對於所有其他標準屬性,預設行為應該是令人滿意的。
char * ambuildphasename (int64 phasenum);
返回給定建置階段編號的文字名稱。階段編號是通過 pgstat_progress_update_param
介面在索引建置期間報告的那些編號。然後,階段名稱在 pg_stat_progress_create_index
視圖中公開。
bool amvalidate (Oid opclassoid);
驗證指定運算子類別的目錄條目,盡可能以存取方法合理的方式進行驗證。例如,這可能包括測試是否提供了所有必需的支援函式。amvalidate
函式如果運算子類別無效,則必須傳回 false。問題應使用 ereport
訊息回報,通常在 INFO
層級。
void amadjustmembers (Oid opfamilyoid, Oid opclassoid, List *operators, List *functions);
驗證運算子族群中建議的新運算子和函式成員,盡可能以存取方法合理的方式進行驗證,並在預設值不令人滿意時設定其相依性類型。這會在 CREATE OPERATOR CLASS
和 ALTER OPERATOR FAMILY ADD
期間呼叫;在後者的情況下,opclassoid
為 InvalidOid
。List
引數是 OpFamilyMember
結構的清單,如 amapi.h
中所定義。此函式執行的測試通常是 amvalidate
執行的測試的子集,因為 amadjustmembers
無法假設它看到的是完整的成員集。例如,檢查支援函式的簽名是合理的,但不檢查是否提供了所有必需的支援函式。任何問題都可以透過拋出錯誤來回報。OpFamilyMember
結構的相依性相關欄位由核心程式碼初始化,如果在 CREATE OPERATOR CLASS
中,則建立對運算子類別的硬性相依性,如果這是 ALTER OPERATOR FAMILY ADD
,則建立對運算子族群的軟性相依性。amadjustmembers
可以調整這些欄位,如果其他行為更合適。例如,GIN、GiST 和 SP-GiST 總是將運算子成員設定為對運算子族群具有軟性相依性,因為運算子和運算子類別之間的連線在這些索引類型中相對較弱;因此,允許自由新增和移除運算子成員是合理的。可選的支援函式通常也給予軟性相依性,以便在必要時可以移除它們。
索引的目的當然是支援掃描與可索引 WHERE
條件(通常稱為限定詞或掃描鍵)匹配的元組。索引掃描的語意在下面的第 62.3 節中更完整地描述。索引存取方法可以支援「普通」索引掃描、「位元圖」索引掃描或兩者。索引存取方法必須或可以提供的掃描相關函式為:
IndexScanDesc ambeginscan (Relation indexRelation, int nkeys, int norderbys);
準備索引掃描。nkeys
和 norderbys
參數表示掃描中將使用的 quals 和排序運算子的數量;這些對於空間分配目的可能很有用。請注意,掃描鍵的實際值尚未提供。結果必須是 palloc'd 結構。由於實作原因,索引存取方法必須透過呼叫 RelationGetIndexScan()
來建立此結構。在大多數情況下,除了進行該呼叫並可能獲取鎖定之外,ambeginscan
幾乎不做任何事情;索引掃描啟動的有趣部分在 amrescan
中。
void amrescan (IndexScanDesc scan, ScanKey keys, int nkeys, ScanKey orderbys, int norderbys);
開始或重新啟動索引掃描,可能使用新的掃描鍵。(要使用先前傳遞的鍵重新啟動,則為 keys
和/或 orderbys
傳遞 NULL。)請注意,鍵或 order-by 運算子的數量不能大於傳遞給 ambeginscan
的數量。實際上,當巢狀迴圈聯結選擇了一個新的外部元組時,會使用重新啟動功能,因此需要一個新的鍵比較值,但掃描鍵結構保持不變。
bool amgettuple (IndexScanDesc scan, ScanDirection direction);
提取給定掃描中的下一個元組,沿給定方向移動(索引中的正向或反向)。如果獲得了元組,則傳回 true,如果沒有匹配的元組,則傳回 false。在 true 的情況下,元組 TID 會儲存在 scan
結構中。請注意,「成功」僅表示索引包含與掃描鍵匹配的條目,而不是元組一定仍然存在於堆積中,或者會通過呼叫者的快照測試。成功後,amgettuple
還必須將 scan->xs_recheck
設定為 true 或 false。False 表示可以確定索引條目與掃描鍵匹配。True 表示不能確定,並且在提取堆積元組後,必須針對堆積元組重新檢查掃描鍵表示的條件。此條款支援「有損」索引運算子。請注意,重新檢查只會延伸到掃描條件;部分索引謂詞(如果有的話)永遠不會被 amgettuple
呼叫者重新檢查。
如果索引支援僅索引掃描(即,amcanreturn
對其任何欄位傳回 true),則成功後,AM 還必須檢查 scan->xs_want_itup
,如果為 true,則必須傳回索引條目的原始索引資料。amcanreturn
傳回 false 的欄位可以作為空值傳回。資料可以以儲存在 scan->xs_itup
的 IndexTuple
指標形式傳回,並帶有元組描述符 scan->xs_itupdesc
;或者以儲存在 scan->xs_hitup
的 HeapTuple
指標形式傳回,並帶有元組描述符 scan->xs_hitupdesc
。(當重建可能無法放入 IndexTuple
的資料時,應使用後一種格式。)在任何一種情況下,指標引用的資料的管理都是存取方法的責任。資料必須至少保持有效,直到掃描的下一次 amgettuple
、amrescan
或 amendscan
呼叫。
只有在存取方法支援「普通」索引掃描時,才需要提供 amgettuple
函式。如果沒有,則其 IndexAmRoutine
結構中的 amgettuple
欄位必須設定為 NULL。
int64 amgetbitmap (IndexScanDesc scan, TIDBitmap *tbm);
提取給定掃描中的所有元組,並將它們新增到呼叫者提供的 TIDBitmap
(也就是說,將元組 ID 的集合 OR 到位元圖中已有的集合中)。傳回提取的元組數量(這可能只是一個近似計數,例如,某些 AM 不會偵測重複項)。在將元組 ID 插入到位元圖時,amgetbitmap
可以指示需要重新檢查特定元組 ID 的掃描條件。這與 amgettuple
的 xs_recheck
輸出參數類似。注意:在目前的實作中,對此功能的支援與對位元圖本身的有損儲存的支援混合在一起,因此呼叫者會重新檢查掃描條件和重新檢查元組的部分索引謂詞(如果有的話)。然而,情況可能並非總是如此。amgetbitmap
和 amgettuple
不能在同一個索引掃描中使用;使用 amgetbitmap
時也有其他限制,如第 62.3 節中所述。
只有在存取方法支援“bitmap”索引掃描時,才需要提供 amgetbitmap
函式。 如果不支援,則其 IndexAmRoutine
結構中的 amgetbitmap
欄位必須設定為 NULL。
void amendscan (IndexScanDesc scan);
結束掃描並釋放資源。 scan
結構本身不應被釋放,但存取方法內部取得的任何鎖定或釘選都必須釋放,以及 ambeginscan
和其他與掃描相關的函式所分配的任何其他記憶體。
void ammarkpos (IndexScanDesc scan);
標記目前掃描位置。 存取方法僅需支援每次掃描一個已記憶的掃描位置。
只有在存取方法支援排序掃描時,才需要提供 ammarkpos
函式。 如果不支援,則其 ammarkpos
欄位在其 IndexAmRoutine
結構中可以設定為 NULL。
void amrestrpos (IndexScanDesc scan);
將掃描還原到最近標記的位置。
只有在存取方法支援排序掃描時,才需要提供 amrestrpos
函式。 如果不支援,則其 amrestrpos
欄位在其 IndexAmRoutine
結構中可以設定為 NULL。
除了支援一般的索引掃描之外,某些類型的索引可能希望支援平行索引掃描,這允許多個後端合作執行索引掃描。 索引存取方法應安排好事情,以便每個合作的程序返回普通、非平行索引掃描將執行的元組的子集,但方式是這些子集的聯合等於普通、非平行索引掃描將返回的元組集合。 此外,雖然並行掃描返回的元組不需要有任何全域排序,但每個合作後端內返回的元組子集的排序必須符合請求的排序。 可以實作以下函式來支援並行索引掃描。
Size amestimateparallelscan (int nkeys, int norderbys);
估計並返回存取方法執行並行掃描所需的動態共享記憶體的位元組數。(此數字是 ParallelIndexScanDescData
中 AM 獨立資料所需空間的補充,而不是取代。)
nkeys
和 norderbys
參數表示掃描中將使用的限定詞和排序運算子的數量;相同的值將傳遞給 amrescan
。 請注意,掃描鍵的實際值尚未提供。
對於不支援並行掃描或所需額外儲存位元組數為零的存取方法,沒有必要實作此函式。
void aminitparallelscan (void *target);
將呼叫此函式以在並行掃描開始時初始化動態共享記憶體。 target
將指向至少先前由 amestimateparallelscan
返回的位元組數,並且此函式可以使用該空間量來儲存其希望的任何資料。
對於不支援並行掃描或所需共享記憶體空間不需要初始化的情況,沒有必要實作此函式。
void amparallelrescan (IndexScanDesc scan);
如果實作了此函式,則當必須重新啟動並行索引掃描時將呼叫它。 它應重置由 aminitparallelscan
設定的任何共享狀態,以便從頭開始重新啟動掃描。
如果您在文件中發現任何不正確、與您使用特定功能時的體驗不符或需要進一步澄清的內容,請使用此表格來報告文件問題。