这次要解读的查询语句如下:
select * from classes;
执行
建表语句
create table classes (
id integer primary key not null,
name varchar(30) not null
);
数据
-- classes
0, 'A'
1, 'B'
2, 'C'
3, 'D'
查询结果
id | name
----+------
0 | 'A'
1 | 'B'
2 | 'C'
3 | 'D'
(4 rows)
跟源码
主流程
程序入口exec_simple_query,此处query_string就是我们输入的查询命令,MessageType为QUERY_MESSAGE。
exec_simple_query (query_string=0x7f311b82c060 "select * from classes;", messageType=QUERY_MESSAGE, msg=0x7f3124c40a90)
exec_simple_query函数的主流程如下:
static void exec_simple_query(const char* query_string, MessageType messageType, StringInfo msg = NULL)
{
...
// 报告后端线程正在处理查询语句
pgstat_report_activity(STATE_RUNNING, query_string);
...
// 开启事务
start_xact_command();
...
// SQL解析
parsetree_list = pg_parse_query(reparsed_query.empty() ?
query_string : reparsed_query.c_str(), &query_string_locationlist);
...
/*
* Run through the raw parsetree(s) and process each one.
*/
// 遍历parsetree_list
foreach (parsetree_item, parsetree_list) {
...
Node* parsetree = (Node*)lfirst(parsetree_item);
...
// 操作类型,当前为"SELECT"
commandTag = CreateCommandTag(parsetree);
...
/* Make sure we are in a transaction command */
start_xact_command();
...
/*
* Set up a snapshot if parse analysis/planning will need one.
*/
// 设置快照
if (analyze_requires_snapshot(parsetree)) {
PushActiveSnapshot(GetTransactionSnapshot());
snapshot_set = true;
}
...
// 分析解析树转换为查询树并重写查询树
querytree_list = pg_analyze_and_rewrite(parsetree, query_string, NULL, 0);
...
// 生成计划树
plantree_list = pg_plan_queries(querytree_list, 0, NULL);
...
// 创建未命令的portal来运行查询
portal = CreatePortal("", true, true);
...
// 启动portal
PortalStart(portal, NULL, 0, InvalidSnapshot);
...
// 运行portal,然后删除它及receiver
(void)PortalRun(portal, FETCH_ALL, isTopLevel, receiver, receiver, completionTag);
(*receiver->rDestroy)(receiver);
PortalDrop(portal, false);
...
// 事务提交
finish_xact_command();
...
// 命令完成
EndCommand(completionTag, dest);
...
}
}
查询编译
除开事务部分,这个函数又可以分为查询编译部分和查询执行部分。查询编译部分的两个主要函数如下:
parsetree_list = pg_parse_query(); // 词法分析和语法分析
querytree_list = pg_analyze_and_rewrite(parsetree, query_string, NULL, 0); // 语义分析和重写
pg_parse_query()会在内部传递sql_query_string给raw_parse()函数,由raw_parse()函数调用base__yyparse进行词法分析和语法分析,生成语法树添加到链表parsetree_list中。生成的分析树会传给pg_analyze_and_rewrite()函数进行语义分析和重写,其中pg_analyze_and_rewrite()会调用parse_analyze()函数进行语义分析并生成查询树,用query结构体表示。之后会将查询树传递给函数pg_write_query()进行查询重写。
在上述的此法分析和语法分析阶段。最终会生成一个List结构,包含了所有的语法分析树,该lList中的每个ListCell包含一棵语法分析树。这个结构将会用于后续的语义分析。
语义分析的关键结构是ParseState,其保存了很多语义分析的中间信息。结构如下:
struct ParseState {
struct ParseState* parentParseState; /* 指向外层查询 */
const char* p_sourcetext; /* 原始sql命令 */
List* p_rtable; /* 范围表 */
List* p_joinexprs; /* 连接表达式 */
List* p_joinlist; /* 连接项 */
List* p_relnamespace; /* 表名集合 */
List* p_varnamespace; /* 属性名集合 */
bool p_lateral_active;
List* p_ctenamespace; /* 公共表达式集合 */
List* p_future_ctes; /* 不在p_ctenamespace中的公共表达式 */
CommonTableExpr* p_parent_cte;
List* p_windowdefs; /* window子句的原始定义 */
ParseExprKind p_expr_kind;
List* p_rawdefaultlist;
int p_next_resno; /* 下一个分配给目标属性的资源号 */
List* p_locking_clause; /* 原始的for update/for share信息 */
Node* p_value_substitute;
bool p_hasAggs; /* 是否有聚集函数 */
bool p_hasWindowFuncs; /* 是否有窗口函数 */
bool p_hasSubLinks; /* 是否有自链接 */
bool p_hasModifyingCTE;
bool p_is_insert; /* 是否为insert语句 */
bool p_locked_from_parent;
bool p_resolve_unknowns; /* resolve unknown-type SELECT outputs as type text */
bool p_hasSynonyms;
Relation p_target_relation;
RangeTblEntry* p_target_rangetblentry; // 目标表在range_table对应的项
...
在parse_analyze函数中生成ParseState结构,然后传递到transformTopLevelStmt()函数中,transformTopLevelStmt会继续传递其到transformStmt()函数中进行赋值。在transformStmt()函数中,会判断parseTree的类型,然后传递给各个专有的transform函数进行赋值。这里的select语句就会进入这个分支。
case T_SelectStmt: {
SelectStmt* n = (SelectStmt*)parseTree;
if (n->valuesLists) {
result = transformValuesClause(pstate, n);
} else if (n->op == SETOP_NONE) {
if (analyzerRoutineHook == NULL || analyzerRoutineHook->transSelect == NULL) {
result = transformSelectStmt(pstate, n, isFirstNode, isCreateView);
} else {
result = analyzerRoutineHook->transSelect(pstate, n, isFirstNode, isCreateView);
}
} else {
result = transformSetOperationStmt(pstate, n);
}
};
这里select有三种处理函数:
transformValuesClause(); // 处理带有select value的语句的语义
transformSelectStmt(); // 处理基本的select语句的语义
transformSetOperationStmt(); // 处理带有union、intersect、except的select语句的语义
这里因为执行的sql语句没有values,所以会进入transformSelectStmt进行处理。
transformSelectStmt (pstate=0x7efc25f94060, stmt=0x7efc26a7d7f8, isFirstNode=true, isCreateView=false) at analyze.cpp:2165
主要流程如下:
这样查询的结果会保存在Query结构体中,然后返回给exec_simple_query函数。
Query结构体如下所示:
typedef struct Query {
NodeTag type;
CmdType commandType; /* 命令类型 */
QuerySource querySource; /* 查询来源 */
uint64 queryId; /* 查询树的识别符 */
bool canSetTag; /* 如果是原始查询则为false,如果是查询重写或者是查询规划新增则为true */
Node* utilityStmt; /* 定义游标或者不可优化的查询语句 */
int resultRelation; /* 结果关系 */
bool hasAggs; /* 目标属性或者having子句是否有聚集函数 */
bool hasWindowFuncs; /* 目标属性中是否有窗口函数 */
bool hasSubLinks; /* 是否有子查询 */
bool hasDistinctOn; /* 是否有distinct子句 */
bool hasRecursive; /* 公共表达式是否允许递归 */
bool hasModifyingCTE; /* with 子句是否包含insert/update/delete */
bool hasForUpdate; /* 是否有for update或者for share子句 */
bool hasRowSecurity; /* 重写是否应用行级访问控制 */
bool hasSynonyms; /* 范围表是否有同义词 */
List* cteList; /* with子句,用于公共表达式 */
List* rtable; /* 范围表 */
FromExpr* jointree; /* 连接树,描述from和where子句出现的连接 */
List* targetList; /* 目标属性 */
List* starStart; /* Corresponding p_star_start in ParseState */
List* starEnd; /* Corresponding p_star_end in ParseState */
List* starOnly; /* Corresponding p_star_only in ParseState */
List* returningList; /* return-values list (of TargetEntry) */
List* groupClause; /* a list of SortGroupClause's */
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) */
List* rowMarks; /* a list of RowMarkClause's */
Node* setOperations; /* set-operation tree if this is top level of
* a UNION/INTERSECT/EXCEPT query */
List *constraintDeps; /* a list of pg_constraint OIDs that the query
* depends on to be semantically valid */
HintState* hintState;
...
Query会传递给优化器产生进行重写优化。
List* QueryRewrite(Query* parsetree)
{
...
/*
* Step 1
*
* Apply all non-SELECT rules possibly getting 0 or many queries
*/
// 应用所有non-SELECT重写
querylist = RewriteQuery(parsetree, NIL);
/*
* Step 2
*
* Apply all the RIR rules on each query
*
* This is also a handy place to mark each query with the original queryId
*/
// 应用所有RIR规则
results = NIL;
foreach (l, querylist) {
Query* query = (Query*)lfirst(l);
query = fireRIRrules(query, NIL, false);
query->queryId = input_query_id;
results = lappend(results, query);
}
...
return results;
}
计划树的结构如下:
typedef struct Plan {
NodeTag type;
int plan_node_id; /* node id */
int parent_node_id; /* parent node id */
RemoteQueryExecType exec_type;
/*
* estimated execution costs for plan (see costsize.c for more info)
*/
Cost startup_cost; /* cost expended before fetching any tuples */
Cost total_cost; /* total cost (assuming all tuples fetched) */
/*
* planner's estimate of result size of this plan step
*/
double plan_rows; /* number of global rows plan is expected to emit */
double multiple;
int plan_width; /* average row width in bytes */
int dop; /* degree of parallelism of current plan */
/*
* machine learning model estimations
*/
double pred_rows;
double pred_startup_time;
double pred_total_time;
long pred_max_memory;
...
查询执行
执行器总体执行流程包括策略选择模块,Portal、执行组件executor和ProcessUtility。
Portal是执行SQL语句的载体,每条sql语句对应唯一的portal,不同的查询类型对应的portal类型也有区别。
回到exec_simple_query函数,已经生成了计划树plantree_list。
先调用CreatePortal创建Portal。
portal = CreatePortal("", true, true);
/* Don't display the portal in pg_cursors */
portal->visible = false;
Portal执行流程。
其中左边的分支为执行非DDL语句,右边的分支为执行DDL语句。