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 / 9.0 / 8.4 / 8.3 / 8.2 / 8.1

63.2. 索引訪問方法函式 #

索引訪問方法必須在 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);

將新元組插入現有索引。 valuesisnull 陣列給出了要索引的鍵值,heap_tid 是要索引的 TID。如果訪問方法支援唯一索引(其 amcanunique 標誌為 true),則 checkUnique 指示要執行的唯一性檢查型別。這取決於唯一約束是否可延遲;有關詳細資訊,請參見 第 63.5 節。通常,訪問方法在執行唯一性檢查時僅需要 heapRelation 引數(因為那時它必須檢視堆以驗證元組的有效性)。

indexUnchanged 布林值提供了一個關於要索引的元組性質的提示。當它為 true 時,元組是索引中現有元組的副本。新元組是邏輯上未更改的後續 MVCC 元組版本。當發生 UPDATE 且不修改任何由索引覆蓋的列,但仍需要在索引中建立新版本時,會發生這種情況。索引 AM 可以使用此提示來決定在同一邏輯行的多個版本累積的索引部分應用自底向上的索引刪除。請注意,更新非鍵列或僅出現在部分索引謂詞中的列不會影響 indexUnchanged 的值。核心程式碼使用低開銷的方法確定每個元組的 indexUnchanged 值,該方法允許假陽性和假陰性。索引 AM 不得將 indexUnchanged 視為關於元組可見性或版本化的權威資訊來源。

僅當 checkUniqueUNIQUE_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);

從索引中刪除元組。這是一個“批次刪除”操作,旨在透過掃描整個索引並檢查每個條目是否應被刪除來實現。必須按照 callback(TID, callback_state) returns bool 的樣式呼叫傳入的 callback 函式,以確定由其引用的 TID 標識的任何特定索引條目是否應被刪除。必須返回 NULL 或一個包含有關刪除操作效果的統計資訊的 palloc'd 結構。如果沒有需要傳遞給 amvacuumcleanup 的資訊,返回 NULL 是可以接受的。

由於 maintenance_work_mem 的限制,當需要刪除大量元組時,ambulkdelete 可能需要被呼叫多次。 stats 引數是此索引前一次呼叫的結果(對於 VACUUM 操作中的第一次呼叫,它是 NULL)。這允許 AM 跨整個操作累積統計資訊。通常,如果傳入的 stats 非空,ambulkdelete 將修改並返回相同的結構。

IndexBulkDeleteResult *
amvacuumcleanup (IndexVacuumInfo *info,
                 IndexBulkDeleteResult *stats);

VACUUM 操作(零次或多次 ambulkdelete 呼叫)完成後進行清理。除了返回索引統計資訊外,這不必做任何事情,但它可能會執行批次清理,例如回收空的索引頁。stats 是最後一個 ambulkdelete 呼叫返回的任何內容,或者如果 ambulkdelete 未被呼叫(因為不需要刪除任何元組),則為 NULL。如果結果非 NULL,則必須是 palloc'd 結構。其中包含的統計資訊將用於更新 pg_class,並在給出 VERBOSE 時由 VACUUM 報告。如果索引在 VACUUM 操作期間沒有發生任何變化,返回 NULL 是可以接受的,但否則應返回正確的統計資訊。

amvacuumcleanup 也將在 ANALYZE 操作完成時被呼叫。在這種情況下,stats 始終為 NULL,並且任何返回值都將被忽略。可以透過檢查 info->analyze_only 來區分這種情況。建議訪問方法除了在 autovacuum 工作程序中進行插入後清理外,不做任何其他事情。

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);

估算索引掃描的成本。此函式將在下面的 第 63.6 節 中得到全面描述。

int
amgettreeheight (Relation rel);

計算樹形索引的高度。此資訊將作為 path->indexinfo->tree_height 提供給 amcostestimate 函式,並可用於支援成本估算。結果未在其他任何地方使用,因此此函式實際上可用於計算成本估算函式可能想知道(適合整數)的任何型別的索引資料。如果計算成本很高,將其結果作為 RelationData.rd_amcache 的一部分進行快取可能會很有用。

bytea *
amoptions (ArrayType *reloptions,
           bool validate);

