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

QT项目实战:如何在正点原子Linux开发板上集成CVR-100UC身份证阅读器(附完整代码) 本文详细介绍了在正点原子Linux开发板上集成CVR-100UC身份证阅读器的完整实战流程。内容涵盖驱动适配、库文件部署、QT项目框架搭建、核心功能封装以及界面集成,并提供了完整的代码示例,帮助开发者快速实现嵌入式环境下的身份证信息读取功能。 阅读详情

一、概述

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

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

【DRAM存储器五十六】LPDDR5介绍--WCK2CK Leveling和Read gate training 简述LPDDR5 WCK2CK leveling和read gate traning基本原理与操作步骤 阅读详情

相关推荐

5分钟搞定!用ZLMediaKit+GB28181搭建低成本外网视频监控(含避坑指南)

本文详细介绍了如何利用开源ZLMediaKit流媒体服务器与国标GB28181协议,快速搭建低成本、可外网访问的企业级视频监控系统。文章提供了从服务端部署、设备配置到外网访问优化的完整实战指南,并重点分享了网络配置、性能调优及常见问题的避坑方案,帮助用户实现高清视频流的稳定推送与多格式播放。

bush的博客 332

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

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

BADAO_LIUMANG_QIZHI的博客 195

深度解析大语言模型中的词向量

​ 在学习完语言模型中的编码器与解码器知识后,让我们继续深入学习大语言模型中一个非常关键的技术:词向量表示,以及如何通过Transformer模型实现对next token的预测。 ​

产品老A,8年互联网大厂AIPM,专注探索新型人机交互 3282

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

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

米莱虾 265

双层优化入门(1)—基本原理与求解方法(附matlab代码)

双层优化问题(Bilevel Programming Problems),也被称为双层规划,最早由Stackelberg与1934年在经济学相关研究中提出,因此也被称为Stackelberg问题。双层规划问题一般具有层次性、独立性、冲突性、优先性和自主性等特点。 本文介绍了双层优化的原理与求解方法,并提供了相应的matlab代码供参考学习。

配电网和matlab的博客 5万+

从Excel到‘一张图’办案:手把手教你用AbutionGraph构建公安经侦知识图谱(含数据建模避坑指南)

本文详细介绍了如何利用AbutionGraph时序图数仓技术,将公安经侦领域的Excel、银行流水等碎片化数据转化为可交互的知识图谱。通过数据清洗、实体关系模型设计、时序数据处理等步骤,实现高效智能分析,提升办案效率。特别包含数据建模避坑指南和实战案例,助力公安金融案件侦破。

weixin_33702545的博客 341

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

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

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

别只当玩具!聊聊这个555金属探测器电路背后的电磁感应与RLC调谐原理

本文深入解析了555金属探测器电路的工作原理,重点探讨了电磁感应与RLC调谐原理。通过分析电感线圈的电磁特性、RLC谐振电路的行为以及555定时器的多重功能,揭示了这一看似简单的装置如何实现金属检测。文章还提供了灵敏度优化方案和常见问题解决方法,帮助读者从科学角度理解这一实用技术。

weixin_29204205的博客 477

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

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

m0_62221377的博客 428

79、昇腾系列服务器/昇腾系列推理卡/昇腾系列嵌入式开发板+Dify+one api部署DeepSeek-R1-Distill-Qwen-32BW8A8+RAG本地模型知识库和负载均衡双实例

阶段,安装过程缺什么,,在docker外面git下源码,进入容器内部进行量化,这里的容器建议在创建个8卡的容器,双卡容器量化会显示npu显存不够,除非你用cpu转模型,我就懒得创建容器了,使用cpu量化吧,巨慢;创建容器,进入容器,计划使用两张昇腾NPU卡推理DeepSeek-R1-Distill-Qwen-32B的W8A8模型,所以构建的容器用两张卡,选6、7卡吧,0-6号卡可以跑文本嵌入模型、重排序模型;直接进入量化阶段,在容器外面操作即可,环境不用管,因为系统已经默认配置了环境,直接跳到。

sxj731533730 6118

[个人自用,全网最优]EDEM-FLUENT个人自学教程,拒绝手忙脚乱无用功!

[个人自用,全网最优]EDEM-FLUENT个人自学教程,拒绝手忙脚乱无用功!

zheaa的博客 4827

45-存储过程与函数

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

yuhou25的博客 152

YOLOv11改进入门篇 | 手把手讲解改进模块如何实现高效涨点,以SimAM注意力模块为例

本文记录的是基于SimAM注意力模块的YOLOv11目标检测方法研究。SimAM注意力模块通过优化能量函数来获得每个神经元的三维权重,而或增加计算复杂度。若是有轻量化需求的小伙伴,无参的注意力模块也许是一个不错的选择。

Limiiiing的博客 9036

AI全栈实战 | 3.9-01 爬虫入门:requests 加 BeautifulSoup 三分钟跑通,但 90% 的人栽在这 4 个“礼貌“问题上

⚠️:本文内容仅用于。请遵守目标站点的服务条款与相关法律法规,不得对目标服务造成负担,不得采集、传播个人隐私数据。技术是中性的,边界在你手里。爬虫的入门技术(requests 发请求 + BeautifulSoup 解析)三分钟就能学会。真正区分"会用爬虫"和"会做爬虫的人"的,是这篇我们跑通"请求→解析→存储"的最小闭环,然后重点把"合规与礼貌"这根弦给你绷上。

源码获取 | 运行部署 | 定制二开修改,如果回复不及时请耐心等待,请见谅。 392

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

上一篇: 海山数据库(He3DB)源码解读:T_CreateRoleStmt原理浅析
下一篇: 海山数据库(He3DB)源码详解:两阶段提交(2PC)事务块内核函数执行过程
DAWN1221
博客等级 码龄7年 893粉丝 95原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值