话说 查询“ select cname, comp from test1, test2 where test1.id=test2.id; ” 发送到服务器端, 走查询分支 exec_simple_query ,先调用 start_xact_command 初始化了事务管理相关对象和资源,接着调用 pg_parse_query ,通过 Lex 和 Yacc 对传入 SQL 语句进行词法语法解析,生成解析树。下来调用 GetTransactionSnapshot 方法做内存快照,然后调用 pg_analyze_and_rewrite 方法,进行语义分析把 parsetree 转换成 querytree ,然后对该 querytree 进行重写。接着调用 pg_plan_queries 方法,根据 querytree 做查询 规划,生成查询计划树 plantree 。然后调用了一系列方法 CreatePortal 、 PortalStart 、 PortalSetResultFormat 、 CreateDestReceiver 、 PortalRun 、 PortalDrop ,创建 portal 、初始化 portal 、 设置结果列格式、创建目的地接收者、运行portal执行查询并返回结果,然后释放 portal 相关资源,再调用 finish_xact_command 释放事务相关资源。这个例子终于执行完了。
1
下面是执行 plantree 的调用序列图。
执行 plantree 的调用序列图
上图红色方框中显示了执行 plantree 的过程,主要分为 portal 创建和初始化( CreatePortal 、 PortalStart )、执行( PortalRun )、释放资源( PortalDrop )三个部分。 portal 是个抽象概念,它表示一个正在运行或可运行 query 的执行状态。 Portal 支持 SQL 层的 CURSOR 和协议层的 portal 。从上图中可以看出,在各个部分, portal 调用了执行器 executor 的对应方法 Executor Start 、 Executor Run 、 Executor Drop 完成了相关操作。
简单描述一下执行过程,方法 CreatePortal 在内存上下文 PortalMemory 里创建 portal ,并在 portal 管理器 "Portal hash" 中注册(参见《 PostgreSQL 服务过程中的那些事一:启动 postgres 服务进程一 . 七:初始化 portal 管理环境》);调用 PortalDefineQuery 为新建的 portal 设置 sourceText (就是例子里的源 SQL 语句)、 stmts (就是上一节讨论的 plantree 列表)等字段,将 portal 状态设置为 PORTAL_DEFINED ;再调用 PortalStart 初始化 portal ,在该例子里选择 PORTAL_ONE_SELECT 策略,调用 CreateQueryDesc 方法,为 portal 创建查询描述符,将 portal 状态设置为 PORTAL_READY ;调用方法 PortalRun ,根据选择策略调用相应方法,根据 portal 相关由 plantree 转化来的各节点结构由下至上逐层进行处理,涉及扫描数据、进行投射、结果缓存等;最后调用 PortalDrop 释放 portal 相关资源。
portal 有多个执行策略,依赖于执行什么样的 query (其实就是根据不同的 SQL 语句调用不同的方法以完成相应操作)。(注意:在所有情况下,一个 portal 仅执行一个源 SQL query ,并且从用户的视点,仅产生一个结果。但是,规则重写器可以扩展一个源 query 成 0 或多个实际的 query 。)下面是 portal 的四种策略
PORTAL_ONE_SELECT :这个 portal 包含一个 SELECT 查询。我们增加运行一个执行器作为被要求的结果。这个策略还支持可持有的 cursor (为了事务结束后访问结果,执行器结果可以被转储到一个 tuplestore 里)。
PORTAL_ONE_RETURNING :这个 portal 包含一个伴有 RETURNING 子句的 INSERT/UPDATE/DELETE 查询(由规则重写器重写增加的可能的辅助查询 /query )。在第一次执行时,我们运行 portal 以完成和转储主 query 的结果到 portal 的 tuplestore 里;然后这个结果按要求返回给用户。(我们不支持 query 的部分遍历悬挂,因为 AFTER 触发器代码不能处理,并且还因为我们不想在执行所有辅助 query 时冒失败风险。)
PORTAL_ONE_MOD_WITH : portal 包含一个 SELECT qiery ,但它包含数据修改 CTE 。这个目前和处理 PORTAL_ONE_RETURNING 的情况一样,因为需要触发触发器的可能性。将来处理这种情况的行为可以更像 PORTAL_ONE_SELECT 。
PORTAL_UTIL_SELECT : portal 包含一个 utility 语句,其返回一个象 SELECT 那样的结果(例如, WXPLAIN 或者 SHOW )。在第一次执行时,我们运行这个语句任何转储其结果到 portal 的 tuplestore 里;然后这个结果按要求的返回给客户端。
PORTAL_MULTI_QUERY :所有其它情况。这儿,我们不支持 portal 执行: portal 的 query 会被运行以完成第一次调用。
下面是 portal 和其执行状态、策略的类型定义及执行器状态结构定义,其他涉及到节点结构定义略去。
typedef enum PortalStrategy
{
PORTAL_ONE_SELECT ,
PORTAL_ONE_RETURNING ,
PORTAL_ONE_MOD_WITH ,
PORTAL_UTIL_SELECT ,
PORTAL_MULTI_QUERY
} PortalStrategy ;
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 ;
typedef struct PortalData * Portal ;
typedef struct PortalData
{
/* Bookkeeping data */
const char * name ; /* portal's name */
const char * prepStmtName ; /* source prepared statement (NULL if none) */
MemoryContext heap ; /* subsidiary memory for portal */
ResourceOwner resowner ; /* resources owned by portal */
void (* cleanup ) ( Portal portal); /* cleanup hook */
SubTransactionId createSubid ; /* the ID of the creating subxact */
/*
* if createSubid is InvalidSubTransactionId, the portal is held over from
* a previous transaction
*/
/* The query or queries the portal will execute */
const char * sourceText ; /* text of query (as of 8.4, never NULL) */
const char * commandTag ; /* command tag for original query */
List * stmts ; /* PlannedStmts and/or utility statements */
CachedPlan * cplan ; /* CachedPlan, if stmts are from one */
ParamListInfo portalParams ; /* params to pass to query */
/* Features/options */
PortalStrategy strategy ; /* see above */
int cursorOptions ; /* DECLARE CURSOR option bits */
/* Status data */
PortalStatus status ; /* see above */
bool portalPinned ; /* a pinned portal can't be dropped */
/* If not NULL, Executor is active; call ExecutorEnd eventually: */
QueryDesc * queryDesc ; /* info needed for executor invocation */
/* If portal returns tuples, this is their tupdesc : */
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 */
/*
* Where we store tuples for a held cursor or a PORTAL_ONE_RETURNING or
* PORTAL_UTIL_SELECT query. (A cursor held past the end of its
* transaction no longer has any active executor state.)
*/
Tuplestorestate * holdStore ; /* store for holdable cursors */
MemoryContext holdContext ; /* memory containing holdStore */
/*
* atStart, atEnd and portalPos indicate the current cursor position.
* portalPos is zero before the first row, N after fetching N'th row of
* query. After we run off the end, portalPos = # of rows in query, and
* atEnd is true. If portalPos overflows, set posOverflow (this causes us
* to stop relying on its value for navigation). Note that atStart
* implies portalPos == 0, but not the reverse (portalPos could have
* overflowed).
*/
bool atStart ;
bool atEnd ;
bool posOverflow ;
long portalPos ;
/* Presentation data, primarily used by the pg_cursors system view */
TimestampTz creation_time ; /* time at which this portal was defined */
bool visible ; /* include this portal in pg_cursors? */
} PortalData ;
执行器调用的主工作状态
typedef struct EState
{
NodeTag type ;
/* Basic state for all query types: */
ScanDirection es_direction ; /* current scan direction */
Snapshot es_snapshot ; /* time qual to use */
Snapshot es_crosscheck_snapshot ; /* crosscheck time qual for RI */
List * es_range_table ; /* List of RangeTblEntry */
PlannedStmt * es_plannedstmt ; /* link to top of plan tree */
JunkFilter * es_junkFilter ; /* top-level junk filter, if any */
/* If query can insert/delete tuples, the command ID to mark them with */
CommandId es_output_cid ;
/* Info about target table(s) for insert/update/delete queries: */
ResultRelInfo * es_result_relations ; /* array of ResultRelInfos */
int es_num_result_relations ; /* length of array */
ResultRelInfo * es_result_relation_info ; /* currently active array elt */
/* Stuff used for firing triggers: */
List * es_trig_target_relations ; /* trigger-only ResultRelInfos */
TupleTableSlot * es_trig_tuple_slot ; /* for trigger output tuples */
TupleTableSlot * es_trig_oldtup_slot ; /* for TriggerEnabled */
/* Parameter info: */
ParamListInfo es_param_list_info ; /* values of external params */
ParamExecData * es_param_exec_vals ; /* values of internal params */
/* Other working state: */
MemoryContext es_query_cxt ; /* per-query context in which EState lives */
List * es_tupleTable ; /* List of TupleTableSlots */
List * es_rowMarks ; /* List of ExecRowMarks */
uint32 es_processed ; /* # of tuples processed */
Oid es_lastoid ; /* last oid processed (by INSERT) */
int es_top_eflags ; /* eflags passed to ExecutorStart */
int es_instrument ; /* OR of InstrumentOption flags */
bool es_select_into ; /* true if doing SELECT INTO */
bool es_into_oids ; /* true to generate OIDs in SELECT INTO */
bool es_finished ; /* true when ExecutorFinish is done */
List * es_exprcontexts ; /* List of ExprContexts within EState */
List * es_subplanstates ; /* List of PlanState for SubPlans */
List * es_auxmodifytables ; /* List of secondary ModifyTableStates */
/*
* this ExprContext is for per-output-tuple operations, such as constraint
* checks and index-value computations. It will be reset for each output
* tuple. Note that it will be created only if needed.
*/
ExprContext * es_per_tuple_exprcontext ;
/*
* These fields are for re-evaluating plan quals when an updated tuple is
* substituted in READ COMMITTED mode. es_epqTuple[] contains tuples that
* scan plan nodes should return instead of whatever they'd normally
* return, or NULL if nothing to return; es_epqTupleSet[] is true if a
* particular array entry is valid; and es_epqScanDone[] is state to
* remember if the tuple has been returned already. Arrays are of size
* list_length(es_range_table) and are indexed by scan node scanrelid - 1.
*/
HeapTuple * es_epqTuple ; /* array of EPQ substitute tuples */
bool * es_epqTupleSet ; /* true if EPQ tuple is provided */
bool * es_epqScanDone ; /* true if EPQ tuple has been fetched */
/*
* this field added at end of struct to avoid post-release ABI breakage in
* existing release branches. It'll be in a more logical place in 9.2.
*/
TupleTableSlot * es_trig_newtup_slot ; /* for TriggerEnabled */
} EState ;
下面是执行这个查询的 portal 相关内存结构图:
portal 相关内存结构图
就到这儿吧。
------------
转载请注明出处,来自博客:
blog.csdn.net/beiigang
beigang.iteye.com