解析並驗證索引的 reloptions 陣列。僅當索引存在非 NULL reloptions 陣列時才會呼叫此函式。reloptions 是一個 text 陣列,包含形式為 name=value 的條目。該函式應構造一個 bytea 值,該值將被複制到索引的 relcache 條目的 rd_options 欄位。 bytea 值的資料內容由訪問方法自行定義;大多數標準訪問方法使用 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。否則,將使用 index_oidattno 都為零呼叫 amproperty 方法來呼叫 pg_indexam_has_property,或者在 pg_index_has_property 呼叫中使用有效的 index_oid 和零的 attno,或者在 pg_index_column_has_property 呼叫中使用有效的 index_oid 和大於零的 attnoprop 是一個列舉值,用於標識正在測試的屬性,而 propname 是原始屬性名稱字串。如果核心程式碼不識別屬性名稱,則 propAMPROP_UNKNOWN。訪問方法可以透過檢查 propname 是否匹配來定義自定義屬性名稱(為與核心程式碼保持一致,請使用 pg_strcasecmp 進行匹配);對於核心程式碼已知的名稱,最好檢查 prop。如果 amproperty 方法返回 true,則它已確定屬性測試結果:它必須將 *res 設定為要返回的布林值,或將 *isnull 設定為 true 以返回 NULL。(兩個引用變數在呼叫前都初始化為 false。)如果 amproperty 方法返回 false,則核心程式碼將繼續其正常邏輯來確定屬性測試結果。

支援排序運算子的訪問方法應實現 AMPROP_DISTANCE_ORDERABLE 屬性測試,因為核心程式碼不知道如何進行此操作,並將返回 NULL。如果 AMPROP_RETURNABLE 測試比開啟索引並呼叫 amcanreturn(這是核心程式碼的預設行為)更便宜,那麼實現此測試也可能是有益的。對於所有其他標準屬性,預設行為應該令人滿意。

char *
ambuildphasename (int64 phasenum);

返回給定構建階段編號的文字名稱。階段編號是在索引構建期間透過 pgstat_progress_update_param 介面報告的。然後,階段名稱將顯示在 pg_stat_progress_create_index 檢視中。

bool
amvalidate (Oid opclassoid);

在合理範圍內驗證指定運算子類的目錄條目。例如,這可能包括測試是否提供了所有必需的支援函式。如果 opclass 無效,amvalidate 函式必須返回 false。應使用 ereport 訊息報告問題,通常在 INFO 級別。

void
amadjustmembers (Oid opfamilyoid,
                 Oid opclassoid,
                 List *operators,
                 List *functions);

在合理範圍內驗證運算子族的擬議新運算子和函式成員,並在預設依賴型別不滿意時設定它們的依賴型別。這會在 CREATE OPERATOR CLASSALTER OPERATOR FAMILY ADD 期間呼叫(後一種情況下的 opclassoidInvalidOid)。 List 引數是 amapi.h 中定義的 OpFamilyMember 結構列表。此函式進行的測試通常是 amvalidate 執行的測試的子集,因為 amadjustmembers 不能假定它看到的是完整的成員集。例如,檢查支援函式的簽名是合理的,但檢查是否提供了所有必需的支援函式則不然。任何問題都可以透過丟擲錯誤來報告。 OpFamilyMember 結構中的依賴項相關欄位由核心程式碼初始化,以在 CREATE OPERATOR CLASS 的情況下建立對 opclass 的硬依賴,或在 ALTER OPERATOR FAMILY ADD 的情況下建立對 opfamily 的軟依賴。amadjustmembers 可以調整這些欄位,如果其他行為更合適。例如,GIN、GiST 和 SP-GiST 總是將運算子成員設定為對 opfamily 具有軟依賴,因為在這些索引型別中,運算子與 opclass 之間的連線相對較弱;因此,允許自由新增和刪除運算子成員是合理的。可選的支援函式通常也具有軟依賴,以便在必要時可以刪除它們。

索引的目的是支援掃描匹配可索引的 WHERE 條件的元組,這通常稱為 限定符掃描鍵。索引掃描的語義將在下面的 第 63.3 節 中得到更全面的描述。索引訪問方法可以支援“普通”索引掃描、“點陣圖”索引掃描,或兩者都支援。索引訪問方法必須或可能提供的與掃描相關的函式是:

IndexScanDesc
ambeginscan (Relation indexRelation,
             int nkeys,
             int norderbys);

為索引掃描做準備。nkeysnorderbys 引數指示將在掃描中使用的限定符和排序運算子的數量;這些可能對空間分配有用。請注意,掃描鍵的實際值尚未提供。結果必須是一個 palloc'd 結構。出於實現原因,索引訪問方法 必須 透過呼叫 RelationGetIndexScan() 來建立此結構。在大多數情況下,ambeginscan 除了進行該呼叫和可能獲取鎖之外,幾乎不做其他事情;索引掃描啟動的有趣部分在 amrescan 中。

void
amrescan (IndexScanDesc scan,
          ScanKey keys,
          int nkeys,
          ScanKey orderbys,
          int norderbys);

啟動或重新啟動索引掃描,可能帶有新的掃描鍵。(要使用先前傳遞的鍵重新啟動,請將 NULL 傳遞給 keys 和/或 orderbys。)請注意,鍵的數量或排序運算子的數量不能大於傳遞給 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 的列可以返回為 null。資料可以以 IndexTuple 指標的形式儲存在 scan->xs_itup 中,元組描述符為 scan->xs_itupdesc;或者以 HeapTuple 指標的形式儲存在 scan->xs_hitup 中,元組描述符為 scan->xs_hitupdesc。(後一種格式應用於可能無法容納到 IndexTuple 中的資料重建。)在這兩種情況下,指向資料的管理都是訪問方法的責任。資料必須在掃描的下一次 amgettupleamrescanamendscan 呼叫之前保持有效。

