PostgreSQL -public schema

Overview

上一篇文章中,介绍了理解 PostgreSQL schema的基础知识、创建和删除机制,并回顾了几个用例。本文将扩展这些基础知识并探讨与schema相关的权限管理。
但有一个需要澄清的初步事项。在上一篇文章中,讨论了一个可能的混淆点,涉及到“schema”一词的多重使用。在PostgreSQL数据库上下文中,该术语的专门含义与其在关系数据库管理系统中通常的使用方式不同。对于当前主题,有另一个类似的术语混乱,涉及“public”一词。

在初始创建数据库时,新创建的PostgreSQL数据库包括一个预定义的名为“public”的schema。它是一个和其他schema一样的schema,但同一个词也用作一个关键字,在本应使用实际role name的上下文中表示“all users”,例如schema权限管理。下面的例子将澄清这一术语的重要性及其两种不同用法。

查询Schema权限

在使用示例代码来授予和撤销schema权限之前,需要回顾一下如何检查schema的权限。使用 psql 命令行界面,我们使用 dn+ 命令列出schema和相关权限。对于新创建的 sampledb 数据库,我们会看到public schema的以下內容:

sampledb=# dn+ 
                          List of schemas
  Name  |  Owner   |  Access privileges   |      Description      
--------+----------+----------------------+------------------------
 public | postgres | postgres=UC/postgres+| standard public schema
        |          | =UC/postgres         |
(1 row)

前两列和第四列非常简单:如前所述,显示默认创建的schema名为“public”,描述为“standard public schema”,由角色“postgres”拥有。(除非另有指定,否则schema所有权设置为创建schema的角色。)第三列列出了访问权限,这是这里感兴趣的内容。权限信息的格式提供了三个项目:privlileges grantee(权限被授予者)、privilege(权限)和privilege grantor(权限授予者),格式为“grantee=privileges/grantor”,即等号左侧是接收权限的角色,等号右侧是一组指定特定权限的字母,最后在斜线后面是授予权限的角色。可能会有多个此类权限信息规范,以"+"号分隔列出,因为权限是累加的。
对于schema,有两种可能的权限可以分别授予:U 代表“USAGE”(使用)和 C 代表“CREATE”(创建)。前者是一个角色查找schema中包含的数据库对象(如表和视图)所必需的权限;后者允许一个角色在schema中创建数据库对象。其他类型的数据库对象还有其他权限字母,但对于schema,只适用于 U 和 C。
因此,为了解释上面的权限列表,第一个规范告诉我们,postgres 用户被授予在public schema上update和create的权限。

请注意,对于上面的第二个规范,等号左侧会出现一个空字符串。这就是通过前面提到的 PUBLIC 关键字授予all users的权限的表示方式。

后一种在public schema上授予"all users" usage和create权限的规范,被一些人视为可能违背了通用安全原则的最佳实践,因为最佳实践通常倾向于从默认限制访问开始,要求数据库管理员明确授予适当且最低必要的访问权限。这些在public schema上的宽松权限是有意在系统中配置的,目的是为了方便和兼容旧系统。
还要注意,除了宽松的权限设置外,public schema唯一特别之处在于它也列在了search_path中,正如我们在上一篇文章中讨论的那样。这同样是为了方便:search_path配置和宽松的权限设置共同使新数据库的使用变得像是没有schema这个概念一样。

public schema的历史背景

这种兼容性问题起源于大约十五年前(在 PostgreSQL 7.3 版之前,参见 7.3 版发布说明),当时 PostgreSQL 还没有schema 特征。在 7.3 版引入schema功能时,通过配置public schema的宽松权限和 search_path,使得旧版应用程序(这些应用程序不支持schema)能够在升级后的数据库功能中无需修改地运行,从而确保了兼容性。
除此之外,public schema没有什么特别之处:如果用例不需要它,一些 DBA 会删除它;其他人则通过撤销默认权限来锁定它。

撤销权限

schema权限通过 GRANT 和 REVOKE 命令进行管理,分别用于添加和撤销权限。我们将尝试一些限制public schema的权限具体示例,但一般语法是:

REVOKE [ GRANT OPTION FOR ]
    { { CREATE | USAGE } [, ...] | ALL [ PRIVILEGES ] }
    ON SCHEMA schema_name [, ...]
    FROM { [ GROUP ] role_name | PUBLIC } [, ...]
    [ CASCADE | RESTRICT ]

因此,作为初始的示例,我们从public schema中移除create权限。请注意,在这些示例中,小写单词“public”指的是schema,可以替换为数据库中存在的任何其他有效schema名称。大写的“PUBLIC”是一个特殊关键字,表示“所有用户”,可以用特定角色名称或逗号分隔的角色名称列表替换,以实现更精细的访问控制。

sampledb=# REVOKE CREATE ON SCHEMA public FROM PUBLIC;
REVOKE
sampledb=# dn+
                          List of schemas
  Name  |  Owner   |  Access privileges   |      Description       
--------+----------+----------------------+------------------------
 public | postgres | postgres=UC/postgres+| standard public schema
        |          | =U/postgres          | 
