W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
一個對PostgreSQL有用的擴展通常包括多個 SQL 對象,例如,一種新的數(shù)據(jù)類型將需要新函數(shù)、新操作符以及可能的新索引操作符類。將所有這些對象收集到一個單一包中有助于簡化數(shù)據(jù)庫管理。PostgreSQL稱這樣一個包為一個擴展。要定義一個擴展,你至少需要一個包含創(chuàng)建該擴展的對象的SQL命令的 腳本文件以及一個指定擴展本身的一些基本屬性的控制文件。如果擴展包括 C 代碼,通常還有一個 C 代碼編譯而成的共享庫文件。一旦你有了這些文件,一個簡單的CREATE EXTENSION命令可以把這些對象載入到你的數(shù)據(jù)庫。
使用一個擴展而不是只運行SQL腳本載入一堆“松散”對象到數(shù)據(jù)庫的主要優(yōu)點是,PostgreSQL將能理解該擴展的對象是一起的。你可以用一個單一的
DROP EXTENSION
命令刪除所有的對象(不用維護一個單獨的“卸載”腳本)。 甚至更有用的一點是,pg_dump知道它不應該轉(zhuǎn)儲該擴展中的個體成員對象 — 它將只在轉(zhuǎn)儲中包括一個CREATE EXTENSION
命令。這大大簡化了遷移到一個包含不同于舊版擴展中對象的新版擴展的工作。不過,注意在把這樣一個轉(zhuǎn)儲載入到一個新數(shù)據(jù)庫時,該擴展的控制、腳本和其他文件必須可用。
PostgreSQL不會讓你刪除包含在一個擴展中的個體對象,除非刪除整個擴展。還有,雖然你能夠改變一個擴展的成員對象的定義(例如,通過CREATE OR REPLACE FUNCTION
改變一個函數(shù)),記住被修改后的定義將不會被pg_dump轉(zhuǎn)儲。這種改變通常只有在你并發(fā)地在擴展腳本文件中做出相同更改時才有意義(但是對于包含配置數(shù)據(jù)的表有特殊的規(guī)定,見
第 37.17.3 節(jié))。在生產(chǎn)環(huán)境中,通常更好的方式是創(chuàng)建一個擴展更新腳本來執(zhí)行對擴展中成員對象的更改。
擴展腳本可能會設置擴展中所含對象的特權(quán),使用GRANT
和REVOKE
語句。 每一個對象的最終特權(quán)集合(如果設置了任何特權(quán))將被存儲在pg_init_privs
系統(tǒng)目錄中。
使用pg_dump時,CREATE EXTENSION
命令將被包括在轉(zhuǎn)儲中,后面會跟著必要的GRANT
和REVOKE
語句集合來將對象的特權(quán)設置成取得該轉(zhuǎn)儲時的樣子。
PostgreSQL當前不支持擴展腳本發(fā)出CREATE POLICY
或者SECURITY LABEL
語句。這些東西的設置應該在擴展被創(chuàng)建好之后來進行。所有在擴展對象上創(chuàng)建的 RLS 策略和安全標簽都將被包括在pg_dump創(chuàng)建的轉(zhuǎn)儲中。
擴展機制也對打包調(diào)整一個擴展中所含 SQL 對象定義的修改腳本有規(guī)定。例如,如果一個擴展的 1.1 版本比 1.0 版本增加了一個函數(shù)并且更改了另一個函數(shù)的函數(shù)體,該擴展的作者可以提供一個更新腳本來做這兩個更改。那么ALTER EXTENSION UPDATE
命令可以被用來應用這些更改并且跟蹤在給定數(shù)據(jù)庫中實際安裝的是該擴展的哪個版本。
能作為一個擴展的成員的 SQL 對象的種類如ALTER EXTENSION所示。尤其是數(shù)據(jù)庫集簇范圍的對象(例如數(shù)據(jù)庫、角色和表空間)不能作為擴展成員,因為一個擴展只在一個數(shù)據(jù)庫范圍內(nèi)可見(盡管一個擴展腳本并沒有被禁止創(chuàng)建這些對象,但是這樣做將無法把它們作為擴展的一部分來跟蹤)。還要注意雖然一個表可以是一個擴展的成員,它的扶助對象(例如索引)不會被直接認為是該擴展的成員。另一個重點是模式可以屬于擴展,但是反過來不行:一個擴展本身有一個不被限定的名稱并且不存在于任何模式 “中”。不過,擴展的成員對象只要對象類型合適就可以屬于模式。一個擴展擁有包含其成員對象的模式可能合適也可能不合適。
如果一個擴展的腳本創(chuàng)建任何臨時對象(例如臨時表),在當前會話的剩余部分會把它們當作擴展的成員,但是在會話結(jié)束會自動刪除它們,這和任何其他臨時對象是一樣的。對于不刪除整個擴展就不能刪除擴展的成員對象的規(guī)則來說,這是一種例外。
CREATE EXTENSION命令依賴每一個擴展都有的控制文件,控制文件必須被命名為擴展的名稱加上一個后綴.control
,并且必須被放在安裝的SHAREDIR/extension
目錄中。還必須至少有一個
SQL腳本文件,它遵循命名模式
(例如,extension
--version
.sqlfoo--1.0.sql
表示擴展foo
的1.0
版本)。默認情況下,腳本文件也被放置在SHAREDIR/extension
目錄中,但是控制文件中可以為腳本文件指定一個不同的目錄。
一個擴展控制文件的格式與postgresql.conf
文件相同,即是一個parameter_name
=
value
賦值的列表,每行一個。允許空行和#
引入的注釋。注意對任何不是單一詞或數(shù)字的值加上引號。
一個控制文件可以設置下列參數(shù):
directory
(string
)包含擴展的SQL腳本文件的目錄。除非給出一個絕對路徑,這個目錄名是相對于安裝的SHAREDIR
目錄。默認行為等效于指定directory = 'extension'
。
default_version
(string
)該擴展的默認版本(就是如果在CREATE EXTENSION
中沒有指定版本時將會被安裝的那一個)。盡管可以忽略這個參數(shù),但如果沒有出現(xiàn)VERSION
選項時那將會導致CREATE EXTENSION
失敗,因此你通常不會希望這樣做。
comment
(string
)一個關(guān)于該擴展的注釋(任意字符串)。該注釋會在初始創(chuàng)建擴展時應用,但是擴展更新時不會引用該注釋(因為可能會覆蓋用戶增加的注釋)。擴展的注釋也可以通過在腳本文件中寫上COMMENT命令來設置。
encoding
(string
)該腳本文件使用的字符集編碼。當腳本文件包含任何非 ASCII 字符時,可以指定這個參數(shù)。否則文件都會被假定為數(shù)據(jù)庫編碼。
module_pathname
(string
)這個參數(shù)的值將被用于替換腳本文件中每一次出現(xiàn)的MODULE_PATHNAME
。如果設置,將不會進行替換。通常,這會被設置為$libdir/
并且接著shared_library_name
MODULE_PATHNAME
被用在CREATE FUNCTION
命令中進行 C-語言函數(shù)的創(chuàng)建,因此該腳本文件不必把共享庫的名稱硬編碼在其中。
requires
(string
)這個擴展依賴的其他擴展名的一個列表,例如requires = 'foo, bar'
。被依賴的擴展必須先于這個擴展安裝。
superuser
(boolean
)如果這個參數(shù)為true
(默認情況),只有超級用戶能夠創(chuàng)建該擴展或者將它更新到一個新版本。 (另請參見下面的 trusted
) 如果被設置為false
,只需要用來執(zhí)行安裝中命令或者更新腳本的特權(quán)。 如果任何腳本命令需要超級用戶權(quán)限,這將通常被設置為true
。 (這樣的命令總之都會失敗,但提前給出錯誤對用戶更友好。)
trusted
(boolean
)這個參數(shù),如果設置為 true
(非默認值),則允許某些非超級用戶安裝superuser
已設置為 true
的擴展。 具體地說,對于在當前數(shù)據(jù)庫上具有CREATE
特權(quán)的任何人,都允許安裝。 當執(zhí)行CREATE EXTENSION
的用戶不是超級用戶,但允許通過此參數(shù)安裝時,則此安裝或更新腳本作為引導超級用戶運行,而不是作為調(diào)用用戶。
這個參數(shù)是不相干的,如果 superuser
為 false
。 通常,對于可能允許訪問其他超級用戶功能的擴展這將不被設為真(true),例如文件系統(tǒng)訪問。 此外,標記一個可信的擴展需要大量額外的努力,以安全的編寫擴展的安裝和更新腳本;參見第 37.17.6 節(jié)。
relocatable
(boolean
)如果一個擴展可能在初始創(chuàng)建之后將其所含的對象移動到一個不同的模式中,它就是relocatable。默認值是false
,即該擴展是不可重定位的。詳見第 37.17.2 節(jié)。
schema
(string
)這個參數(shù)只能為非可重定位擴展設置。它強制擴展被載入到給定的模式中而非其他模式中。只有在初始創(chuàng)建一個擴展時才會參考schema
參數(shù),擴展更新時則不會參考這個參數(shù)。詳見第 37.17.2 節(jié)。
除了主要控制文件
,一個擴展還可以有二級控制文件,它們以extension
.control
的風格命名。如果提供了二級控制文件,它們必須被放置在腳本文件的目錄中。二級控制文件遵循主要控制文件相同的格式。在安裝或更新該擴展的版本時,一個二級控制文件中設置的任何參數(shù)將覆蓋主要控制文件中的設置。不過,參數(shù)extension
--version
.controldirectory
以及default_version
不能在二級控制文件中設置。
一個擴展的SQL腳本文件能夠包含任何 SQL 命令,除了事務控制命令(BEGIN
、COMMIT
等)以及不能在一個事務塊中執(zhí)行的命令(如VACUUM
)。這是因為腳本文件會被隱式地在一個事務塊中被執(zhí)行。
一個擴展的SQL腳本文件也能包含以\echo
開始的行,它將被擴展機制忽略(當作注釋)。如果腳本文件被送給psql而不是由CREATE EXTENSION
載入(見第 37.17.7 節(jié)中的示例腳本),這種機制通常被用來拋出錯誤。如果沒有這種功能,用戶可能會意外地把該擴展的內(nèi)容作為
“松散的”對象而不是一整個擴展載入,這樣的狀態(tài)恢復起來比較麻煩。
如果擴展腳本包含字符串 @extowner@
,該字符串將替換為調(diào)用 CREATE EXTENSION
or ALTER EXTENSION
的用戶的名稱(適當引用)。 通常,此特性會被標記為受信任的擴展使用,將所選對象的所有權(quán)分配給調(diào)用用戶,而不是引導超級用戶。 (但是,在這樣做時應該小心謹慎。例如,將c語言函數(shù)的所有權(quán)分配給非超級用戶將為該用戶創(chuàng)建一個權(quán)限升級路徑。)
盡管腳本文件可以包含指定編碼允許的任何字符,但是控制文件應該只包含純 ASCII 字符,因為PostgreSQL沒有辦法知道一個控制文件是什么編碼。實際上,如果你想在擴展的注釋中使用非 ASCII 字符只有一個問題。推薦的方法是不使用控制文件的comment
參數(shù),而是使用腳本文件中的COMMENT ON EXTENSION
來設置注釋。
用戶常常希望把擴展中包含的對象載入到一個與擴展的作者所設想的不一樣的模式中。對于這種可重定位性,有三種支持的級別:
一個完全可重定位的擴展能在任何時候被移動到另一個模式中,即使在它被載入到一個數(shù)據(jù)庫中之后。這種移動通過ALTER EXTENSION SET SCHEMA
命令完成,該命令會自動地把所有成員對象重命名到新的模式中。通常,只有擴展不包含任何對其所在模式的內(nèi)部假設時才可能這樣做。還有,該擴展的對象必須全部在同一個模式中(忽略那些不屬于任何模式的對象,例如過程語言)。要讓一個擴展變成完全可定位,在它的控制文件中設置relocatable = true
。
一個擴展可能在安裝過程中是可重定位的,但是安裝完后就不再可重定位。典型的情況是擴展的腳本文件需要顯式地引用目標模式,例如為 SQL 函數(shù)設置search_path
屬性。對于這樣一種擴展,在其控制文件中設置relocatable = false
,并且使用@extschema@
在腳本文件中引用目標模式。在腳本被執(zhí)行前,所有這個字符串的出現(xiàn)都將被替換為實際的目標模式名。用戶可以使用
CREATE EXTENSION
的SCHEMA
選項設置目標模式名。
如果擴展根本就不支持重定位,在它的控制文件中設置relocatable = false
,并且還設置schema
為想要的目標模式的名稱。這將阻止使用CREATE EXTENSION
的SCHEMA
選項修改目標模式,除非它指定的是和控制文件中相同的模式。如果該擴展包括關(guān)于模式名的內(nèi)部假設且模式名不能使用
@extschema@
的方法替換,這種選擇通常是必須的。@extschema@
替換機制在這種情況中也是可用的,不過由于模式名已經(jīng)被控制文件所決定,它的使用受到了很大的限制。
在所有情況下,腳本文件將被用search_path執(zhí)行,它最初會被設置為指向目標模式,也就是說CREATE EXTENSION
做的也是等效的工作:
SET LOCAL search_path TO @extschema@, pg_temp;
這允許由這個腳本文件創(chuàng)建的對象進入到目標模式中。如果腳本文件希望,它可以改變search_path
,但這種用法通常是不受歡迎的。在CREATE EXTENSION
結(jié)束后,search_path
會被恢復到之前的設置。
如果控制文件中給出了schema
參數(shù),目標模式就由該參數(shù)決定,否則目標模式由CREATE EXTENSION
的SCHEMA
選項給出,如果以上兩者都沒有給出則會用當前默認的對象創(chuàng)建模式(在調(diào)用者search_path
中的第一個)。當使用擴展文件的schema
參數(shù)時,如果目標模式還不存在將創(chuàng)建它,但是在另外兩種情況下它必須已經(jīng)存在。
如果在控制文件中的requires
中列舉了任何先導擴展,它們的目標模式會被追加到search_path
的初始設置中,遵循新擴展的目標模式。 這允許新擴展的腳本文件能夠看到它們的對象。
為了安全,pg_temp
在所有情況下都會自動添加到 search_path
的末尾。
盡管一個不可重定位的擴展能夠包含散布在多個模式中的對象,通常還是值得將意圖用于外部使用的所有對象放置在一個模式中,這被認為是該擴展的目標模式。這樣一種安排可以在依賴的擴展創(chuàng)建過程中方便地與search_path
的默認設置一起工作。
某些擴展包括配置表,其中包含可以由用戶在擴展安裝后增加或修改的數(shù)據(jù)。通常,如果一個表是一個擴展的一部分,該表的定義和內(nèi)容都不會被pg_dump轉(zhuǎn)儲。但是對于一個配置表來說并不希望是這樣的行為,任何用戶做出的數(shù)據(jù)修改都需要被包括在轉(zhuǎn)儲中,否則該擴展在重載之后的行為將和轉(zhuǎn)儲之前不同。
要解決這個問題,一個擴展的腳本文件可以把一個它創(chuàng)建的表或者序列標記為配置關(guān)系,這將導致pg_dump把該表或者序列的內(nèi)容(而不是它的定義)包括在轉(zhuǎn)儲中。要這樣做,在創(chuàng)建表或序列之后調(diào)用函數(shù)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', '');
可以用這種方法標記任意數(shù)量的表或者序列。與serial
或者bigserial
列相關(guān)聯(lián)的序列也可以被標記。
當pg_extension_config_dump
的第二個參數(shù)是一個空字符串時,該表的全部內(nèi)容都會被pg_dump轉(zhuǎn)儲。這通常只有在表被擴展腳本創(chuàng)建為初始為空時才正確。如果在表中混合有初始數(shù)據(jù)和用戶提供的數(shù)據(jù),pg_extension_config_dump
的第二個參數(shù)提供了一種WHERE
條件來選擇要被轉(zhuǎn)儲的數(shù)據(jù)。例如,你可能會做
CREATE TABLE my_config (key text, value text, standard_entry boolean); SELECT pg_catalog.pg_extension_config_dump('my_config', 'WHERE NOT standard_entry');
并且確保只有擴展腳本創(chuàng)建的行中standard_entry
才為真。
對于序列,pg_extension_config_dump
的第二個參數(shù)沒有影響。
更復雜的情況(例如用戶可能會修改初始提供的數(shù)據(jù))可以通過在配置表上創(chuàng)建觸發(fā)器來處理,觸發(fā)器將負責保證被修改的行會被正確地標記。
你可通過再次調(diào)用pg_extension_config_dump
來修改與一個配置表相關(guān)的過濾條件(這通常對于一個擴展更新腳本有用)。將一個表標記為不再是一個配置表的方法是用ALTER EXTENSION ... DROP TABLE
將它與擴展脫離開。
注意這些表之間的外鍵關(guān)系將會指導這些表被 pg_dump 轉(zhuǎn)儲的順序。特別地,pg_dump 將嘗試 先轉(zhuǎn)儲被引用的表再轉(zhuǎn)儲引用表。由于外鍵關(guān)系是在 CREATE EXTENSION 時間(先于數(shù)據(jù)被載入 到表中)建立的,環(huán)狀依賴還沒有建立。當環(huán)狀依賴存在時,數(shù)據(jù)將仍然被轉(zhuǎn)儲,但是該轉(zhuǎn)儲無法被 直接恢復并且必須要用戶的介入。
與serial
或者bigserial
列相關(guān)聯(lián)的序列需要被直接標記以轉(zhuǎn)儲它們的狀態(tài)。只標記它們的父關(guān)系不足以轉(zhuǎn)儲它們的狀態(tài)。
擴展機制的一個優(yōu)點是它提供了方便的方法來管理那些定義擴展中對象的 SQL 命令的更新。 這是通過為擴展的安裝腳本的每一個發(fā)行版本關(guān)聯(lián)一個版本名稱或者版本號實現(xiàn)的。 此外,如果你希望用戶能夠動態(tài)地把他們的數(shù)據(jù)庫從一個版本更新到下一個版本,你應該提供更新腳本來做必要的更改。 更新腳本的名稱遵循
模式 (例如,extension
--old_version
--target_version
.sqlfoo--1.0--1.1.sql
包含著把擴展foo
的版本1.0
修改成版本
1.1
的命令)。
假定有一個合適的更新腳本可用,命令ALTER EXTENSION UPDATE
將把一個已安裝的擴展更新到指定的新版本。更新腳本運行在與CREATE EXTENSION
提供給安裝腳本相同的環(huán)境中:特別是search_path
會按照相同的方式設置,并且該腳本創(chuàng)建的任何新對象會被自動地加入到擴展中。此外,如果腳本選擇刪除擴展的成員對象,它們會自動與擴展解除關(guān)聯(lián)。
如果一個擴展具有二級控制文件,用于更新腳本的控制參數(shù)是那些與新目標版本相關(guān)的參數(shù)。
ALTER EXTENSION
能夠執(zhí)行更新腳本的序列來實現(xiàn)一個要求的更新。例如,如果只有foo--1.0--1.1.sql
和foo--1.1--2.0.sql
可用,當前安裝了1.0
版本并且要求更新到版本2.0
,
ALTER EXTENSION
將依次應用它們。
PostgreSQL并不假定任何有關(guān)版本名稱的性質(zhì):例如,它不知道1.1
是否跟在1.0
后面。它只是匹配可用的版本名稱并且遵照要求應用最少更新腳本的路徑進行(一個版本名稱實際上可以是不含--
或者前導或后綴-
的字符串)。
有時提供“降級”腳本也有用,例如foo--1.1--1.0.sql
允許把版本1.1
相關(guān)的改變恢復原狀。如果你這樣做,要當心降級腳本被意外應用的可能性,因為它會得到一個較短的路徑。危險的情況是,有一個跳過幾個版本的“快速路徑”更新腳本還有一個降級到該快速路徑開始點的降級腳本。先應用降級然后再應用快速路徑可能比一次升級一個版本需要更少的步驟。如果降級版本刪除了任何不可替代的對象,這將會得到意想不到的結(jié)果。
要檢查意料之外的更新路徑,可使用這個命令:
SELECT * FROM pg_extension_update_paths('extension_name
');
這會為指定的擴展顯示已知的每一個可區(qū)分的版本名對,每一個版本名對還帶有一個從源版本到目標版本的更新路徑序列,如果沒有可用的更新路徑則這部份信息為NULL
。該路徑顯示為用--
分隔符的文本形式。如果你更喜歡數(shù)組格式,可以使用regexp_split_to_array(path,'--')
。
一個已經(jīng)存在一段時間的擴展可能存在多個版本,作者將需要為它們編寫更新腳本。例如,如果你已經(jīng)發(fā)布了擴展foo
的版本1.0
、1.1
和1.2
,就應該有更新腳本foo--1.0--1.1.sql
和foo--1.1--1.2.sql
。在
PostgreSQL10之前,還有必要創(chuàng)建新的腳本文件foo--1.1.sql
和foo--1.2.sql
,它們直接構(gòu)建比較新的擴展版本,或者新的版本無法被直接安裝,而是通過先安裝1.0
然后更新。那種方式是無聊的重復性工作,但是現(xiàn)在它是不必要的了,因為CREATE EXTENSION
能夠自動遵循更新鏈。例如,如果只有腳本文件
foo--1.0.sql
、foo--1.0--1.1.sql
和foo--1.1--1.2.sql
可用,那么安裝版本1.2
的請求會通過按順序運行上述三個腳本來實現(xiàn)。這種處理和先安裝1.0
然后更新到1.2
是一樣的(和
ALTER EXTENSION UPDATE
一樣,如果有多條路徑可用則優(yōu)先選擇最短的)。按這種風格安排擴展 的腳本文件可以減少生產(chǎn)小更新所需的維護工作量。
如果以這種風格維護的擴展中使用了二級(版本相關(guān)的)控制文件,記住每個版本都需要一個控制文件,即使它沒有單獨的安裝腳本,因為該控制文件將決定如何執(zhí)行到這個版本的隱式更新。例如,如果foo--1.0.control
指定有requires
= 'bar'
,但foo
的其他控制文件沒有這樣做,在從1.0
更新到另一個版本時,該擴展對bar
的依賴將被刪除。
廣泛分布的擴展應該很少考慮它們所占用的數(shù)據(jù)庫。 因此,以一種不會被基于搜索路徑的攻擊所破壞的安全風格編寫擴展提供的函數(shù)是合適的.
將superuser
設置為真的擴展還必須考慮在其安裝和更新腳本中執(zhí)行的操作的安全危害。 對于惡意用戶來說,創(chuàng)建特洛伊木馬對象并不非常困難,這些木馬對象會影響到后續(xù)粗心編寫的擴展腳本的執(zhí)行,從而允許該用戶獲得超級用戶特權(quán)。
如果擴展標記為 trusted
,則安裝用戶可以選擇其安裝模式,安裝用戶可能會有意使用不安全的模式,希望獲得超級用戶權(quán)限。 因此,從安全角度來看,受信任的擴展是非常易受攻擊的,并且必須仔細檢查其所有腳本命令,以確保不會出現(xiàn)危害。
關(guān)于安全地編寫函數(shù)的建議在下面的第 37.17.6.1 節(jié) 中, 關(guān)于安全編寫安裝腳本的建議在 第 37.17.6.2 節(jié)中提供。
擴展提供的SQL-language和PL-language 函數(shù)在執(zhí)行時面臨基于搜索路徑的攻擊風險,因為解析這些函數(shù)發(fā)生在執(zhí)行時,而不是創(chuàng)建時。
CREATE FUNCTION
參考頁包含有關(guān)安全地編寫SECURITY DEFINER
函數(shù)的建議。 將這些技術(shù)應用于擴展提供的任何函數(shù)是一種很好的做法,因為該函數(shù)可能由具有高權(quán)限的用戶調(diào)用。
如果無法將search_path
設置為僅包含安全模式,假設每個無資格的名稱都可以解析為惡意用戶定義的對象。 小心隱式依賴于search_path
的構(gòu)造;例如,IN
和CASE
總是使用搜索路徑選擇操作符。 在它們的位置,使用expression
WHENOPERATOR(
和 schema
.=) ANYCASE WHEN
。expression
通用擴展通常不應假定它已安裝到安全模式中,這意味著即使是對其自身對象的模式限定引用也不是完全無風險的。 例如,如果擴展定義了一個函數(shù)myschema.myfunc(bigint)
,則諸如myschema.myfunc(42)
此類的調(diào)用可以被惡意函數(shù)myschema.myfunc(integer)
捕獲。 請注意,函數(shù)和操作符形參的數(shù)據(jù)類型與聲明的實參類型完全匹配,必要時可使用顯式強制轉(zhuǎn)換。
編寫擴展安裝或更新腳本以防止腳本執(zhí)行時發(fā)生基于搜索路徑的攻擊。 如果腳本中的對象引用可以解析到腳本作者預期之外的其他對,則可能會立即發(fā)生妥協(xié),或者在稍后使用錯誤定義的擴展對象時發(fā)生妥協(xié)。
DDL 命令例如 CREATE FUNCTION
和 CREATE OPERATOR CLASS
通常是安全的,但要注意任何具有通用表達式作為組件的命令。 例如, CREATE VIEW
需要經(jīng)過審查, 就像 CREATE FUNCTION
中的 DEFAULT
表達式一樣。
有時,擴展腳本可能需要執(zhí)行通用 SQL,例如,通過DDL無法進行目錄調(diào)整。 小心地使用安全操作執(zhí)行此類search_path
; 不要 相信由 CREATE/ALTER EXTENSION
提供的路徑是安全的。 最佳做法是暫時設置search_path
到
'pg_catalog, pg_temp'
,并根據(jù)需要顯式插入對擴展安裝架構(gòu)的引用。 (這種做法可能也有助于創(chuàng)建視圖。) 示例可以在PostgreSQL源代碼分發(fā)版本的contrib
模塊中找到。
交叉擴展引用極難做到完全安全,部分由于不確定另一個擴展位于哪個模式中。 如果兩個擴展都安裝在同一模式中,則危險會減少,因為在安裝時間中無法將惡意對象置于引用的擴展search_path
。 但是,目前沒有任何機制要求這樣做。 目前,最佳做法是,如果擴展依賴于另一個擴展,則不要標記它受信任的擴展,除非該擴展始終安裝在pg_catalog
中。
不要使用CREATE OR REPLACE FUNCTION
函數(shù),除了在必須更改已知已是擴展成員的函數(shù)定義的更新腳本中。 (對于其他OR REPLACE
選項也一樣)。 不必要地使用OR REPLACE
不僅有意外覆蓋他人功能的風險,而且會產(chǎn)生安全風險,因為覆蓋的函數(shù)仍歸其原始所有者所有,而原始所有者可以對其進行修改。
這里是一個只用SQL的擴展的完整例子,一個兩個元素的組合類型,它可以在它的槽(命名為“k”和“v”)中存儲任何類型的值。非文本值會被自動強制為文本進行存儲。
腳本文件pair--1.0.sql
看起來像這樣:
-- 如果腳本是由 psql 而不是 CREATE EXTENSION 執(zhí)行,則報錯
\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" 容易操作,但限定名稱更好。
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 擴展
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,它在第 37.18 節(jié)中描述。命令make install
將把控制和腳本文件安裝到pg_config報告的正確的目錄中。
一旦文件被安裝,使用 命令就可以把對象載入到任何特定的數(shù)據(jù)庫中。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: