本文主要介绍PG在执行查询时,对SQL的语义分析重写过程中的查询对象解析过程,处理的函数为addRangeTableEntry,分析查询对象信息。
一、源码解读
本函数是解析查询(包含增删改查操作)执行过程中涉及的查询对象(表、视图、子查询等)的信息。
每次调用只解析一个对象。
/* src/backend/parser/parse_relation.c */
/*
* Add an entry for a relation to the pstate's range table (p_rtable).
*
* Note: formerly this checked for refname conflicts, but that's wrong.
* Caller is responsible for checking for conflicts in the appropriate scope.
*/
RangeTblEntry *
addRangeTableEntry(ParseState *pstate, // 查询信息
RangeVar *relation, // 本次分析的关系对象
Alias *alias, // 别名
bool inh, // 是否是被继承的表
bool inFromCl) // 是否是from 语句中的对象
{
RangeTblEntry *rte = makeNode(RangeTblEntry); // 创建一个表实例
char *refname = alias ? alias->aliasname : relation->relname;
LOCKMODE lockmode;
Relation rel; // 关系缓存实体
Assert(pstate != NULL);
rte->rtekind = RTE_RELATION;
rte->alias = alias;
/*
* Get the rel's OID. This access also ensures that we have an up-to-date
* relcache entry for the rel. Since this is typically the first access
* to a rel in a statement, be careful to get the right access level
* depending on whether we're doing SELECT FOR UPDATE/SHARE.
*/
lockmode = isLockedRefname(pstate, refname) ? RowShareLock : AccessShareLock; // 根据查询分析,判断锁表类型
rel = parserOpenTable(pstate, relation, lockmode); // 从缓存中获取关系缓存实体
rte->relid = RelationGetRelid(rel); // 获取关系缓存实体ID
rte->relkind = rel->rd_rel->relkind; // 关系实体类型
/*
* Build the list of effective column names using user-supplied aliases
* and/or actual column names.
*/
rte->eref = makeAlias(refname, NIL);
buildRelationAliases(rel->rd_att, alias, rte->eref); // 构建别名信息
/*
* Drop the rel refcount, but keep the access lock till end of transaction
* so that the table can't be deleted or have its schema modified
* underneath us.
*/
heap_close(rel, NoLock);
/*
* Set flags and access permissions.
*
* The initial default on access checks is always check-for-READ-access,
* which is the right thing for all except target tables.
*/
rte->lateral = false;
rte->inh = inh;
rte->inFromCl = inFromCl;
rte->requiredPerms = ACL_SELECT;
rte->checkAsUser = InvalidOid; /* not set-uid by default, either */
rte->selectedCols = NULL;
rte->insertedCols = NULL;
rte->updatedCols = NULL;
/*
* Add completed RTE to pstate's range table list, but not to join list
* nor namespace --- caller must do that if appropriate.
*/
pstate->p_rtable = lappend(pstate->p_rtable, rte); // 将表实例rte附加到pstate->p_rtable列表中
return rte;
}
二、数据结构信息
- ParseState -SQL语义分析过程信息对象
/*
* State information used during parse analysis
*
* parentParseState: NULL in a top-level ParseState. When parsing a subquery,
* links to current parse state of outer query.
*
* p_sourcetext: source string that generated the raw parsetree being
* analyzed, or NULL if not available. (The string is used only to
* generate cursor positions in error messages: we need it to convert
* byte-wise locations in parse structures to character-wise cursor
* positions.)
*
* p_rtable: list of RTEs that will become the rangetable of the query.
* Note that neither relname nor refname of these entries are necessarily
* unique; searching the rtable by name is a bad idea.
*
* p_joinexprs: list of JoinExpr nodes associated with p_rtable entries.
* This is one-for-one with p_rtable, but contains NULLs for non-join
* RTEs, and may be shorter than p_rtable if the last RTE(s) aren't joins.
*
* p_joinlist: list of join items (RangeTblRef and JoinExpr nodes) that
* will become the fromlist of the query's top-level FromExpr node.
*
* p_namespace: list of ParseNamespaceItems that represents the current
* namespace for table and column lookup. (The RTEs listed here may be just
* a subset of the whole rtable. See ParseNamespaceItem comments below.)
*
* p_lateral_active: true if we are currently parsing a LATERAL subexpression
* of this parse level. This makes p_lateral_only namespace items visible,
* whereas they are not visible when p_lateral_active is FALSE.
*
* p_ctenamespace: list of CommonTableExprs (WITH items) that are visible
* at the moment. This is entirely different from p_namespace because a CTE
* is not an RTE, rather "visibility" means you could make an RTE from it.
*
* p_future_ctes: list of CommonTableExprs (WITH items) that are not yet
* visible due to scope rules. This is used to help improve error messages.
*
* p_parent_cte: CommonTableExpr that immediately contains the current query,
* if any.
*
* p_target_relation: target relation, if query is INSERT, UPDATE, or DELETE.
*
* p_target_rangetblentry: target relation's entry in the rtable list.
*
* p_is_insert: true to process assignment expressions like INSERT, false
* to process them like UPDATE. (Note this can change intra-statement, for
* cases like INSERT ON CONFLICT UPDATE.)
*
* p_windowdefs: list of WindowDefs representing WINDOW and OVER clauses.
* We collect these while transforming expressions and then transform them
* afterwards (so that any resjunk tlist items needed for the sort/group
* clauses end up at the end of the query tlist). A WindowDef's location in
* this list, counting from 1, is the winref number to use to reference it.
*
* p_expr_kind: kind of expression we're currently parsing, as per enum above;
* EXPR_KIND_NONE when not in an expression.
*
* p_next_resno: next TargetEntry.resno to assign, starting from 1.
*
* p_multiassign_exprs: partially-processed MultiAssignRef source expressions.
*
* p_locking_clause: query's FOR UPDATE/FOR SHARE clause, if any.
*
* p_locked_from_parent: true if parent query level applies FOR UPDATE/SHARE
* to this subquery as a whole.
*
* p_resolve_unknowns: resolve unknown-type SELECT output columns as type TEXT
* (this is true by default).
*
* p_hasAggs, p_hasWindowFuncs, etc: true if we've found any of the indicated
* constructs in the query.
*
* p_last_srf: the set-returning FuncExpr or OpExpr most recently found in
* the query, or NULL if none.
*
* p_pre_columnref_hook, etc: optional parser hook functions for modifying the
* interpretation of ColumnRefs and ParamRefs.
*
* p_ref_hook_state: passthrough state for the parser hook functions.
*/
struct ParseState
{
struct ParseState *parentParseState; /* stack link */
const char *p_sourcetext; /* 查询的脚本源码 source text, or NULL if not available */
List *p_rtable; /* 查询设计的对象 range table so far */
List *p_joinexprs; /*对象关联条件 JoinExprs for RTE_JOIN p_rtable entries */
List *p_joinlist; /*对象关联列表 join items so far (will become FromExpr
* node's fromlist) */
List *p_namespace; /* currently-referenceable RTEs (List of
* ParseNamespaceItem) */
bool p_lateral_active; /* p_lateral_only items visible? */
List *p_ctenamespace; /* current namespace for common table exprs */
List *p_future_ctes; /* common table exprs not yet in namespace */
CommonTableExpr *p_parent_cte; /* this query's containing CTE */
Relation p_target_relation; /* INSERT/UPDATE/DELETE target rel */
RangeTblEntry *p_target_rangetblentry; /* target rel's RTE */
bool p_is_insert; /* process assignment like INSERT not UPDATE */
List *p_windowdefs; /* raw representations of window clauses */
ParseExprKind p_expr_kind; /* what kind of expression we're parsing */
int p_next_resno; /* next targetlist resno to assign */
List *p_multiassign_exprs; /* junk tlist entries for multiassign */
List *p_locking_clause; /* 锁 raw FOR UPDATE/FOR SHARE info */
bool p_locked_from_parent; /* 锁 parent has marked this subquery
* with FOR UPDATE/FOR SHARE */
bool p_resolve_unknowns; /* resolve unknown-type SELECT outputs as
* type text */
QueryEnvironment *p_queryEnv; /* curr env, incl refs to enclosing env */
/* Flags telling about things found in the query: */
bool p_hasAggs;
bool p_hasWindowFuncs;
bool p_hasTargetSRFs;
bool p_hasSubLinks;
bool p_hasModifyingCTE;
Node *p_last_srf; /* most recent set-returning func/op found */
/*
* Optional hook functions for parser callbacks. These are null unless
* set up by the caller of make_parsestate.
*/
PreParseColumnRefHook p_pre_columnref_hook;
PostParseColumnRefHook p_post_columnref_hook;
ParseParamRefHook p_paramref_hook;
CoerceParamHook p_coerce_param_hook;
void *p_ref_hook_state; /* common passthrough link for above */
};
- RangeVar -查询的对象
/*
* RangeVar - range variable, used in FROM clauses 查询的对象
*
* Also used to represent table names in utility statements; there, the alias
* field is not used, and inh tells whether to apply the operation
* recursively to child tables. In some contexts it is also useful to carry
* a TEMP table indication here.
*/
typedef struct RangeVar
{
NodeTag type;
char *catalogname; /* 编目/数据库名 the catalog (database) name, or NULL */
char *schemaname; /* 模式表 the schema name, or NULL */
char *relname; /* 对象名称 the relation/sequence name */
bool inh; /* expand rel by inheritance? recursively act
* on children? */
char relpersistence; /* see RELPERSISTENCE_* in pg_class.h */
Alias *alias; /* 查询中的别名 table alias & optional column aliases */
int location; /* token location, or -1 if unknown */
} RangeVar;
- Alias-查询对象化名
/*
* Alias - 化名
* specifies an alias for a range variable; the alias might also
* specify renaming of columns within the table.
*
* Note: colnames is a list of Value nodes (always strings). In Alias structs
* associated with RTEs, there may be entries corresponding to dropped
* columns; these are normally empty strings (""). See parsenodes.h for info.
*/
typedef struct Alias
{
NodeTag type;
char *aliasname; /* 化名 aliased rel name (never qualified) */
List *colnames; /* 字段名列表 optional list of column aliases */
} Alias;
- RangeTblEntry - 查询树节点中的表实体对象
/* src/include/nodes/parsenodes.h */
/*--------------------
* RangeTblEntry -
* A range table is a List of RangeTblEntry nodes.
*
* 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.
*
* Note that we consider RTE_RELATION to cover anything that has a pg_class
* entry. relkind distinguishes the sub-cases.
*
* alias is an Alias node representing the AS alias-clause attached to the
* FROM expression, or NULL if no clause.
*
* 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.
*
* 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.
*
* 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.)
*
* 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.
*
* 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.
*
* 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.
*
* 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.
*
* 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.
*--------------------
*/
typedef enum RTEKind // 实体类型
{
RTE_RELATION, /* 引用 ordinary relation reference */
RTE_SUBQUERY, /* 子查询 subquery in FROM */
RTE_JOIN, /* 链接 join */
RTE_FUNCTION, /* 函数 function in FROM */
RTE_TABLEFUNC, /* 表函数 TableFunc(.., column list) */
RTE_VALUES, /* 值 VALUES (<exprlist>), (<exprlist>), ... */
RTE_CTE, /* 公共表表达式 common table expr (WITH list element) */
RTE_NAMEDTUPLESTORE /* tuple存储 tuplestore, e.g. for AFTER triggers */
} 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):
*
* 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.
*/
Oid relid; /* OID of the relation */
char relkind; /* relation kind (see pg_class.relkind) */
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):
*
* 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.
*/
JoinType jointype; /* type of join */
List *joinaliasvars; /* list of alias-var expansions */
/*
* Fields valid for a function RTE (else NIL/zero):
*
* 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().
*/
List *functions; /* 表函数列表 list of RangeTblFunction nodes */
bool funcordinality; /* is this called WITH ORDINALITY? */
/*
* Fields valid for a TableFunc RTE (else NULL):
*/
TableFunc *tablefunc;
/*
* Fields valid for a values RTE (else NIL):
*/
List *values_lists; /* list of expression lists */
/*
* Fields valid for a CTE RTE (else NULL/zero):
*/
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 table functions, values, CTE and ENR RTEs (else NIL):
*
* 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 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.
*/
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):
*/
char *enrname; /* name of ephemeral named relation */
double enrtuples; /* estimated or actual from caller */
/*
* Fields valid in all RTEs:
*/
Alias *alias; /* 别名 user-written alias clause, if any */
Alias *eref; /* expanded reference names */
bool lateral; /* subquery, function, or values is LATERAL? */
bool inh; /* inheritance requested? */
bool inFromCl; /* present in FROM clause? */
AclMode requiredPerms; /* bitmask of required access permissions */
Oid checkAsUser; /* 对象ID if valid, check access as this role */
Bitmapset *selectedCols; /* columns needing SELECT permission */
Bitmapset *insertedCols; /* columns needing INSERT permission */
Bitmapset *updatedCols; /* columns needing UPDATE permission */
List *securityQuals; /* security barrier quals to apply, if any */
} RangeTblEntry;
- Relation - 关系缓存实体 的内容
里面主要缓存了各种描述、主键、外键、触发器、索引、约束、对象锁...等对象的所有信息
/* src/include/utils/rel.h */
/*
* Here are the contents of a relation cache entry.
*/
typedef struct RelationData
{
RelFileNode rd_node; /* relation physical identifier */
/* use "struct" here to avoid needing to include smgr.h: */
struct SMgrRelationData *rd_smgr; /* cached file handle, or NULL */
int rd_refcnt; /* reference count */
BackendId rd_backend; /* owning backend id, if temporary relation */
bool rd_islocaltemp; /* rel is a temp rel of this session */
bool rd_isnailed; /* rel is nailed in cache */
bool rd_isvalid; /* relcache entry is valid */
char rd_indexvalid; /* state of rd_indexlist: 0 = not valid, 1 =
* valid, 2 = temporarily forced */
bool rd_statvalid; /* is rd_statlist valid? */
/*
* rd_createSubid is the ID of the highest subtransaction the rel has
* survived into; or zero if the rel was not created in the current top
* transaction. This can be now be relied on, whereas previously it could
* be "forgotten" in earlier releases. Likewise, rd_newRelfilenodeSubid is
* the ID of the highest subtransaction the relfilenode change has
* survived into, or zero if not changed in the current transaction (or we
* have forgotten changing it). rd_newRelfilenodeSubid can be forgotten
* when a relation has multiple new relfilenodes within a single
* transaction, with one of them occurring in a subsequently aborted
* subtransaction, e.g. BEGIN; TRUNCATE t; SAVEPOINT save; TRUNCATE t;
* ROLLBACK TO save; -- rd_newRelfilenode is now forgotten
*/
SubTransactionId rd_createSubid; /* rel was created in current xact */
SubTransactionId rd_newRelfilenodeSubid; /* new relfilenode assigned in
* current xact */
Form_pg_class rd_rel; /* RELATION tuple */
TupleDesc rd_att; /* tuple descriptor */
Oid rd_id; /* relation's object id */
LockInfoData rd_lockInfo; /* lock mgr's info for locking relation */
RuleLock *rd_rules; /* rewrite rules */
MemoryContext rd_rulescxt; /* private memory cxt for rd_rules, if any */
TriggerDesc *trigdesc; /* Trigger info, or NULL if rel has none */
/* use "struct" here to avoid needing to include rowsecurity.h: */
struct RowSecurityDesc *rd_rsdesc; /* row security policies, or NULL */
/* data managed by RelationGetFKeyList: */
List *rd_fkeylist; /* list of ForeignKeyCacheInfo (see below) */
bool rd_fkeyvalid; /* true if list has been computed */
MemoryContext rd_partkeycxt; /* private memory cxt for the below */
struct PartitionKeyData *rd_partkey; /* partition key, or NULL */
MemoryContext rd_pdcxt; /* private context for partdesc */
struct PartitionDescData *rd_partdesc; /* partitions, or NULL */
List *rd_partcheck; /* partition CHECK quals */
/* data managed by RelationGetIndexList: */
List *rd_indexlist; /* list of OIDs of indexes on relation */
Oid rd_oidindex; /* OID of unique index on OID, if any */
Oid rd_pkindex; /* OID of primary key, if any */
Oid rd_replidindex; /* OID of replica identity index, if any */
/* data managed by RelationGetStatExtList: */
List *rd_statlist; /* list of OIDs of extended stats */
/* data managed by RelationGetIndexAttrBitmap: */
Bitmapset *rd_indexattr; /* columns used in non-projection indexes */
Bitmapset *rd_projindexattr; /* columns used in projection indexes */
Bitmapset *rd_keyattr; /* cols that can be ref'd by foreign keys */
Bitmapset *rd_pkattr; /* cols included in primary key */
Bitmapset *rd_idattr; /* included in replica identity index */
Bitmapset *rd_projidx; /* Oids of projection indexes */
PublicationActions *rd_pubactions; /* publication actions */
/*
* rd_options is set whenever rd_rel is loaded into the relcache entry.
* Note that you can NOT look into rd_rel for this data. NULL means "use
* defaults".
*/
bytea *rd_options; /* parsed pg_class.reloptions */
/* These are non-NULL only for an index relation: */
Form_pg_index rd_index; /* pg_index tuple describing this index */
/* use "struct" here to avoid needing to include htup.h: */
struct HeapTupleData *rd_indextuple; /* all of pg_index tuple */
/*
* index access support info (used only for an index relation)
*
* Note: only default support procs for each opclass are cached, namely
* those with lefttype and righttype equal to the opclass's opcintype. The
* arrays are indexed by support function number, which is a sufficient
* identifier given that restriction.
*
* Note: rd_amcache is available for index AMs to cache private data about
* an index. This must be just a cache since it may get reset at any time
* (in particular, it will get reset by a relcache inval message for the
* index). If used, it must point to a single memory chunk palloc'd in
* rd_indexcxt. A relcache reset will include freeing that chunk and
* setting rd_amcache = NULL.
*/
Oid rd_amhandler; /* OID of index AM's handler function */
MemoryContext rd_indexcxt; /* private memory cxt for this stuff */
/* use "struct" here to avoid needing to include amapi.h: */
struct IndexAmRoutine *rd_amroutine; /* index AM's API struct */
Oid *rd_opfamily; /* OIDs of op families for each index col */
Oid *rd_opcintype; /* OIDs of opclass declared input data types */
RegProcedure *rd_support; /* OIDs of support procedures */
FmgrInfo *rd_supportinfo; /* lookup info for support procedures */
int16 *rd_indoption; /* per-column AM-specific flags */
List *rd_indexprs; /* index expression trees, if any */
List *rd_indpred; /* index predicate tree, if any */
Oid *rd_exclops; /* OIDs of exclusion operators, if any */
Oid *rd_exclprocs; /* OIDs of exclusion ops' procs, if any */
uint16 *rd_exclstrats; /* exclusion ops' strategy numbers, if any */
void *rd_amcache; /* available for use by index AM */
Oid *rd_indcollation; /* OIDs of index collations */
/*
* foreign-table support
*
* rd_fdwroutine must point to a single memory chunk palloc'd in
* CacheMemoryContext. It will be freed and reset to NULL on a relcache
* reset.
*/
/* use "struct" here to avoid needing to include fdwapi.h: */
struct FdwRoutine *rd_fdwroutine; /* cached function pointers, or NULL */
/*
* Hack for CLUSTER, rewriting ALTER TABLE, etc: when writing a new
* version of a table, we need to make any toast pointers inserted into it
* have the existing toast table's OID, not the OID of the transient toast
* table. If rd_toastoid isn't InvalidOid, it is the OID to place in
* toast pointers inserted into this rel. (Note it's set on the new
* version of the main heap, not the toast table itself.) This also
* causes toast_save_datum() to try to preserve toast value OIDs.
*/
Oid rd_toastoid; /* Real TOAST table's OID, or InvalidOid */
/* use "struct" here to avoid needing to include pgstat.h: */
struct PgStat_TableStatus *pgstat_info; /* statistics collection area */
} RelationData;
typedef struct RelationData *Relation;
/* ----------------
* RelationPtr is used in the executor to support index scans
* where we have to keep track of several index relations in an
* array. -cim 9/10/89
* ----------------
*/
typedef Relation *RelationPtr;
三、函数调用信息
- 执行的SQL
postgres=# select s.*
from t_student s
where s.sdept='IS'
order by s.sno desc
limit 2;
- 函数调用栈信息
Breakpoint 3, addRangeTableEntry (pstate=0x1f98638, relation=0x1f97e48,
alias=0x1f97ea0, inh=true, inFromCl=true) at parse_relation.c:1200
1200 RangeTblEntry *rte = makeNode(RangeTblEntry);
(gdb) bt
/* 函数调用栈信息 */
#0 addRangeTableEntry (pstate=0x1f98638, relation=0x1f97e48, alias=0x1f97ea0,
inh=true, inFromCl=true) at parse_relation.c:1200
#1 0x00000000005ec7ed in transformTableEntry (pstate=0x1f98638, r=0x1f97e48)
at parse_clause.c:435
#2 0x00000000005ee210 in transformFromClauseItem (pstate=0x1f98638,
n=0x1f97e48, top_rte=0x7ffc63ea0a28, top_rti=0x7ffc63ea0a24,
namespace=0x7ffc63ea0a18) at parse_clause.c:1121
#3 0x00000000005ec0d3 in transformFromClause (pstate=0x1f98638,
frmList=0x1f97f00) at parse_clause.c:139
#4 0x00000000005b54a3 in transformSelectStmt (pstate=0x1f98638,
stmt=0x1f981c0) at analyze.c:1212
#5 0x00000000005b3a43 in transformStmt (pstate=0x1f98638, parseTree=0x1f981c0)
at analyze.c:301
#6 0x00000000005b3919 in transformOptionalSelectInto (pstate=0x1f98638,
parseTree=0x1f981c0) at analyze.c:246
#7 0x00000000005b37d2 in transformTopLevelStmt (pstate=0x1f98638,
parseTree=0x1f985a0) at analyze.c:196
#8 0x00000000005b361b in parse_analyze (parseTree=0x1f985a0,
sourceText=0x1f971c8 "select s.*\nfrom t_student s\nwhere s.sdept='IS'\norder by s.sno desc \nlimit 2;", paramTypes=0x0, numParams=0, queryEnv=0x0)
at analyze.c:116
#9 0x00000000008c31dd in pg_analyze_and_rewrite (parsetree=0x1f985a0,
query_string=0x1f971c8 "select s.*\nfrom t_student s\nwhere s.sdept='IS'\nor---Type <return> to continue, or q <return> to quit---
der by s.sno desc \nlimit 2;", paramTypes=0x0, numParams=0, queryEnv=0x0)
at postgres.c:666
#10 0x00000000008c3847 in exec_simple_query (
query_string=0x1f971c8 "select s.*\nfrom t_student s\nwhere s.sdept='IS'\norder by s.sno desc \nlimit 2;") at postgres.c:1047
#11 0x00000000008c7d23 in PostgresMain (argc=1, argv=0x1fc31a0,
dbname=0x1fc3000 "postgres", username=0x1fc2fe0 "appusr")
at postgres.c:4153
#12 0x000000000082405c in BackendRun (port=0x1fb8fb0) at postmaster.c:4361
#13 0x00000000008237c0 in BackendStartup (port=0x1fb8fb0) at postmaster.c:4033
#14 0x000000000081fb58 in ServerLoop () at postmaster.c:1706
#15 0x000000000081f3f0 in PostmasterMain (argc=3, argv=0x1f91ce0)
at postmaster.c:1379
#16 0x00000000007469d4 in main (argc=3, argv=0x1f91ce0) at main.c:228
/* 函数调用参数 */
(gdb) p *pstate
$122 = {parentParseState = 0x0,
p_sourcetext = 0x1f971c8 "select s.*\nfrom t_student s\nwhere s.sdept='IS'\norder by s.sno desc \nlimit 2;", p_rtable = 0x0, p_joinexprs = 0x0,
p_joinlist = 0x0, p_namespace = 0x0, p_lateral_active = false,
p_ctenamespace = 0x0, p_future_ctes = 0x0, p_parent_cte = 0x0,
p_target_relation = 0x0, p_target_rangetblentry = 0x0, p_is_insert = false,
p_windowdefs = 0x0, p_expr_kind = EXPR_KIND_NONE, p_next_resno = 1,
p_multiassign_exprs = 0x0, p_locking_clause = 0x0,
p_locked_from_parent = false, p_resolve_unknowns = true, p_queryEnv = 0x0,
p_hasAggs = false, p_hasWindowFuncs = false, p_hasTargetSRFs = false,
p_hasSubLinks = false, p_hasModifyingCTE = false, p_last_srf = 0x0,
p_pre_columnref_hook = 0x0, p_post_columnref_hook = 0x0,
p_paramref_hook = 0x0, p_coerce_param_hook = 0x0, p_ref_hook_state = 0x0}
(gdb) p *relation
$123 = {type = T_RangeVar, catalogname = 0x0, schemaname = 0x0,
relname = 0x1f97e00 "t_student", inh = true, relpersistence = 112 'p',
alias = 0x1f97ea0, location = 16}
(gdb) p *relation->alias
$124 = {type = T_Alias, aliasname = 0x1f97e28 "s", colnames = 0x0}
(gdb) p *alias
$125 = {type = T_Alias, aliasname = 0x1f97e28 "s", colnames = 0x0}
/* 函数运行过程 */
(gdb) n
1201 char *refname = alias ? alias->aliasname : relation->relname;
(gdb) p *rte
$126 = {type = T_RangeTblEntry, rtekind = RTE_RELATION, relid = 0,
relkind = 0 '\000', 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 = 0x0, lateral = false, inh = false,
inFromCl = false, requiredPerms = 0, checkAsUser = 0, selectedCols = 0x0,
insertedCols = 0x0, updatedCols = 0x0, securityQuals = 0x0}
(gdb) n
1205 Assert(pstate != NULL);
(gdb) p *refname
$127 = 115 's'
(gdb) p refname
$128 = 0x1f97e28 "s"
(gdb) n
1207 rte->rtekind = RTE_RELATION;
(gdb) n
1208 rte->alias = alias;
(gdb) n
1216 lockmode = isLockedRefname(pstate, refname) ? RowShareLock : AccessShareLock;// 根据查询分析,判断锁表类型
(gdb) n
1217 rel = parserOpenTable(pstate, relation, lockmode); // 从缓存中获取关系缓存实体
(gdb) p lockmode
$129 = 1
(gdb) n
1218 rte->relid = RelationGetRelid(rel); // 获取关系缓存实体ID
(gdb) p *rel
$130 = {rd_node = {spcNode = 1663, dbNode = 13878, relNode = 16408},
rd_smgr = 0x2021f40, rd_refcnt = 1, rd_backend = -1, rd_islocaltemp = false,
rd_isnailed = false, rd_isvalid = true, rd_indexvalid = 1 '\001',
rd_statvalid = true, rd_createSubid = 0, rd_newRelfilenodeSubid = 0,
rd_rel = 0x7fcfc6d309a0, rd_att = 0x7fcfc6d30ab8, rd_id = 16408,
rd_lockInfo = {lockRelId = {relId = 16408, dbId = 13878}}, rd_rules = 0x0,
rd_rulescxt = 0x0, trigdesc = 0x0, rd_rsdesc = 0x0, rd_fkeylist = 0x0,
rd_fkeyvalid = false, rd_partkeycxt = 0x0, rd_partkey = 0x0, rd_pdcxt = 0x0,
rd_partdesc = 0x0, rd_partcheck = 0x0, rd_indexlist = 0x7fcfc6c72668,
rd_oidindex = 0, rd_pkindex = 16411, rd_replidindex = 16411,
rd_statlist = 0x0, rd_indexattr = 0x0, rd_projindexattr = 0x0,
rd_keyattr = 0x0, rd_pkattr = 0x0, rd_idattr = 0x0, rd_projidx = 0x0,
rd_pubactions = 0x0, rd_options = 0x0, rd_index = 0x0, rd_indextuple = 0x0,
rd_amhandler = 0, rd_indexcxt = 0x0, rd_amroutine = 0x0, rd_opfamily = 0x0,
rd_opcintype = 0x0, rd_support = 0x0, rd_supportinfo = 0x0,
rd_indoption = 0x0, rd_indexprs = 0x0, rd_indpred = 0x0, rd_exclops = 0x0,
rd_exclprocs = 0x0, rd_exclstrats = 0x0, rd_amcache = 0x0,
rd_indcollation = 0x0, rd_fdwroutine = 0x0, rd_toastoid = 0,
pgstat_info = 0x2015c60}
(gdb) n
1219 rte->relkind = rel->rd_rel->relkind; // 关系实体类型
(gdb) p rte->relid
$131 = 16408
(gdb) n
1225 rte->eref = makeAlias(refname, NIL);
(gdb) p rte->relkind
$132 = 114 'r'
(gdb) n
1226 buildRelationAliases(rel->rd_att, alias, rte->eref); // 构建别名信息
(gdb)
1233 heap_close(rel, NoLock);
(gdb)
1241 rte->lateral = false;
(gdb)
1242 rte->inh = inh;
(gdb)
1243 rte->inFromCl = inFromCl;
(gdb)
1245 rte->requiredPerms = ACL_SELECT;
(gdb)
1246 rte->checkAsUser = InvalidOid; /* not set-uid by default, either */
(gdb)
1247 rte->selectedCols = NULL;
(gdb)
1248 rte->insertedCols = NULL;
(gdb)
1249 rte->updatedCols = NULL;
(gdb)
1255 pstate->p_rtable = lappend(pstate->p_rtable, rte); // 将表实例rte附加到pstate->p_rtable列表中
(gdb)
1257 return rte;
(gdb)
1258 }
// 返回RangeTblEntry 对象指针
(gdb) p *rte
$133 = {type = T_RangeTblEntry, rtekind = RTE_RELATION, relid = 16408,
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 = 0x1f97ea0, eref = 0x1f98980, lateral = false,
inh = true, inFromCl = true, requiredPerms = 2, checkAsUser = 0,
selectedCols = 0x0, insertedCols = 0x0, updatedCols = 0x0,
securityQuals = 0x0}
(gdb) p *rte->alias
$134 = {type = T_Alias, aliasname = 0x1f97e28 "s", colnames = 0x0}
(gdb) p *rte->eref
$135 = {type = T_Alias, aliasname = 0x1f989b8 "s", colnames = 0x1f98a48}
(gdb) p *rte->eref->colnames
$136 = {type = T_List, length = 5, head = 0x1f98a20, tail = 0x1f98c18}
(gdb) p *((Node*)rte->eref->colnames->head->data->ptr_value)
$137 = {type = T_String}
(gdb) p *((Value*)rte->eref->colnames->head->data->ptr_value)
$138 = {type = T_String, val = {ival = 33130968, str = 0x1f989d8 "sno"}}
(gdb) p *((Value*)rte->eref->colnames->head->next->data->ptr_value)
$139 = {type = T_String, val = {ival = 33131136, str = 0x1f98a80 "sname"}}
(gdb) p *((Value*)rte->eref->colnames->head->next->next->data->ptr_value)
$140 = {type = T_String, val = {ival = 33131248, str = 0x1f98af0 "ssex"}}
(gdb) p *((Value*)rte->eref->colnames->head->next->next->next->data->ptr_value)
$141 = {type = T_String, val = {ival = 33131360, str = 0x1f98b60 "sage"}}
(gdb) p *((Value*)rte->eref->colnames->head->next->next->next->next->data->ptr_value)
$142 = {type = T_String, val = {ival = 33131472, str = 0x1f98bd0 "sdept"}}
(gdb) n
// 回到上一级函数transformTableEntry
transformTableEntry (pstate=0x1f98638, r=0x1f97e48) at parse_clause.c:437
437 return rte;
(gdb) n
438 }
(gdb) n
// 回到上一级函数 transformFromClauseItem
transformFromClauseItem (pstate=0x1f98638, n=0x1f97e48,
top_rte=0x7ffc63ea0a28, top_rti=0x7ffc63ea0a24, namespace=0x7ffc63ea0a18)
at parse_clause.c:1124
1124 rtindex = list_length(pstate->p_rtable);
(gdb) bt
#0 transformFromClauseItem (pstate=0x1f98638, n=0x1f97e48,
top_rte=0x7ffc63ea0a28, top_rti=0x7ffc63ea0a24, namespace=0x7ffc63ea0a18)
at parse_clause.c:1124
#1 0x00000000005ec0d3 in transformFromClause (pstate=0x1f98638,
frmList=0x1f97f00) at parse_clause.c:139
#2 0x00000000005b54a3 in transformSelectStmt (pstate=0x1f98638,
stmt=0x1f981c0) at analyze.c:1212
#3 0x00000000005b3a43 in transformStmt (pstate=0x1f98638, parseTree=0x1f981c0)
at analyze.c:301
#4 0x00000000005b3919 in transformOptionalSelectInto (pstate=0x1f98638,
parseTree=0x1f981c0) at analyze.c:246
#5 0x00000000005b37d2 in transformTopLevelStmt (pstate=0x1f98638,
parseTree=0x1f985a0) at analyze.c:196
#6 0x00000000005b361b in parse_analyze (parseTree=0x1f985a0,
sourceText=0x1f971c8 "select s.*\nfrom t_student s\nwhere s.sdept='IS'\norder by s.sno desc \nlimit 2;", paramTypes=0x0, numParams=0, queryEnv=0x0)
at analyze.c:116
#7 0x00000000008c31dd in pg_analyze_and_rewrite (parsetree=0x1f985a0,
query_string=0x1f971c8 "select s.*\nfrom t_student s\nwhere s.sdept='IS'\norder by s.sno desc \nlimit 2;", paramTypes=0x0, numParams=0, queryEnv=0x0)
at postgres.c:666
#8 0x00000000008c3847 in exec_simple_query (
query_string=0x1f971c8 "select s.*\nfrom t_student s\nwhere s.sdept='IS'\nor---Type <return> to continue, or q <return> to quit---
der by s.sno desc \nlimit 2;") at postgres.c:1047
#9 0x00000000008c7d23 in PostgresMain (argc=1, argv=0x1fc31a0,
dbname=0x1fc3000 "postgres", username=0x1fc2fe0 "appusr")
at postgres.c:4153
#10 0x000000000082405c in BackendRun (port=0x1fb8fb0) at postmaster.c:4361
#11 0x00000000008237c0 in BackendStartup (port=0x1fb8fb0) at postmaster.c:4033
#12 0x000000000081fb58 in ServerLoop () at postmaster.c:1706
#13 0x000000000081f3f0 in PostmasterMain (argc=3, argv=0x1f91ce0)
at postmaster.c:1379
#14 0x00000000007469d4 in main (argc=3, argv=0x1f91ce0) at main.c:228
(gdb) n
1125 Assert(rte == rt_fetch(rtindex, pstate->p_rtable));
(gdb)
1126 *top_rte = rte;
(gdb)
1127 *top_rti = rtindex;
(gdb)
1128 *namespace = list_make1(makeDefaultNSItem(rte));
(gdb)
1129 rtr = makeNode(RangeTblRef);
(gdb) p *namespace
$143 = (List *) 0x1f98cf0
(gdb) p **namespace
$144 = {type = T_List, length = 1, head = 0x1f98cc8, tail = 0x1f98cc8}
(gdb) p *(*namespace)->head->data->ptr_value
Attempt to dereference a generic pointer.
(gdb) p *((Node*)(*namespace)->head->data->ptr_value)
$145 = {type = 33130600}
(gdb) n
1130 rtr->rtindex = rtindex;
(gdb) n
1131 return (Node *) rtr;
(gdb) p *rtr
$146 = {type = T_RangeTblRef, rtindex = 1}
(gdb) finish // 回到上一级函数 transformFromClause
Run till exit from #0 transformFromClauseItem (pstate=0x1f98638, n=0x1f97e48,
top_rte=0x7ffc63ea0a28, top_rti=0x7ffc63ea0a24, namespace=0x7ffc63ea0a18)
at parse_clause.c:1131
0x00000000005ec0d3 in transformFromClause (pstate=0x1f98638, frmList=0x1f97f00)
at parse_clause.c:139
139 n = transformFromClauseItem(pstate, n,
Value returned is $147 = (Node *) 0x1f98d28
(gdb) bt
#0 0x00000000005ec0d3 in transformFromClause (pstate=0x1f98638,
frmList=0x1f97f00) at parse_clause.c:139
#1 0x00000000005b54a3 in transformSelectStmt (pstate=0x1f98638,
stmt=0x1f981c0) at analyze.c:1212
#2 0x00000000005b3a43 in transformStmt (pstate=0x1f98638, parseTree=0x1f981c0)
at analyze.c:301
#3 0x00000000005b3919 in transformOptionalSelectInto (pstate=0x1f98638,
parseTree=0x1f981c0) at analyze.c:246
#4 0x00000000005b37d2 in transformTopLevelStmt (pstate=0x1f98638,
parseTree=0x1f985a0) at analyze.c:196
#5 0x00000000005b361b in parse_analyze (parseTree=0x1f985a0,
sourceText=0x1f971c8 "select s.*\nfrom t_student s\nwhere s.sdept='IS'\norder by s.sno desc \nlimit 2;", paramTypes=0x0, numParams=0, queryEnv=0x0)
at analyze.c:116
#6 0x00000000008c31dd in pg_analyze_and_rewrite (parsetree=0x1f985a0,
query_string=0x1f971c8 "select s.*\nfrom t_student s\nwhere s.sdept='IS'\norder by s.sno desc \nlimit 2;", paramTypes=0x0, numParams=0, queryEnv=0x0)
at postgres.c:666
#7 0x00000000008c3847 in exec_simple_query (
query_string=0x1f971c8 "select s.*\nfrom t_student s\nwhere s.sdept='IS'\norder by s.sno desc \nlimit 2;") at postgres.c:1047
#8 0x00000000008c7d23 in PostgresMain (argc=1, argv=0x1fc31a0,
dbname=0x1fc3000 "postgres", username=0x1fc2fe0 "appusr")
---Type <return> to continue, or q <return> to quit---
at postgres.c:4153
#9 0x000000000082405c in BackendRun (port=0x1fb8fb0) at postmaster.c:4361
#10 0x00000000008237c0 in BackendStartup (port=0x1fb8fb0) at postmaster.c:4033
#11 0x000000000081fb58 in ServerLoop () at postmaster.c:1706
#12 0x000000000081f3f0 in PostmasterMain (argc=3, argv=0x1f91ce0)
at postmaster.c:1379
#13 0x00000000007469d4 in main (argc=3, argv=0x1f91ce0) at main.c:228
(gdb) n
144 checkNameSpaceConflicts(pstate, pstate->p_namespace, namespace);
(gdb) n
147 setNamespaceLateralState(namespace, true, true);
(gdb) n
149 pstate->p_joinlist = lappend(pstate->p_joinlist, n);
(gdb) n
150 pstate->p_namespace = list_concat(pstate->p_namespace, namespace);
(gdb) n
132 foreach(fl, frmList)
(gdb) n
159 setNamespaceLateralState(pstate->p_namespace, false, true);
(gdb) n
160 }
(gdb)
四、总结
1、通过观察addRangeTableEntry的执行过程,了解SQL语义解析transformFromClause的处理过程。
2、表结构信是从缓存中结构读取,然后获取自己需要的信息。
3、语义分析后转换为relid(关联对象id),提升查询执行的处理效率。
网友评论