博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
PostgreSQL在何处处理 sql查询之三十三
阅读量:7005 次
发布时间:2019-06-27

本文共 15240 字,大约阅读时间需要 50 分钟。

接前面,在 PortalStart 中调用了 ExecutorStart,ExecutorStart 会调用 InitPlan:

/* ---------------------------------------------------------------- *        InitPlan * *        Initializes the query plan: open files, allocate storage *        and start up the rule manager * ---------------------------------------------------------------- */static voidInitPlan(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;    /*     * Do permissions checks     */    ExecCheckRTPerms(rangeTable, true);    /*     * initialize the node's execution state     */    estate->es_range_table = rangeTable;    estate->es_plannedstmt = plannedstmt;    /*     * initialize result relation stuff, and open/lock the result rels.     *     * We must do this before initializing the plan tree, else we might try to     * do a lock upgrade if a result rel is also a source rel.     */    if (plannedstmt->resultRelations)    {        List       *resultRelations = plannedstmt->resultRelations;        int            numResultRelations = list_length(resultRelations);        ResultRelInfo *resultRelInfos;        ResultRelInfo *resultRelInfo;        resultRelInfos = (ResultRelInfo *)            palloc(numResultRelations * sizeof(ResultRelInfo));        resultRelInfo = resultRelInfos;        foreach(l, resultRelations)        {            Index        resultRelationIndex = lfirst_int(l);            Oid            resultRelationOid;            Relation    resultRelation;            resultRelationOid = getrelid(resultRelationIndex, rangeTable);            resultRelation = heap_open(resultRelationOid, RowExclusiveLock);            InitResultRelInfo(resultRelInfo,                              resultRelation,                              resultRelationIndex,                              estate->es_instrument);            resultRelInfo++;        }        estate->es_result_relations = resultRelInfos;        estate->es_num_result_relations = numResultRelations;        /* es_result_relation_info is NULL except when within ModifyTable */        estate->es_result_relation_info = NULL;    }    else    {        /*         * if no result relation, then set state appropriately         */        estate->es_result_relations = NULL;        estate->es_num_result_relations = 0;        estate->es_result_relation_info = NULL;    }    /*     * Similarly, we have to lock relations selected FOR UPDATE/FOR SHARE     * before we initialize the plan tree, else we'd be risking lock upgrades.     * While we are at it, build the ExecRowMark list.     */    estate->es_rowMarks = NIL;    foreach(l, plannedstmt->rowMarks)    {        PlanRowMark *rc = (PlanRowMark *) lfirst(l);        Oid            relid;        Relation    relation;        ExecRowMark *erm;        /* ignore "parent" rowmarks; they are irrelevant at runtime */        if (rc->isParent)            continue;        switch (rc->markType)        {            case ROW_MARK_EXCLUSIVE:            case ROW_MARK_SHARE:                relid = getrelid(rc->rti, rangeTable);                relation = heap_open(relid, RowShareLock);                break;            case ROW_MARK_REFERENCE:                relid = getrelid(rc->rti, rangeTable);                relation = heap_open(relid, AccessShareLock);                break;            case ROW_MARK_COPY:                /* there's no real table here ... */                relation = NULL;                break;            default:                elog(ERROR, "unrecognized markType: %d", rc->markType);                relation = NULL;    /* keep compiler quiet */                break;        }        /* Check that relation is a legal target for marking */        if (relation)            CheckValidRowMarkRel(relation, rc->markType);        erm = (ExecRowMark *) palloc(sizeof(ExecRowMark));        erm->relation = relation;        erm->rti = rc->rti;        erm->prti = rc->prti;        erm->rowmarkId = rc->rowmarkId;        erm->markType = rc->markType;        erm->noWait = rc->noWait;        ItemPointerSetInvalid(&(erm->curCtid));        estate->es_rowMarks = lappend(estate->es_rowMarks, erm);    }    /*     * Initialize the executor's tuple table to empty.     */    estate->es_tupleTable = NIL;    estate->es_trig_tuple_slot = NULL;    estate->es_trig_oldtup_slot = NULL;    estate->es_trig_newtup_slot = NULL;    /* mark EvalPlanQual not active */    estate->es_epqTuple = NULL;    estate->es_epqTupleSet = NULL;    estate->es_epqScanDone = NULL;    /*     * Initialize private state information for each SubPlan.  We must do this     * before running ExecInitNode on the main query tree, since     * ExecInitSubPlan expects to be able to find these entries.     */    Assert(estate->es_subplanstates == NIL);    i = 1;                        /* subplan indices count from 1 */    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;        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++;    }    /*     * Initialize the private state information for all the nodes in the query     * tree.  This opens files, allocates storage and leaves us ready to start     * processing tuples.     */    planstate = ExecInitNode(plan, estate, eflags);    /*     * Get the tuple descriptor describing the type of tuples to return.     */    tupType = ExecGetResultType(planstate);    /*     * Initialize the junk filter if needed.  SELECT queries need a filter if     * there are any junk attrs in the top-level tlist.     */    if (operation == CMD_SELECT)    {        bool        junk_filter_needed = false;        ListCell   *tlist;        foreach(tlist, plan->targetlist)        {            TargetEntry *tle = (TargetEntry *) lfirst(tlist);            if (tle->resjunk)            {                junk_filter_needed = true;                break;            }        }        if (junk_filter_needed)        {            JunkFilter *j;            j = ExecInitJunkFilter(planstate->plan->targetlist,                                   tupType->tdhasoid,                                   ExecInitExtraTupleSlot(estate));            estate->es_junkFilter = j;            /* Want to return the cleaned tuple type */            tupType = j->jf_cleanTupType;        }    }    queryDesc->tupDesc = tupType;    queryDesc->planstate = planstate;}

