一、概述
GrantRole 在 He3DB 中用于授予用户或角色对数据库对象的权限,是数据库权限管理的重要工具。通过合理使用 GRANT 和 REVOKE 命令,可以确保数据库的安全性和数据访问的控制。
二、GrantRole 命令的执行流程
- PostgresMain
- exec_simple_query →执行简单的 SQL 查询;
- StartTransactionCommand → 开始事务;
- pg_parse_query →解析为内部的抽象语法树(AST);
- PortalRun
- standard_ProcessUtility →权限检查和准备;
- GrantRole→处理创建角色的具体逻辑;
- CommandCounterIncrement→增量更新当前的命令计数器;
- CommitTransactionCommand→ 提交当前事务;
- finish_xact_command→ 在事务结束时,执行必要的清理和关闭操作;
三、核心结构体介绍
(一) GrantRoleStmt 是一个结构体,用于在 PostgreSQL 中表示授予或撤销角色的语句。以下是每个变量的详细解释:
typedef struct GrantRoleStmt
{
NodeTag type;
List *granted_roles; /* list of roles to be granted/revoked */
List *grantee_roles; /* list of member roles to add/delete */
bool is_grant; /* true = GRANT, false = REVOKE */
bool admin_opt; /* with admin option */
RoleSpec *grantor; /* set grantor to other than current role */
DropBehavior behavior; /* drop behavior (for REVOKE) */
} GrantRoleStmt;
- NodeTag type
类型: NodeTag
解释: NodeTag 是一个枚举类型,表示不同类型的节点。在这里,type 用于标识这是一个 GrantRoleStmt 节点。通常,type 的值为 T_GrantRoleStmt。 - List *granted_roles
类型: List *
解释: 这是一个链表(List),包含了将被授予或撤销的角色列表。每个元素是一个角色名称或角色标识符。这个变量指定了要操作的角色。 - List *grantee_roles
类型: List *
解释: 这同样是一个链表,包含了被授予或撤销角色的成员列表。换句话说,这些是将要被添加或删除的角色成员。这个变量指定了接受操作的角色。 - bool is_grant
类型: bool
解释: 这是一个布尔值,表示当前操作是授予角色 (GRANT) 还是撤销角色 (REVOKE)。如果 is_grant 为 true,则表示这是一个 GRANT 操作;如果为 false,则表示这是一个 REVOKE 操作。 - bool admin_opt
类型: bool
解释: 这是一个布尔值,表示是否授予 WITH ADMIN OPTION 选项。如果 admin_opt 为 true,则表示被授予的角色可以进一步将该角色授予其他用户;如果为 false,则不能进一步授予。 - RoleSpec *grantor
类型: RoleSpec *
解释: 这是一个指向 RoleSpec(角色规范)的指针,表示授予或撤销角色的执行者。通常情况下,这个值为 NULL,表示当前用户是执行者;如果不是 NULL,则表示由指定的角色执行操作。 - DropBehavior behavior
类型: DropBehavior
解释: 这个变量表示撤销角色时的行为。DropBehavior 是一个枚举类型,可能的值包括:
DROP_RESTRICT: 如果角色有依赖关系,则不允许撤销。
DROP_CASCADE: 撤销角色时,同时撤销依赖该角色的所有对象。
四、核心代码解析
在 He3DB 的源代码中, GrantRole(@src\backend\commands\user.c) 是处理角色授予和撤销的函数,通常用于执行相关的 SQL 语句。当调用 GrantRole 函数时,会根据 GrantRoleStmt 结构体中的信息执行授予或撤销角色的操作。
/*
* GrantRoleStmt
*
* Grant/Revoke roles to/from roles
*/
void
GrantRole(GrantRoleStmt *stmt)
{
Relation pg_authid_rel;
Oid grantor = 0;
List *grantee_ids = NULL;
ListCell *item = NULL;
if (stmt->grantor)
grantor = get_rolespec_oid(stmt->grantor, false);
else
grantor = GetUserId();
grantee_ids = roleSpecsToIds(stmt->grantee_roles);
/* AccessShareLock is enough since we aren't modifying pg_authid */
pg_authid_rel = table_open(AuthIdRelationId, AccessShareLock);
/*
* Step through all of the granted roles and add/remove entries for the
* grantees, or, if admin_opt is set, then just add/remove the admin
* option.
*
* Note: Permissions checking is done by AddRoleMems/DelRoleMems
*/
foreach(item, stmt->granted_roles)
{
AccessPriv *priv = (AccessPriv *) lfirst(item);
char *rolename = priv->priv_name;
Oid roleid = 0;
/* Must reject priv(columns) and ALL PRIVILEGES(columns) */
if (rolename == NULL || priv->cols != NIL)
ereport(ERROR,
(errcode(ERRCODE_INVALID_GRANT_OPERATION),
errmsg("column names cannot be included in GRANT/REVOKE ROLE")));
roleid = get_role_oid(rolename, false);
if (stmt->is_grant)
AddRoleMems(rolename, roleid,
stmt->grantee_roles, grantee_ids,
grantor, stmt->admin_opt);
else
DelRoleMems(rolename, roleid,
stmt->grantee_roles, grantee_ids,
stmt->admin_opt);
}
/*
* Close pg_authid, but keep lock till commit.
*/
table_close(pg_authid_rel, NoLock);
}
-
函数声明:GrantRole 函数接收一个指向 GrantRoleStmt 结构的指针,该结构包含了角色授予的相关信息。
-
变量定义:
pg_authid_rel:用于操作 pg_authid 表的关系句柄。
grantor:存储授予者的角色 ID(OID)。
grantee_ids:被授予角色的 ID 列表。 -
获取授予者 OID:
如果 stmt->grantor 指定了授予者,使用 get_rolespec_oid 函数获取其 OID;否则使用当前用户的 ID(GetUserId())。 -
转换被授予者角色:
使用 roleSpecsToIds 函数将被授予者的角色信息转换为角色 ID 列表。 -
打开 pg_authid 表:
以共享锁的方式打开 pg_authid 表,准备进行角色授予操作。
遍历授予的角色: -
通过 foreach 遍历 stmt->granted_roles 列表。
检查角色名称是否为空,或者是否包含列权限。若是,则返回错误。
获取角色 OID,并根据是授予还是撤回操作,调用相应的函数 AddRoleMems 或 DelRoleMems。 -
关闭表:
在结束时关闭 pg_authid 表,但保持锁直到事务提交。
AddRoleMems 是一个静态函数,用于在He3DB 中将角色成员添加到指定的角色。
{
Relation pg_authmem_rel;
TupleDesc pg_authmem_dsc;
ListCell *specitem = NULL;
ListCell *iditem = NULL;
Assert(list_length(memberSpecs) == list_length(memberIds));
/* Skip permission check if nothing to do */
if (!memberIds)
return;
/*
* Check permissions: must have createrole or admin option on the role to
* be changed. To mess with a superuser role, you gotta be superuser.
*/
if (superuser_arg(roleid))
{
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to alter superusers")));
}
else
{
if (!have_createrole_privilege() &&
!is_admin_of_role(grantorId, roleid))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must have admin option on role \"%s\"",
rolename)));
}
/*
* The charter of pg_database_owner is to have exactly one, implicit,
* situation-dependent member. There's no technical need for this
* restriction. (One could lift it and take the further step of making
* pg_database_ownercheck() equivalent to has_privs_of_role(roleid,
* ROLE_PG_DATABASE_OWNER), in which case explicit, situation-independent
* members could act as the owner of any database.)
*/
if (roleid == ROLE_PG_DATABASE_OWNER)
ereport(ERROR,
errmsg("role \"%s\" cannot have explicit members", rolename));
/*
* The role membership grantor of record has little significance at
* present. Nonetheless, inasmuch as users might look to it for a crude
* audit trail, let only superusers impute the grant to a third party.
*/
if (grantorId != GetUserId() && !superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to set grantor")));
pg_authmem_rel = table_open(AuthMemRelationId, RowExclusiveLock);
pg_authmem_dsc = RelationGetDescr(pg_authmem_rel);
forboth(specitem, memberSpecs, iditem, memberIds)
{
RoleSpec *memberRole = lfirst_node(RoleSpec, specitem);
Oid memberid = lfirst_oid(iditem);
HeapTuple authmem_tuple;
HeapTuple tuple;
Datum new_record[Natts_pg_auth_members] = {0};
bool new_record_nulls[Natts_pg_auth_members] = {0};
bool new_record_repl[Natts_pg_auth_members] = {0};
/*
* pg_database_owner is never a role member. Lifting this restriction
* would require a policy decision about membership loops. One could
* prevent loops, which would include making "ALTER DATABASE x OWNER
* TO proposed_datdba" fail if is_member_of_role(pg_database_owner,
* proposed_datdba). Hence, gaining a membership could reduce what a
* role could do. Alternately, one could allow these memberships to
* complete loops. A role could then have actual WITH ADMIN OPTION on
* itself, prompting a decision about is_admin_of_role() treatment of
* the case.
*
* Lifting this restriction also has policy implications for ownership
* of shared objects (databases and tablespaces). We allow such
* ownership, but we might find cause to ban it in the future.
* Designing such a ban would more troublesome if the design had to
* address pg_database_owner being a member of role FOO that owns a
* shared object. (The effect of such ownership is that any owner of
* another database can act as the owner of affected shared objects.)
*/
if (memberid == ROLE_PG_DATABASE_OWNER)
ereport(ERROR,
errmsg("role \"%s\" cannot be a member of any role",
get_rolespec_name(memberRole)));
/*
* Refuse creation of membership loops, including the trivial case
* where a role is made a member of itself. We do this by checking to
* see if the target role is already a member of the proposed member
* role. We have to ignore possible superuserness, however, else we
* could never grant membership in a superuser-privileged role.
*/
if (is_member_of_role_nosuper(roleid, memberid))
ereport(ERROR,
(errcode(ERRCODE_INVALID_GRANT_OPERATION),
errmsg("role \"%s\" is a member of role \"%s\"",
rolename, get_rolespec_name(memberRole))));
/*
* Check if entry for this role/member already exists; if so, give
* warning unless we are adding admin option.
*/
authmem_tuple = SearchSysCache2(AUTHMEMROLEMEM,
ObjectIdGetDatum(roleid),
ObjectIdGetDatum(memberid));
if (HeapTupleIsValid(authmem_tuple) &&
(!admin_opt ||
((Form_pg_auth_members) GETSTRUCT(authmem_tuple))->admin_option))
{
ereport(NOTICE,
(errmsg("role \"%s\" is already a member of role \"%s\"",
get_rolespec_name(memberRole), rolename)));
ReleaseSysCache(authmem_tuple);
continue;
}
/* Build a tuple to insert or update */
MemSet(new_record, 0, sizeof(new_record));
MemSet(new_record_nulls, false, sizeof(new_record_nulls));
MemSet(new_record_repl, false, sizeof(new_record_repl));
new_record[Anum_pg_auth_members_roleid - 1] = ObjectIdGetDatum(roleid);
new_record[Anum_pg_auth_members_member - 1] = ObjectIdGetDatum(memberid);
new_record[Anum_pg_auth_members_grantor - 1] = ObjectIdGetDatum(grantorId);
new_record[Anum_pg_auth_members_admin_option - 1] = BoolGetDatum(admin_opt);
if (HeapTupleIsValid(authmem_tuple))
{
new_record_repl[Anum_pg_auth_members_grantor - 1] = true;
new_record_repl[Anum_pg_auth_members_admin_option - 1] = true;
tuple = heap_modify_tuple(authmem_tuple, pg_authmem_dsc,
new_record,
new_record_nulls, new_record_repl);
CatalogTupleUpdate(pg_authmem_rel, &tuple->t_self, tuple);
ReleaseSysCache(authmem_tuple);
}
else
{
tuple = heap_form_tuple(pg_authmem_dsc,
new_record, new_record_nulls);
CatalogTupleInsert(pg_authmem_rel, tuple);
}
/* CCI after each change, in case there are duplicates in list */
CommandCounterIncrement();
}
/*
* Close pg_authmem, but keep lock till commit.
*/
table_close(pg_authmem_rel, NoLock);
}
其中
static void AddRoleMems(const char *rolename, Oid roleid,
List *memberSpecs, List *memberIds,
Oid grantorId, bool admin_opt)
定义了一个静态函数 AddRoleMems,用于为指定角色添加成员。
参数包括角色名称 rolename、角色 ID roleid、成员规格 memberSpecs、成员 ID 列表 memberIds、授予者 ID grantorId、以及是否具有管理权限的标志 admin_opt。
{
Relation pg_authmem_rel;
TupleDesc pg_authmem_dsc;
ListCell *specitem = NULL;
ListCell *iditem = NULL;
Assert(list_length(memberSpecs) == list_length(memberIds));
定义了一些局部变量,其中 pg_authmem_rel 是角色成员表的关系(Relation),pg_authmem_dsc 是角色成员表的模式描述(Tuple Descriptor)。
specitem 和 iditem 用于迭代 memberSpecs 和 memberIds 列表。
使用 Assert 确保 memberSpecs 和 memberIds 的长度相同,确保每个成员都有对应的规格。
/* Skip permission check if nothing to do */
if (!memberIds)
return;
如果没有要添加的成员 ID,则直接返回,不执行后续操作。
/*
* Check permissions: must have createrole or admin option on the role to
* be changed. To mess with a superuser role, you gotta be superuser.
*/
if (superuser_arg(roleid))
{
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to alter superusers")));
}
检查权限:如果要更改的角色是超级用户(superuser),则调用 superuser() 检查当前用户是否为超级用户,若不是则抛出错误。
else
{
if (!have_createrole_privilege() &&
!is_admin_of_role(grantorId, roleid))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must have admin option on role \"%s\"",
rolename)));
}
如果要更改的角色不是超级用户,则检查当前用户是否具有 CREATEROLE 权限或者是否是要更改角色的管理员。如果没有,则抛出权限不足的错误。
/*
* The charter of pg_database_owner is to have exactly one, implicit,
* situation-dependent member. There's no technical need for this
* restriction. (One could lift it and take the further step of making
* pg_database_ownercheck() equivalent to has_privs_of_role(roleid,
* ROLE_PG_DATABASE_OWNER), in which case explicit, situation-independent
* members could act as the owner of any database.)
*/
if (roleid == ROLE_PG_DATABASE_OWNER)
ereport(ERROR,
errmsg("role \"%s\" cannot have explicit members", rolename));
检查 roleid 是否为 ROLE_PG_DATABASE_OWNER(数据库所有者角色),如果是,则抛出错误,因为该角色不能有显示的成员。
/*
* The role membership grantor of record has little significance at
* present. Nonetheless, inasmuch as users might look to it for a crude
* audit trail, let only superusers impute the grant to a third party.
*/
if (grantorId != GetUserId() && !superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to set grantor")));
如果授予者 ID 不是当前用户且当前用户不是超级用户,则抛出错误,表示只有超级用户才能将角色授予其他用户。
pg_authmem_rel = table_open(AuthMemRelationId, RowExclusiveLock);
pg_authmem_dsc = RelationGetDescr(pg_authmem_rel);
打开角色成员表(pg_auth_members)并获取其模式描述。
forboth(specitem, memberSpecs, iditem, memberIds)
使用 forboth 宏同时迭代 memberSpecs 和 memberIds 列表。
{
RoleSpec *memberRole = lfirst_node(RoleSpec, specitem);
Oid memberid = lfirst_oid(iditem);
HeapTuple authmem_tuple;
HeapTuple tuple;
Datum new_record[Natts_pg_auth_members] = { 0 };
bool new_record_nulls[Natts_pg_auth_members] = { 0 };
bool new_record_repl[Natts_pg_auth_members] = { 0 };
在每次迭代中,获取角色规格和成员 ID,并定义用于存放新记录的数组(new_record、new_record_nulls 和 new_record_repl)。
/*
* pg_database_owner is never a role member.
* ...
*/
if (memberid == ROLE_PG_DATABASE_OWNER)
ereport(ERROR,
errmsg("role \"%s\" cannot be a member of any role",
get_rolespec_name(memberRole)));
检查 memberid 是否为 ROLE_PG_DATABASE_OWNER,如果是,则抛出错误,因为该角色不能是任何角色的成员。
/*
* Refuse creation of membership loops, including the trivial case
* where a role is made a member of itself.
*/
if (is_member_of_role_nosuper(roleid, memberid))
ereport(ERROR,
(errcode(ERRCODE_INVALID_GRANT_OPERATION),
errmsg("role \"%s\" is a member of role \"%s\"",
rolename, get_rolespec_name(memberRole))));
检查指定的角色是否已经是要添加的成员的成员,以拒绝创建循环的角色成员关系。如果是,则抛出错误。
authmem_tuple = SearchSysCache2(AUTHMEMROLEMEM,
ObjectIdGetDatum(roleid),
ObjectIdGetDatum(memberid));
查询系统缓存以查找是否已存在此角色和成员的角色成员关系记录。
if (HeapTupleIsValid(authmem_tuple) &&
(!admin_opt ||
((Form_pg_auth_members) GETSTRUCT(authmem_tuple))->admin_option))
{
ereport(NOTICE,
(errmsg("role \"%s\" is already a member of role \"%s\"",
get_rolespec_name(memberRole), rolename)));
ReleaseSysCache(authmem_tuple);
continue;
}
如果已找到角色成员关系并且不需要管理权限,或者已存在的记录具有管理权限,则发出通知,表示该角色已经是成员,并释放系统缓存,继续处理下一个成员。
MemSet(new_record, 0, sizeof(new_record));
MemSet(new_record_nulls, false, sizeof(new_record_nulls));
MemSet(new_record_repl, false, sizeof(new_record_repl));
清零 new_record、new_record_nulls 和 new_record_repl 数组,以便为创建新的成员记录做准备。
new_record[Anum_pg_auth_members_roleid - 1] = ObjectIdGetDatum(roleid);
new_record[Anum_pg_auth_members_member - 1] = ObjectIdGetDatum(memberid);
new_record[Anum_pg_auth_members_grantor - 1] = ObjectIdGetDatum(grantorId);
new_record[Anum_pg_auth_members_admin_option - 1] = BoolGetDatum(admin_opt);
将角色 ID、成员 ID、授予者 ID 和管理选项的值设置到 new_record 数组中。
if (HeapTupleIsValid(authmem_tuple))
{
new_record_repl[Anum_pg_auth_members_grantor - 1] = true;
new_record_repl[Anum_pg_auth_members_admin_option - 1] = true;
tuple = heap_modify_tuple(authmem_tuple, pg_authmem_dsc,
new_record,
new_record_nulls, new_record_repl);
CatalogTupleUpdate(pg_authmem_rel, &tuple->t_self, tuple);
ReleaseSysCache(authmem_tuple);
}
else
{
tuple = heap_form_tuple(pg_authmem_dsc,
new_record, new_record_nulls);
CatalogTupleInsert(pg_authmem_rel, tuple);
}
如果找到已存在的角色成员记录,则更新该记录的信息。否则,创建一条新的成员记录并插入到数据库中。
CommandCounterIncrement();
}
table_close(pg_authmem_rel, NoLock);
}
每次成功添加成员后,递增命令计数器,以确保事务的可见性。
关闭角色成员表,释放资源。
DelRoleMems 是一个用于在He3DB 中从指定角色中删除角色成员的函数。
/*
* DelRoleMems -- Remove given members from the specified role
*
* rolename: name of role to del from (used only for error messages)
* roleid: OID of role to del from
* memberSpecs: list of RoleSpec of roles to del (used only for error messages)
* memberIds: OIDs of roles to del
* admin_opt: remove admin option only?
*/
static void
DelRoleMems(const char *rolename, Oid roleid,
List *memberSpecs, List *memberIds,
bool admin_opt)
{
Relation pg_authmem_rel;
TupleDesc pg_authmem_dsc;
ListCell *specitem = NULL;
ListCell *iditem = NULL;
Assert(list_length(memberSpecs) == list_length(memberIds));
/* Skip permission check if nothing to do */
if (!memberIds)
return;
/*
* Check permissions: must have createrole or admin option on the role to
* be changed. To mess with a superuser role, you gotta be superuser.
*/
if (superuser_arg(roleid))
{
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to alter superusers")));
}
else
{
if (!have_createrole_privilege() &&
!is_admin_of_role(GetUserId(), roleid))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must have admin option on role \"%s\"",
rolename)));
}
pg_authmem_rel = table_open(AuthMemRelationId, RowExclusiveLock);
pg_authmem_dsc = RelationGetDescr(pg_authmem_rel);
forboth(specitem, memberSpecs, iditem, memberIds)
{
RoleSpec *memberRole = lfirst(specitem);
Oid memberid = lfirst_oid(iditem);
HeapTuple authmem_tuple;
/*
* Find entry for this role/member
*/
authmem_tuple = SearchSysCache2(AUTHMEMROLEMEM,
ObjectIdGetDatum(roleid),
ObjectIdGetDatum(memberid));
if (!HeapTupleIsValid(authmem_tuple))
{
ereport(WARNING,
(errmsg("role \"%s\" is not a member of role \"%s\"",
get_rolespec_name(memberRole), rolename)));
continue;
}
if (!admin_opt)
{
/* Remove the entry altogether */
CatalogTupleDelete(pg_authmem_rel, &authmem_tuple->t_self);
}
else
{
/* Just turn off the admin option */
HeapTuple tuple;
Datum new_record[Natts_pg_auth_members] = {
0
};
bool new_record_nulls[Natts_pg_auth_members] = {
0
};
bool new_record_repl[Natts_pg_auth_members] = {
0
};
/* Build a tuple to update with */
MemSet(new_record, 0, sizeof(new_record));
MemSet(new_record_nulls, false, sizeof(new_record_nulls));
MemSet(new_record_repl, false, sizeof(new_record_repl));
new_record[Anum_pg_auth_members_admin_option - 1] = BoolGetDatum(false);
new_record_repl[Anum_pg_auth_members_admin_option - 1] = true;
tuple = heap_modify_tuple(authmem_tuple, pg_authmem_dsc,
new_record,
new_record_nulls, new_record_repl);
CatalogTupleUpdate(pg_authmem_rel, &tuple->t_self, tuple);
}
ReleaseSysCache(authmem_tuple);
/* CCI after each change, in case there are duplicates in list */
CommandCounterIncrement();
}
/*
* Close pg_authmem, but keep lock till commit.
*/
table_close(pg_authmem_rel, NoLock);
}
其中
{
Relation pg_authmem_rel;
TupleDesc pg_authmem_dsc;
ListCell *specitem = NULL;
ListCell *iditem = NULL;
pg_authmem_rel 用来存储角色成员的关系(表)。
pg_authmem_dsc 用来存储表的描述符。
specitem 和 iditem 用于遍历 memberSpecs 和 memberIds 列表。
Assert(list_length(memberSpecs) == list_length(memberIds));
确保 memberSpecs 和 memberIds 列表的长度相等,如果不相等会触发断言错误。
/* Skip permission check if nothing to do */
if (!memberIds)
return;
如果 memberIds 为空,则没有成员需要处理,直接返回。
/*
* Check permissions: must have createrole or admin option on the role to
* be changed. To mess with a superuser role, you gotta be superuser.
*/
if (superuser_arg(roleid))
{
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to alter superusers")));
}
如果要修改的角色是超级用户角色 (superuser_arg(roleid) 返回 true),则当前用户必须是超级用户,否则报告权限不足的错误。
else
{
if (!have_createrole_privilege() &&
!is_admin_of_role(GetUserId(), roleid))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must have admin option on role \"%s\"",
rolename)));
}
对于非超级用户角色:如果用户既没有创建角色权限,也不是该角色的管理员,则报告权限不足的错误。
pg_authmem_rel = table_open(AuthMemRelationId, RowExclusiveLock);
pg_authmem_dsc = RelationGetDescr(pg_authmem_rel);
打开存储角色成员关系的表,并获取其描述符,使用 RowExclusiveLock 锁定表。
forboth(specitem, memberSpecs, iditem, memberIds)
开始遍历 memberSpecs 和 memberIds 列表。
{
RoleSpec *memberRole = lfirst(specitem);
Oid memberid = lfirst_oid(iditem);
HeapTuple authmem_tuple;
在循环内部:获取当前成员的规范和 ID。定义 authmem_tuple 用于存储角色-成员关系的元组。
/*
* Find entry for this role/member
*/
authmem_tuple = SearchSysCache2(AUTHMEMROLEMEM,
ObjectIdGetDatum(roleid),
ObjectIdGetDatum(memberid));
在系统缓存中查找角色和成员的关系元组。如果找不到对应的元组,将在后面的代码中处理。
if (!HeapTupleIsValid(authmem_tuple))
{
ereport(WARNING,
(errmsg("role \"%s\" is not a member of role \"%s\"",
get_rolespec_name(memberRole), rolename)));
continue;
}
如果没有找到有效的元组,报告警告,指出该成员不是角色的成员,并继续处理下一个成员。
if (!admin_opt)
{
/* Remove the entry altogether */
CatalogTupleDelete(pg_authmem_rel, &authmem_tuple->t_self);
}
如果 admin_opt 为 false,则从表中完全删除该角色和成员的关系。
else
{
/* Just turn off the admin option */
HeapTuple tuple;
Datum new_record[Natts_pg_auth_members] = {
0
};
bool new_record_nulls[Natts_pg_auth_members] = {
0
};
bool new_record_repl[Natts_pg_auth_members] = {
0
};
/* Build a tuple to update with */
MemSet(new_record, 0, sizeof(new_record));
MemSet(new_record_nulls, false, sizeof(new_record_nulls));
MemSet(new_record_repl, false, sizeof(new_record_repl));
如果 admin_opt 为 true,则不删除该角色和成员关系,而是仅关闭管理员选项。定义辅助变量以构建更新后的元组。
new_record[Anum_pg_auth_members_admin_option - 1] = BoolGetDatum(false);
new_record_repl[Anum_pg_auth_members_admin_option - 1] = true;
把管理员选项设置为 false,并更新标志。
tuple = heap_modify_tuple(authmem_tuple, pg_authmem_dsc,
new_record,
new_record_nulls, new_record_repl);
CatalogTupleUpdate(pg_authmem_rel, &tuple->t_self, tuple);
}
修改元组并将更新后的元组写回数据库。
ReleaseSysCache(authmem_tuple);
/* CCI after each change, in case there are duplicates in list */
CommandCounterIncrement();
}
/*
* Close pg_authmem, but keep lock till commit.
*/
table_close(pg_authmem_rel, NoLock);
}
释放系统缓存中的元组,以便释放资源。
在每次更改后调用命令计数器递增以确保数据一致性,特别是处理重复列表时。
结束循环。
在函数结束时关闭表,但保持锁定状态直到事务提交。这样可以确保一致性。