1. 查询基本流程
1.PLSQL语句,string
2.生成语法树,parseTree
3.生成查询树,queryTree
4.生成优化查询树列表,queryTreeList
5.生成计划语句树,planStmtTree
6.生成计划执行状态树,planStateTree
2. 各个结构切换源码分析
2.1 把用户输入的string的SQL语句转换成原始语法树(parseTree)。
例如:insert into test values(1, ‘xxxx’); 是用户发过来的,由postgres服务进程通过网络获取到,如果调试的话可以从函数exec_simple_query()开始。SQL语句到语法树的转换是函数raw_parser()完成的,返回一个语法树list。
List *
raw_parser(const char *str)
{
core_yyscan_t yyscanner;
base_yy_extra_type yyextra;
int yyresult;
/* 初始化flex扫描器 */
yyscanner = scanner_init(str, &yyextra.core_yy_extra,
ScanKeywords, NumScanKeywords);
/* base_yylex() only needs this much initialization */
yyextra.have_lookahead = false;
/* 初始化bison解析器 */
parser_init(&yyextra);
/* 执行语法分析 */
yyresult = base_yyparse(yyscanner);
/* 解析完成后调用,在scanner_init()之后需要进行清理 */
scanner_finish(yyscanner);
if (yyresult) /* error */
return NIL;
/* 返回原始语法树列表, list里存储的类型是struct RawStmt */
return yyextra.parsetree;
}
返回的list其实体是struct RawStmt结构体, 在文件src\include\nodes\parsenodes.h中定义。在该文件中所有*Stmt结构体都是以NodeTag为基类,用来标识其节点类型。
typedef struct RawStmt
{
NodeTag type;
Node *stmt; /* raw parse tree */
int stmt_location; /* start location, or -1 if unknown */
int stmt_len; /* length in bytes; 0 means "rest of string" */
} RawStmt;
例如我们举例的insert语句的结构体如下, 他的type类型是T_InsertStmt,该属性用来在后面把语法树转化成查询树时使用,整个转换过程通过Node类型来指导执行逻辑。
typedef struct InsertStmt
{
NodeTag type;
RangeVar *relation; /* relation to insert into */
List *cols; /* optional: names of the target columns */
Node *selectStmt; /* the source SELECT/VALUES, or NULL */
OnConflictClause *onConflictClause; /* ON CONFLICT clause */
List *returningList; /* list of expressions to return */
WithClause *withClause; /* WITH clause */
OverridingKind override; /* OVERRIDING clause */
} InsertStmt;
2.2 原始语法树转换为查询树,queryTree
- 整个过程可以分成两个步骤,语法树到查询树和查询重写。
完成转换后生成查询树结构Query,如下。语法树转查询树的入口函数是transformTopLevelStmt(),该函数根据语法树,实施深度优先遍历所有节点。最后创建一个Query对象,整个转换过程就是填充Query结构中的各个成员变量,例如成员变量targetList,如果的我们语句是:”INSERT INTO p4(num, name) VALUES(nums[1], names[1])”, 其中nums、names都是数组,那么targetList保存的就是如何访问这两个数组对象的方式。
List cteList; / WITH list (of CommonTableExpr’s) */
上面的是关于with as语句的相关信息,如果我们要增加一个新的语法特性,就需要在这里增加一个特性的相关属性。
typedef struct Query
{
NodeTag type;
CmdType commandType; /* select|insert|update|delete|utility */
QuerySource querySource; /* where did I come from? */
List *targetList; /* target list (of TargetEntry) */
List *cteList; /* WITH list (of CommonTableExpr's) */
后面省略
} Query;
- 查询重写过程
入口是pg_rewrite_query(query),参数就是上面返回的Query指针。具体实施是在QueryRewrite()->RewriteQuery()函数,返回一个QueryList。
if (query->commandType == CMD_UTILITY)
{
/* don't rewrite utilities, just dump 'em into result list */
querytree_list = list_make1(query);
}
else
{
/* rewrite regular queries */
querytree_list = QueryRewrite(query);
}
2.3 查询树转计划语句树,planStmtTree
循环查询树List,调用pg_plan_query()把查询树转换成PlannedStmt(数据结构往下看)。
/*
* 为已经重写的查询树列表生成计划树。
* 正常可优化的语句在结果列表中生成PlannedStmt。实用语句仅由它们的语句节点表示。
* 所有的语句解析完成,return stmt_list。
*/
List *
pg_plan_queries(List *querytrees, int cursorOptions, ParamListInfo boundParams)
{
List *stmt_list = NIL;
ListCell *query_list;
foreach(query_list, querytrees)
{
Query *query = lfirst_node(Query, query_list);
PlannedStmt *stmt;
if (query->commandType == CMD_UTILITY)
{
/* Utility commands require no planning. */
stmt = makeNode(PlannedStmt);
stmt->commandType = CMD_UTILITY;
stmt->canSetTag = query->canSetTag;
stmt->utilityStmt = query->utilityStmt;
stmt->stmt_location = query->stmt_location;
stmt->stmt_len = query->stmt_len;
}
else
{
stmt = pg_plan_query(query, cursorOptions, boundParams);
}
stmt_list = lappend(stmt_list, stmt);
}
return stmt_list;
}
typedef struct PlannedStmt
typedef struct PlannedStmt
{
NodeTag type;
CmdType commandType; /* select|insert|update|delete|utility */
uint64 queryId; /* query identifier (copied from Query) */
bool hasReturning; /* is it insert|update|delete RETURNING? */
bool hasModifyingCTE; /* has insert|update|delete in WITH? */
bool canSetTag; /* do I set the command result tag? */
bool transientPlan; /* redo plan when TransactionXmin changes? */
bool dependsOnRole; /* is plan specific to current role? */
bool parallelModeNeeded; /* parallel mode required to execute? */
int jitFlags; /* which forms of JIT should be performed */
struct Plan *planTree; /* tree of Plan nodes */
List *rtable; /* list of RangeTblEntry nodes */
/* rtable indexes of target relations for INSERT/UPDATE/DELETE */
List *resultRelations; /* integer list of RT indexes, or NIL */
/*
* rtable indexes of partitioned table roots that are UPDATE/DELETE
* targets; needed for trigger firing.
*/
List *rootResultRelations;
List *subplans; /* Plan trees for SubPlan expressions; note
* that some could be NULL */
Bitmapset *rewindPlanIDs; /* indices of subplans that require REWIND */
List *rowMarks; /* a list of PlanRowMark's */
List *relationOids; /* OIDs of relations the plan depends on */
List *invalItems; /* other dependencies, as PlanInvalItems */
List *paramExecTypes; /* type OIDs for PARAM_EXEC Params */
Node *utilityStmt; /* non-null if this is utility stmt */
/* statement location in source string (copied from Query) */
int stmt_location; /* start location, or -1 if unknown */
int stmt_len; /* length in bytes; 0 means "rest of string" */
} PlannedStmt;
2.4 计划语句树转计划执行状态树,planStateTree
转换后的结果放到queryDesc->estate->es_subplanstates中。在执行器执行过程中,便是依据es_subplanstates链表和树形结构进行执行。
static void
InitPlan(QueryDesc *queryDesc, int eflags)
{
CmdType operation = queryDesc->operation;
PlannedStmt *plannedstmt = queryDesc->plannedstmt;
Plan *plan = plannedstmt->planTree;
List *rangeTable = plannedstmt->rtable;
EState *estate = queryDesc->estate;
PlanState *planstate;
TupleDesc tupType;
ListCell *l;
int i;
// 把所有子查询生成PlanState,放到estate->es_subplanstates列表中
foreach(l, plannedstmt->subplans)
{
Plan *subplan = (Plan *) lfirst(l);
PlanState *subplanstate;
int sp_eflags;
/*
* A subplan will never need to do BACKWARD scan nor MARK/RESTORE. If
* it is a parameterless subplan (not initplan), we suggest that it be
* prepared to handle REWIND efficiently; otherwise there is no need.
*/
sp_eflags = eflags
& (EXEC_FLAG_EXPLAIN_ONLY | EXEC_FLAG_WITH_NO_DATA);
if (bms_is_member(i, plannedstmt->rewindPlanIDs))
sp_eflags |= EXEC_FLAG_REWIND;
subplanstate = ExecInitNode(subplan, estate, sp_eflags);
estate->es_subplanstates = lappend(estate->es_subplanstates,
subplanstate);
i++;
}
发现更多宝藏
我在喜马拉雅上分享声音
《PostgreSQL数据库内核分析》,点开链接可以听听,有点意思。
《数据库系统概论(第4版)》,点开链接可以听听,有点意思。
其他相关文章分享列表:
第 23 课 PostgreSQL 创建自己的数据库、模式、用户
第 22 课 PostgreSQL 控制文件
第 21 课 PostgreSQL 日志系统
第 16 课 查询过程源码分析
第 15 课 PostgreSQL 系统参数配置
第 14 课 PostgreSQL 数据存储结构
第 13 课 PostgreSQL 存储之Page(页面)源码分析
第 12 课 PostgreSQL 认证方式
第 11 课 PostgreSQL 增加一个内核C函数
第 10 课 PostgreSQL 在内核增加一个配置参数
第 09 课 PostgreSQL 4种进程启动方式
第 08 课 PostgreSQL 事务介绍
第 07 课 PostgreSQL 数据库、模式、表、空间、用户间的关系
第 06 课 PostgreSQL 系统表介绍
第 05 课 PostgreSQL 编译源代码进行开发
第 04 课 PostgreSQL 安装最新的版本
第 03 课 PostgreSQL 代码结构
第 02 课 PostgreSQL 的特性、应用、安装
第 01 课 PostgreSQL 简介及发展历程
上面文章都在专辑中:PostgreSQL专辑链接,点我查看
如果有用,可以收藏这篇文件,随时在更新….
更多交流加群: PostgreSQL内核开发群 876673220
亲,记得点赞、留言、打赏额!!!