模式 ========= 一个PostgreSQL数据库集簇中包含一个或更多命名的数据库。用户和用户组被整个集簇共享,但没有其他数据在数据库之间共享。任何给定客户端连接只能访问在连接中指定的数据库中的数据。 .. note:: .. rubric:: 注意 :name: 注意 :class: title 一个集簇的用户并不必拥有访问集簇中每一个数据库的权限。用户名的共享意味着不可能在同一个集簇中出现重名的不同用户,例如两个数据库中都有叫\ ``joe``\ 的用户。但系统可以被配置为只允许\ ``joe``\ 访问某些数据库。 一个数据库包含一个或多个命名\ *模式*\ ,模式中包含着表。模式还包含其他类型的命名对象,包括数据类型、函数和操作符。相同的对象名称可以被用于不同的模式中二不会出现冲突,例如\ ``schema1``\ 和\ ``myschema``\ 都可以包含名为\ ``mytable``\ 的表。和数据库不同,模式并不是被严格地隔离:一个用户可以访问他们所连接的数据库中的所有模式内的对象,只要他们有足够的权限。 下面是一些使用方案的原因: .. container:: itemizedlist - 允许多个用户使用一个数据库并且不会互相干扰。 - 将数据库对象组织成逻辑组以便更容易管理。 - 第三方应用的对象可以放在独立的模式中,这样它们就不会与其他对象的名称发生冲突。 模式类似于操作系统层的目录,但是模式不能嵌套。 创建模式 -------------- 要创建一个模式,可使用\ `CREATE SCHEMA `__\ 命令,并且给出选择的模式名称。例如: .. code:: programlisting CREATE SCHEMA myschema; 在一个模式中创建或访问对象,需要使用由模式名和表名构成的\ *限定名*\ ,模式名和表名之间以点号分隔: .. code:: synopsis schema.table 在任何需要一个表名的地方都可以这样用,包括表修改命令和后续章节要讨论的数据访问命令(为了简洁我们在这里只谈到表,但是这种方式对其他类型的命名对象同样有效,例如类型和函数)。 事实上,还有更加通用的语法: .. code:: synopsis database.schema.table 也可以使用,但是目前它只是在\ *形式上*\ 与SQL标准兼容。如果我们写一个数据库名称,它必须是我们正在连接的数据库。 因此,如果要在一个新模式中创建一个表,可用: .. code:: programlisting CREATE TABLE myschema.mytable ( ... ); 要删除一个为空的模式(其中的所有对象已经被删除),可用: .. code:: programlisting DROP SCHEMA myschema; 要删除一个模式以及其中包含的所有对象,可用: .. code:: programlisting DROP SCHEMA myschema CASCADE; 有关于此的更一般的机制请参见\ `第 5.14 节 `__\ 。 我们常常希望创建一个由其他人所拥有的模式(因为这是将用户动作限制在良定义的名字空间中的方法之一)。其语法是: .. code:: programlisting CREATE SCHEMA schema_name AUTHORIZATION user_name; 我们甚至可以省略模式名称,在此种情况下模式名称将会使用用户名,参见\ `第 5.9.6 节 `__\ 。 以\ ``pg_``\ 开头的模式名被保留用于系统目的,所以不能被用户所创建。 公共模式 -------------- 在前面的小节中,我们创建的表都没有指定任何模式名称。默认情况下这些表(以及其他对象)会自动的被放入一个名为“public”的模式中。任何新数据库都包含这样一个模式。因此,下面的命令是等效的: .. code:: programlisting CREATE TABLE products ( ... ); 以及: .. code:: programlisting CREATE TABLE public.products ( ... ); 模式搜索路径 -------------- 限定名写起来很冗长,通常最好不要把一个特定模式名拉到应用中。因此,表名通常被使用\ *非限定名*\ 来引用,它只由表名构成。系统将沿着一条\ *搜索路径*\ 来决定该名称指的是哪个表,搜索路径是一个进行查看的模式列表。 搜索路径中第一个匹配的表将被认为是所需要的。如果在搜索路径中没有任何匹配,即使在数据库的其他模式中存在匹配的表名也将会报告一个错误。 在不同方案中创建命名相同的对象的能力使得编写每次都准确引用相同对象的查询变得复杂。这也使得用户有可能更改其他用户查询的行为,不管是出于恶意还是无意。由于未经限定的名称在查询中以及在PostgreSQL内部的广泛使用,在\ ``search_path``\ 中增加一个方案实际上是信任所有在该方案中具有\ ``CREATE``\ 特权的用户。在你运行一个普通查询时,恶意用户可以在你的搜索路径中的以方案中创建能够夺取控制权并且执行任意SQL函数的对象,而这些事情就像是你在执行一样。 搜索路径中的第一个模式被称为当前模式。除了是第一个被搜索的模式外,如果\ ``CREATE TABLE``\ 命令没有指定模式名,它将是新创建表所在的模式。 要显示当前搜索路径,使用下面的命令: .. code:: programlisting SHOW search_path; 在默认设置下这将返回: .. code:: screen search_path -−-−-−-−-−-−-− "$user", public 第一个元素说明一个和当前用户同名的模式会被搜索。如果不存在这个模式,该项将被忽略。第二个元素指向我们已经见过的公共模式。 搜索路径中的第一个模式是创建新对象的默认存储位置。这就是默认情况下对象会被创建在公共模式中的原因。当对象在任何其他没有模式限定的环境中被引用(表修改、数据修改或查询命令)时,搜索路径将被遍历直到一个匹配对象被找到。因此,在默认配置中,任何非限定访问将只能指向公共模式。 要把新模式放在搜索路径中,我们可以使用: .. code:: programlisting SET search_path TO myschema,public; (我们在这里省略了\ ``$user``\ ,因为我们并不立即需要它)。然后我们可以删除该表而无需使用方案进行限定: .. code:: programlisting DROP TABLE mytable; 同样,由于\ ``myschema``\ 是路径中的第一个元素,新对象会被默认创建在其中。 我们也可以这样写: .. code:: programlisting SET search_path TO myschema; 这样我们在没有显式限定时再也不必去访问公共模式了。公共模式没有什么特别之处,它只是默认存在而已,它也可以被删除。 其他操作模式搜索路径的方法请见\ `第 9.25 节 `__\ 。 搜索路径对于数据类型名称、函数名称和操作符名称的作用与表名一样。数据类型和函数名称可以使用和表名完全相同的限定方式。如果我们需要在一个表达式中写一个限定的操作符名称,我们必须写成一种特殊的形式: .. code:: synopsis OPERATOR(schema.operator) 这是为了避免句法歧义。例如: .. code:: programlisting SELECT 3 OPERATOR(pg_catalog.+) 4; 实际上我们通常都会依赖于搜索路径来查找操作符,因此没有必要去写如此“丑陋”的东西。 .. container:: sect2 :name: DDL-SCHEMAS-PRIV .. container:: titlepage .. container:: .. container:: .. rubric:: 5.9.4. 模式和权限 :name: 模式和权限 :class: title 模式和权限 -------------- 默认情况下,用户不能访问不属于他们的方案中的任何对象。要允许这种行为,模式的拥有者必须在该模式上授予\ ``USAGE``\ 权限。为了允许用户使用方案中的对象,可能还需要根据对象授予额外的权限。 一个用户也可以被允许在其他某人的模式中创建对象。要允许这种行为,模式上的\ ``CREATE``\ 权限必须被授予。注意在默认情况下,所有人都拥有在\ ``public``\ 模式上的\ ``CREATE``\ 和\ ``USAGE``\ 权限。这使得用户能够连接到一个给定数据库并在它的\ ``public``\ 模式中创建对象。回收这一特权的\ `使用模式 `__\ 调用: .. code:: programlisting REVOKE CREATE ON SCHEMA public FROM PUBLIC; (第一个“public”是方案,第二个“public”指的是“每一个用户”。第一种是一个标识符,第二种是一个关键词,所以两者的大小写不同。请回想\ `第 4.1.1 节 `__\ 中的指导方针。) 系统目录模式 -------------- 除\ ``public``\ 和用户创建的模式之外,每一个数据库还包括一个\ ``pg_catalog``\ 模式,它包含了系统表和所有内建的数据类型、函数以及操作符。\ ``pg_catalog``\ 总是搜索路径的一个有效部分。如果没有在路径中显式地包括该模式,它将在路径中的模式\ *之前*\ 被搜索。这保证了内建的名称总是能被找到。然而,如果我们希望用用户定义的名称重载内建的名称,可以显式的将\ ``pg_catalog``\ 放在搜索路径的末尾。 由于系统表名称以\ ``pg_``\ 开头,最好还是避免使用这样的名称,以避免和未来新版本中 可能出现的系统表名发生冲突。系统表将继续采用以\ ``pg_``\ 开头的方式,这样它们不会 与非限制的用户表名称冲突。 使用模式 -------------- 模式能够以多种方式组织数据.\ *secure schema usage pattern*\ 防止不受信任的用户更改其他用户查询的行为。 当数据库不使用安全模式使用方式时,希望安全地查询该数据库的用户将在每个会话开始时采取保护操作。 具体的说,他们将通过设置\ ``search_path``\ 到空字符串或在其它情况下从\ ``search_path``\ 中删除非超级用户可写的模式来开始每个会话。 默认配置可以很容易的支持一些使用模式。 .. container:: itemizedlist - 将普通用户约束在其私有的方案中。要实现这一点,发出\ ``REVOKE CREATE ON SCHEMA public FROM PUBLIC``\ ,并且为每一个用户创建一个用其用户名命名的方案。 回想一下以\ ``$user``\ 开头的默认搜索路径,该路径解析为用户名。 因此,如果每个用户都有单独的模式,则默认情况下他们访问自己的模式。 在不受信任的用户已经登录的数据库中采用此模式后,请考虑审计名字类似于模式\ ``pg_catalog``\ 中的对象的公共模式。 此方式是一种安全模式的使用方式,除非不受信任的用户是数据库所有者或拥有\ ``CREATEROLE``\ 权限,在这种情况下没有安全模式使用方式存在。 - 从默认搜索路径中删除公共模式,通过修改\ ```postgresql.conf`` 或通过发出\ ``ALTER ROLE ALL SET search_path ="$user"``\ 。 每一个都保留在公共模式中创建对象的能力,但是只有符合资格的名称才会选择这些对象。 虽然符合资格的表引用是可以的,但是要调用公共模式中的函数\ `will be unsafe or unreliable `__\ 。 如果在公共模式中创建函数或扩展,请改用第一个方式。 否则,与第一个模式一样,这是安全的,除非不受信任的用户是数据库所有者或拥有\ ``CREATEROLE``\ 权限。 - 保持默认。所有用户都隐式地访问公共模式。这模拟了方案根本不可用的情况,可以用于从无模式感知的世界平滑过渡。 但是,这绝不是一个安全的模式。只有当数据库仅有单个用户或者少数相互信任的用户时,才可以接受。 对于任何一种模式,为了安装共享的应用(所有人都要用其中的表,第三方提供的额外函数,等等),可把它们放在单独的方案中。记住授予适当的特权以允许其他用户访问它们。然后用户可以通过以方案名限定名称的方式来引用这些额外的对象,或者他们可以把额外的方案放在自己的搜索路径中。 可移植性 -------------- 在SQL标准中,在由不同用户拥有的同一个模式中的对象是不存在的。此外,某些实现不允许创建与拥有者名称不同名的模式。事实上,在那些仅实现了标准中基本模式支持的数据库中,模式和用户的概念是等同的。因此,很多用户认为限定名称实际上是由\ ``user_name``.\ *``table_name``*\ 组成的。如果我们为每一个用户都创建了一个模式,PostgreSQL实际也是这样认为的。 同样,在SQL标准中也没有\ ``public``\ 模式的概念。为了最大限度的与标准一致,我们不应使用(甚至是删除)\ ``public``\ 模式。 当然,某些SQL数据库系统可能根本没有实现方案,或者提供允许跨数据库访问的名字空间。如果需要使用这样一些系统,最好不要使用方案。