支持的版本:当前 (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 的模式中。每个新数据库都包含这样一个模式。因此,以下是等效的

CREATE TABLE products ( ... );

CREATE TABLE public.products ( ... );

5.10.3. 模式搜索路径 #

限定名称书写起来很繁琐,而且最好不要将特定的模式名称硬编码到应用程序中。因此,表通常由非限定名称引用,该名称仅包含表名。系统通过遵循搜索路径来确定所指的表,该路径是要查找的模式的列表。搜索路径中第一个匹配的表被认为是想要查找的表。如果搜索路径中没有匹配项,则会报告错误,即使数据库中其他模式中存在匹配的表名也是如此。

在不同的模式中创建名称相似的对象的能力使编写每次都引用完全相同对象的查询变得复杂。它还使用户有可能恶意或意外地更改其他用户查询的行为。由于查询中非限定名称的普遍性及其在 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;

然后,我们不再有权访问公共模式,除非显式限定。公共模式没有什么特别之处,除了它默认存在。它也可以被删除。

有关操作模式搜索路径的其他方法,另请参见 第 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 数据库系统可能根本不实现模式,或者通过允许(可能有限的)跨数据库访问来提供命名空间支持。如果您需要使用这些系统,则可以通过完全不使用模式来实现最大的可移植性。

提交更正

如果您在文档中发现任何不正确的内容、与您使用特定功能的经验不符或需要进一步澄清的内容,请使用此表单报告文档问题。