本节简单介绍了Postgresql在执行逻辑优化中对应Relation的数据结构:RangeTblEntry.
一、数据结构
RangeTblEntry
RTE可能是普通表/FROM中的子查询/JOIN语句的结果等(只有显式的JOIN语法会产生RTE,由多个FROM项生成的隐式连接则不会生成.这是因为 我们只需要RTE来处理sql特性,比如外部连接和连接输出列别名)
/*--------------------
* RangeTblEntry -
* A range table is a List of RangeTblEntry nodes.
* RangeTblEntry节点链表
*
* A range table entry may represent a plain relation, a sub-select in
* FROM, or the result of a JOIN clause. (Only explicit JOIN Syntax
* produces an RTE, not the implicit join resulting from multiple FROM
* items. This is because we only need the RTE to deal with sql features
* like outer joins and join-output-column aliasing.) Other special
* RTE types also exist, as indicated by RTEKind.
* RTE可能是普通表/FROM中的子查询/JOIN语句的结果.
* (只有显示的JOIN语法会产生RTE,由多个FROM项生成的隐式连接则不会生成.这是因为
* 我们只需要RTE来处理sql特性,比如外部连接和连接输出列别名)
*
* Note that we consider RTE_RELATION to cover anything that has a pg_class
* entry. relkind distinguishes the sub-cases.
* 注意RTE_RELATION指代了存储在pg_class中的relation信息.
*
* alias is an Alias node representing the AS alias-clause attached to the
* FROM expression, or NULL if no clause.
* alias是FROM表达式中AS别名子句中的Alias节点.
*
* eref is the table reference name and column reference names (either
* real or aliases). Note that system columns (OID etc) are not included
* in the column list.
* eref->aliasname is required to be present, and should generally be used
* to identify the RTE for error messages etc.
* eref是表和列引用名称.要注意的是系统列(如OID)不在列链表中.
*
* In RELATION RTEs, the colnames in both alias and eref are indexed by
* physical attribute number; this means there must be colname entries for
* dropped columns. When building an RTE we insert empty strings ("") for
* dropped columns. Note however that a stored rule may have nonempty
* colnames for columns dropped since the rule was created (and for that
* matter the colnames might be out of date due to column renamings).
* The same comments apply to FUNCTION RTEs when a function's return type
* is a named composite type.
* 在RELATION RTEs中,别名和eref都通过物理属性编号来索引.
*
* In JOIN RTEs, the colnames in both alias and eref are one-to-one with
* joinaliasvars entries. A JOIN RTE will omit columns of its inputs when
* those columns are kNown to be dropped at parse time. Again, however,
* a stored rule might contain entries for columns dropped since the rule
* was created. (This is only possible for columns not actually referenced
* in the rule.) When loading a stored rule, we replace the joinaliasvars
* items for any such columns with null pointers. (We can't simply delete
* them from the joinaliasvars list, because that would affect the attnums
* of Vars referencing the rest of the list.)
* 在JOIN RTEs中,不管是别名还是eref与joinaliasvars是一一对应的.
* JOIN RTE会省略掉在解析阶段发现的需要drop的列.
* 但是,已存储的规则可能包含已drop的列.
*
* inh is true for relation references that should be expanded to include
* inheritance children, if the rel has any. This *must* be false for
* RTEs other than RTE_RELATION entries.
* 如关系引用需要扩展用于包含继承子关系,则inh字段为T.除了RTE_RELATION条目,其他RTEs必须为F.
*
* inFromCl marks those range variables that are listed in the FROM clause.
* It's false for RTEs that are added to a query behind the scenes, such
* as the NEW and OLD variables for a rule, or the subqueries of a UNION.
* This flag is not used anymore during parsing, since the parser Now uses
* a separate "namespace" data structure to control visibility, but it is
* needed by ruleutils.c to determine whether RTEs should be shown in
* decompiled queries.
* inFromCl标记了FROM语句包含了哪些RangeVar.
* 在解析阶段,该标记不会使用,因为解析器使用独立的"namespace"数据结构来控制可见性,
* 但在ruleutils.c中需要用到该标记来确定RTEs是否在反编译查询中显示.
*
* requiredPerms and checkAsUser specify run-time access permissions
* checks to be performed at query startup. The user must have *all*
* of the permissions that are OR'd together in requiredPerms (zero
* indicates no permissions checking). If checkAsUser is not zero,
* then do the permissions checks using the access rights of that user,
* not the current effective user ID. (This allows rules to act as
* setuid gateways.) Permissions checks only apply to RELATION RTEs.
* requiredPerms和checkAsUser用于表示在查询启动时需要检查的运行时访问权限.
*
* For SELECT/INSERT/UPDATE permissions, if the user doesn't have
* table-wide permissions then it is sufficient to have the permissions
* on all columns identified in selectedCols (for SELECT) and/or
* insertedCols and/or updatedCols (INSERT with ON CONFLICT DO UPDATE may
* have all 3). selectedCols, insertedCols and updatedCols are bitmapsets,
* which cannot have negative integer members, so we subtract
* FirstLowInvalidHeapAttributeNumber from column numbers before storing
* them in these fields. A whole-row Var reference is represented by
* setting the bit for InvalidAttrNumber.
* 对于SELECT/INSERT/UPDATE权限,如果用户没有表级权限,但有足够的权限访问在
* selectedCols (SELECT) and/or
* insertedCols and/or updatedCols (INSERT with ON CONFLICT DO UPDATE may
* have all 3)中定义的列.
*
* updatedCols is also used in some other places, for example, to determine
* which triggers to fire and in FDWs to kNow which changed columns they
* need to ship off. Generated columns that are caused to be updated by an
* update to a base column are collected in extraUpdatedCols. This is not
* considered for permission checking, but it is useful in those places
* that want to kNow the full set of columns being updated as opposed to
* only the ones the user explicitly mentioned in the query. (There is
* currently no need for an extraInsertedCols, but it Could exist.)
* updatedCols可能会用于其他地方,比如用于确定哪个触发器会被触发,在FDWs中哪些修改的列需要发送等.
*
* securityQuals is a list of security barrier quals (boolean expressions),
* to be tested in the listed order before returning a row from the
* relation. It is always NIL in parser output. Entries are added by the
* rewriter to implement security-barrier views and/or row-level security.
* Note that the planner turns each boolean expression into an implicitly
* AND'ed sublist, as is its usual habit with qualification expressions.
* securityQuals是安全栏表达式链表(布尔表达式),在返回行前用于校验.
*--------------------
*/
typedef enum RTEKind
{
//常规的关系引用
RTE_RELATION, /* ordinary relation reference */
//FROM中的子查询
RTE_SUBQUERY, /* subquery in FROM */
//JOIN
RTE_JOIN, /* join */
//FROM中的函数
RTE_FUNCTION, /* function in FROM */
//TableFunc(..,列链表)
RTE_TABLEFUNC, /* TableFunc(.., column list) */
//VALUES (<exprlist>), (<exprlist>), ...
RTE_VALUES, /* VALUES (<exprlist>), (<exprlist>), ... */
//CTE
RTE_CTE, /* common table expr (WITH list element) */
//tuplestore,比如AFTER触发器
RTE_NAMEDTUPLESTORE, /* tuplestore, e.g. for AFTER triggers */
//表示空的FROM语句.通过规划器添加,在解析和重写阶段不会出现
RTE_RESULT /* RTE represents an empty FROM clause; such
* RTEs are added by the planner, they're not
* present during parsing or rewriting */
} RTEKind;
typedef struct RangeTblEntry
{
NodeTag type;
//详见上述说明
RTEKind rtekind; /* see above */
/*
* XXX the fields applicable to only some rte kinds should be merged into
* a union. I didn't do this yet because the diffs would impact a lot of
* code that is being actively worked on. FIXME someday.
*/
/*
* Fields valid for a plain relation RTE (else zero):
* 以下字段对普通关系RTE有用(其他类型的RTE,值为0)
*
* As a special case, RTE_NAMEDTUPLESTORE can also set relid to indicate
* that the tuple format of the tuplestore is the same as the referenced
* relation. This allows plans referencing AFTER trigger transition
* tables to be invalidated if the underlying table is altered.
* 作为一个特例,RTE_NAMEDTUPLESTORE可设置relid,用以表示该tuple是属于哪个relation的.
*
* rellockmode is really LOCKMODE, but it's declared int to avoid having
* to include lock-related headers here. It must be RowExclusiveLock if
* the RTE is an INSERT/UPDATE/DELETE target, else RowShareLock if the RTE
* is a SELECT FOR UPDATE/FOR SHARE target, else AccessShareLock.
* rellockmode是实际的LOCKMODE,定义为int类型是为了避免包含锁相关的头文件.
* 如果RTE是INSERT/UPDATE/DELETE,必须是RowExclusiveLock,
* 如果是SELECT FOR UPDATE/FOR SHARE,是RowShareLock
* 否则,是AccessShareLock
*
* Note: in some cases, rule expansion may result in RTEs that are marked
* with RowExclusiveLock even though they are not the target of the
* current query; this happens if a DO ALSO rule simply scans the original
* target table. We leave such RTEs with their original lockmode so as to
* avoid getting an additional, lesser lock.
*/
Oid relid; /* OID of the relation */
char relkind; /* relation kind (see pg_class.relkind) */
int rellockmode; /* lock level that query requires on the rel */
struct TableSampleClause *tablesample; /* sampling info, or NULL */
/*
* Fields valid for a subquery RTE (else NULL):
* 用于子查询
*/
//子查询
Query *subquery; /* the sub-query */
//来自于安全栏视图?
bool security_barrier; /* is from security_barrier view? */
/*
* Fields valid for a join RTE (else NULL/zero):
* 用于连接RTE(否则为NULL/0)
*
* joinaliasvars is a list of (usually) Vars corresponding to the columns
* of the join result. An alias Var referencing column K of the join
* result can be replaced by the K'th element of joinaliasvars --- but to
* simplify the task of reverse-listing aliases correctly, we do not do
* that until planning time. In detail: an element of joinaliasvars can
* be a Var of one of the join's input relations, or such a Var with an
* implicit coercion to the join's output column type, or a COALESCE
* expression containing the two input column Vars (possibly coerced).
* Within a Query loaded from a stored rule, it is also possible for
* joinaliasvars items to be null pointers, which are placeholders for
* (necessarily unreferenced) columns dropped since the rule was made.
* Also, once planning begins, joinaliasvars items can be almost anything,
* as a result of subquery-flattening substitutions.
* joinaliasvars是对应join结果的Vars链表.
* 引用连接结果列K的别名Var可以通过joinaliasvars中的第K个元素代替,这一步在计划阶段才去做.
*/
JoinType jointype; /* type of join */
List *joinaliasvars; /* list of alias-var expansions */
/*
* Fields valid for a function RTE (else NIL/zero):
* 函数RTE
*
* When funcordinality is true, the eref->colnames list includes an alias
* for the ordinality column. The ordinality column is otherwise
* implicit, and must be accounted for "by hand" in places such as
* expandRTE().
* funcordinality为T,则eref->colnames链表包含原列的别名.
*/
List *functions; /* list of RangeTblFunction nodes */
bool funcordinality; /* is this called WITH ORDINALITY? */
/*
* Fields valid for a TableFunc RTE (else NULL):
* 用于TableFunc RTE
*/
TableFunc *tablefunc;
/*
* Fields valid for a values RTE (else NIL):
* 用于Values RTE
*/
List *values_lists; /* list of expression lists */
/*
* Fields valid for a CTE RTE (else NULL/zero):
* 用于CTE RTE
*/
//WITH链表条目名称
char *ctename; /* name of the WITH list item */
//查询层次编号
Index ctelevelsup; /* number of query levels up */
//是否递归?
bool self_reference; /* is this a recursive self-reference? */
/*
* Fields valid for CTE, VALUES, ENR, and TableFunc RTEs (else NIL):
* 用于CTE, VALUES, ENR, and TableFunc RTEs
*
* We need these for CTE RTEs so that the types of self-referential
* columns are well-defined. For VALUES RTEs, storing these explicitly
* saves having to re-determine the info by scanning the values_lists. For
* ENRs, we store the types explicitly here (we Could get the information
* from the catalogs if 'relid' was supplied, but we'd still need these
* for TupleDesc-based ENRs, so we might as well always store the type
* info here). For TableFuncs, these fields are redundant with data in
* the TableFunc node, but keeping them here allows some code sharing with
* the other cases.
* CTE : 自引用列可被定义.
* VALUES : 显式存储这些信息可以避免扫描values_lists.
* ENRs : 显式存储
* TableFuncs : 这些字段与TableFunc节点中的数据重复,用于共享信息
*
* For ENRs only, we have to consider the possibility of dropped columns.
* A dropped column is included in these lists, but it will have zeroes in
* all three lists (as well as an empty-string entry in eref). Testing
* for zero coltype is the standard way to detect a dropped column.
* 对于ERNs,不得不考虑已删除字段的可能性.
*/
List *coltypes; /* OID list of column type OIDs */
List *coltypmods; /* integer list of column typmods */
List *colcollations; /* OID list of column collation OIDs */
/*
* Fields valid for ENR RTEs (else NULL/zero):
* ENR RTEs的字段
*/
char *enrname; /* name of ephemeral named relation */
double enrtuples; /* estimated or actual from caller */
/*
* Fields valid in all RTEs:
* 所有RTE都有的字段
*/
//用户自定义别名
Alias *alias; /* user-written alias clause, if any */
//已扩展的引用名
Alias *eref; /* expanded reference names */
//子查询/函数/VALUES是LAteraL
bool lateral; /* subquery, function, or values is LAteraL? */
//是否继承?
bool inh; /* inheritance requested? */
//在FROM中出现?
bool inFromCl; /* present in FROM clause? */
//访问权限位掩码
AclMode requiredPerms; /* bitmask of required access permissions */
//如有效,则使用此角色检查访问权限
Oid checkAsUser; /* if valid, check access as this role */
//需要SELECT的权限
Bitmapset *selectedCols; /* columns needing SELECT permission */
Bitmapset *insertedCols; /* columns needing INSERT permission */
Bitmapset *updatedCols; /* columns needing UPDATE permission */
//将要被update的列
Bitmapset *extraUpdatedCols; /* generated columns being updated */
//需应用的安全栏
List *securityQuals; /* security barrier quals to apply, if any */
} RangeTblEntry;
二、源码解读
N/A
三、跟踪分析
测试sql语句:
-- 用于测试的查询语句
testdb=# select * from (
testdb(# select t_dwxx.dwmc,t_grxx.grbh,t_grxx.xm,t_jfxx.ny,t_jfxx.je
testdb(# from t_dwxx inner join t_grxx on t_dwxx.dwbh = t_grxx.dwbh
testdb(# inner join t_jfxx on t_grxx.grbh = t_jfxx.grbh
testdb(# where t_dwxx.dwbh IN ('1001')
testdb(# union all
testdb(# select t_dwxx.dwmc,t_grxx.grbh,t_grxx.xm,t_jfxx.ny,t_jfxx.je
testdb(# from t_dwxx inner join t_grxx on t_dwxx.dwbh = t_grxx.dwbh
testdb(# inner join t_jfxx on t_grxx.grbh = t_jfxx.grbh
testdb(# where t_dwxx.dwbh IN ('1002')
testdb(# ) as ret
testdb-# order by ret.grbh
testdb-# limit 4;
样例数据如下:
...
(gdb) set $rtable=$query->rtable
(gdb) p *$rtable
$8 = {type = T_List, length = 3, head = 0x170be48, tail = 0x170f6b0}
(gdb) p *(Node *)($rtable->head->data.ptr_value)
$9 = {type = T_RangeTblEntry}
(gdb) p *(RangeTblEntry *)($rtable->head->data.ptr_value)
$10 = {type = T_RangeTblEntry, rtekind = RTE_SUBQUERY, relid = 0, relkind = 0 '\000', tablesample = 0x0,
subquery = 0x1667500, security_barrier = false, jointype = JOIN_INNER, joinaliasvars = 0x0,
functions = 0x0, funcordinality = false, tablefunc = 0x0, values_lists = 0x0, ctename = 0x0,
ctelevelsup = 0, self_reference = false, coltypes = 0x0, coltypmods = 0x0, colcollations = 0x0,
enrname = 0x0, enrtuples = 0, alias = 0x1666d40, eref = 0x170bc18, lateral = false, inh = true,
inFromCl = true, requiredPerms = 2, checkAsUser = 0, selectedCols = 0x0, insertedCols = 0x0,
updatedCols = 0x0, securityQuals = 0x0}
(gdb) set $rte=(RangeTblEntry *)($rtable->head->data.ptr_value)
(gdb) p *$rte->subquery
$12 = {type = T_Query, commandType = CMD_SELECT, querySource = QSRC_ORIGINAL, queryId = 0, canSetTag = true,
utilityStmt = 0x0, resultRelation = 0, hasAggs = false, hasWindowFuncs = false, hasTargetSRFs = false,
hasSubLinks = false, hasdistinctOn = false, hasRecursive = false, hasModifyingCTE = false,
hasForUpdate = false, hasRowSecurity = false, cteList = 0x0, rtable = 0x16fe4e8, jointree = 0x170bbe8,
targetList = 0x170b358, override = OVERRIDING_NOT_SET, onConflict = 0x0, returningList = 0x0,
groupClause = 0x0, groupingSets = 0x0, havingQual = 0x0, windowClause = 0x0, distinctClause = 0x0,
sortClause = 0x0, limitOffset = 0x0, limitCount = 0x0, rowMarks = 0x0, setoperations = 0x1667610,
constraintDeps = 0x0, withCheckOptions = 0x0, stmt_location = 0, stmt_len = 0}
(gdb) p *$rte->alias
$13 = {type = T_Alias, aliasname = 0x1666d28 "ret", colnames = 0x0}
(gdb) p *$rte->eref
$14 = {type = T_Alias, aliasname = 0x170bc48 "ret", colnames = 0x170bcb8}
(gdb) p *$rte->eref->colnames
$15 = {type = T_List, length = 5, head = 0x170bc98, tail = 0x170be28}
(gdb) p *(Node *)$rte->eref->colnames->head->data.ptr_value
$16 = {type = T_String}
(gdb) p *(Value *)$rte->eref->colnames->head->data.ptr_value
$17 = {type = T_String, val = {ival = 24165472, str = 0x170bc60 "dwmc"}}
---->subquery
(gdb) p *(RangeTblEntry *)$rte->subquery->rtable->head->data.ptr_value
$26 = {type = T_RangeTblEntry, rtekind = RTE_SUBQUERY, relid = 0, relkind = 0 '\000', tablesample = 0x0,
subquery = 0x16faf98, security_barrier = false, jointype = JOIN_INNER, joinaliasvars = 0x0,
functions = 0x0, funcordinality = false, tablefunc = 0x0, values_lists = 0x0, ctename = 0x0,
ctelevelsup = 0, self_reference = false, coltypes = 0x0, coltypmods = 0x0, colcollations = 0x0,
enrname = 0x0, enrtuples = 0, alias = 0x16fe240, eref = 0x16fe290, lateral = false, inh = false,
inFromCl = false, requiredPerms = 0, checkAsUser = 0, selectedCols = 0x0, insertedCols = 0x0,
updatedCols = 0x0, securityQuals = 0x0}
(gdb) set $rte_sq_rte=((RangeTblEntry *)$rte->subquery->rtable->head->data.ptr_value)
(gdb) p *(RangeTblEntry *)$rte_sq_rte->subquery->rtable->head->data.ptr_value
$30 = {type = T_RangeTblEntry, rtekind = RTE_RELATION, relid = 26754, relkind = 114 'r', tablesample = 0x0,
subquery = 0x0, security_barrier = false, jointype = JOIN_INNER, joinaliasvars = 0x0, functions = 0x0,
funcordinality = false, tablefunc = 0x0, values_lists = 0x0, ctename = 0x0, ctelevelsup = 0,
self_reference = false, coltypes = 0x0, coltypmods = 0x0, colcollations = 0x0, enrname = 0x0,
enrtuples = 0, alias = 0x0, eref = 0x16677c0, lateral = false, inh = true, inFromCl = true,
requiredPerms = 2, checkAsUser = 0, selectedCols = 0x16fbda8, insertedCols = 0x0, updatedCols = 0x0,
securityQuals = 0x0}
(gdb) set $rte_sq_rte_sq_rte=(RangeTblEntry *)$rte_sq_rte->subquery->rtable->head->data.ptr_value
(gdb) p *$rte_sq_rte_sq_rte
$42 = {type = T_RangeTblEntry, rtekind = RTE_RELATION, relid = 26754, relkind = 114 'r', tablesample = 0x0,
subquery = 0x0, security_barrier = false, jointype = JOIN_INNER, joinaliasvars = 0x0, functions = 0x0,
funcordinality = false, tablefunc = 0x0, values_lists = 0x0, ctename = 0x0, ctelevelsup = 0,
self_reference = false, coltypes = 0x0, coltypmods = 0x0, colcollations = 0x0, enrname = 0x0,
enrtuples = 0, alias = 0x0, eref = 0x16677c0, lateral = false, inh = true, inFromCl = true,
requiredPerms = 2, checkAsUser = 0, selectedCols = 0x16fbda8, insertedCols = 0x0, updatedCols = 0x0,
securityQuals = 0x0}
(gdb) set $rte_sq_rte_sq_rtable=$rte_sq_rte->subquery->rtable
(gdb) p *(RangeTblEntry *)($rte_sq_rte_sq_rtable->head->data.ptr_value)
$60 = {type = T_RangeTblEntry, rtekind = RTE_RELATION, relid = 26754, relkind = 114 'r', tablesample = 0x0,
subquery = 0x0, security_barrier = false, jointype = JOIN_INNER, joinaliasvars = 0x0, functions = 0x0,
funcordinality = false, tablefunc = 0x0, values_lists = 0x0, ctename = 0x0, ctelevelsup = 0,
self_reference = false, coltypes = 0x0, coltypmods = 0x0, colcollations = 0x0, enrname = 0x0,
enrtuples = 0, alias = 0x0, eref = 0x16677c0, lateral = false, inh = true, inFromCl = true,
requiredPerms = 2, checkAsUser = 0, selectedCols = 0x16fbda8, insertedCols = 0x0, updatedCols = 0x0,
securityQuals = 0x0}
(gdb) p *(RangeTblEntry *)($rte_sq_rte_sq_rtable->head->next->data.ptr_value)
$61 = {type = T_RangeTblEntry, rtekind = RTE_RELATION, relid = 26757, relkind = 114 'r', tablesample = 0x0,
subquery = 0x0, security_barrier = false, jointype = JOIN_INNER, joinaliasvars = 0x0, functions = 0x0,
funcordinality = false, tablefunc = 0x0, values_lists = 0x0, ctename = 0x0, ctelevelsup = 0,
self_reference = false, coltypes = 0x0, coltypmods = 0x0, colcollations = 0x0, enrname = 0x0,
enrtuples = 0, alias = 0x0, eref = 0x16fb4f0, lateral = false, inh = true, inFromCl = true,
requiredPerms = 2, checkAsUser = 0, selectedCols = 0x16fbe10, insertedCols = 0x0, updatedCols = 0x0,
securityQuals = 0x0}
(gdb) p *(RangeTblEntry *)($rte_sq_rte_sq_rtable->head->next->next->data.ptr_value)
$62 = {type = T_RangeTblEntry, rtekind = RTE_JOIN, relid = 0, relkind = 0 '\000', tablesample = 0x0,
subquery = 0x0, security_barrier = false, jointype = JOIN_INNER, joinaliasvars = 0x16fbff8,
functions = 0x0, funcordinality = false, tablefunc = 0x0, values_lists = 0x0, ctename = 0x0,
ctelevelsup = 0, self_reference = false, coltypes = 0x0, coltypmods = 0x0, colcollations = 0x0,
enrname = 0x0, enrtuples = 0, alias = 0x0, eref = 0x16fc318, lateral = false, inh = false, inFromCl = true,
requiredPerms = 0, checkAsUser = 0, selectedCols = 0x0, insertedCols = 0x0, updatedCols = 0x0,
securityQuals = 0x0}
(gdb) p *(RangeTblEntry *)($rte_sq_rte_sq_rtable->head->next->next->next->data.ptr_value)
$63 = {type = T_RangeTblEntry, rtekind = RTE_RELATION, relid = 26760, relkind = 114 'r', tablesample = 0x0,
subquery = 0x0, security_barrier = false, jointype = JOIN_INNER, joinaliasvars = 0x0, functions = 0x0,
funcordinality = false, tablefunc = 0x0, values_lists = 0x0, ctename = 0x0, ctelevelsup = 0,
self_reference = false, coltypes = 0x0, coltypmods = 0x0, colcollations = 0x0, enrname = 0x0,
enrtuples = 0, alias = 0x0, eref = 0x16fc678, lateral = false, inh = true, inFromCl = true,
requiredPerms = 2, checkAsUser = 0, selectedCols = 0x16fd1d0, insertedCols = 0x0, updatedCols = 0x0,
securityQuals = 0x0}
(gdb) p *(RangeTblEntry *)($rte_sq_rte_sq_rtable->tail->data.ptr_value)
$64 = {type = T_RangeTblEntry, rtekind = RTE_JOIN, relid = 0, relkind = 0 '\000', tablesample = 0x0,
subquery = 0x0, security_barrier = false, jointype = JOIN_INNER, joinaliasvars = 0x16fd3b8,
functions = 0x0, funcordinality = false, tablefunc = 0x0, values_lists = 0x0, ctename = 0x0,
ctelevelsup = 0, self_reference = false, coltypes = 0x0, coltypmods = 0x0, colcollations = 0x0,
enrname = 0x0, enrtuples = 0, alias = 0x0, eref = 0x16fd798, lateral = false, inh = false, inFromCl = true,
requiredPerms = 0, checkAsUser = 0, selectedCols = 0x0, insertedCols = 0x0, updatedCols = 0x0,
securityQuals = 0x0}
四、参考资料
N/A