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

Amesim(五):单个悬架系统搭建 一、组成 单个悬架系统组成包括:端子,质量块*2,弹簧,阻尼器,速度路面反馈,路面激励。 二、搭建 打开Amesim,在 library 选择 1D Mechanical 选择质量模块,添加到skech中,并按 ctrl+r 旋转质量块,将箭头朝上,设置规定的力传递正方向 添加弹簧阻尼器,与质量块连接 添加VX模块,将路面的激励传递转化为速度和位移,传递给悬架系统 在... 阅读详情

一、概述

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

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

Python爬虫实战:汽车之家车型参数与口碑数据抓取分析 在当今信息爆炸的时代,购车决策已从传统的4S店咨询转变为数据驱动的智能化过程。汽车之家作为国内领先的汽车信息平台,汇聚了海量车型参数、车主口碑和专业评测数据。本文将详细介绍如何使用Python最新爬虫技术构建一个高效、稳定的汽车数据采集系统,通过多维度数据分析辅助用户做出更明智的购车决策。本项目将采用异步爬虫、智能反爬绕过、数据清洗与分析等现代化技术栈,完整实现从数据采集到可视化分析的全流程。本文详细介绍了如何使用Python最新技术构建一个功能完整的汽车之家数据爬虫系统。 阅读详情

相关推荐

揭秘 | 顶刊论文插图中的高级配色方案(附RGB/HEX码解析)

本文揭秘了顶级期刊如《Nature》《Science》插图的高级配色方案,详细解析了RGB/HEX码的应用技巧。从双色配色的黄金组合到复杂数据可视化的多色解决方案,提供了实用的配色法则和跨平台一致性建议,帮助科研人员提升图表专业度和视觉传达效果。

weixin_28523493的博客 985

SpringSecurity安全框架(基础入门)

这里写自定义目录标题欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入 欢迎使用Markdown编辑器 你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Mar

weixin_48948094的博客 843

本地部署 Anything LLM+Ollama+DeepSeek R1 大模型并实现外部访问

DeepSeek 一经发布就引起社会的广泛的关注,因为 DeepSeek 的价格低廉,性能卓越,提供了多种使用方式,满足不同用户的需求和场景。本文将详细的介绍如何在本地 Windows 上安装部署 Anything LLM + Ollama 来实现用户和 DeepSeek-r1 对话的功能以及利用路由侠内网穿透实现外网访问。

lyxnwct的博客 1895

在Codex 让AI模型 拥有长期智能记忆

本文介绍Claude-Mem V15新功能——智能记忆召回,解决AI遗忘、重复提示、执行不一致等问题。通过本地部署claude-mem,实现自动记录决策(如数据库选型)、跨会话智能召回,支持记忆写入、检索、遗忘全流程闭环。相比传统记忆方案,其基于Hooks自动捕获工具调用,结构化存储,可追溯、可共享、可清理,显著提升工程效率与协作一致性,是长期项目中理想的“工作黑匣子”与“工程脑”。

米莱虾 264

等时替代模型( Isotemporal Substitution Model)

文献阅读:Associations of sedentary time and physical activity with adverse health conditions: Outcome-wide analyses using isotemporal substitution model(该论文于2022年4月28日发表在柳叶刀子刊 EClinicalMedicine 上)

姚巨龙的博客 1万+

如何在R中高效安装ropls及其依赖包

本文详细介绍了在R语言中高效安装ropls包及其依赖的完整流程。作为Bioconductor生物信息学分析包,ropls的安装需通过BiocManager管理器进行。文章提供了标准安装步骤、跨系统避坑指南及安装后验证方法,帮助用户解决R版本、编译环境和网络镜像等常见问题,确保代谢组学数据分析工具顺利部署。

efc123456的博客 908

Java EE 进阶:8.MyBatis 操作数据库(入门)(第一弹)

Java EE 进阶:8.MyBatis 操作数据库(入门)(第一弹)

要努力去发光,而不是被照亮~ 115

计算机视觉(相机标定;内参;外参;畸变系数)