只有當訪問方法支援“普通”索引掃描時,才需要提供 amgettuple 函式。如果不支援,則必須將其 IndexAmRoutine 結構中的 amgettuple 欄位設定為 NULL。

int64
amgetbitmap (IndexScanDesc scan,
             TIDBitmap *tbm);

獲取給定掃描中的所有元組,並將它們新增到呼叫者提供的 TIDBitmap 中(即,將元組 ID 集與點陣圖中已有的任何集合進行 OR 操作)。返回獲取的元組數量(這可能只是一個近似計數,例如一些 AM 不檢測重複項)。在將元組 ID 插入點陣圖時,amgetbitmap 可以指示需要重新檢查特定元組 ID 的掃描條件。這類似於 amgettuplexs_recheck 輸出引數。注意:在當前實現中,對該功能的支援與點陣圖本身的無損儲存支援混淆,因此呼叫者會重新檢查掃描條件和部分索引謂詞(如果存在)以獲取可重檢查的元組。但這不一定總是如此。amgetbitmapamgettuple 不能在同一個索引掃描中使用;使用 amgetbitmap 時還有其他限制,如 第 63.3 節 所述。

只有當訪問方法支援“點陣圖”索引掃描時,才需要提供 amgetbitmap 函式。如果不支援,則必須將其 IndexAmRoutine 結構中的 amgetbitmap 欄位設定為 NULL。

void
amendscan (IndexScanDesc scan);

結束掃描並釋放資源。 scan 結構本身不應被釋放,但訪問方法內部獲取的任何鎖或 Pin 必須被釋放,以及 ambeginscan 和其他與掃描相關的函式分配的任何其他記憶體。

void
ammarkpos (IndexScanDesc scan);

標記當前掃描位置。訪問方法每個掃描只需要支援一個已記住的掃描位置。

只有當訪問方法支援有序掃描時,才需要提供 ammarkpos 函式。如果不支援,則可以在其 IndexAmRoutine 結構中將 ammarkpos 欄位設定為 NULL。

void
amrestrpos (IndexScanDesc scan);

將掃描恢復到最近標記的位置。

只有當訪問方法支援有序掃描時,才需要提供 amrestrpos 函式。如果不支援,則可以在其 IndexAmRoutine 結構中將 amrestrpos 欄位設定為 NULL。

除了支援普通索引掃描外,某些型別的索引可能還希望支援 並行索引掃描,這允許多個後端協同執行索引掃描。索引訪問方法應安排使每個協作程序返回一個子集,該子集是普通非並行索引掃描將返回的元組,但其方式是這些子集的並集等於普通非並行索引掃描將返回的元組集。此外,雖然並行掃描返回的元組不需要有全域性排序,但每個協作後端返回的該子集的排序必須與請求的排序匹配。以下函式可用於支援並行索引掃描:

Size
amestimateparallelscan (Relation indexRelation,
                        int nkeys,
                        int norderbys);

估算並返回訪問方法執行並行掃描所需的動態共享記憶體位元組數。(此數量是 ParallelIndexScanDescData 中 AM 獨立資料所需數量的附加量,而不是替代量。)

nkeysnorderbys 引數指示將在掃描中使用的限定符和排序運算子的數量;這些相同的值將傳遞給 amrescan。請注意,掃描鍵的實際值尚未提供。

對於不支援並行掃描的訪問方法,或者所需的附加儲存位元組數為零的情況,沒有必要實現此函式。

void
aminitparallelscan (void *target);

當必須重啟並行索引掃描時,將呼叫此函式來重置由 aminitparallelscan 設定的任何共享狀態,以便從頭開始重啟掃描。

對於不支援並行掃描的訪問方法,或者在共享記憶體空間需要無初始化的場景下,沒有必要實現此函式。

void
amparallelrescan (IndexScanDesc scan);

如果實現了此函式,將在並行索引掃描開始時呼叫此函式。 target 將指向至少等於 amestimateparallelscan 之前返回的位元組數,並且此函式可以使用該數量的空間來儲存它想要的任何資料。

CompareType
amtranslatestrategy (StrategyNumber strategy, Oid opfamily, Oid opcintype);

StrategyNumber
amtranslatecmptype (CompareType cmptype, Oid opfamily, Oid opcintype);

如果實現了此函式,將在並行索引掃描開始時呼叫此函式。 target 將指向至少等於 amestimateparallelscan 之前返回的位元組數,並且此函式可以使用該數量的空間來儲存它想要的任何資料。

提交更正

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