2025年9月25日: PostgreSQL 18 发布!
支持的版本: 当前 (18) / 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 数据库系统可能根本不实现模式,或者通过允许(可能受限的)跨数据库访问来提供命名空间支持。如果您需要与这些系统协同工作,那么不使用模式将实现最大的可移植性。

提交更正

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