PostgreSQL Executor(1): Portal

参考:https://www.cnblogs.com/flying-tiger/p/6100794.html/

Portal的字面意是“入口”。在PostgreSQL中,Portal用来表示一个正在执行的或者是可执行的查询的执行状态。

Portal记录了与执行相关的所有信息,例如查询树、计划树和执行状态。确实可以将Portal看作执行查询计划的“入口”。

对于用户提交的普通查询语句,在服务端会创建一个匿名的Portal对象。而对于SQL中的游标声明语句,也会创建一个对应的Portal对象。

Portal的数据

执行器使用PortalData来存储查询计划树、结果元组的描述符(TupleDesc)、执行状态、查询结果等数据。PortalData结构体定义在src/include/utils/portal.h中。

typedef struct PortalData
{
    /* Bookkeeping data */
    const char *name;           /* portal's name */
    ......
    /* 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 */
    ......
    ParamListInfo portalParams; /* params to pass to query */

    /* Features/options */
    PortalStrategy strategy;    /* see above */

    /* 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 */
    ......
}   PortalData;

Portal的状态

Portal有六种状态,它们的定义如下:

/*
 * A portal is always in one of these states.  It is possible to transit
 * from ACTIVE back to READY if the query is not run to completion;
 * otherwise we never back up in status.
 */
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刚被创建时,状态为PORTAL_NEW

通过PortalDefineQuery将查询计划等数据设置到Portal上之后,Portal的状态变为PORTAL_DEFINED

通过PortalStart为执行做好准备之后,Portal的状态变为PORTAL_READY

PortalRun的开始,会将Portal的状态标记为PORTAL_ACTIVE。在PORTAL_MULTI_QUERY的执行策略下,在执行完成后会将Portal的状态变更为PORTAL_DONE,而在其它执行策略下,会将状态变更为PORTAL_READY

而在上述过程中发生错误,都会将Portal的状态设置为PORTAL_FAILED

可优化语句和不可优化语句

PostgreSQL将用户输入的SQL语句分为两类:可优化语句(Optimizable Statement)和不可优化语句(Non-optimizable Statement)。

可优化语句就是通常讲的DML语句,包括INSERT/DELETE/UPDATE/SELECT。这类语句都要查询到满足条件的远组并返回给用户。在查询优化阶段会根据查询优化理论进行重写和优化以提高查询效率,因此称为可优化语句。

不可优化语句包括DDL、DCL等语句,例如创建表、删除表、创建用户等。这类语句包含查询数据之外的各类操作,功能相对独立,因此也称为功能性语句。功能性语句没有优化的价值。

Portal的执行策略

Portal有五种执行策略:

typedef enum PortalStrategy
{
    PORTAL_ONE_SELECT,
    PORTAL_ONE_RETURNING,
    PORTAL_ONE_MOD_WITH,
    PORTAL_UTIL_SELECT,
    PORTAL_MULTI_QUERY
} PortalStrategy;

Portal采用哪种优化策略取决于执行的是什么样子的查询。需要注意的是,在用户角度看到的一个单一的查询语句经过查询重写之后可能会变成零个或多个实际的查询。

  • PORTAL_ONE_SELECT

    Portal只包含一个SELECT查询。因为需要查询结果,因此执行器被递增的执行。这个策略也支持holdable的游标(执行结果可以存储到tuplestore中,以便在事务完成后访问)。

  • PORTAL_ONE_RETURNING

    Portal包含一个带有RETURNING子句(另外也可能有查询重写增加的辅助查询)的INSERT/UPDATE/DELETE查询。在第一次执行时,Portal被执行完成,并将主查询的结果存储到Portal的tuplestore中,然后将结果返回给客户端。

  • PORTAL_ONE_MOD_WITH

    Portal包含一个SELECT查询,同时存在修改数据的CTE(Common Table Expression)。当前处理方式与PORTAL_ONE_RETURNING相同。

  • PORTAL_UTIL_SELECT

    Portal包含一个功能性(Utility)语句,返回类似于SELECT的结果(比如EXPLAIN和SHOW)。在第一次执行时,Portal被执行完成,并将主查询的结果存储到Portal的tuplestore中,然后将结果返回给客户端。

  • PORTAL_MULTI_QUERY

    包含所有其它情况。Portal不支持部分执行:在Portal第一次执行时完成所有的查询。

具体代码位于src/backend/tcop/pquery.c中函数ChoosePortalStrategy

Portal的执行

所有SQL语句的执行都必须从一个Portal开始。Portal的执行流程依次经历PortalStartPortalRunPortalDrop三个过程。每种执行策略都实现了单独的执行流程,会经历不同的处理过程。

所有流程都在exec_simple_query函数内部进行。Portal的执行流程如下:

  1. 调用函数CreatePortal创建一个干净的Portal,它的内存上下文、资源跟踪器清理函数都已经设置好,但是sourceText、stmts字段还未设置;

  2. 调用函数PortalDefineQuery为刚刚创建的Portal设置sourceText、stmt等,并且设置Portal的状态为PORTAL_DEFINED;

  3. 调用函数PortalStart对定义好的Portal进行初始化:

    1. 调用函数ChoosePortalStrategy为portal选择策略;

    2. 如果选择的是PORTAL_ONE_SELECT,则调用CreateQueryDesc为Portal创建查询描述符;

    3. 如果选择的是PORTAL_ONE_RETURNING或者PORTAL_ONE_MOD_WITH,则调用ExecCleanTypeFromTL为Portal创建返回元组的描述符;

    4. 对于PORTAL_UTIL_SELECT则调用UtilityTupleDescriptor为Portal创建查询描述符;

    5. 对于PORTAL_MULTI_QUERY这里则不做操作;

    6. 将Portal的状态设置为PORTAL_READY

  4. 调用函数PortalRun执行Portal,这就按照既定的策略调用相关执行部件执行Portal;

  5. 调用函数PortalDrop清理Portal,释放资源。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容