相关 《Postgresql源码(61)查询执行——最外层Portal模块》 《Postgresql源码(62)查询执行——子模块ProcessUtility》
1 背景
- 本篇介绍查询执行最外面一层:portal模块。部分摘自《PostgreSQL数据库内核分析》。
- 按照最近读代码的线索,后面几篇侧重分析下查询执行的架子,下一篇ProcessUtility。
- 查询执行在查询编译后面执行,负责执行具体的SQL 或 按前一阶段生成的计划来执行具体的PLAN。
PG14中截取部分SQL语法:https://www.postgresql.org/docs/14/sql-commands.html
代码语言:javascript复制ABORT — abort the current transaction
...
ALTER INDEX — change the definition of an index
...
ALTER TABLE — change the definition of a table
ALTER TABLESPACE — change the definition of a tablespace
...
BEGIN — start a transaction block
CALL — invoke a procedure
CHECKPOINT — force a write-ahead log checkpoint
CLOSE — close a cursor
...
CREATE FOREIGN DATA WRAPPER — define a new foreign-data wrapper
CREATE FOREIGN TABLE — define a new foreign table
CREATE FUNCTION — define a new function
...
CREATE TABLE — define a new table
CREATE TABLE AS — define a new table from the results of a query
CREATE TABLESPACE — define a new tablespace
...
DELETE — delete rows of a table
...
EXPLAIN — show the execution plan of a statement
...
REVOKE — remove access privileges
...
VACUUM — garbage-collect and optionally analyze a database
...
大致看过可以发现:
- 大部分SQL语句如创建表、启动事务等,会提供单一的、具体的某个功能点,这类功能无法被优化器优化,执行过程固定,不会有变化;
- 另外一类SQL如增删改查,这类SQL可以被优化器优化,定制执行计划,执行过程会有不同,完全跟着执行计划来。
如果我们自己来设计这个系统,应该也会把SQL执行分成两类,带执行计划的DML(增删改查)不带执行计划的DDL。
2 查询执行整体
PG中的SQL在经过语法解析、查询编译后,进入执行模块,整形模块的分三个子模块:
- 入口:portal子模块(下图蓝色)
- 处理DML的Executor子模块(下图绿色)
- 处理DDL的ProcessUtility子模块(下图橙色)
SQL会在查询编译阶段得到plantree_list,在portal模块启动时(函数PortalStart),根据plantree_list中具体情况(函数ChoosePortalStrategy),来决定PortalStrategy的值,后面执行根据PortalStrategy来决定进入Executor还是ProcessUtility。
3 portal工作流程
3.1 portal关键数据结构
一、portal状态
代码语言:javascript复制typedef enum PortalStatus
{
PORTAL_NEW, /* freshly created */
PORTAL_DEFINED, /* PortalDefineQuery done */
PORTAL_READY, /* PortalStart complete, can run it */
PORTAL_ACTIVE, /* portal is running (can't delete it) */
PORTAL_DONE, /* portal is finished (don't re-run it) */
PORTAL_FAILED /* portal got error (can't re-run it) */
} PortalStatus;
二、portal结构
关键变量:
- 【0】SQL语句
- 【1】注意:执行计划树链表
- 【2】根绝策略决定走Executor还是ProcessUtility
- 【3】当前portal状态
- 【4】Executor执行需要的查询描述符
- 【5】描述返回的元组结构
-
typedef struct PortalData
{
/* Bookkeeping data */
const char *name; /* portal's name */
const char *prepStmtName; /* source prepared statement (NULL if none) */
MemoryContext portalContext; /* subsidiary memory for portal */
...
// 【0】SQL语句
const char *sourceText; /* text of query (as of 8.4, never NULL) */
CommandTag commandTag; /* command tag for original query */
QueryCompletion qc; /* command completion data for executed query */
// 【1】注意:执行计划树链表
List *stmts; /* list of PlannedStmts */
CachedPlan *cplan; /* CachedPlan, if stmts are from one */
ParamListInfo portalParams; /* params to pass to query */
QueryEnvironment *queryEnv; /* environment for query */
// 【2】根绝策略决定走Executor还是ProcessUtility
PortalStrategy strategy; /* see above */
int cursorOptions; /* DECLARE CURSOR option bits */
bool run_once; /* portal will only be run once */
// 【3】当前portal状态
PortalStatus status; /* see above */
...
// 【4】Executor执行需要的查询描述符
QueryDesc *queryDesc; /* info needed for executor invocation */
// 【5】描述返回的元组结构
TupleDesc tupDesc; /* descriptor for result tuples */
/* and these are the format codes to use for the columns: */
int16 *formats; /* a format code for each column */
...
} PortalData;
from 《PostgreSQL数据库内核分析》
3.2 portal执行流程
简版:
代码语言:javascript复制exec_simple_query
|
PortalStart
|
PortalRun
|
PortalDrop
PortalStart
执行一些初始化工作,比如
- 选择执行策略:ChoosePortalStrategy
- 如果要走Executor了,就先执行下ExecutorStart初始化Executor。
PortalStart
portal->strategy = ChoosePortalStrategy(portal->stmts)
switch (portal->strategy)
{
case PORTAL_ONE_SELECT:
// 拿快照
PushActiveSnapshot
// 创建查询描述符
CreateQueryDesc
// 初始化Executor
ExecutorStart
case PORTAL_ONE_RETURNING:
case PORTAL_ONE_MOD_WITH:
PortalGetPrimaryStmt
ExecCleanTypeFromTL
case PORTAL_UTIL_SELECT:
PortalGetPrimaryStmt
UtilityTupleDescriptor
case PORTAL_MULTI_QUERY:
portal->tupDesc = NULL
}
portal->status = PORTAL_READY;
其中执行策略的选择ChoosePortalStrategy:
PortalRun
PortalRun是一级portal执行函数,负责分发给二级portal执行函数
代码语言:javascript复制PortalRun
switch (portal->strategy)
case PORTAL_ONE_SELECT:
case PORTAL_ONE_RETURNING:
case PORTAL_ONE_MOD_WITH:
case PORTAL_UTIL_SELECT:
PortalRunSelect
case PORTAL_MULTI_QUERY:
PortalRunMulti
PortalRun二级执行函数有四个,其中两个从PortalRun调入
代码语言:javascript复制PortalRunSelect <-- PortalRun
PortalRunMulti <-- PortalRun
PortalRun三级执行函数有两个,从PortalRunMulti调入
代码语言:javascript复制PortalRunUtility <-- PortalRunMulti
PortalRunFetch <-- (游标专用)从SPI系统_SPI_cursor_operation
<-- 或 standard_ProcessUtility的PerformPortalFetch 调入
执行过程:
PortalDrop
清理
代码语言:javascript复制PortalDrop
PortalHashTableDelete
PortalReleaseCachedPlan
ResourceOwnerRelease
...
MemoryContextDelete(portal->portalContext)
pfree(portal)