支援的版本:目前 (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 / 8.0 / 7.4 / 7.3

5.10. 結構描述 #

PostgreSQL 資料庫叢集包含一個或多個具名資料庫。角色和一些其他物件類型在整個叢集中共享。用戶端與伺服器的連線只能存取單一資料庫中的資料,即連線請求中指定的資料庫。

注意

叢集的使用者不一定有權限存取叢集中的每個資料庫。角色名稱的共享意味著在同一個叢集的兩個資料庫中不能有不同的角色命名為 joe;但系統可以設定為僅允許 joe 存取某些資料庫。

資料庫包含一個或多個具名結構描述,而結構描述又包含表格。結構描述還包含其他類型的具名物件,包括資料類型、函式和運算子。在一個結構描述中,相同類型的兩個物件不能具有相同的名稱。此外,表格、序列、索引、檢視表、具體化檢視表和外部表格共享相同的命名空間,因此,例如,如果索引和表格位於同一個結構描述中,則它們必須具有不同的名稱。相同的物件名稱可以在不同的結構描述中使用,而不會發生衝突;例如,schema1myschema 都可以包含名為 mytable 的表格。與資料庫不同,結構描述並非嚴格分離:如果使用者具有權限,則可以存取他們所連線的資料庫中任何結構描述中的物件。

可能想要使用結構描述的原因有以下幾個

  • 允許多個使用者使用一個資料庫,而不會相互干擾。

  • 將資料庫物件組織成邏輯群組,使其更易於管理。

  • 協力廠商應用程式可以放入單獨的結構描述中,因此它們不會與其他物件的名稱衝突。

結構描述類似於作業系統層級的目錄,但結構描述不能巢狀。

5.10.1. 建立結構描述 #

若要建立結構描述,請使用 CREATE SCHEMA 命令。為結構描述指定您選擇的名稱。例如

CREATE SCHEMA myschema;

若要在結構描述中建立或存取物件,請寫入一個限定名稱,該名稱由結構描述名稱和表格名稱組成,並以點分隔

schema.table

這適用於預期表格名稱的任何位置,包括表格修改命令和以下章節中討論的資料存取命令。(為簡潔起見,我們將僅談論表格,但相同的概念適用於其他類型的具名物件,例如類型和函式。)

實際上,更一般的語法

database.schema.table

也可以使用,但目前這只是為了符合 SQL 標準的形式。如果您寫入資料庫名稱,則它必須與您連線的資料庫相同。

因此,若要在新結構描述中建立表格,請使用

CREATE TABLE myschema.mytable (
 ...
);

若要捨棄一個結構描述(如果它是空的,即已捨棄其中的所有物件),請使用

DROP SCHEMA myschema;

若要捨棄一個結構描述,包括其中包含的所有物件,請使用

DROP SCHEMA myschema CASCADE;

有關此背後的通用機制的描述,請參閱第 5.15 節

通常您會想要建立由其他人擁有的結構描述(因為這是將使用者的活動限制在明確定義的命名空間中的方法之一)。其語法為

CREATE SCHEMA schema_name AUTHORIZATION user_name;

您甚至可以省略結構描述名稱,在這種情況下,結構描述名稱將與使用者名稱相同。有關這如何有用的資訊,請參閱第 5.10.6 節

pg_ 開頭的結構描述名稱保留供系統使用,使用者無法建立。

5.10.2. Public 結構描述 #

在前面的章節中,我們建立了表格,而沒有指定任何結構描述名稱。預設情況下,此類表格(和其他物件)會自動放入名為public的結構描述中。每個新資料庫都包含這樣一個結構描述。因此,以下是等效的

CREATE TABLE products ( ... );

CREATE TABLE public.products ( ... );

5.10.3. 結構描述搜尋路徑 #

限定名稱寫起來很麻煩,而且最好不要將特定的結構描述名稱寫入應用程式中。因此,表格通常由未限定名稱引用,該名稱僅由表格名稱組成。系統透過遵循搜尋路徑來確定要尋找哪個表格,搜尋路徑是要查找的結構描述的清單。搜尋路徑中第一個符合的表格被視為所需表格。如果搜尋路徑中沒有符合的表格,則會報告錯誤,即使資料庫中的其他結構描述中存在符合的表格名稱。

在不同結構描述中建立類似命名物件的能力,使得編寫每次都精確引用相同物件的查詢變得複雜。它也為使用者開啟了變更其他使用者查詢行為的潛力,無論是惡意還是意外。由於查詢中未限定名稱的普遍存在及其在 PostgreSQL 內部結構中的使用,將結構描述新增到 search_path 實際上信任所有具有該結構描述上 CREATE 權限的使用者。當您執行普通查詢時,能夠在搜尋路徑的結構描述中建立物件的惡意使用者可以控制並執行任意 SQL 函式,就像您執行它們一樣。

搜尋路徑中第一個被命名的綱要被稱為目前的綱要。除了是第一個被搜尋的綱要之外,它也是當 CREATE TABLE 指令沒有指定綱要名稱時,新表格將被建立於其中的綱要。

要顯示目前的搜尋路徑,請使用以下指令:

SHOW search_path;

在預設設定中,這會返回:

 search_path
--------------
 "$user", public

第一個元素指定要搜尋與目前使用者同名的綱要。如果不存在這樣的綱要,則忽略該條目。第二個元素指的是我們已經看過的 public 綱要。

搜尋路徑中第一個存在的綱要是建立新物件的預設位置。這就是預設情況下物件會建立在 public 綱要中的原因。當在任何其他上下文中引用物件而沒有綱要限定(表格修改、資料修改或查詢指令)時,會遍歷搜尋路徑直到找到匹配的物件。因此,在預設配置中,任何未限定的存取再次只能參考 public 綱要。

要將我們的新綱要放入路徑中,我們使用:

SET search_path TO myschema,public;

(我們在這裡省略 $user,因為我們沒有立即需要它。)然後我們可以不使用綱要限定來存取表格:

DROP TABLE mytable;

此外,由於 myschema 是路徑中的第一個元素,因此預設情況下會在其中建立新物件。

我們也可以寫成:

SET search_path TO myschema;

然後,我們在沒有明確限定的情況下,將無法再存取 public 綱要。public 綱沒有什麼特別之處,除了它預設存在。它也可以被刪除。

另請參閱 第 9.27 節,以了解操作綱要搜尋路徑的其他方法。

搜尋路徑對於資料類型名稱、函式名稱和運算子名稱的作用方式與對表格名稱的作用方式相同。資料類型和函式名稱可以與表格名稱完全相同的方式進行限定。如果您需要在表達式中編寫限定的運算子名稱,則有一個特殊的規定:您必須寫入:

OPERATOR(schema.operator)

這是為了避免語法上的歧義。一個例子是:

SELECT 3 OPERATOR(pg_catalog.+) 4;

實際上,通常依賴於運算子的搜尋路徑,以避免編寫如此醜陋的東西。

5.10.4. 綱要和權限 #

預設情況下,使用者無法存取他們不擁有的綱要中的任何物件。為了允許這樣做,綱要的擁有者必須授予該綱要上的 USAGE 權限。預設情況下,每個人都擁有 public 綱要上的該權限。為了允許使用者使用綱要中的物件,可能需要授予額外的權限,如同物件的適用情況。

也可以允許使用者在其他人的綱要中建立物件。為了允許這樣做,需要授予該綱要上的 CREATE 權限。在從 PostgreSQL 14 或更早版本升級的資料庫中,每個人都擁有 public 綱要上的該權限。某些 使用模式 需要撤銷該權限。

REVOKE CREATE ON SCHEMA public FROM PUBLIC;

(第一個 public 是綱要,第二個 public 表示 每個使用者。在第一種意義上,它是一個識別碼,在第二種意義上,它是一個關鍵字,因此大小寫不同;回想一下 第 4.1.1 節 中的準則。)

5.10.5. 系統目錄綱要 #

除了 public 和使用者建立的綱要之外,每個資料庫都包含一個 pg_catalog 綱要,其中包含系統表格和所有內建的資料類型、函式和運算子。pg_catalog 始終有效地成為搜尋路徑的一部分。如果它沒有在路徑中明確命名,那麼它會隱含地在搜尋路徑的綱要 之前 被搜尋。這確保了內建名稱始終可以被找到。但是,如果您希望使用者定義的名稱覆蓋內建名稱,您可以顯式地將 pg_catalog 放置在搜尋路徑的末尾。

由於系統表格名稱以 pg_ 開頭,因此最好避免使用此類名稱,以確保如果將來某個版本定義了與您的表格同名的系統表格,您不會遭受衝突。(使用預設搜尋路徑,對您的表格名稱的未限定引用將被解析為系統表格。)系統表格將繼續遵循名稱以 pg_ 開頭的慣例,以便它們不會與未限定的使用者表格名稱衝突,只要使用者避免使用 pg_ 前綴。

5.10.6. 使用模式 #

綱要可以用於以多種方式組織您的資料。一個安全綱要使用模式可防止不受信任的使用者更改其他使用者查詢的行為。當資料庫不使用安全綱要使用模式時,希望安全地查詢該資料庫的使用者將在每個會話開始時採取保護措施。具體來說,他們將通過將 search_path 設置為空字串,或者從 search_path 中刪除非超級使用者可寫入的綱要來開始每個會話。預設配置很容易支持以下幾種使用模式:

  • 將普通使用者限制為使用者私有綱要。要實現此模式,首先請確保沒有任何綱要具有公共 CREATE 權限。然後,對於每個需要建立非臨時物件的使用者,建立一個與該使用者同名的綱要,例如 CREATE SCHEMA alice AUTHORIZATION alice。(回想一下,預設搜尋路徑以 $user 開頭,它解析為使用者名稱。因此,如果每個使用者都有一個單獨的綱要,他們預設會存取自己的綱要。)除非不受信任的使用者是資料庫擁有者或已被授予相關角色的 ADMIN OPTION,否則此模式是一種安全綱要使用模式,在這種情況下,不存在安全綱要使用模式。

    PostgreSQL 15 及更高版本中,預設配置支援此使用模式。在早期版本中,或者在使用從早期版本升級的資料庫時,您需要從 public 綱要中刪除公共 CREATE 權限(發出 REVOKE CREATE ON SCHEMA public FROM PUBLIC)。然後考慮審計 public 綱要中以 pg_catalog 綱要中的物件命名的物件。

  • 通過修改 postgresql.conf 或發出 ALTER ROLE ALL SET search_path = "$user",從預設搜尋路徑中刪除 public 綱要。然後,授予在 public 綱要中建立的權限。只有限定名稱才會選擇 public 綱要物件。雖然限定表格引用沒有問題,但在 public 綱要中調用函式 將是不安全或不可靠的。如果您在 public 綱要中建立函式或擴展,請改用第一種模式。否則,與第一種模式一樣,除非不受信任的使用者是資料庫擁有者或已被授予相關角色的 ADMIN OPTION,否則這是安全的。

  • 保留預設的搜尋路徑,並授予在 public 綱要中建立物件的權限。所有使用者都會隱式地存取 public 綱要。這模擬了綱要完全不可用的情況,從而實現從非綱要感知世界到綱要世界的平滑過渡。然而,這永遠不是一種安全的模式。只有在資料庫只有單一使用者或少數相互信任的使用者時,這種做法才是可以接受的。在從 PostgreSQL 14 或更早版本升級的資料庫中,這是預設值。

對於任何模式,若要安裝共享應用程式(每個人都要使用的表格、第三方提供的額外函式等),請將它們放入單獨的綱要中。請記得授予適當的權限,以允許其他使用者存取它們。然後,使用者可以透過使用綱要名稱限定名稱來引用這些額外的物件,或者他們可以根據自己的選擇將額外的綱要放入其搜尋路徑中。

5.10.7. 可移植性 #

在 SQL 標準中,不存在同一綱要中的物件由不同使用者擁有的概念。此外,某些實作不允許您建立名稱與其擁有者不同的綱要。事實上,在僅實作標準中指定的基本綱要支援的資料庫系統中,綱要和使用者的概念幾乎是等效的。因此,許多使用者認為限定名稱實際上由 user_name.table_name組成。如果您為每個使用者建立一個按使用者劃分的綱要,PostgreSQL 實際上就會這樣運行。

此外,SQL 標準中沒有 public 綱要的概念。為了最大限度地符合標準,您不應該使用 public 綱要。

當然,某些 SQL 資料庫系統可能根本不實作綱要,或者透過允許(可能有限的)跨資料庫存取來提供命名空間支援。如果您需要使用這些系統,那麼透過完全不使用綱要可以實現最大的可移植性。

提交更正

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