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

一、概述

   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);
}

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值