先从 tupDesc 来入手吧:

/* * This struct is passed around within the backend to describe the structure * of tuples.  For tuples coming from on-disk relations, the information is * collected from the pg_attribute, pg_attrdef, and pg_constraint catalogs. * Transient row types (such as the result of a join query) have anonymous * TupleDesc structs that generally omit any constraint info; therefore the * structure is designed to let the constraints be omitted efficiently. * * Note that only user attributes, not system attributes, are mentioned in * TupleDesc; with the exception that tdhasoid indicates if OID is present. * * If the tupdesc is known to correspond to a named rowtype (such as a table's * rowtype) then tdtypeid identifies that type and tdtypmod is -1.    Otherwise * tdtypeid is RECORDOID, and tdtypmod can be either -1 for a fully anonymous * row type, or a value >= 0 to allow the rowtype to be looked up in the * typcache.c type cache. * * Tuple descriptors that live in caches (relcache or typcache, at present) * are reference-counted: they can be deleted when their reference count goes * to zero.  Tuple descriptors created by the executor need no reference * counting, however: they are simply created in the appropriate memory * context and go away when the context is freed.  We set the tdrefcount * field of such a descriptor to -1, while reference-counted descriptors * always have tdrefcount >= 0. */typedef struct tupleDesc{    int            natts;            /* number of attributes in the tuple */    Form_pg_attribute *attrs;    /* attrs[N] is a pointer to the description of Attribute Number N+1 */    TupleConstr *constr;        /* constraints, or NULL if none */    Oid            tdtypeid;        /* composite type ID for tuple type */    int32        tdtypmod;        /* typmod for tuple type */    bool        tdhasoid;        /* tuple has oid attribute in its header */    int            tdrefcount;        /* reference count, or -1 if not counting */}    *TupleDesc;

对InitPlan进行简化和进一步分析:

static voidInitPlan(QueryDesc *queryDesc, int eflags){    ...    /*     * initialize result relation stuff, and open/lock the result rels.     *     * We must do this before initializing the plan tree, else we might try to     * do a lock upgrade if a result rel is also a source rel.     */    if (plannedstmt->resultRelations)    {        ...    }    else    {        /*         * if no result relation, then set state appropriately         */        estate->es_result_relations = NULL;        estate->es_num_result_relations = 0;        estate->es_result_relation_info = NULL;    }    ...    queryDesc->tupDesc = tupType;    queryDesc->planstate = planstate;}

实际测试后发现 select * from tst01 这样的SQL文,

得到的 (plannedstmt->resultRelations) 判断值为false。

然后,在  foreach(l, plannedstmt->rowMarks) 之前加点判断:

l= list_head(plannedstmt->rowMarks);    if (l != NULL)        fprintf(stderr, "l is not null\n");    else        fprintf(stderr,"l is null\n");

发现,l 是空值。

