一個有用的 PostgreSQL 擴充套件通常包含多個 SQL 物件;例如,一個新的資料類型將需要新的函式、新的運算子,並且可能需要新的索引運算子類別。將所有這些物件收集到一個單一封裝中,以簡化資料庫管理是很有幫助的。PostgreSQL 稱此類封裝為擴充套件。要定義一個擴充套件,您至少需要一個指令碼檔案,其中包含用於建立擴充套件物件的SQL指令,以及一個控制檔案,其中指定了擴充套件本身的一些基本屬性。如果擴充套件包含 C 程式碼,通常也會有一個共享函式庫檔案,C 程式碼已建置到其中。一旦您有了這些檔案,一個簡單的 CREATE EXTENSION
指令就會將物件載入到您的資料庫中。
使用擴充套件,而不是僅僅執行SQL指令碼以將一堆「鬆散」的物件載入到您的資料庫中的主要優點是,PostgreSQL 將了解擴充套件的物件是相關聯的。您可以使用一個 DROP EXTENSION
指令刪除所有物件(無需維護單獨的「解除安裝」指令碼)。更有用的是,pg_dump 知道不應該傾印擴充套件的個別成員物件 — 它只會在傾印中包含一個 CREATE EXTENSION
指令。這極大地簡化了遷移到可能包含比舊版本更多或不同物件的擴充套件新版本。但是請注意,將此類傾印載入到新資料庫時,您必須擁有擴充套件的控制、指令碼和其他檔案。
PostgreSQL 不允許您刪除擴充套件中包含的個別物件,除非刪除整個擴充套件。此外,雖然您可以變更擴充套件成員物件的定義(例如,透過用於函式的 CREATE OR REPLACE FUNCTION
),但請記住,修改後的定義不會由 pg_dump 傾印。只有當您同時在擴充套件的指令碼檔案中進行相同的變更時,此類變更通常才有意義。(但是,對於包含組態資料的表有特殊的規定;請參閱第 36.17.3 節。)在生產情況下,通常最好建立一個擴充套件更新指令碼來執行對擴充套件成員物件的變更。
擴充套件指令碼可以使用 GRANT
和 REVOKE
陳述式,在屬於擴充套件的物件上設定權限。每個物件的最終權限集(如果已設定)將儲存在 pg_init_privs
系統目錄中。當使用 pg_dump 時,CREATE EXTENSION
指令將包含在傾印中,後面接著設定物件權限所需的 GRANT
和 REVOKE
陳述式集,使其與傾印時的權限相同。
PostgreSQL 目前不支援擴充套件指令碼發出 CREATE POLICY
或 SECURITY LABEL
陳述式。預期這些陳述式將在建立擴充套件後設定。擴充套件物件上的所有 RLS 政策和安全性標籤將包含在由 pg_dump 建立的傾印中。
擴充套件機制也提供了封裝修改指令碼的規定,這些指令碼會調整擴充套件中包含的 SQL 物件的定義。例如,如果擴充套件的 1.1 版與 1.0 版相比增加了一個函式並變更了另一個函式的主體,則擴充套件作者可以提供一個更新指令碼,該指令碼僅進行這兩個變更。然後可以使用 ALTER EXTENSION UPDATE
指令來套用這些變更並追蹤實際安裝在給定資料庫中的擴充套件版本。
擴充套件可以包含的 SQL 物件種類顯示在ALTER EXTENSION
的描述中。值得注意的是,資料庫叢集範圍的物件,例如資料庫、角色和表格空間,不能成為擴充套件的成員,因為擴充套件僅在一個資料庫中為人所知。(儘管擴充套件腳本不禁止建立此類物件,但如果這樣做,它們將不會被追蹤為擴充套件的一部分。)另外請注意,雖然一個資料表可以成為擴充套件的成員,但它的附屬物件,例如索引,不會被直接視為擴充套件的成員。另一個重點是,綱要可以屬於擴充套件,但反之則不然:擴充套件本身具有未限定的名稱,並且不存在於任何綱要「內」。然而,擴充套件的成員物件,將在適當時屬於它們的物件類型所屬的綱要。擴充套件擁有其成員物件所在的綱要可能是適當的,也可能是不適當的。
如果擴充套件的腳本建立任何暫時物件(例如暫時表格),則這些物件將被視為擴充套件成員,直到目前工作階段結束,但在工作階段結束時會自動刪除,就像任何暫時物件一樣。這是擴充套件成員物件無法在不刪除整個擴充套件的情況下刪除的規則的例外情況。
CREATE EXTENSION
指令依賴於每個擴充套件的控制檔,該檔案必須與擴充套件同名,並帶有 .control
副檔名,並且必須放置在安裝目錄的 SHAREDIR/extension
目錄中。還必須至少有一個SQL腳本檔案,其命名模式為
(例如,擴充套件 extension
--version
.sqlfoo
的 1.0
版本的檔案名稱為 foo--1.0.sql
)。預設情況下,腳本檔案也放置在 SHAREDIR/extension
目錄中;但控制檔可以為腳本檔案指定不同的目錄。
擴充套件控制檔的檔案格式與 postgresql.conf
檔案的格式相同,也就是 parameter_name
=
value
賦值的清單,每行一個。允許使用空白行和以 #
開頭的註解。請務必引用任何非單個單字或數字的值。
控制檔可以設定以下參數:
directory
(string
) #包含擴充套件的SQL腳本檔案的目錄。除非給出絕對路徑,否則該名稱是相對於安裝目錄的 SHAREDIR
目錄。預設行為等同於指定 directory = 'extension'
。
default_version
(string
) #擴充套件的預設版本(如果在 CREATE EXTENSION
中未指定版本,則將安裝該版本)。雖然可以省略它,但如果沒有出現 VERSION
選項,則會導致 CREATE EXTENSION
失敗,因此通常不希望這樣做。
comment
(string
) #關於擴充套件的註解(任何字串)。註解會在最初建立擴充套件時應用,但不會在擴充套件更新期間應用(因為這可能會覆蓋使用者新增的註解)。或者,可以使用在腳本檔案中編寫 COMMENT 指令來設定擴充套件的註解。
encoding
(string
) #腳本檔案使用的字元集編碼。如果腳本檔案包含任何非 ASCII 字元,則應指定此項。否則,將假定檔案採用資料庫編碼。
module_pathname
(string
) #此參數的值將替換腳本檔案中每次出現的 MODULE_PATHNAME
。如果未設定,則不進行替換。通常,這會設定為 $libdir/
,然後在 C 語言函式的 shared_library_name
CREATE FUNCTION
指令中使用 MODULE_PATHNAME
,以便腳本檔案不需要硬式編碼共享程式庫的名稱。
requires
(string
) #此擴充套件所依賴的擴充套件名稱清單,例如 requires = 'foo, bar'
。必須先安裝這些擴充套件,然後才能安裝此擴充套件。
no_relocate
(string
) #此擴充套件所依賴的擴充套件名稱清單,這些擴充套件應禁止透過 ALTER EXTENSION ... SET SCHEMA
變更其綱要。如果此擴充套件的腳本以無法追蹤重新命名的方式引用所需擴充套件的綱要名稱(使用 @extschema:
語法),則需要此設定。name
@
superuser
(boolean
) #如果此參數為 true
(預設值),則只有超級使用者才能建立擴充套件或將其更新到新版本(但另請參閱下面的 trusted
)。如果設定為 false
,則只需要執行安裝或更新腳本中的指令所需的權限。如果任何腳本指令需要超級使用者權限,則通常應將此設定為 true
。(無論如何,這些指令都會失敗,但預先顯示錯誤更方便使用者。)
trusted
(boolean
) #如果此參數設定為 true
(不是預設值),則允許一些非超級使用者安裝 superuser
設定為 true
的擴充套件。具體來說,允許對目前資料庫具有 CREATE
權限的任何人進行安裝。當執行 CREATE EXTENSION
的使用者不是超級使用者,但由於此參數的緣故而被允許安裝時,安裝或更新腳本將作為引導超級使用者執行,而不是作為呼叫使用者執行。如果 superuser
為 false
,則此參數無關緊要。通常,對於可能允許存取原本僅限於超級使用者才能使用的功能的擴充套件(例如檔案系統存取),不應將此設定為 true。此外,將擴充套件標記為受信任需要額外的努力來安全地編寫擴充套件的安裝和更新腳本;請參閱 第 36.17.6 節。
relocatable
(boolean
) #如果可以在初始建立擴充套件後將其包含的物件移動到不同的綱要中,則該擴充套件是可重新定位的。預設值為 false
,也就是說,擴充套件不可重新定位。有關更多資訊,請參閱第 36.17.2 節。
schema
(string
) #這個參數只能為不可重定位的擴充功能設定。它強制擴充功能必須載入到指定的綱要中,而不能載入到其他綱要。只有在最初建立擴充功能時才會參考 schema
參數,更新擴充功能時則不會參考。有關更多資訊,請參閱第 36.17.2 節。
除了主要控制檔
之外,擴充功能還可以有次要控制檔,其命名樣式為 extension
.control
。如果提供了這些檔案,則它們必須位於腳本檔案目錄中。次要控制檔的格式與主要控制檔相同。在安裝或更新到該版本的擴充功能時,次要控制檔中設定的任何參數都會覆蓋主要控制檔。但是,不能在次要控制檔中設定 extension
--version
.controldirectory
和 default_version
參數。
擴充功能的SQL腳本檔案可以包含任何 SQL 命令,但交易控制命令(BEGIN
、COMMIT
等)以及無法在交易區塊內執行的命令(例如 VACUUM
)除外。這是因為腳本檔案隱式地在交易區塊內執行。
擴充功能的SQL腳本檔案也可以包含以 \echo
開頭的行,這些行將被擴充功能機制忽略(視為註釋)。此功能通常用於在腳本檔案被饋送到 psql 而不是通過 CREATE EXTENSION
載入時拋出錯誤(請參閱第 36.17.7 節中的範例腳本)。如果沒有這個功能,使用者可能會意外地將擴充功能的內容載入為“鬆散的”物件,而不是作為擴充功能,這是一種很難恢復的狀態。
如果擴充功能腳本包含字串 @extowner@
,則該字串將被替換為呼叫 CREATE EXTENSION
或 ALTER EXTENSION
的使用者名稱(適當引用的)。通常,此功能由標記為受信任的擴充功能使用,以將選定物件的所有權分配給呼叫的使用者,而不是引導超級使用者。(但是,應該謹慎地這樣做。例如,將 C 語言函數的所有權分配給非超級使用者會為該使用者創建權限提升路徑。)
雖然腳本檔案可以包含指定編碼允許的任何字元,但控制檔應僅包含純 ASCII,因為 PostgreSQL 無法知道控制檔的編碼方式。實際上,只有當您想在擴充功能的註釋中使用非 ASCII 字元時,這才是一個問題。在這種情況下,建議的做法是不使用控制檔的 comment
參數,而是在腳本檔案中使用 COMMENT ON EXTENSION
來設定註釋。
使用者通常希望將擴充功能中包含的物件載入到與擴充功能作者預期的綱要不同的綱要中。有三個支援的可重定位性級別
完全可重定位的擴充功能可以隨時移動到另一個綱要中,即使在已載入到資料庫中之後也是如此。這是通過 ALTER EXTENSION SET SCHEMA
命令完成的,該命令會自動將所有成員物件重新命名到新的綱要中。通常,只有在擴充功能不包含有關其任何物件位於哪個綱要中的內部假設時,才有可能這樣做。此外,擴充功能的物件必須首先全部位於一個綱要中(忽略不屬於任何綱要的物件,例如程序語言)。通過在其控制檔中設定 relocatable = true
來標記完全可重定位的擴充功能。
擴充功能可能在安裝期間可以重定位,但在安裝後則無法重定位。如果擴充功能的腳本檔案需要顯式引用目標綱要,例如在為 SQL 函數設定 search_path
屬性時,通常會發生這種情況。對於這種擴充功能,請在其控制檔中設定 relocatable = false
,並使用 @extschema@
在腳本檔案中引用目標綱要。在執行腳本之前,此字串的所有出現都會被實際目標綱要的名稱替換(如果需要,則用雙引號引起來)。使用者可以使用 CREATE EXTENSION
的 SCHEMA
選項來設定目標綱要。
如果擴充功能根本不支援重定位,請在其控制檔中設定 relocatable = false
,並將 schema
設定為預期目標綱要的名稱。這將阻止使用 CREATE EXTENSION
的 SCHEMA
選項,除非它指定了與控制檔中命名的相同綱要。如果擴充功能包含有關其綱要名稱的內部假設,而這些假設無法通過使用 @extschema@
來替換,則通常需要選擇此選項。在這種情況下,@extschema@
替換機制也可用,儘管它的用途有限,因為綱要名稱是由控制檔決定的。
在所有情況下,腳本檔案都將在最初設定為指向目標綱要的search_path的情況下執行;也就是說,CREATE EXTENSION
的作用相當於這樣
SET LOCAL search_path TO @extschema@, pg_temp;
這允許腳本檔案創建的物件進入目標綱要。腳本檔案可以根據需要更改 search_path
,但通常不建議這樣做。search_path
在 CREATE EXTENSION
完成後會恢復到其先前的設定。
目標綱要由控制檔中的 schema
參數確定(如果已給出),否則由 CREATE EXTENSION
的 SCHEMA
選項確定(如果已給出),否則由當前預設物件建立綱要(呼叫者的 search_path
中的第一個)確定。當使用控制檔的 schema
參數時,如果目標綱要不存在,則會建立該綱要,但在其他兩種情況下,它必須已經存在。
如果在控制檔的 requires
中列出了任何先決條件擴充功能,則它們的目標綱要會新增到 search_path
的初始設定中,位於新擴充功能的目標綱要之後。這允許它們的物件對新擴充功能的腳本檔案可見。
為了安全起見,在所有情況下,pg_temp
都會自動附加到 search_path
的末尾。
雖然不可重定位的擴充功能可以包含散布在多個綱要中的物件,但通常最好將所有用於外部使用的物件放置在單一綱要中,該綱要被視為擴充功能的目標綱要。 這種安排在使用相依擴充功能建立期間,能方便地與 search_path
的預設設定一起運作。
如果擴充功能參考屬於另一個擴充功能的物件,建議您使用綱要限定這些參考。為此,請在擴充功能的指令檔中寫入 @extschema:
,其中 name
@name
是另一個擴充功能的名稱(必須列在該擴充功能的 requires
列表中)。此字串將替換為該擴充功能目標綱要的名稱(必要時會加上雙引號)。 雖然這種表示法避免了在擴充功能的指令檔中對綱要名稱進行硬式編碼假設,但其使用可能會將另一個擴充功能的綱要名稱嵌入到此擴充功能的已安裝物件中。(通常,當 @extschema:
用在字串文字中時,例如函式主體或 name
@search_path
設定。在其他情況下,物件參考會在剖析期間縮減為 OID,不需要後續查找。)如果另一個擴充功能的綱要名稱如此嵌入,您應該透過將另一個擴充功能的名稱新增到此擴充功能的 no_relocate
列表中,來防止在您的擴充功能安裝後重新定位另一個擴充功能。
某些擴充功能包含設定表,其中包含使用者在安裝擴充功能後可能新增或變更的資料。 通常,如果表是擴充功能的一部分,則 pg_dump 既不會轉儲表的定義,也不會轉儲其內容。 但對於設定表而言,此行為是不需要的;使用者所做的任何資料變更都需要包含在轉儲中,否則擴充功能在轉儲和還原後會表現不同。
為了解決這個問題,擴充功能的指令檔可以將它建立的表或序列標記為設定關聯,這將導致 pg_dump 在轉儲中包含表或序列的內容(而不是其定義)。 為此,在建立表或序列後,呼叫函式 pg_extension_config_dump(regclass, text)
,例如
CREATE TABLE my_config (key text, value text); CREATE SEQUENCE my_config_seq; SELECT pg_catalog.pg_extension_config_dump('my_config', ''); SELECT pg_catalog.pg_extension_config_dump('my_config_seq', '');
可以透過這種方式標記任意數量的表或序列。與 serial
或 bigserial
欄位相關聯的序列也可以標記。
當 pg_extension_config_dump
的第二個引數是空字串時,pg_dump 會轉儲表的全部內容。 僅當表最初為空(由擴充功能指令檔建立)時,此操作通常才是正確的。 如果表中混合了初始資料和使用者提供的資料,則 pg_extension_config_dump
的第二個引數提供了一個 WHERE
條件,用於選取要轉儲的資料。 例如,您可以執行以下操作
CREATE TABLE my_config (key text, value text, standard_entry boolean); SELECT pg_catalog.pg_extension_config_dump('my_config', 'WHERE NOT standard_entry');
然後確保 standard_entry
僅在擴充功能指令檔建立的列中為 true。
對於序列,pg_extension_config_dump
的第二個引數沒有任何作用。
更複雜的情況,例如最初提供的列可能被使用者修改,可以透過在設定表上建立觸發程序來處理,以確保正確標記修改後的列。
您可以透過再次呼叫 pg_extension_config_dump
來變更與設定表相關聯的篩選條件。(這通常在擴充功能更新指令碼中很有用。)將表標記為不再是設定表的唯一方法是使用 ALTER EXTENSION ... DROP TABLE
將其與擴充功能取消關聯。
請注意,這些表之間的外鍵關係將決定 pg_dump 轉儲表的順序。 具體來說,pg_dump 將嘗試先轉儲被參考的表,然後再轉儲參考的表。 由於外鍵關係是在 CREATE EXTENSION 時設定的(在資料載入到表中之前),因此不支援循環相依性。 當存在循環相依性時,資料仍然會被轉儲,但轉儲將無法直接還原,並且需要使用者介入。
與 serial
或 bigserial
欄位相關聯的序列需要直接標記以轉儲其狀態。 標記其父關聯不足以達到此目的。
擴充功能機制的一個優點是,它提供了管理擴充功能物件定義的 SQL 指令更新的便捷方式。 這是透過將版本名稱或編號與擴充功能安裝指令檔的每個發佈版本相關聯來完成的。 此外,如果您希望使用者能夠動態地將其資料庫從一個版本更新到下一個版本,則應提供更新指令碼,這些指令碼會進行必要的變更以從一個版本轉到下一個版本。 更新指令碼的名稱遵循模式
(例如,extension
--old_version
--target_version
.sqlfoo--1.0--1.1.sql
包含將擴充功能 foo
的版本 1.0
修改為版本 1.1
的指令)。
假設有合適的更新指令碼可用,指令 ALTER EXTENSION UPDATE
將已安裝的擴充功能更新為指定的全新版本。 更新指令碼在與 CREATE EXTENSION
為安裝指令碼提供的相同環境中執行:特別是,search_path
的設定方式相同,並且指令碼建立的任何新物件都會自動新增到擴充功能。 此外,如果指令碼選擇捨棄擴充功能成員物件,它們會自動與擴充功能取消關聯。
如果擴充功能具有次要控制檔案,則用於更新指令碼的控制參數是與指令碼目標(全新)版本相關聯的參數。
ALTER EXTENSION
能夠執行一系列更新指令碼檔案,以實現請求的更新。 例如,如果只有 foo--1.0--1.1.sql
和 foo--1.1--2.0.sql
可用,則當目前安裝了 1.0
時,如果請求更新到版本 2.0
,ALTER EXTENSION
將按順序應用它們。
PostgreSQL 不會對版本名稱的屬性做任何假設:例如,它不知道 1.1
是否遵循 1.0
。 它只是比對可用的版本名稱,並遵循需要套用最少更新指令碼的路徑。(版本名稱實際上可以是任何不包含 --
或開頭或結尾的 -
的字串。)
有時提供「降級」指令碼很有用,例如 foo--1.1--1.0.sql
以允許還原與版本 1.1
相關聯的變更。 如果您這樣做,請小心降級指令碼可能會意外地被套用,因為它會產生較短的路徑。 風險的情況是,存在一個「快速路徑」更新指令碼,它會向前跳幾個版本,以及一個降級指令碼,指向快速路徑的起點。 套用降級指令碼,然後套用快速路徑可能比一次前進一個版本所需的步驟更少。 如果降級指令碼捨棄了任何不可替代的物件,則會產生不理想的結果。
若要檢查意外的更新路徑,請使用此指令
SELECT * FROM pg_extension_update_paths('extension_name
');
這會顯示指定擴充套件的每個不同已知版本名稱對,以及從來源版本到目標版本所採取的更新路徑順序,如果沒有可用的更新路徑,則顯示 NULL
。路徑以文字形式顯示,並使用 --
分隔符號。如果您喜歡陣列格式,可以使用 regexp_split_to_array(path,'--')
。
已經存在一段時間的擴充套件可能有多個版本,作者需要為這些版本編寫更新腳本。例如,如果您發布了 foo
擴充套件,版本為 1.0
、1.1
和 1.2
,則應該有更新腳本 foo--1.0--1.1.sql
和 foo--1.1--1.2.sql
。在 PostgreSQL 10 之前,還需要創建新的腳本文件 foo--1.1.sql
和 foo--1.2.sql
,直接構建較新的擴充套件版本,否則較新的版本無法直接安裝,只能先安裝 1.0
然後更新。這既繁瑣又重複,但現在沒有必要了,因為 CREATE EXTENSION
可以自動追蹤更新鏈。例如,如果只有腳本文件 foo--1.0.sql
、foo--1.0--1.1.sql
和 foo--1.1--1.2.sql
可用,則要求安裝版本 1.2
將透過按順序執行這三個腳本來完成。處理方式與您先安裝 1.0
然後更新到 1.2
相同。(與 ALTER EXTENSION UPDATE
一樣,如果有多個路徑可用,則首選最短的路徑。)以這種方式安排擴充套件的腳本文件可以減少產生小更新所需的維護工作量。
如果您將次要(特定於版本)的控制檔案與以這種方式維護的擴充套件一起使用,請記住,即使每個版本沒有獨立的安裝腳本,也需要一個控制檔案,因為該控制檔案將決定如何執行對該版本的隱式更新。例如,如果 foo--1.0.control
指定 requires = 'bar'
,但 foo
的其他控制檔案沒有,則當從 1.0
更新到另一個版本時,擴充套件對 bar
的依賴關係將被移除。
廣泛分發的擴充套件應該對其佔用的資料庫做出很少的假設。因此,以安全的方式編寫擴充套件提供的函數是合適的,這種方式不會受到基於搜尋路徑的攻擊的影響。
將 superuser
屬性設定為 true 的擴充套件也必須考慮其安裝和更新腳本中執行的動作的安全性風險。惡意使用者創建木馬程式對象並非難事,這些對象會危害日後不小心編寫的擴充套件腳本的執行,從而允許該使用者獲取超級使用者權限。
如果擴充套件標記為 trusted
,則其安裝模式可以由安裝使用者選擇,該使用者可能會故意使用不安全的模式,以希望獲得超級使用者權限。因此,受信任的擴充套件從安全角度來看非常脆弱,必須仔細檢查其所有腳本命令,以確保不可能發生任何妥協。
有關安全編寫函數的建議,請參閱下面的 第 36.17.6.1 節,有關安全編寫安裝腳本的建議,請參閱 第 36.17.6.2 節。
擴充套件提供的 SQL 語言和 PL 語言函數在執行時有受到基於搜尋路徑攻擊的風險,因為這些函數的解析是在執行時而不是創建時發生的。
CREATE FUNCTION
參考頁包含有關安全編寫 SECURITY DEFINER
函數的建議。對於擴充套件提供的任何函數,應用這些技術都是一個好習慣,因為該函數可能由高權限使用者呼叫。
如果您無法將 search_path
設置為僅包含安全的模式,請假設每個未限定名稱都可以解析為惡意使用者定義的物件。注意隱式依賴 search_path
的結構;例如,IN
和 CASE
始終使用搜尋路徑選擇運算子。用 expression
WHENOPERATOR(
和 schema
.=) ANYCASE WHEN
代替它們。expression
通用擴充套件通常不應假設它已安裝到安全的模式中,這意味著即使對其自身物件的模式限定引用也不是完全沒有風險的。例如,如果擴充套件定義了一個函數 myschema.myfunc(bigint)
,則諸如 myschema.myfunc(42)
之類的呼叫可能會被敵意函數 myschema.myfunc(integer)
捕獲。請注意,函數和運算子參數的數據類型與宣告的參數類型完全匹配,並在必要時使用顯式轉換。
應編寫擴充套件安裝或更新腳本,以防止腳本執行時發生基於搜尋路徑的攻擊。如果腳本中的物件引用可以解析為腳本作者不打算使用的其他物件,則可能會立即發生妥協,或者在以後使用錯誤定義的擴充套件物件時發生。
諸如 CREATE FUNCTION
和 CREATE OPERATOR CLASS
之類的 DDL 命令通常是安全的,但要注意任何將通用表達式作為組件的命令。例如,需要審查 CREATE VIEW
,以及 CREATE FUNCTION
中的 DEFAULT
表達式。
有時,擴充套件腳本可能需要執行通用 SQL,例如進行 DDL 無法實現的目錄調整。請注意使用安全的 search_path
執行此類命令;不要信任 CREATE/ALTER EXTENSION
提供的路徑是安全的。最佳實務是暫時將 search_path
設置為 'pg_catalog, pg_temp'
,並在需要時顯式插入對擴充套件安裝模式的引用。(這種實務也可能有助於創建視圖。)可以在 PostgreSQL 原始碼發布中的 contrib
模組中找到範例。
跨擴充套件引用極難完全安全地實現,部分原因是無法確定另一個擴充套件位於哪個模式中。如果兩個擴充套件都安裝在同一模式中,則可以減少風險,因為在這種情況下,敵意物件無法放置在安裝時 search_path
中被引用擴充套件的前面。但是,目前沒有任何機制可以要求這樣做。目前,最佳實務是不要將依賴於另一個擴充套件的擴充套件標記為受信任,除非另一個擴充套件始終安裝在 pg_catalog
中。
以下是一個完整的擴充套件範例SQL-only 擴充功能,一種雙元素複合型別,可以在其槽中儲存任何型別的值,這些槽的名稱分別為 “k” 和 “v”。非文字值會自動強制轉換為文字以進行儲存。
腳本檔案 pair--1.0.sql
如下所示
-- complain if script is sourced in psql, rather than via CREATE EXTENSION \echo Use "CREATE EXTENSION pair" to load this file. \quit CREATE TYPE pair AS ( k text, v text ); CREATE FUNCTION pair(text, text) RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::@extschema@.pair;'; CREATE OPERATOR ~> (LEFTARG = text, RIGHTARG = text, FUNCTION = pair); -- "SET search_path" is easy to get right, but qualified names perform better. CREATE FUNCTION lower(pair) RETURNS pair LANGUAGE SQL AS 'SELECT ROW(lower($1.k), lower($1.v))::@extschema@.pair;' SET search_path = pg_temp; CREATE FUNCTION pair_concat(pair, pair) RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1.k OPERATOR(pg_catalog.||) $2.k, $1.v OPERATOR(pg_catalog.||) $2.v)::@extschema@.pair;';
控制檔案 pair.control
如下所示
# pair extension comment = 'A key/value pair data type' default_version = '1.0' # cannot be relocatable because of use of @extschema@ relocatable = false
雖然您幾乎不需要 makefile 就可以將這兩個檔案安裝到正確的目錄中,但您可以使用包含以下內容的 Makefile
EXTENSION = pair DATA = pair--1.0.sql PG_CONFIG = pg_config PGXS := $(shell $(PG_CONFIG) --pgxs) include $(PGXS)
這個 makefile 依賴於PGXS,這在第 36.18 節中有描述。 make install
命令會將控制和腳本檔案安裝到 pg_config 報告的正確目錄中。
安裝檔案後,使用 CREATE EXTENSION
命令將物件載入到任何特定的資料庫中。
如果您在文件中發現任何不正確、與您使用特定功能的經驗不符或需要進一步澄清的地方,請使用此表單來回報文件問題。