一個PostgreSQL數據庫集群包含一個或多個已命名數據庫。用戶和用戶組在整個集群范圍內是共享的,但是其它數據並不共享。任何與服務器連接的客戶都只能訪問那個在連接請求里聲明的數據庫。
注意: 集群中的用戶並不一定要有訪問集群內所有數據庫的權限。共享用戶名的意思是不能有重名用戶。假定同一個集群里有兩個數據庫和一個joe用戶,系統可以配置成只允許joe 訪問其中的一個數據庫。
一個數據庫包含一個或多個已命名的模式,模式又包含表。模式還可以包含其它對象,包括數據類型、函數、操作符等。同一個對象名可以在不同的模式里使用而不會導致沖突;比如,schema1和myschema都可以包含一個名為mytable的表。和數據庫不同,模式不是嚴格分離的:只要有權限,一個用戶可以訪問他所連接的數據庫中的任意模式中的對象。
我們需要模式的原因有好多:
- 允許多個用戶使用一個數據庫而不會干擾其它用戶。
- 把數據庫對象組織成邏輯組,讓它們更便於管理。
- 第三方的應用可以放在不同的模式中,這樣它們就不會和其它對象的名字沖突。
模式類似於操作系統層次的目錄,只不過模式不能嵌套。
1. 創建模式
要創建一個模式,使用CREATE SCHEMA命令。給出你選擇的模式名字。比如:
CREATE SCHEMA myschema;
要創建或者訪問在模式中的對象,寫出一個受修飾的名字,這個名字包含模式名以及表名,它們之間用一個句點分開:
schema.table
這個方式在任何需要表名字的地方都可用,包括后面章節討論的表修改命令和數據訪問命令。出於簡化,我們將只討論表,這個概念適用於所有其它已命名對象類型,比如數據類型和函數。
實際上,更一般的語法
database.schema.table
也可以使用,但目前它只是為了和 SQL 標准形式上兼容。如果你寫了一個數據庫名,那么它必須和你當前連接的數據庫同名。
要在新模式里創建一個表,用
CREATE TABLE myschema.mytable (
...
);
如果一個模式是空的(所有它里面的對象都已經刪除),那么刪除一個模式的命令如下:
DROP SCHEMA myschema;
要刪除一個模式及其包含的所有對象,可以使用:
DROP SCHEMA myschema CASCADE;
通常你想創建一個他人擁有的模式(因為這是一種限制用戶在定義良好的模式中的活動的方法)。其語法如下:
CREATE SCHEMA schemaname AUTHORIZATION username;
你甚至可以省略模式名字,這時模式名將和用戶名同名。
以pg_開頭的模式名是保留給系統使用的,用戶不能創建這樣的名字。
2. Public 模式
在前面的小節里,我們沒有聲明任何模式名字就創建了表。缺省時,這樣的表(以及其它對象)都自動放到一個叫做"public"的模式中去了。每個新數據庫都包含一個這樣的模式。因此,下面的命令是等效的:
CREATE TABLE products ( ... );
和:
CREATE TABLE public.products ( ... );
3. 模式搜索路徑
全稱的名字寫起來非常費勁,並且我們最好不要在應用里直接寫上特定的模式名。因此,表通常都是用未修飾的名字引用的,這樣的名字里只有表名字。系統通過查找一個搜索路徑來判斷一個表究竟是哪個表,這個路徑是一個需要查找的模式名列表。在搜索路徑里找到的第一個表將被使用。如果在搜索路徑中沒有找到表,那么就報告一個錯誤(即使在數據庫里的其它模式中存在此表也如此)。
在搜索路徑中的第一個模式叫做"當前模式"。除了是搜索的第一個模式之外,它還是在CREATE TABLE沒有聲明模式名的時候,新建表的默認所在地。
要顯示當前搜索路徑,使用下面的命令:
SHOW search_path;
在缺省的設置中,返回下面的東西:
search_path
--------------
"$user",public
第一個元素聲明搜索和當前用戶同名的模式。因為還沒有這樣的模式存在,所以這條記錄被忽略。第二個元素指向我們已經看過的公共模式。
搜索路徑中第一個存在的模式是創建新對象的缺省位置。這就是為什么缺省的對象都會創建在 public 模式里的原因。如果在其它環境中引用對象且沒有模式修飾,那么系統會遍歷搜索路徑,直到找到一個匹配的對象。因此,在缺省的配置里,任何未修飾的訪問只能引用 public 模式。
要設置模式的搜索路徑,可以用(省略了$user是因為並不立即需要它)
SET search_path TO myschema,public;
然后我們就可以不使用模式修飾來訪問表了:
DROP TABLE mytable;
同樣,因為myschema是路徑中的第一個元素,新對象缺省時將創建在這里。
我們也可以寫成:
SET search_path TO myschema;
然后我們如果不明確修飾的話,就不能再訪問 public 模式了。public 模式沒有任何特殊之處,只不過它缺省時就存在。我們也可以刪除它。
搜索路徑對於數據類型名、函數名、操作符名的運作方式和表名完全相同。數據類型和函數名可以像表名一樣加以修飾。如果你需要在表達式里寫一個有模式修飾的操作符,你必須這么寫:
OPERATOR(schema.operator)
這樣是為了避免語法歧義。下面是一個例子:
SELECT 3 OPERATOR(pg_catalog.+) 4;
實踐中我們通常依賴搜索路徑尋找操作符,這樣就不用寫這么難看的東西了。
4. 模式和權限
缺省時,用戶無法訪問模式中不屬於他們所有的對象。為了讓他們能夠訪問,模式的所有者需要在模式上賦予他們USAGE權限。為了讓用戶使用模式中的對象,我們可能需要賦予適合該對象的額外權限。
用戶也可以在別人的模式里創建對象。要允許這么做,需要被賦予在該模式上的CREATE權限。請注意,缺省時每個人都在public模式上有CREATE和USAGE權限。這樣就允許所有可以連接到指定數據庫上的用戶在這里創建對象。如果你不打算這么做,可以撤銷這個權限:
REVOKE CREATE ON SCHEMA public FROM PUBLIC;
第一個"public"是模式,第二個"public"意思是"所有用戶"。第一句里它是個標識符,而第二句里是個關鍵字,所以有不同的大小寫。
5. 系統表模式
除了public和用戶創建的模式之外,每個數據庫都包含一個pg_catalog模式,它包含系統表和所有內置數據類型、函數、操作符。pg_catalog總是搜索路徑中的一部分。如果它沒有明確出現在路徑中,那么它隱含地在所有路徑之前搜索。這樣就保證了內置名字總是可以被搜索。不過,你可以明確地把pg_catalog放在搜索路徑之后,如果你想使用用戶自定義的名字覆蓋內置的名字的話。
自從系統表名以pg_開頭開始,最好避免使用這樣的名字,以保證自己將來不會和新版本沖突:那些版本也許會定義一些和你的表同名的表(在缺省搜索路徑中,一個對你的表的無修飾引用將解析為系統表)。系統表將繼續遵循以pg_開頭的傳統,因此,只要你的表不是以pg_開頭,就不會和無修飾的用戶表名字沖突。
6. 使用方式
模式可以用多種方式組織數據。下面是一些建議使用的模式,它們也很容易在缺省配置中得到支持:
如果沒有創建任何模式,那么所有用戶隱含都訪問 public 模式。這樣就模擬了沒有模式的時候的情景。這種設置建議主要用在只有一個用戶或者數據庫里只有幾個可信用戶的情形。這樣的設置也允許我們平滑地從無模式的環境過渡。
你可以為每個用戶創建一個模式,名字和用戶相同。要記得缺省的搜索路徑從$user開始,它會解析為用戶名。因此,如果每個用戶都有一個獨立的模式,那么他們缺省時訪問他們自己的模式。
如果你使用了這樣的設置,那么你可能還想撤銷對 public 模式的訪問(或者刪除它),這樣,用戶就真的限制於他們自己的模式了。
要安裝共享的應用(被所有人使用的表、第三方提供的額外函數等等),我們可以把它們放到獨立的模式中。只要記得給需要訪問它們的用戶賦予合適的權限就可以了。然后用戶就可以通過用一個模式名修飾來使用這些額外的對象,或者他們可以把額外的模式放到他們的搜索路徑中。
7. 移植性
在 SQL 標准里,在同一個模式里的對象被不同的用戶所有的概念是不存在的。而且,有些實現不允許你創建和它們的所有者不同名的模式。實際上,模式的概念和用戶在那些只實現了標准中規定的基本模式支持的數據庫系統里幾乎是一樣的。因此,許多用戶考慮對名字加以修飾,使它們真正由username.tablename 組成。如果你為每個用戶都創建了一個模式,這實際上就是PostgreSQL的行為。
同樣,在 SQL 標准里也沒有public模式的概念。為了最大限度地遵循標准,你不應該使用(可能甚至是應該刪除)public模式。
當然,有些數據庫系統可能根本沒有模式,或者是通過允許跨數據庫訪問來提供模式的功能。如果你需要在這些系統上干活,那么為了最大限度的移植性,應該根本不使用模式。