/* ---------------------------------------------------------------- *        InitPlan * *        Initializes the query plan: open files, allocate storage *        and start up the rule manager * ---------------------------------------------------------------- */static voidInitPlan(QueryDesc *queryDesc, int eflags){    ...    if (plannedstmt->resultRelations)    {       ...    }    else    {        /*         * if no result relation, then set state appropriately         */        estate->es_result_relations = NULL;        estate->es_num_result_relations = 0;        estate->es_result_relation_info = NULL;    }    /*     * Similarly, we have to lock relations selected FOR UPDATE/FOR SHARE     * before we initialize the plan tree, else we'd be risking lock upgrades.     * While we are at it, build the ExecRowMark list.     */    estate->es_rowMarks = NIL;    foreach(l, plannedstmt->rowMarks)    {       ...    }    /*     * Initialize the executor's tuple table to empty.     */    estate->es_tupleTable = NIL;    estate->es_trig_tuple_slot = NULL;    estate->es_trig_oldtup_slot = NULL;    estate->es_trig_newtup_slot = NULL;    /* mark EvalPlanQual not active */    estate->es_epqTuple = NULL;    estate->es_epqTupleSet = NULL;    estate->es_epqScanDone = NULL;    ...}

接着往下分析:

发现运行 select * from test01 where id<10 这样的sql文的时候,

foreach(l, plannedstmt->subplans) 也一次没有得到执行。

static voidInitPlan(QueryDesc *queryDesc, int eflags){    ...    if (plannedstmt->resultRelations)    {       ...    }    else    {        /*         * if no result relation, then set state appropriately         */        estate->es_result_relations = NULL;        estate->es_num_result_relations = 0;        estate->es_result_relation_info = NULL;    }    ...    foreach(l, plannedstmt->rowMarks)    {       ...    }    /*     * Initialize the executor's tuple table to empty.     */    estate->es_tupleTable = NIL;    estate->es_trig_tuple_slot = NULL;    estate->es_trig_oldtup_slot = NULL;    estate->es_trig_newtup_slot = NULL;    /* mark EvalPlanQual not active */    estate->es_epqTuple = NULL;    estate->es_epqTupleSet = NULL;    estate->es_epqScanDone = NULL;    ...    i = 1;                        /* subplan indices count from 1 */    foreach(l, plannedstmt->subplans)    {       ...    }    /*     * Initialize the private state information for all the nodes in the query     * tree.  This opens files, allocates storage and leaves us ready to start     * processing tuples.     */    planstate = ExecInitNode(plan, estate, eflags);    /*     * Get the tuple descriptor describing the type of tuples to return.     */    tupType = ExecGetResultType(planstate);    ...}

接下来看 tupType到底是什么,也就是表的记录的属性信息。

typedef struct tupleDesc{    int            natts;            /* number of attributes in the tuple */    Form_pg_attribute *attrs;    /* attrs[N] is a pointer to the description of Attribute Number N+1 */    TupleConstr *constr;        /* constraints, or NULL if none */    Oid            tdtypeid;        /* composite type ID for tuple type */    int32        tdtypmod;        /* typmod for tuple type */    bool        tdhasoid;        /* tuple has oid attribute in its header */    int            tdrefcount;        /* reference count, or -1 if not counting */}    *TupleDesc;

重新整理一下:

static voidInitPlan(QueryDesc *queryDesc, int eflags){    ...    if (plannedstmt->resultRelations)    {       ...    }    else    {        estate->es_result_relations = NULL;        estate->es_num_result_relations = 0;        estate->es_result_relation_info = NULL;    }    .../*     * Initialize the private state information for all the nodes in the query     * tree.  This opens files, allocates storage and leaves us ready to start     * processing tuples.     */    planstate = ExecInitNode(plan, estate, eflags);    /*     * Get the tuple descriptor describing the type of tuples to return.     */    tupType = ExecGetResultType(planstate);    /*     * Initialize the junk filter if needed.  SELECT queries need a filter if     * there are any junk attrs in the top-level tlist.     */    if (operation == CMD_SELECT)    {bool        junk_filter_needed = false;        ListCell   *tlist;        foreach(tlist, plan->targetlist)        {            fprintf(stderr,"In foreach (tlist, plan->targetlist) \n");            TargetEntry *tle = (TargetEntry *) lfirst(tlist);            if (tle->resjunk)            {                junk_filter_needed = true;                break;            }        }        ...    }    queryDesc->tupDesc = tupType;    queryDesc->planstate = planstate;}

有趣的事情来了:这一段,

如果我 select id from tst04; for 循环执行一次

如果我 select id,val from tst04; for 循环执行二次。

也就是说 plan->targelist 的长度,就是 select 列表里字段的个数。

foreach(tlist, plan->targetlist)        {            fprintf(stderr,"In foreach (tlist, plan->targetlist) \n");            TargetEntry *tle = (TargetEntry *) lfirst(tlist);            if (tle->resjunk)            {                junk_filter_needed = true;                break;            }        }

 事实上,这个在  调用 InitPlan之前,就已经准备好了:

Plan       *plan = plannedstmt->planTree;

 

转载地址:http://ecutl.baihongyu.com/

你可能感兴趣的文章