5.8. 模式

一個 PostgreSQL 數據庫集群 包含一個或多個命名的數據庫。用戶和用戶組在整個集群的範圍內 是共享的,但是其它數據並不是共享的。任何給定的與服務器的客戶連接都只 能訪問在一個數據庫裡的數據,就是那個在連接請求裡聲明的。

注意: 一個集群的用戶並不一定要有訪問集群內所有數據庫的權限。 共享用戶名的意思是不能有同名用戶,也就是,在同一個集群裡的兩個 數據庫裡都有叫 joe 的用戶;但是系統可以配置成 只允許 joe 訪問某些數據庫。

一個數據庫包含一個或多個命名的 模式, 模式又包含表。模式還包含其它命名的對象,包括數據類型,函數, 以及操作符。同一個對象名可以在不同的模式裡使用而不會導致衝突; 比如,schema1myschema 都可以包含叫做 mytable 的表。和數據庫不同,模式不是嚴格分離的: 一個用戶可以訪問他所連接的數據庫種的任意模式中的對象, 只要他有權限。

我們需要模式的原因有好多:

模式類似于操作系統層次的目錄,只不過模式不能嵌套。

5.8.1. 創建一個模式

要創建一個獨立的模式,使用命令 CREATE SCHEMA。 給出你選擇的模式名字。比如:

CREATE SCHEMA myschema;

要創建或者訪問在模式中的對象,寫出一個受修飾的名字, 這個名字包含模式名以及表(對象名),它們之間用一個句點分開:

schema.table

實際上,更一般的語法是

database.schema.table

這個語法也可以使用,但目前它只是為了和 SQL 標準形式上兼容; 如果你寫了一個數據庫名,那麼它必須和你當前連接的數據庫同名。

要在新模式裡創建一個表,用

CREATE TABLE myschema.mytable (
 ...
);

這樣的方法在任何需要一個表名字的地方都可以用, 包括修改表的命令和我們在下一章要講的數據訪問命令。

如果一個模式是空的(所有它裡面的對象都已經刪除),那麼刪除一個模式的命令

DROP SCHEMA myschema;

要刪除一個包含所有對象的模式,使用

DROP SCHEMA myschema CASCADE;

參閱 Section 5.10 獲取躲藏在這些動作背後 的東西的一般機制的描述。

通常你想創建一個別人擁有的模式(因為這是一種限制你的用戶 在定義良好的模式中的活動的方法)。其語法如下:

CREATE SCHEMA schemaname AUTHORIZATION username;

你甚至可以省略模式名字,這時模式名將和用戶名同名。 參閱 Section 5.8.6 獲取這種情況 的適用場合。

pg_ 開頭的模式名是保留給系統使用的, 用戶不能創建這樣的名字。

5.8.2. Public 模式

在前面的小節裡,我們沒有聲明任何模式名字就創建了表。 缺省時,這樣的表(以及其他對象)都自動放到一個叫做"public" 的模式中去了。每個新數據庫都包含一個這樣的模式。因此,下面的命令是等效的:

CREATE TABLE products ( ... );

CREATE TABLE public.products ( ... );

5.8.3. 模式搜索路徑

全稱的名字寫起來非常費勁,並且我們最好不要在應用裡直接 寫上特定的模式名。因此,表通常都是用未修飾的名字 引用的,這樣的名字裡只有表名字。系統通過查找一個搜索路徑 來判斷一個表究竟是哪個表,這個路徑是一個需要查找的模式列表。 在搜索路徑裡找到的第一個表將被當作選定的表。如果在搜索路徑中 沒有匹配表,那麼就報告一個錯誤,即使匹配表的名字在數據庫其它的 模式中存在也如此。

在搜索路徑中的第一個模式叫做當前模式。除了是搜索的第一個模式之外, 它還是在 CREATE TABLE 沒有聲明模式名的時候,新建表 所在的地方。

要顯示當前搜索路徑,使用下面的命令:

SHOW search_path;

在缺省的設置中,返回下面的東西:

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

第一個元素聲明將要搜索一個和當前用戶同名的模式。 因為還沒有這樣的模式存在,所以這條記錄被忽略。第二個元素指向 我們已經看過的公共模式。

搜索路徑中存在的第一個模式是創建新對象的缺省位置。 這就是為什麼缺省的對象都會創建在 public 模式裡的原因。 如果在任何其它環境中引用對象,而且沒有用模式修飾 (表修改,數據變更,或者查詢命令),那麼系統會遍歷 搜索路徑,直到找到一個匹配的對象。因此,在缺省的配置裡, 任何未修飾的訪問同樣也只能引用 public 模式。

