zoukankan      html  css  js  c++  java
  • PostgreSQL在何处处理 sql查询之三十三

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

    /* ----------------------------------------------------------------
     *        InitPlan
     *
     *        Initializes the query plan: open files, allocate storage
     *        and start up the rule manager
     * ----------------------------------------------------------------
     */
    static void
    InitPlan(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 void
    InitPlan(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 void
    InitPlan(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 void
    InitPlan(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 void
    InitPlan(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;
  • 相关阅读:
    Designing With Web Standard(一)
    再听姜育恒
    终于找到Effective C Sharp电子版了
    继续下一个题目
    想做就做,要做得漂亮
    空悲还是空杯
    整理,中庸
    分布式系统设计随想
    日志log4
    log4net更换目录
  • 原文地址:https://www.cnblogs.com/gaojian/p/3106372.html
Copyright © 2011-2022 走看看