Apache AGE中RTE转PNSI结构体时perminfoindex错误的解决方法问询
解决PostgreSQL 13-16版本升级中Apache AGE的RTE/PNSI权限信息错误问题
版本变更背景
从PostgreSQL 13到16,RangeTblEntry(RTE)和ParseNamespaceItem(PNSI)的结构体成员发生了关键变化:
---------------------------------- RTE <RangeTblEntry> 新增成员: + perinfoindex: Index + join_using_alias: Alias 移除成员: - requiredPerms: AclMode - checkAsUser: Oid - selectedCols: Bitmapset* - insertedCols: Bitmapset* - updatedCols: Bitmapset* - extraUpdatedCols: Bitmapset* ---------------------------------- PNSI <ParseNamespaceItem> 新增成员: + p_names: *Alias + p_perminfo: *RTEPermissioninfo ----------------------------------
Apache AGE正在验证用PNSI替换RTE的可行性,但在某段代码中触发了权限检查错误:调用markVarForSelectPriv时,内部调用markRTEForSelectPriv和getRTEPermissionInfo,抛出"invalid perminfoindex 1 in RTE with relid..."的错误。
触发错误的AGE代码
static List *expand_pnsi_attrs(ParseState *pstate, ParseNamespaceItem *pnsi, int sublevels_up, bool require_col_privs, int location) { RangeTblEntry *rte = pnsi->p_rte; RTEPermissionInfo *perminfo = pnsi->p_perminfo; List *names, *vars; ListCell *name, *var; List *te_list = NIL; int var_prefix_len = strlen(AGE_DEFAULT_VARNAME_PREFIX); int alias_prefix_len = strlen(AGE_DEFAULT_ALIAS_PREFIX); expandRTE(rte, pnsi->p_rtindex, sublevels_up, location, false, &names, &vars); /* * Require read access to the table. This is normally redundant with the * markVarForSelectPriv calls below, but not if the table has zero * columns. */ if (rte->rtekind == RTE_RELATION) { Assert(perminfo != NULL); perminfo->requiredPerms |= ACL_SELECT; } /* iterate through the variables */ forboth(name, names, var, vars) { char *label = strVal(lfirst(name)); Var *varnode = (Var *)lfirst(var); TargetEntry *te; /* we want to skip our "hidden" variables */ if (strncmp(AGE_DEFAULT_VARNAME_PREFIX, label, var_prefix_len) == 0) continue; /* we want to skip out "hidden" aliases */ if (strncmp(AGE_DEFAULT_ALIAS_PREFIX, label, alias_prefix_len) == 0) continue; /* add this variable to the list */ te = makeTargetEntry((Expr *)varnode, (AttrNumber)pstate->p_next_resno++, label, false); te_list = lappend(te_list, te); /* Require read access to each column */ markVarForSelectPriv(pstate, varnode); } Assert(name == NULL && var == NULL); /* lists not the same length? */ return te_list; }
问题根源
PostgreSQL 16中,RTE通过perminfoindex字段关联到ParseState结构体中p_perminfos列表对应的RTEPermissionInfo实例,权限检查函数(如getRTEPermissionInfo)依赖这个索引查找权限信息。
当前AGE代码直接操作PNSI的p_perminfo,但未同步设置RTE的perminfoindex,导致权限检查时找不到对应的权限信息,触发索引无效的错误。
修复方案
核心是让RTE的perminfoindex正确关联到ParseState中对应的RTEPermissionInfo实例,修改expand_pnsi_attrs函数中的权限处理逻辑:
修改后的代码片段
if (rte->rtekind == RTE_RELATION) { Assert(perminfo != NULL); // 确保perminfo已加入ParseState的权限列表,并同步RTE的perminfoindex if (rte->perminfoindex == 0) // PG中0表示未初始化的索引 { // 将perminfo添加到ParseState的权限信息列表 pstate->p_perminfos = lappend(pstate->p_perminfos, perminfo); // 设置RTE的索引为列表的当前长度(列表索引从1开始) rte->perminfoindex = list_length(pstate->p_perminfos); } perminfo->requiredPerms |= ACL_SELECT; }
修复逻辑说明
- 检查RTE的
perminfoindex是否为0(PG中默认未初始化的索引值),如果是,说明该RTE还未关联到ParseState的权限列表。 - 将PNSI持有的
perminfo添加到pstate->p_perminfos列表中。 - 将RTE的
perminfoindex设置为列表的当前长度(因为PG的列表索引从1开始计数)。 - 之后再修改
perminfo->requiredPerms的操作就能被权限检查函数正确识别。
这样修改后,markVarForSelectPriv内部的权限检查流程就能通过RTE的perminfoindex找到正确的RTEPermissionInfo实例,避免触发索引无效的错误。
内容的提问来源于stack exchange,提问作者Matheus Farias
相关产品推荐
相关产品推荐

