kaywang 发表于 2016-11-21 07:45:53

PostgreSQL服务过程中的那些事二:Pg服务进程处理简单查询六:执行器执行

       话说查询“select cname, comp from test1, test2 wheretest1.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的对应方法ExecutorStart、ExecutorRun、ExecutorDrop完成了相关操作。
简单描述一下执行过程,方法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 (NULLif 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*/
 
    /*
     * ifcreateSubid is InvalidSubTransactionId, the portal is held over from
     * aprevious transaction
     */
 
    /* The query or queries the portal will execute */
    const char *sourceText;     /* text of query (as of 8.4, neverNULL) */
    const char *commandTag;     /* command tag for original query*/
    List      *stmts;          /* PlannedStmts and/or utilitystatements */
    CachedPlan *cplan;          /* CachedPlan, if stmtsare from one */
 
    ParamListInfo portalParams; /* paramsto 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 bedropped */
 
    /* If not NULL, Executor is active; call ExecutorEndeventually: */
    QueryDesc  *queryDesc;      /* info needed for executorinvocation */
 
    /* 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 heldpast the end of its
     *transaction no longer has any active executor state.)
     */
    Tuplestorestate *holdStore; /* store for holdablecursors */
    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
     * tostop 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_cursorssystem view */
    TimestampTz creation_time;  /* time at which this portal wasdefined */
    bool        visible;        /* include this portal inpg_cursors? */
}   PortalData;
 
执行器调用的主工作状态
typedef struct EState
{
    NodeTag     type;
 
    /* Basic state for all query types: */
    ScanDirection es_direction; /* current scan direction */
    Snapshot    es_snapshot;    /* time qualto use */
    Snapshot    es_crosscheck_snapshot; /* crosschecktime qualforRI */
    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 tomark them with */
    CommandId   es_output_cid;
 
    /* Info about target table(s) for insert/update/deletequeries: */
    ResultRelInfo *es_result_relations; /* array ofResultRelInfos */
    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; /* fortrigger 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 whichEState lives */
 
    List      *es_tupleTable;  /* List of TupleTableSlots */
 
    List      *es_rowMarks; /* List of ExecRowMarks */
 
    uint32      es_processed;   /* # of tuples processed */
    Oid         es_lastoid;     /* last oidprocessed (byINSERT) */
 
    int         es_top_eflags;  /* eflagspassed toExecutorStart */
    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 SELECTINTO */
    bool        es_finished;    /* true when ExecutorFinish isdone */
 
    List      *es_exprcontexts; /* List of ExprContexts withinEState */
 
    List      *es_subplanstates;       /* List of PlanState for SubPlans*/
 
    List      *es_auxmodifytables;     /* List of secondaryModifyTableStates */
 
    /*
     *this ExprContext is for per-output-tuple operations, such as constraint
     *checks and index-value computations.  Itwill be reset for each output
     *tuple.  Note that it will be created onlyif needed.
     */
    ExprContext *es_per_tuple_exprcontext;
 
    /*
     *These fields are for re-evaluating plan qualswhen 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 structto avoid post-release ABI breakage in
     *existing release branches.  It'll be in amore logical place in 9.2.
     */
    TupleTableSlot *es_trig_newtup_slot;        /* for TriggerEnabled */
} EState;
 
         下面是执行这个查询的portal相关内存结构图:
 

portal相关内存结构图
 
 
就到这儿吧。
 




------------
转载请注明出处,来自博客:
blog.csdn.net/beiigang
beigang.iyunv.com

 
页: [1]
查看完整版本: PostgreSQL服务过程中的那些事二:Pg服务进程处理简单查询六:执行器执行