目录 一、预备知识 1、坐标系变换过程(相机成像过程) (1)相机坐标系转换为图像坐标系(透视投影变换遵循的是针孔成像原理) (2)齐次坐标的引入原因:(为什么引入齐次坐标???) 2、内参与外参矩阵的构成 3、畸变参数 二、相机标定 1、张正友标定法(光学标定)及其求解思路 2、求解内参矩阵与外参矩阵的思路: (1)计算内参与外参矩阵的乘积M (2)求解内参矩阵A (3)求解外参矩阵(R1 R2 T) 三、参数优化 1、L-M算法 2、相机标定步骤 四、相关代码及其实验结.

QYJ_WORKHARDING的博客 6万+

Spring Boot 数据源与 MyBatis(-Plus) 自定义配置详解

本文详解 Spring Boot 中单数据源环境下 HikariCP 与 MyBatis-Plus 的自定义配置。通过手动声明 SqlSessionFactory,实现下划线转驼峰、日志输出、一级缓存策略等精细化控制。结合 HikariCP 核心参数调优(如最大连接数、连接生命周期),确保高性能与稳定性。适用于仅需访问单一数据库的场景,避免多数据源复杂性,兼顾性能与可维护性。

BADAO_LIUMANG_QIZHI的博客 194

SAP RAP 校验全流程:从 EML 读取到消息上屏与阻止落库

本文系统梳理了 SAP ABAP RAP 中的校验流程(The Validation Process),结合 Airline/Connection 案例,详细讲解了三类核心校验:语义键唯一性、外键有效性和字段间一致性。文章覆盖了 EML 的 READ ENTITIES 操作、reported-* 与 failed-* 结构的使用方法、消息创建与字段绑定技巧,以及处理草稿数据时的 UNION 查询策略。通过代码示例演示了如何实现校验逻辑、生成错误消息并阻止无效数据持久化,同时提供了常见问题的排查建议。全文注重

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 331

TC3XX - MCAL知识点(三):MCU EB-tresos配置说明及代码浅析

AURIX 2G Tc3xx的配置相较AURIX 1G Tc2xx来讲简化了许多,但是依赖于数据手册的理解来讲整个MCU功能做了较大增强,此处先简单的介绍一下,本文前期先补充介绍一下英飞凌手册的阅读规则,因为后面的文章会基于此文的一些缩写进行截图寄存器之类的。MCU在AUTOSAR架构里面的图示如下MCU驱动程序负责配置SCU、GTM、CCU6、GPT12和STM外设。驱动程序提供由AUTOSAR指定的运行时服务。

梦想技术家 2872

Docker Compose:搭建本地多服务开发环境

从一个 Java API、PostgreSQL 和 Redis 开始,配置服务名、端口、环境变量、健康检查、数据卷和依赖关系。理解 Compose 文件如何被解析、项目如何隔离,以及哪些数据不能依赖“重建容器”保留,能够把个人开发环境整理成团队可复用的启动入口。

m0_62221377的博客 425

RTT Studio和Cubemx联合开发

1. RTT studio创建工程 创建工程 ## 创建完成以后的目录结构 2. 配置CubuMx 双击cubumx的图标打开CubeMx 配置时钟 生成代码 构建后的代码结构 编译代码满屏错误:不要慌 3. 新增脚本 新建scons脚本文件 SConscript 脚本内容如下 import os from building import * cwd = GetCurrentDir() src = Glob('*.c') # add cubemx drivers src = Split('''

Car12 1563

45-存储过程与函数

匿名块跑完就没了,想下次再用只能复制粘贴。给块起个名字存进数据库,就成了存储过程与函数:一次编译、处处调用、权限收口,还能让 Java 侧少拼 SQL。这篇讲封装、调用、限制与排错的完整套路。CREATE OR REPLACE 覆盖式创建,事务由调用方控制;参数三模式 IN 只进、OUT 只出、IN OUT 双向,OUT 参数调用时要备好变量接;调用三姿势 EXEC、CALL、匿名块,命名传参=>最清晰;函数必有 RETURN,想在 SQL 里调用就不能碰 DML 和事务控制;

yuhou25的博客 152

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

上一篇: 海山数据库(He3DB)源码解读:T_CreateRoleStmt原理浅析
下一篇: 海山数据库(He3DB)源码详解:主备复制pg_wal_replay_resume
DAWN1221
博客等级 码龄7年 893粉丝 95原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值