(1 row)

在这个schema权限列表中,与第一个列表唯一的区别是第二个权限规范中缺少了“C”,这证实了我们的命令已经生效:除了postgres用户之外的用户不再能在public schema中创建表、视图或其他对象。

请注意,上述从public schema中撤销create权限的命令是针对之前公布的漏洞CVE-2018-1058的建议缓解措施,该漏洞源于public schema上的默认权限设置。

进一步的限制可能需要通过删除使用权限来完全拒绝对此schema的查找访问:

sampledb=# REVOKE USAGE ON SCHEMA public FROM PUBLIC;
REVOKE
sampledb=# dn+
                          List of schemas
  Name  |  Owner   |  Access privileges   |      Description       
--------+----------+----------------------+------------------------
 public | postgres | postgres=UC/postgres | standard public schema
(1 row)

由于非所有者用户的所有可用的schema权限均已被撤销,因此整个第二个权限规范在上面的列表中消失。

我们使用两个单独的命令执行的操作可以通过一个指定所有权限的命令简洁地完成,如下所示:

sampledb=# REVOKE ALL PRIVILEGES ON SCHEMA public FROM PUBLIC;
REVOKE

此外,还可以撤销schema所有者的权限:

sampledb=# REVOKE ALL PRIVILEGES ON SCHEMA public FROM postgres;
REVOKE
sampledb=# dn+
                        List of schemas
  Name  |  Owner   | Access privileges |      Description       
--------+----------+-------------------+------------------------
 public | postgres |                   | standard public schema
(1 row)

但这实际上并没有实现任何实际效果,因为schema所有者仅凭所有权就保留了对其所拥有schema的全部权限,而不管明确分配与否。
public schema的宽松权限分配是与初始数据库创建相关的特殊产物。后续在现有数据库中创建的schema遵循了最佳实践,即开始时不分配任何权限。。例如,在创建名为“private”的新schema后检查schema权限,显示新schema没有权限:

sampledb=# create schema private;
CREATE SCHEMA
sampledb=# dn+
                          List of schemas
  Name   |  Owner   |  Access privileges   |      Description       
---------+----------+----------------------+------------------------
 private | postgres |                      | 
 public  | postgres |                      | standard public schema
(2 rows)

Granting Privileges

添加权限的命令的一般形式为:

GRANT { { CREATE | USAGE } [, ...] | ALL [ PRIVILEGES ] }
    ON SCHEMA schema_name [, ...]
    TO role_specification [, ...] [ WITH GRANT OPTION ]
where role_specification can be:
  [ GROUP ] role_name
  | PUBLIC
  | CURRENT_USER
  | SESSION_USER

例如,使用此命令,我们可以允许所有角色通过添加usage权限来查找private schema的数据库对象

sampledb=# GRANT USAGE ON SCHEMA private TO PUBLIC;
GRANT
sampledb=# dn+
                          List of schemas
  Name   |  Owner   |  Access privileges   |      Description       
---------+----------+----------------------+------------------------
 private | postgres | postgres=UC/postgres+| 
         |          | =U/postgres          | 
 public  | postgres |                      | standard public schema
(2 rows)

请注意,现在我们已经为schema分配了非默认权限,UC权限以postgres所有者作为第一规范出现。第二个规范,=U/postgres,对应于我们刚刚作为postgres用户调用的GRANT命令,授予所"all users"使用权限(回想一下,等号左侧的空字符串表示“all users”)。

例如,可以为名为“user1”的特定角色授予private schema的create和usage权限:

sampledb=# GRANT ALL PRIVILEGES ON SCHEMA private TO user1;
GRANT
sampledb=# dn+
                          List of schemas
  Name   |  Owner   |  Access privileges   |      Description       
---------+----------+----------------------+------------------------
 private | postgres | postgres=UC/postgres+| 
         |          | =U/postgres         +| 
         |          | user1=UC/postgres    | 
 public  | postgres |                      | standard public schema
(2 rows)

我们还未提及一般命令形式中的“WITH GRANT OPTION”子句。正如其字面意思,此子句允许授予的角色将指定的权限授予其他用户,它在权限列表中以附加到特定权限的星号表示。

sampledb=# GRANT ALL PRIVILEGES ON SCHEMA private TO user1 WITH GRANT OPTION;
GRANT
sampledb=# dn+
                          List of schemas
  Name   |  Owner   |  Access privileges   |      Description       
---------+----------+----------------------+------------------------
 private | postgres | postgres=UC/postgres+| 
         |          | =U/postgres         +| 
         |          | user1=U*C*/postgres  | 
 public  | postgres |                      | standard public schema
(2 rows)

结论

最后,请记住,我们只讨论了schema访问权限。虽然 USAGE 权限允许查找schema中的数据库对象,但要实际访问对象以执行特定操作(例如读取、写入、执行等),角色还必须具有对这些特定数据库对象执行这些操作的适当权限。

