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;
原文地址:https://www.cnblogs.com/gaojian/p/3106372.html