一个PostgreSQL数据库集簇中包含一个或更多命名的数据库。 角色和一些其他对象类型被整个集簇共享,连接到服务器的客户端只能访问单个数据库中的数据,在连接请求中指定的那一个。
一个集簇的用户并不必拥有访问集簇中每一个数据库的权限。 角色名的共享意味着不可能在同一个集簇中出现重名的不同角色,例如两个数据库中都有叫joe的用户。 但系统可以被配置为只允许joe访问某些数据库。
一个数据库包含一个或多个命名的模式,模式中又包含表。 模式还包含其他类型的命名对象,包括数据类型、函数和操作符。 在同一个模式中,同一类型的两个对象不能有相同的名称。 此外,表、序列、索引、视图、物化视图和外部表共享同一个名字空间, 因此例如当它们位于同一模式中时,索引和表就必须具有不同的名称。 相同的对象名可以在不同模式中重复使用而不发生冲突;例如, schema1和myschema都可以包含名为mytable的表。 与数据库不同,模式并不是被严格隔离的:只要拥有相应权限,用户就可以访问其所连接数据库中任意模式里的对象。
下面是一些使用模式的原因:
允许多个用户使用一个数据库并且不会互相干扰。
将数据库对象组织成逻辑组以便更容易管理。
第三方应用的对象可以放在独立的模式中,这样它们就不会与其他对象的名称发生冲突。
模式类似于操作系统层的目录,但是模式不能嵌套。
要创建一个模式,可使用CREATE SCHEMA命令,并且给出选择的模式名称。例如:
CREATE SCHEMA myschema;
在一个模式中创建或访问对象,需要使用由模式名和表名构成的限定名,模式名和表名之间以点号分隔:
schema.table
在任何需要一个表名的地方都可以这样用,包括表修改命令和后续章节要讨论的数据访问命令(为了简洁我们在这里只谈到表,但是这种方式对其他类型的命名对象同样有效,例如类型和函数)。
实际上,也可以使用更通用的语法
database.schema.table
,但目前这只是为了形式上符合 SQL 标准。如果写出数据库名称,它必须与当前连接的数据库相同。
因此,如果要在一个新模式中创建一个表,可用:
CREATE TABLE myschema.mytable ( ... );
要删除一个为空的模式(其中的所有对象已经被删除),可用:
DROP SCHEMA myschema;
要删除一个模式以及其中包含的所有对象,可用:
DROP SCHEMA myschema CASCADE;
有关于此的更一般的机制请参见Section 5.14。
我们常常希望创建一个由其他人所拥有的模式(因为这是将用户动作限制在良定义的名字空间中的方法之一)。其语法是:
CREATE SCHEMAschema_nameAUTHORIZATIONuser_name;
我们甚至可以省略模式名称,在此种情况下模式名称将会使用用户名,参见Section 5.9.6。
以pg_开头的模式名被保留用于系统目的,所以不能被用户所创建。
在前面的小节中,我们创建表时都没有指定模式名。默认情况下,这些表(以及其他对象)会自动放入名为“public”的模式中。每个新数据库都包含这样一个模式。因此,下面两条命令是等效的:
CREATE TABLE products ( ... );
以及:
CREATE TABLE public.products ( ... );
限定名写起来很繁琐,而且通常最好不要把某个特定模式名硬编码到应用中。因此,表通常通过非限定名来引用,也就是只写表名。系统会沿着一条搜索路径来决定该名称指的是哪个表;搜索路径就是要查找的一组模式列表。搜索路径中第一个匹配的表会被视为目标表。如果搜索路径中没有任何匹配,就会报错,即使数据库的其他模式中存在同名表也是如此。
在不同模式中创建命名相同的对象的能力使得编写每次都准确引用相同对象的查询变得复杂。这也使得用户有可能更改其他用户查询的行为,不管是出于恶意还是无意。由于未经限定的名称在查询中以及在PostgreSQL内部的广泛使用,在search_path中增加一个模式实际上是信任所有在该模式中具有CREATE权限的用户。在你运行一个普通查询时,如果恶意用户可以在搜索路径的模式中创建对象,那么他们将能够控制并执行任意SQL函数的对象,而这些事情就像是你在执行一样。
搜索路径中的第一个模式被称为当前模式。除了是第一个被搜索的模式外,如果CREATE TABLE命令没有指定模式名,它将是新创建表所在的模式。
要显示当前搜索路径,可使用下面的命令:
SHOW search_path;
在默认设置下这将返回:
search_path -------------- "$user", public
第一个元素说明一个和当前用户同名的模式会被搜索。如果不存在这个模式,该项将被忽略。第二个元素指向我们已经见过的公共模式。
搜索路径中第一个存在的模式,是创建新对象时的默认位置。这就是默认情况下对象会被创建在公共模式中的原因。当对象在其他任何未限定模式的上下文中被引用时(表修改、数据修改或查询命令),系统会沿搜索路径查找,直到找到匹配对象为止。因此,在默认配置中,任何非限定访问仍然只能指向公共模式。
要把新模式放在搜索路径中,我们可以使用:
SET search_path TO myschema,public;
(我们在这里省略了$user,因为我们并不立即需要它)。然后我们可以删除该表而无需使用模式进行限定:
DROP TABLE mytable;
同样,由于myschema是路径中的第一个元素,新对象默认也会创建在其中。
我们也可以这样写:
SET search_path TO myschema;
这样一来,在没有显式限定时,我们就不再能访问公共模式了。公共模式本身并无特殊之处,只是默认存在而已;它同样可以被删除。
其他操作模式搜索路径的方法请见Section 9.25。
搜索路径对于数据类型名称、函数名称和操作符名称的作用与表名一样。数据类型和函数名称可以使用和表名完全相同的限定方式。如果我们需要在一个表达式中写一个限定的操作符名称,我们必须写成一种特殊的形式:
OPERATOR(schema.operator)
这是为了避免句法歧义。例如:
SELECT 3 OPERATOR(pg_catalog.+) 4;
实际上我们通常都会依赖于搜索路径来查找操作符,因此没有必要去写如此“丑陋”的东西。
默认情况下,用户无法访问他们不拥有的模式中的任何对象。要允许这样做,模式的所有者必须授予该模式上的USAGE权限。 要允许用户使用模式中的对象,可能需要授予其他权限,适用于该对象。
也可以允许用户在他人的模式中创建对象。为此,需要授予该模式上的CREATE权限。注意,默认情况下,每个人都具有CREATE和USAGE权限,它们作用于模式public。这使所有能够连接到某个数据库的用户都可以在它的public模式中创建对象。一些使用方式要求撤销此权限:
REVOKE CREATE ON SCHEMA public FROM PUBLIC;
(第一个“public”是模式名,第二个“public”表示“每个用户”。前一种用法是标识符,后一种是关键字,因此大小写不同;请回顾Section 4.1.1。)
除public和用户创建的模式之外,每一个数据库还包括一个pg_catalog模式,它包含了系统表和所有内置的数据类型、函数以及操作符。pg_catalog总是搜索路径的一个有效部分。如果没有在路径中显式地包括该模式,它将在路径中的模式之前被搜索。这保证了内置的名称总是能被找到。然而,如果我们希望用用户定义的名称重载内置的名称,可以显式的将pg_catalog放在搜索路径的末尾。
由于系统表名以pg_开头,因此最好避免使用这样的名称,以免将来某个版本定义出与你的表同名的系统表。系统表会继续遵循以pg_开头的约定,因此只要用户避免使用pg_前缀,它们就不会与未经限定的用户表名发生冲突。
模式可以用多种方式组织数据。模式的安全使用方式可防止不受信任的用户改变其他用户查询的行为。如果数据库未采用安全的模式使用方式,希望安全查询该数据库的用户应在每个会话开始时采取保护措施。具体而言,应在每个会话开始时将search_path设为空字符串,或者以其他方式移除非超级用户可写的模式,使它们不再出现在以下路径中:search_path。默认配置很容易支持以下几种使用方式:
将普通用户限制在各自私有的模式中。为此,执行REVOKE CREATE ON SCHEMA public FROM PUBLIC,并为每个用户创建一个与其同名的模式。请记住,默认搜索路径以$user开头,它会解析为用户名。因此,如果每个用户都有单独的模式,他们默认访问的就是自己的模式。在已经有不受信任用户登录过的数据库中采用这种方式后,应考虑检查 public 模式中是否存在与pg_catalog模式中的对象同名的对象。除非不受信任的用户是数据库拥有者或持有CREATEROLE权限,否则这是一种安全的模式使用方式;如果存在上述情况,则没有安全的模式使用方式。
通过修改postgresql.conf,或执行ALTER ROLE ALL SET search_path = "$user",将 public 模式从默认搜索路径中移除。每个人仍可在 public 模式中创建对象,但只有限定名才会选中这些对象。虽然限定的表引用没有问题,但调用 public 模式中的函数会不安全或不可靠。如果要在 public 模式中创建函数或扩展,请改用第一种方式。否则,它和第一种方式一样,除非不受信任的用户是数据库拥有者或持有CREATEROLE权限,否则就是安全的。
保持默认设置。所有用户都会隐式访问 public 模式。这模拟了根本没有模式可用的情况,使从不支持模式的环境迁移过来时更加平滑。不过,这绝不是一种安全的方式。它只适用于数据库只有单个用户,或只有少数彼此信任的用户的场景。
对于任何一种模式,如果要安装共享应用 (所有人都要使用的表、第三方提供的附加函数等),可以把它们放进单独的模式中。 记得授予适当的权限,以便其他用户能够访问它们。这样,用户既可以通过带模式名的限定名来引用这些附加对象, 也可以按自己的需要把这些附加模式加入搜索路径。
在 SQL 标准中,不存在同一模式中的对象由不同用户拥有这一概念。 此外,有些实现不允许创建与其所有者名称不同的模式。 事实上,在那些只实现了标准中基本模式支持的数据库系统中,模式和用户这两个概念几乎是等价的。 因此,很多用户认为限定名实际上就是 。 如果你为每个用户都创建一个独立模式,PostgreSQL的行为实际上也会是这样。user_name.table_name
同样,SQL 标准中也没有public模式这一概念。为了尽可能符合标准,你不应使用public模式。
当然,某些SQL数据库系统可能根本没有实现模式,或者提供(很可能是有限制地)允许跨数据库访问的命名空间。如果需要使用这样的系统,为了获得最好的可移植性,最好不要使用模式。