要把新的模式放到路徑中來,我們用

SET search_path TO myschema,public;

(我們在這裡省略了 $user 是因為我們 並不是立即需要它。)然後我們就可以不用加模式修飾訪問 表了:

DROP TABLE mytable;

同樣,因為 myschema 是路徑中的第一個元素, 新對象缺省時將創建在這裡。

我們也可以寫成

SET search_path TO myschema;

然後我們如果不明確修飾的話,就不能再訪問 public 模式了。 public 模式沒有任何特殊之處,只不過它缺省時就存在。 我們也可以把它刪除了。

又見 Section 9.13 獲取其它訪問模式搜索路徑的方法。

搜索路徑對于數據類型名,函數名以及操作符名的運作方式和表名字完全相同。 數據類型和函數名可以象表名字一樣加以修飾。如果你需要再表達式裡寫一個 有修飾的操作符名字,我們有一個特殊的要求:你必須這麼寫

OPERATOR(schema.operator)

這樣是為了避免語法歧義。下面是一個例子

SELECT 3 OPERATOR(pg_catalog.+) 4;

實際上我們通常依賴搜索路徑尋找操作符, 這樣就不用寫這麼難看的東西了。

5.8.4. 模式和權限

缺省時,用戶看不到模式中不屬于他們所有的對象。 為了讓他們看得見,模式的所有者需要在模式上賦予 USAGE 權限。為了讓用戶使用模式中的對象,我們可能需要賦予額外的權限, 只要是適合該對象的。

用戶也可以允許在別人的模式裡創建對象。要允許這麼做, 我們需要賦予在該模式上的 CREATE 權限。 請注意,缺省時,每個人都在 public 模式上 有 CREATE 權限。這樣就允許所有可以連接到 指定數據庫上的用戶在這裡創建對象。如果你不允許這麼做, 你可以撤銷這個權限:

REVOKE CREATE ON public FROM PUBLIC;

(第一個 "public" 是模式,第二個 "public" 意思是"所有用戶"。 第一個是個標識符,而第二個是個保留字,所以有不同的大小寫; 記住我們在 Section 4.1.1 裡面 說過的原則。)

5.8.5. 系統表模式

除了 public 和用戶創建的模式之外, 每個數據庫都包含一個 pg_catalog 模式,它包含 系統表和所有內置數據類型,函數和操作符。pg_catalog 總是搜索路徑中的一部分。如果它沒有明確出現在路徑中,那麼 它會隱含地在路徑裡地模式之前搜索。這樣就保證了內置地名字 總是可以搜索地。不過,你可以明確地把pg_catalog 放在你的搜索路徑的後面,如果你想用用戶定義的名字覆蓋內置的名字的話。

PostgreSQL 版本 7.3 之前, 以 pg_ 開頭的表名字是保留的。這個規則現在 不再是正確的了:如果必要,你可以創建這樣的表名字, 只要是在非系統模式裡。不過,我們最好還是不要使用這樣的名字, 以保證自己將來不會和新版本衝突:那些版本也許會定義一些和 你的表同名的表。(在缺省搜索路徑中,一個對你的表的無修飾 引用將解析為系統表。)系統表將繼續遵循以pg_ 開頭的傳統,因此,只要你的表不是以pg_ 開頭, 就不會和無修飾的用戶表名字衝突。

5.8.6. 使用方式

模式可以以多種方式組織你的數據。下面是一些建議使用的模式, 它們也很容易在缺省配置中得到支持:

5.8.7. 移植性

在 SQL 標準裡,在同一個模式裡的對象由不同的用戶所有的概念是不存在的。 而且,有些實現不允許你創建和它們的所有者不同名的模式。實際上, 模式和用戶的概念在那些只實現了標準中規定的基本模式支持的數據庫系統裡幾乎是一樣的。 因此,許多用戶考慮對名字加以修飾,使它們真正由 username.tablename組成。 如果你為每個用戶都創建了一個模式,這實際上就是 PostgreSQL 的行為。

同樣,在 SQL 標準裡也沒有 public 模式的概念。 為了最大限度地遵循標準,你不應該使用(可能甚至是應該刪除) public 模式。

當然,有些 SQL 數據庫系統可能根本沒有實現模式,或者是通過允許 (可能是有限制的)跨數據庫訪問來提供模式的支持。 如果你需要在這些系統上幹活,那麼最大限度的移植性來自根本不用模式。