在开始对查询优化器的代码进行分析之前,对查询树必须有一定的了解,下面开始分析查询树的结构,同时介绍一下查询树涉及的其他数据结构。
Node的结构
PostgreSQL数据库中的结构体采用了统一的形式,他们都是基于Node结构体进行扩展的,Node结构体包含一个NodeTag成员变量,NodeTag是枚举类型
typedef struct Node
{
NodeTag type;
} Node;
其他的结构体则利用C语言的特性对Node结构体进行扩展,所有结构体的第一个成员变量也是NodeTag枚举类型,例如
typedef struct List
{
NodeTag type; /* T_List, T_IntList, T_OidList, or T_XidList */
int length; /* number of elements currently present */
int max_length; /* allocated length of elements[] */
ListCell *elements; /* re-allocatable array of cells */
/* We may allocate some cells along with the List header: */
ListCell initial_elements[FLEXIBLE_ARRAY_MEMBER];
/* If elements == initial_elements, it's not a separate allocation */
} List;
typedef struct Query
{
NodeTag type;
CmdType commandType; /* select|insert|update|delete|merge|utility */
/* where did I come from? */
QuerySource querySource pg_node_attr(query_jumble_ignore);
/*
* query identifier (can be set by plugins); ignored for equal, as it
* might not be set; also not stored. This is the result of the query
* jumble, hence ignored.
*/
uint64 queryId pg_node_attr(equal_ignore, query_jumble_ignore, read_write_ignore, read_as(0));
/* do I set the command result tag? */
bool canSetTag pg_node_attr(query_jumble_ignore);
Node *utilityStmt; /* non-null if commandType == CMD_UTILITY */
/*
* rtable index of target relation for INSERT/UPDATE/DELETE/MERGE; 0 for
* SELECT. This is ignored in the query jumble as unrelated to the
* compilation of the query ID.
*/
int resultRelation pg_node_attr(query_jumble_ignore);
/* has aggregates in tlist or havingQual */
bool hasAggs pg_node_attr(query_jumble_ignore);
/* has window functions in tlist */
bool hasWindowFuncs pg_node_attr(query_jumble_ignore);
/* has set-returning functions in tlist */
bool hasTargetSRFs pg_node_attr(query_jumble_ignore);
/* has subquery SubLink */
bool hasSubLinks pg_node_attr(query_jumble_ignore);
/* distinctClause is from DISTINCT ON */
bool hasDistinctOn pg_node_attr(query_jumble_ignore);
/* WITH RECURSIVE was specified */
bool hasRecursive pg_node_attr(query_jumble_ignore);
/* has INSERT/UPDATE/DELETE in WITH */
bool hasModifyingCTE pg_node_attr(query_jumble_ignore);
/* FOR [KEY] UPDATE/SHARE was specified */
bool hasForUpdate pg_node_attr(query_jumble_ignore);
/* rewriter has applied some RLS policy */
bool hasRowSecurity pg_node_attr(query_jumble_ignore);
/* is a RETURN statement */
bool isReturn pg_node_attr(query_jumble_ignore);
List *cteList; /* WITH list (of CommonTableExpr's) */
List *rtable; /* list of range table entries */
/*
* list of RTEPermissionInfo nodes for the rtable entries having
* perminfoindex > 0
*/
List *rteperminfos pg_node_attr(query_jumble_ignore);
FromExpr *jointree; /* table join tree (FROM and WHERE clauses);
* also USING clause for MERGE */
List *mergeActionList; /* list of actions for MERGE (only) */
/* whether to use outer join */
bool mergeUseOuterJoin pg_node_attr(query_jumble_ignore);
List *targetList; /* target list (of TargetEntry) */
/* OVERRIDING clause */
OverridingKind override pg_node_attr(query_jumble_ignore);
OnConflictExpr *onConflict; /* ON CONFLICT DO [NOTHING | UPDATE] */
List *returningList; /* return-values list (of TargetEntry) */
List *groupClause; /* a list of SortGroupClause's */
bool groupDistinct; /* is the group by clause distinct? */
List *groupingSets; /* a list of GroupingSet's if present */
Node *havingQual; /* qualifications applied to groups */
List *windowClause; /* a list of WindowClause's */
List *distinctClause; /* a list of SortGroupClause's */
List *sortClause; /* a list of SortGroupClause's */
Node *limitOffset; /* # of result tuples to skip (int8 expr) */
Node *limitCount; /* # of result tuples to return (int8 expr) */
LimitOption limitOption; /* limit type */
List *rowMarks; /* a list of RowMarkClause's */
Node *setOperations; /* set-operation tree if this is top level of
* a UNION/INTERSECT/EXCEPT query */
/*
* A list of pg_constraint OIDs that the query depends on to be
* semantically valid
*/
List *constraintDeps pg_node_attr(query_jumble_ignore);
/* a list of WithCheckOption's (added during rewrite) */
List *withCheckOptions pg_node_attr(query_jumble_ignore);
/*
* The following two fields identify the portion of the source text string
* containing this query. They are typically only populated in top-level
* Queries, not in sub-queries. When not set, they might both be zero, or
* both be -1 meaning "unknown".
*/
/* start location, or -1 if unknown */
int stmt_location;
/* length in bytes; 0 means "rest of string" */
int stmt_len pg_node_attr(query_jumble_ignore);
} Query;
Var结构体
Var结构体表示查询中所涉及的表的列属性,在SQL语句中投影的列属性,约束条件中的列属性都是通过Var来表示的,在语法分析阶段会将列属性用ColumnRef结构体来表示,在语义分析阶段会将语法树中的ColumnRef替换成Var用来表示一个列属性。
typedef struct Var
{
Expr xpr;
/*
* index of this var's relation in the range table, or
* INNER_VAR/OUTER_VAR/etc
*/
int varno;
/*
* attribute number of this var, or zero for all attrs ("whole-row Var")
*/
AttrNumber varattno;
/* pg_type OID for the type of this var */
Oid vartype pg_node_attr(query_jumble_ignore);
/* pg_attribute typmod value */
int32 vartypmod pg_node_attr(query_jumble_ignore);
/* OID of collation, or InvalidOid if none */
Oid varcollid pg_node_attr(query_jumble_ignore);
/*
* RT indexes of outer joins that can replace the Var's value with null.
* We can omit varnullingrels in the query jumble, because it's fully
* determined by varno/varlevelsup plus the Var's query location.
*/
Bitmapset *varnullingrels pg_node_attr(query_jumble_ignore);
/*
* for subquery variables referencing outer relations; 0 in a normal var,
* >0 means N levels up
*/
Index varlevelsup;
/*
* varnosyn/varattnosyn are ignored for equality, because Vars with
* different syntactic identifiers are semantically the same as long as
* their varno/varattno match.
*/
/* syntactic relation index (0 if unknown) */
Index varnosyn pg_node_attr(equal_ignore, query_jumble_ignore);
/* syntactic attribute number */
AttrNumber varattnosyn pg_node_attr(equal_ignore, query_jumble_ignore);
/* token location, or -1 if unknown */
int location;
} Var;
RangeTblEntry结构体
RangeTblEntry(范围表,简称RTE)描述了查询中出现的表,它通常出现在查询语句的FROM字句中,范围表既有常规意义上的堆表,还有子查询,连接表等等。
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, /* tuplestore, e.g. for AFTER triggers */
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
{
pg_node_attr(custom_read_write, custom_query_jumble)
NodeTag type;
RTEKind rtekind; /* see above */
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 */
Index perminfoindex;
Query *subquery; /* the sub-query */
bool security_barrier; /* is from security_barrier view? */
JoinType jointype; /* type of join */
int joinmergedcols; /* number of merged (JOIN USING) columns */
List *joinaliasvars; /* list of alias-var expansions */
List *joinleftcols; /* left-side input column numbers */
List *joinrightcols; /* right-side input column numbers */
Alias *join_using_alias;
List *functions; /* list of RangeTblFunction nodes */
bool funcordinality; /* is this called WITH ORDINALITY? */
TableFunc *tablefunc;
List *values_lists; /* list of expression lists */
char *ctename; /* name of the WITH list item */
Index ctelevelsup; /* number of query levels up */
bool self_reference; /* is this a recursive self-reference? */
List *coltypes; /* OID list of column type OIDs */
List *coltypmods; /* integer list of column typmods */
List *colcollations; /* OID list of column collation OIDs */
char *enrname; /* name of ephemeral named relation */
Cardinality enrtuples; /* estimated or actual from caller */
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? */
List *securityQuals; /* security barrier quals to apply, if any */
} RangeTblEntry;
RangeTblRef结构体
RangeTblEntry只保留在查询树的Query->rtable链表中,而链表是一个线性结构,他如何保存查询树的关系代数表达式中的连接操作呢?答案在Query->jointree中保存各个范围表之间的连接关系。
typedef struct RangeTblRef
{
NodeTag type;
int rtindex;
} RangeTblRef;
JoinExpr结构体
在查询语句中如何显式地指定两个表之间的连接关系,例如 A LEFT JOIN B ON Pab这种形式,就需要一个JoinExpr结构体来表示他们。
typedef struct JoinExpr
{
NodeTag type;
JoinType jointype; /* type of join */
bool isNatural; /* Natural join? Will need to shape table */
Node *larg; /* left subtree */
Node *rarg; /* right subtree */
/* USING clause, if any (list of String) */
List *usingClause pg_node_attr(query_jumble_ignore);
/* alias attached to USING clause, if any */
Alias *join_using_alias pg_node_attr(query_jumble_ignore);
/* qualifiers on join, if any */
Node *quals;
/* user-written alias clause, if any */
Alias *alias pg_node_attr(query_jumble_ignore);
/* RT index assigned for join, or 0 */
int rtindex;
} JoinExpr;
FromExpr结构体
FromExpr和JoinExpr是用来表示表之间的连接关系的结构体,通常来说,FromExpr中的各个表之间的连接关系是Inner Join,这样就可以在FromExpr->fromlist中保存任意多个表,默认他们之间是内连接的关系。
typedef struct FromExpr
{
NodeTag type;
List *fromlist; /* List of join subtrees */
Node *quals; /* qualifiers on join, if any */
} FromExpr;
Query结构体
Query结构体是查询优化模块的输入参数,其源自语法分析模块,一个SQL语句在执行过程中,经过词法解析,语法解析和语义分析之后会生成一颗查询树,PostgreSQL用Query结构体来表示查询树。
typedef struct Query
{
NodeTag type;
CmdType commandType; /* select|insert|update|delete|merge|utility */
/* where did I come from? */
QuerySource querySource pg_node_attr(query_jumble_ignore);
/*
* query identifier (can be set by plugins); ignored for equal, as it
* might not be set; also not stored. This is the result of the query
* jumble, hence ignored.
*/
uint64 queryId pg_node_attr(equal_ignore, query_jumble_ignore, read_write_ignore, read_as(0));
/* do I set the command result tag? */
bool canSetTag pg_node_attr(query_jumble_ignore);
Node *utilityStmt; /* non-null if commandType == CMD_UTILITY */
/*
* rtable index of target relation for INSERT/UPDATE/DELETE/MERGE; 0 for
* SELECT. This is ignored in the query jumble as unrelated to the
* compilation of the query ID.
*/
int resultRelation pg_node_attr(query_jumble_ignore);
/* has aggregates in tlist or havingQual */
bool hasAggs pg_node_attr(query_jumble_ignore);
/* has window functions in tlist */
bool hasWindowFuncs pg_node_attr(query_jumble_ignore);
/* has set-returning functions in tlist */
bool hasTargetSRFs pg_node_attr(query_jumble_ignore);
/* has subquery SubLink */
bool hasSubLinks pg_node_attr(query_jumble_ignore);
/* distinctClause is from DISTINCT ON */
bool hasDistinctOn pg_node_attr(query_jumble_ignore);
/* WITH RECURSIVE was specified */
bool hasRecursive pg_node_attr(query_jumble_ignore);
/* has INSERT/UPDATE/DELETE in WITH */
bool hasModifyingCTE pg_node_attr(query_jumble_ignore);
/* FOR [KEY] UPDATE/SHARE was specified */
bool hasForUpdate pg_node_attr(query_jumble_ignore);
/* rewriter has applied some RLS policy */
bool hasRowSecurity pg_node_attr(query_jumble_ignore);
/* is a RETURN statement */
bool isReturn pg_node_attr(query_jumble_ignore);
List *cteList; /* WITH list (of CommonTableExpr's) */
List *rtable; /* list of range table entries */
/*
* list of RTEPermissionInfo nodes for the rtable entries having
* perminfoindex > 0
*/
List *rteperminfos pg_node_attr(query_jumble_ignore);
FromExpr *jointree; /* table join tree (FROM and WHERE clauses);
* also USING clause for MERGE */
List *mergeActionList; /* list of actions for MERGE (only) */
/* whether to use outer join */
bool mergeUseOuterJoin pg_node_attr(query_jumble_ignore);
List *targetList; /* target list (of TargetEntry) */
/* OVERRIDING clause */
OverridingKind override pg_node_attr(query_jumble_ignore);
OnConflictExpr *onConflict; /* ON CONFLICT DO [NOTHING | UPDATE] */
List *returningList; /* return-values list (of TargetEntry) */
List *groupClause; /* a list of SortGroupClause's */
bool groupDistinct; /* is the group by clause distinct? */
List *groupingSets; /* a list of GroupingSet's if present */
Node *havingQual; /* qualifications applied to groups */
List *windowClause; /* a list of WindowClause's */
List *distinctClause; /* a list of SortGroupClause's */
List *sortClause; /* a list of SortGroupClause's */
Node *limitOffset; /* # of result tuples to skip (int8 expr) */
Node *limitCount; /* # of result tuples to return (int8 expr) */
LimitOption limitOption; /* limit type */
List *rowMarks; /* a list of RowMarkClause's */
Node *setOperations; /* set-operation tree if this is top level of
* a UNION/INTERSECT/EXCEPT query */
/*
* A list of pg_constraint OIDs that the query depends on to be
* semantically valid
*/
List *constraintDeps pg_node_attr(query_jumble_ignore);
/* a list of WithCheckOption's (added during rewrite) */
List *withCheckOptions pg_node_attr(query_jumble_ignore);
/*
* The following two fields identify the portion of the source text string
* containing this query. They are typically only populated in top-level
* Queries, not in sub-queries. When not set, they might both be zero, or
* both be -1 meaning "unknown".
*/
/* start location, or -1 if unknown */
int stmt_location;
/* length in bytes; 0 means "rest of string" */
int stmt_len pg_node_attr(query_jumble_ignore);
} Query;
查询树的遍历
由于在PostgreSQL数据库中的所有节点都以类似于“类”的方式实现的,因此我们可以由基类Node的指针代表任何节点,并且能通过NodeTag快速的识别出当前Node的真实类型,这就为我们遍历查询树提供了便利,PostgreSQL数据库通过提供query_tree_mutator函数和query_tree_walker函数来对查询树进行遍历,实际上还需要借助expression_tree_mutator函数和expression_tree_walker函数来实现walker函数的主要作用是对查询树进行遍历并且可能会修改某个结构体的值,但他不增加或删除节点,如果要增加或删除查询树中的某个节点则应该使用mutator函数。