一、概述
RLS(ROW Level Security) 是PostgreSQL 9.5版本中新增特性,提供了基于行的安全策略,它针对每一个用户限制哪些行可以 被普通的查询返回或者可以被数据修改命令插入、更新或删除。当在一个表上启用行安全性时,所有对该表选择行或者修改行的普通访问都必须被一条 行安全性策略所允许(不过,表的拥有者通常不服从行安全性策略)。
只有所有者才具有启用 / 禁用行级安全性,给表添加策略的权限。
常见命令:
CREATE POLICY
:创建策略
ALTER POLICY
:修改策略
DROP POLICY
:删除策略
ALTER TABLE
:用于行级安全性的启用 / 禁用。
二、DML涉及的主要函数
位于src/backend/rewrite/rowsecurity.c
void
get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index,
List **securityQuals, List **withCheckOptions,
bool *hasRowSecurity, bool *hasSubLinks)
主要逻辑:
- 初始化返回值:将返回值初始化为空列表或 false。
- 检查是否为普通关系:如果不是普通表或分区表,则直接返回。
- 获取用户 ID:使用 rte->checkAsUser 或当前用户的 ID。
- 检查行级安全状态:调用 check_enable_rls 函数检查是否启用行级安全。
- 获取行级安全策略:根据命令类型和用户 ID 获取行级安全策略。
- 添加安全限定条件:将行级安全策略的 USING 子句添加到查询中。
- 添加 WITH CHECK 选项:将行级安全策略的 WITH CHECK 子句添加到查询中。
- 处理特殊情况:如 SELECT 时需要 UPDATE 权限、INSERT … ON CONFLICT DO UPDATE 和MERGE。
- 关闭关系:关闭打开的关系。
- 设置 checkAsUser:将 checkAsUser 设置到安全限定条件和 WITH CHECK 选项中。
- 标记查询为行级安全:标记查询为行级安全,以便在环境变化时重新计划查询。
这个函数确保了只有符合行级安全策略的行才能被用户访问或修改。
三、ALTER TABLE语句涉及的函数
位于src/backend/commands/tablecmds.c
static void
ATExecSetRowSecurity(Relation rel, bool rls)
{
Relation pg_class;
Oid relid;
HeapTuple tuple;
relid = RelationGetRelid(rel);
/* Pull the record for this relation and update it */
pg_class = table_open(RelationRelationId, RowExclusiveLock);
tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for relation %u", relid);
((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = rls;
CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
table_close(pg_class, RowExclusiveLock);
heap_freetuple(tuple);
}
主要逻辑:
- 获取表的 OID。
- 打开系统目录 pg_class 并获取锁。
- 从系统缓存中查找表的元组。
- 修改元组中的 relrowsecurity 字段。
- 将修改后的元组写回到系统目录中。
- 关闭系统目录并释放锁。
- 释放元组。
这个函数是 PostgreSQL 内部实现行级安全策略的关键部分。通过修改 pg_class 表中的 relrowsecurity 字段,可以控制表是否启用行级安全策略。
四、CREATE POLICY涉及的函数
位于src/backend/commands/policy.c
ObjectAddress
CreatePolicy(CreatePolicyStmt *stmt)
主要逻辑:
- 解析命令类型和条件。
- 检查命令类型和条件的合法性。
- 获取表的 OID,并打开目标表。
- 解析 USING 和 WITH CHECK 条件。
- 打开系统表 pg_policy,并检查是否已存在同名策略。
- 插入新的策略元组到系统表。
- 记录依赖关系。
- 使缓存失效。
- 清理资源并返回新策略的对象地址。
五、ALTER POLICY涉及的函数
位于 src/backend/commands/policy.c
ObjectAddress
AlterPolicy(AlterPolicyStmt *stmt)
主要逻辑:
解析角色列表和条件表达式。
获取表的 OID,并打开目标表。
解析 USING 和 WITH CHECK 条件。
打开系统表 pg_policy,并查找指定的策略。
更新策略的字段(如角色列表、USING 条件、WITH CHECK 条件)。
更新依赖关系。
使缓存失效。
清理资源并返回修改后的策略的对象地址。
六、DROP POLICY涉及的函数
位于src/backend/commands/policy.c
void
RemovePolicyById(Oid policy_id)
主要逻辑:
- 打开系统表 pg_policy,并获取锁。
- 查找指定 OID 的策略。
- 检查策略是否存在。
- 获取策略所属的表,并检查表的类型和权限。
- 删除策略。
- 使缓存失效。
- 清理资源并关闭表和系统表。
作者介绍
葛文龙,移动云数据库助理工程师,负责云原生数据库He3DB的研发。