<think>好的,我现在需要解决用户提到的“non-empty schema public no schema history”问题。这个问题看起来和数据库的架构(schema)管理有关,尤其是在使用版本控制工具时,比如Liquibase或者Flyway。用户提到public模式非空但没有架构历史记录,这可能意味着数据库中存在表或其他对象,但版本控制工具没有对应的迁移记录,导致应用启动时检测到架构不一致。 首先,我需要理解这个问题的具体情境。通常,当使用数据库迁移工具时,每个变更(比如创建表、修改列)都会记录在架构历史表中,例如Flyway的flyway_schema_history,或者Liquibase的DATABASECHANGELOG。如果用户直接手动修改了数据库(比如通过psql或其他客户端创建了表),而没有通过迁移工具,那么工具在下次启动时就会发现现有的数据库对象,但历史表中没有对应的记录,从而报错。 接下来,我需要考虑可能的解决方案。根据以往经验,处理这类问题通常有两种方法:一种是初始化基线迁移(Baseline Migration),另一种是生成现有架构的初始脚本并标记为已执行。 第一种方法,基线迁移,适用于已经存在的数据库架构需要被版本控制工具接管的情况。例如,Flyway有baseline命令,可以在现有数据库上标记一个基线版本,这样后续的迁移会从这个版本开始应用。类似地,Liquibase也可以使用特定的更改日志来初始化基线。 第二种方法,手动生成当前架构的SQL脚本,并将其作为初始迁移脚本,然后标记该脚本为已执行。这需要导出当前数据库的结构,并确保迁移工具的历史表中有对应的记录。 另外,还需要考虑用户使用的具体工具,因为不同的工具可能有不同的处理方式。例如,Flyway和Liquibase在操作步骤上有所不同。用户的问题中没有明确说明使用哪种工具,可能需要进一步询问,但根据常见情况,Flyway可能更常用,所以可能需要重点说明Flyway的解决方法。 此外,还需要注意执行这些操作时的风险,比如确保生成的脚本准确无误,避免遗漏任何对象。同时,备份数据库也是重要的步骤,以防万一操作出错。 总结可能的解决方案步骤: 1. 确定使用的迁移工具(Flyway/Liquibase等)。 2. 使用工具提供的基线功能初始化现有架构。 3. 或者手动生成初始SQL脚本并标记为已执行。 4. 验证操作后的迁移是否正常工作。 同时,需要提醒用户后续的变更必须通过迁移工具进行,避免直接修改数据库导致问题再次发生。 可能还需要引用相关文档或最佳实践,例如Flyway官方文档关于基线的部分,或者Liquibase的初始化步骤。但用户提供的引用中并没有直接相关的资料,所以可能需要根据一般知识来回答。 最后,生成相关问题,帮助用户深入理解数据库迁移管理,例如如何避免架构漂移,如何回滚迁移等。</think>关于“非空模式 'public' 但没有模式历史记录”的问题,通常出现在使用数据库迁移工具(如Flyway或Liquibase)时,数据库已存在对象但缺乏迁移记录。以下是分步解决方案: --- ### **步骤1:理解问题原因** 当数据库的`public`模式中已存在表或对象,但迁移工具的历史表(如`flyway_schema_history`或`databasechangelog`)未记录这些变更时,工具会检测到架构状态不一致,导致报错[^1]。 --- ### **步骤2:使用基线迁移(Baseline Migration)** #### Flyway解决方案: 1. **执行基线命令**: 运行`flyway baseline`命令,将当前数据库状态标记为基线版本(如`V1`): ```bash flyway baseline -baselineVersion="1" -baselineDescription="Initial Baseline" ``` 2. **后续迁移**: 新增的迁移脚本版本号需高于基线版本(如`V2__xxx.sql`),工具将从此版本开始应用变更。 #### Liquibase解决方案: 1. **生成初始changelog**: 使用`generate-changelog`命令导出当前架构为初始changelog文件: ```bash liquibase generate-changelog initial.xml ``` 2. **标记为已执行**: 在`databasechangelog`表中手动插入一条记录,或通过`liquibase changelog-sync`标记。 --- ### **步骤3:手动生成初始脚本(无工具支持时)** 1. **导出当前架构**: 使用`pg_dump`导出`public`模式结构(以PostgreSQL为例): ```bash pg_dump -h localhost -U user -s -n public > V1__initial.sql ``` 2. **标记脚本为已执行**: - 将`V1__initial.sql`放入迁移脚本目录。 - 手动在历史表中插入记录(例如Flyway的`flyway_schema_history`): ```sql INSERT INTO flyway_schema_history (version, description, type, script, installed_on, success) VALUES ('1', 'Initial baseline', 'BASELINE', 'V1__initial.sql', NOW(), true); ``` --- ### **步骤4:验证与后续操作** 1. **运行迁移测试**: 执行`flyway migrate`或`liquibase update`,确认无新变更需要应用。 2. **严格遵循流程**: 后续所有架构变更必须通过迁移工具进行,避免直接修改数据库[^3]。 --- ### **注意事项** - **备份数据库**:操作前务必备份,防止数据丢失。 - **一致性检查**:确保生成的脚本完整覆盖现有对象(如表、索引、函数)。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值