暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

海山数据库(He3DB)源码解读:T_GrantRoleStmt原理浅析

数据库 2024-11-18
69

一、概述

   GrantRole 在 He3DB 中用于授予用户或角色对数据库对象的权限,是数据库权限管理的重要工具。通过合理使用 GRANT 和 REVOKE 命令,可以确保数据库的安全性和数据访问的控制。

二、GrantRole 命令的执行流程

  1. PostgresMain
  2. exec_simple_query →执行简单的 SQL 查询;
  3. StartTransactionCommand → 开始事务;
  4. pg_parse_query →解析为内部的抽象语法树(AST);
  5. PortalRun
  6. standard_ProcessUtility →权限检查和准备;
  7. GrantRole→处理创建角色的具体逻辑;
  8. CommandCounterIncrement→增量更新当前的命令计数器;
  9. CommitTransactionCommand→ 提交当前事务;
  10. finish_xact_command→ 在事务结束时,执行必要的清理和关闭操作;
    在这里插入图片描述
图1 GrantRole 命令的执行流程图

三、核心结构体介绍

 (一)  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;
  1. NodeTag type
    类型: NodeTag
    解释: NodeTag 是一个枚举类型,表示不同类型的节点。在这里,type 用于标识这是一个 GrantRoleStmt 节点。通常,type 的值为 T_GrantRoleStmt。
  2. List *granted_roles
    类型: List *
    解释: 这是一个链表(List),包含了将被授予或撤销的角色列表。每个元素是一个角色名称或角色标识符。这个变量指定了要操作的角色。
  3. List *grantee_roles
    类型: List *
    解释: 这同样是一个链表,包含了被授予或撤销角色的成员列表。换句话说,这些是将要被添加或删除的角色成员。这个变量指定了接受操作的角色。
  4. bool is_grant
    类型: bool
    解释: 这是一个布尔值,表示当前操作是授予角色 (GRANT) 还是撤销角色 (REVOKE)。如果 is_grant 为 true,则表示这是一个 GRANT 操作;如果为 false,则表示这是一个 REVOKE 操作。
  5. bool admin_opt
    类型: bool
    解释: 这是一个布尔值,表示是否授予 WITH ADMIN OPTION 选项。如果 admin_opt 为 true,则表示被授予的角色可以进一步将该角色授予其他用户;如果为 false,则不能进一步授予。
  6. RoleSpec *grantor
    类型: RoleSpec *
    解释: 这是一个指向 RoleSpec(角色规范)的指针,表示授予或撤销角色的执行者。通常情况下,这个值为 NULL,表示当前用户是执行者;如果不是 NULL,则表示由指定的角色执行操作。
  7. 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); }
  1. 函数声明:GrantRole 函数接收一个指向 GrantRoleStmt 结构的指针,该结构包含了角色授予的相关信息。

  2. 变量定义:
    pg_authid_rel:用于操作 pg_authid 表的关系句柄。
    grantor:存储授予者的角色 ID(OID)。
    grantee_ids:被授予角色的 ID 列表。

  3. 获取授予者 OID:
    如果 stmt->grantor 指定了授予者,使用 get_rolespec_oid 函数获取其 OID;否则使用当前用户的 ID(GetUserId())。

  4. 转换被授予者角色:
    使用 roleSpecsToIds 函数将被授予者的角色信息转换为角色 ID 列表。

  5. 打开 pg_authid 表:
    以共享锁的方式打开 pg_authid 表,准备进行角色授予操作。
    遍历授予的角色:

  6. 通过 foreach 遍历 stmt->granted_roles 列表。
    检查角色名称是否为空,或者是否包含列权限。若是,则返回错误。
    获取角色 OID,并根据是授予还是撤回操作,调用相应的函数 AddRoleMems 或 DelRoleMems。

  7. 关闭表:
    在结束时关闭 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); }

   释放系统缓存中的元组,以便释放资源。
   在每次更改后调用命令计数器递增以确保数据一致性,特别是处理重复列表时。
   结束循环。
   在函数结束时关闭表,但保持锁定状态直到事务提交。这样可以确保一致性。

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论