溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

PostgreSQL 源碼解讀(79)- 查詢語句#64(create_plan函數(shù)#3-Se...

發(fā)布時間:2020-08-19 10:07:12 來源:ITPUB博客 閱讀:276 作者:husthxd 欄目:關(guān)系型數(shù)據(jù)庫

本節(jié)介紹了創(chuàng)建計劃create_plan函數(shù)中掃描計劃的實現(xiàn)過程,主要的邏輯在函數(shù)create_scan_plan中實現(xiàn)。

一、數(shù)據(jù)結(jié)構(gòu)

Plan
所有計劃節(jié)點通過將Plan結(jié)構(gòu)作為第一個字段從Plan結(jié)構(gòu)“派生”。這確保了在將節(jié)點轉(zhuǎn)換為計劃節(jié)點時,一切都能正常工作。(在執(zhí)行器中以通用方式傳遞時,節(jié)點指針經(jīng)常被轉(zhuǎn)換為Plan *)

/* ----------------
 *      Plan node
 *
 * All plan nodes "derive" from the Plan structure by having the
 * Plan structure as the first field.  This ensures that everything works
 * when nodes are cast to Plan's.  (node pointers are frequently cast to Plan*
 * when passed around generically in the executor)
 * 所有計劃節(jié)點通過將Plan結(jié)構(gòu)作為第一個字段從Plan結(jié)構(gòu)“派生”。
 * 這確保了在將節(jié)點轉(zhuǎn)換為計劃節(jié)點時,一切都能正常工作。
 * (在執(zhí)行器中以通用方式傳遞時,節(jié)點指針經(jīng)常被轉(zhuǎn)換為Plan *)
 *
 * We never actually instantiate any Plan nodes; this is just the common
 * abstract superclass for all Plan-type nodes.
 * 從未實例化任何Plan節(jié)點;這只是所有Plan-type節(jié)點的通用抽象超類。
 * ----------------
 */
typedef struct Plan
{
    NodeTag     type;//節(jié)點類型

    /*
     * 成本估算信息;estimated execution costs for plan (see costsize.c for more info)
     */
    Cost        startup_cost;   /* 啟動成本;cost expended before fetching any tuples */
    Cost        total_cost;     /* 總成本;total cost (assuming all tuples fetched) */

    /*
     * 優(yōu)化器估算信息;planner's estimate of result size of this plan step
     */
    double      plan_rows;      /* 行數(shù);number of rows plan is expected to emit */
    int         plan_width;     /* 平均行大小(Byte為單位);average row width in bytes */

    /*
     * 并行執(zhí)行相關(guān)的信息;information needed for parallel query
     */
    bool        parallel_aware; /* 是否參與并行執(zhí)行邏輯?engage parallel-aware logic? */
    bool        parallel_safe;  /* 是否并行安全;OK to use as part of parallel plan? */

    /*
     * Plan類型節(jié)點通用的信息.Common structural data for all Plan types.
     */
    int         plan_node_id;   /* unique across entire final plan tree */
    List       *targetlist;     /* target list to be computed at this node */
    List       *qual;           /* implicitly-ANDed qual conditions */
    struct Plan *lefttree;      /* input plan tree(s) */
    struct Plan *righttree;
    List       *initPlan;       /* Init Plan nodes (un-correlated expr
                                 * subselects) */

    /*
     * Information for management of parameter-change-driven rescanning
     * parameter-change-driven重掃描的管理信息.
     * 
     * extParam includes the paramIDs of all external PARAM_EXEC params
     * affecting this plan node or its children.  setParam params from the
     * node's initPlans are not included, but their extParams are.
     *
     * allParam includes all the extParam paramIDs, plus the IDs of local
     * params that affect the node (i.e., the setParams of its initplans).
     * These are _all_ the PARAM_EXEC params that affect this node.
     */
    Bitmapset  *extParam;
    Bitmapset  *allParam;
} Plan;

二、源碼解讀

create_scan_plan函數(shù)創(chuàng)建Scan Plan.掃描可以分為順序掃描(全表掃描)/索引掃描/索引快速掃描/TID掃描等多種掃描方式,這里主要介紹常見的順序掃描和索引掃描,相應(yīng)的實現(xiàn)函數(shù)是create_seqscan_plan和create_indexscan_plan.



//--------------------------------------------------- create_scan_plan
/*
 * create_scan_plan
 *   Create a scan plan for the parent relation of 'best_path'.
 *   為relation best_path創(chuàng)建相應(yīng)的掃描計劃
 */
static Plan *
create_scan_plan(PlannerInfo *root, Path *best_path, int flags)
{
    RelOptInfo *rel = best_path->parent;
    List       *scan_clauses;
    List       *gating_clauses;
    List       *tlist;
    Plan       *plan;

    /*
     * Extract the relevant restriction clauses from the parent relation. The
     * executor must apply all these restrictions during the scan, except for
     * pseudoconstants which we'll take care of below.
     * 從父關(guān)系中提取相關(guān)的限制條件。
     * 執(zhí)行器必須在掃描期間應(yīng)用所有這些限制條件,除了偽常量,將在下面處理這些限制。
     *
     * If this is a plain indexscan or index-only scan, we need not consider
     * restriction clauses that are implied by the index's predicate, so use
     * indrestrictinfo not baserestrictinfo.  Note that we can't do that for
     * bitmap indexscans, since there's not necessarily a single index
     * involved; but it doesn't matter since create_bitmap_scan_plan() will be
     * able to get rid of such clauses anyway via predicate proof.
     * 如果這是一個普通的indexscan或index-only掃描,
     * 則不需要考慮由索引謂詞隱含的限制條款,因此使用indrestrictinfo而不是baserestrictinfo。
     * 注意,對于位圖索引掃描,我們不能這樣做,因為不需要一個索引;
     * 但是這并不重要,因為create_bitmap_scan_plan()將能夠通過謂詞證明消除這些子句。
     */
    switch (best_path->pathtype)
    {
        case T_IndexScan:
        case T_IndexOnlyScan://索引掃描,使用索引約束條件
            scan_clauses = castNode(IndexPath, best_path)->indexinfo->indrestrictinfo;
            break;
        default:
            scan_clauses = rel->baserestrictinfo;//默認使用關(guān)系中的約束條件
            break;
    }

    /*
     * If this is a parameterized scan, we also need to enforce all the join
     * clauses available from the outer relation(s).
     * 如果這是一個參數(shù)化掃描,需要從連接外關(guān)系中加入所有可用的連接約束條件
     *
     * For paranoia's sake, don't modify the stored baserestrictinfo list.
     * 由于paranoia's sake,不更新baserestrictinfo鏈表
     */
    if (best_path->param_info)
        scan_clauses = list_concat(list_copy(scan_clauses),
                                   best_path->param_info->ppi_clauses);

    /*
     * Detect whether we have any pseudoconstant quals to deal with.  Then, if
     * we'll need a gating Result node, it will be able to project, so there
     * are no requirements on the child's tlist.
     * 檢測是否有偽常數(shù)函數(shù)需要處理。
     * 然后,需要一個能夠執(zhí)行投影的出口結(jié)果節(jié)點,因此對子tlist沒有需求。
     */
    gating_clauses = get_gating_quals(root, scan_clauses);
    if (gating_clauses)
        flags = 0;

    /*
     * For table scans, rather than using the relation targetlist (which is
     * only those Vars actually needed by the query), we prefer to generate a
     * tlist containing all Vars in order.  This will allow the executor to
     * optimize away projection of the table tuples, if possible.
     * 對于表掃描,不使用關(guān)系targetlist(它只是查詢實際需要的Vars),
     * 我們更喜歡按照順序生成一個包含所有Vars的tlist。
     * 如果可能的話,這將允許執(zhí)行程序優(yōu)化表元組的投影。
     *
     * But if the caller is going to ignore our tlist anyway, then don't
     * bother generating one at all.  We use an exact equality test here, so
     * that this only applies when CP_IGNORE_TLIST is the only flag set.
     * 但是,如果調(diào)用者不管怎樣都要忽略tlist,那么就根本不用去生成一個。
     * 在這里使用了一個完全相等的測試,因此只有當(dāng)CP_IGNORE_TLIST是唯一的標志設(shè)置時才適用。
     */
    if (flags == CP_IGNORE_TLIST)
    {
        tlist = NULL;//使用CP_IGNORE_TLIST標志,則設(shè)置tlist為NULL
    }
    else if (use_physical_tlist(root, best_path, flags))
    {
        if (best_path->pathtype == T_IndexOnlyScan)//索引快速掃描
        {
            /* For index-only scan, the preferred tlist is the index's */
            //對于所有快速掃描,tlist中的列應(yīng)在索引中
            tlist = copyObject(((IndexPath *) best_path)->indexinfo->indextlist);

            /*
             * Transfer sortgroupref data to the replacement tlist, if
             * requested (use_physical_tlist checked that this will work).
             * 如需要,轉(zhuǎn)換sortgroupref數(shù)據(jù)為tlist(use_physical_tlist檢查是否可行)
             */
            if (flags & CP_LABEL_TLIST)
                apply_pathtarget_labeling_to_tlist(tlist, best_path->pathtarget);
        }
        else//非索引快速掃描
        {
            tlist = build_physical_tlist(root, rel);//構(gòu)建物理的tlist
            if (tlist == NIL)
            {
                /* Failed because of dropped cols, so use regular method */
                //build_physical_tlist無法構(gòu)建,則使用常規(guī)方法構(gòu)建
                tlist = build_path_tlist(root, best_path);
            }
            else
            {
                /* As above, transfer sortgroupref data to replacement tlist */
                if (flags & CP_LABEL_TLIST)
                    apply_pathtarget_labeling_to_tlist(tlist, best_path->pathtarget);
            }
        }
    }
    else
    {
        tlist = build_path_tlist(root, best_path);//使用常規(guī)方法構(gòu)建
    }

    switch (best_path->pathtype)//根據(jù)路徑類型進行相應(yīng)的處理
    {
        case T_SeqScan://順序掃描
            plan = (Plan *) create_seqscan_plan(root,
                                                best_path,
                                                tlist,
                                                scan_clauses);
            break;

        case T_SampleScan:
            plan = (Plan *) create_samplescan_plan(root,
                                                   best_path,
                                                   tlist,
                                                   scan_clauses);
            break;

        case T_IndexScan:
            plan = (Plan *) create_indexscan_plan(root,
                                                  (IndexPath *) best_path,
                                                  tlist,
                                                  scan_clauses,
                                                  false);
            break;

        case T_IndexOnlyScan:
            plan = (Plan *) create_indexscan_plan(root,
                                                  (IndexPath *) best_path,
                                                  tlist,
                                                  scan_clauses,
                                                  true);
            break;

        case T_BitmapHeapScan:
            plan = (Plan *) create_bitmap_scan_plan(root,
                                                    (BitmapHeapPath *) best_path,
                                                    tlist,
                                                    scan_clauses);
            break;

        case T_TidScan:
            plan = (Plan *) create_tidscan_plan(root,
                                                (TidPath *) best_path,
                                                tlist,
                                                scan_clauses);
            break;

        case T_SubqueryScan:
            plan = (Plan *) create_subqueryscan_plan(root,
                                                     (SubqueryScanPath *) best_path,
                                                     tlist,
                                                     scan_clauses);
            break;

        case T_FunctionScan:
            plan = (Plan *) create_functionscan_plan(root,
                                                     best_path,
                                                     tlist,
                                                     scan_clauses);
            break;

        case T_TableFuncScan:
            plan = (Plan *) create_tablefuncscan_plan(root,
                                                      best_path,
                                                      tlist,
                                                      scan_clauses);
            break;

        case T_ValuesScan:
            plan = (Plan *) create_valuesscan_plan(root,
                                                   best_path,
                                                   tlist,
                                                   scan_clauses);
            break;

        case T_CteScan:
            plan = (Plan *) create_ctescan_plan(root,
                                                best_path,
                                                tlist,
                                                scan_clauses);
            break;

        case T_NamedTuplestoreScan:
            plan = (Plan *) create_namedtuplestorescan_plan(root,
                                                            best_path,
                                                            tlist,
                                                            scan_clauses);
            break;

        case T_WorkTableScan:
            plan = (Plan *) create_worktablescan_plan(root,
                                                      best_path,
                                                      tlist,
                                                      scan_clauses);
            break;

        case T_ForeignScan:
            plan = (Plan *) create_foreignscan_plan(root,
                                                    (ForeignPath *) best_path,
                                                    tlist,
                                                    scan_clauses);
            break;

        case T_CustomScan:
            plan = (Plan *) create_customscan_plan(root,
                                                   (CustomPath *) best_path,
                                                   tlist,
                                                   scan_clauses);
            break;

        default:
            elog(ERROR, "unrecognized node type: %d",
                 (int) best_path->pathtype);
            plan = NULL;        /* keep compiler quiet */
            break;
    }

    /*
     * If there are any pseudoconstant clauses attached to this node, insert a
     * gating Result node that evaluates the pseudoconstants as one-time
     * quals.
     * 如果這個節(jié)點上附加了偽常量子句,插入一個Result節(jié)點,該節(jié)點將偽常量計算為一次性的條件quals。
     */
    if (gating_clauses)
        plan = create_gating_plan(root, best_path, plan, gating_clauses);

    return plan;
}


//------------------------------------- create_seqscan_plan
/*
 * create_seqscan_plan
 *   Returns a seqscan plan for the base relation scanned by 'best_path'
 *   with restriction clauses 'scan_clauses' and targetlist 'tlist'.
 *   返回seqscan順序掃描計劃(基于基本關(guān)系中的best_path),同時考慮了約束條件scan_clauses和投影列tlist
 */
static SeqScan *
create_seqscan_plan(PlannerInfo *root, Path *best_path,
                    List *tlist, List *scan_clauses)
{
    SeqScan    *scan_plan;
    Index       scan_relid = best_path->parent->relid;

    /* it should be a base rel... */
    //基本關(guān)系
    Assert(scan_relid > 0);
    Assert(best_path->parent->rtekind == RTE_RELATION);

    /* Sort clauses into best execution order */
    //約束條件排序為最佳的執(zhí)行順序
    scan_clauses = order_qual_clauses(root, scan_clauses);

    /* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
    //規(guī)約限制條件信息鏈表到裸表達式,同時忽略pseudoconstants
    scan_clauses = extract_actual_clauses(scan_clauses, false);

    /* Replace any outer-relation variables with nestloop params */
    //參數(shù)化訪問,則使用內(nèi)嵌循環(huán)參數(shù)替換外表變量
    if (best_path->param_info)
    {
        scan_clauses = (List *)
            replace_nestloop_params(root, (Node *) scan_clauses);//約束條件
    }

    scan_plan = make_seqscan(tlist,
                             scan_clauses,
                             scan_relid);//構(gòu)建掃描計劃

    copy_generic_path_info(&scan_plan->plan, best_path);

    return scan_plan;//返回
}

//------------------------------------- copy_generic_path_info

 /*
  * Copy cost and size info from a Path node to the Plan node created from it.
  * The executor usually won't use this info, but it's needed by EXPLAIN.
  * Also copy the parallel-related flags, which the executor *will* use.
  */
 static void
 copy_generic_path_info(Plan *dest, Path *src)
 {
     dest->startup_cost = src->startup_cost;
     dest->total_cost = src->total_cost;
     dest->plan_rows = src->rows;
     dest->plan_width = src->pathtarget->width;
     dest->parallel_aware = src->parallel_aware;
     dest->parallel_safe = src->parallel_safe;
 }
 

//------------------------------------- make_seqscan

static SeqScan *
make_seqscan(List *qptlist,
             List *qpqual,
             Index scanrelid)
{
    SeqScan    *node = makeNode(SeqScan);
    Plan       *plan = &node->plan;

    plan->targetlist = qptlist;
    plan->qual = qpqual;
    plan->lefttree = NULL;
    plan->righttree = NULL;
    node->scanrelid = scanrelid;

    return node;//創(chuàng)建節(jié)點
}

//------------------------------------- create_indexscan_plan

/*
 * create_indexscan_plan
 *    Returns an indexscan plan for the base relation scanned by 'best_path'
 *    with restriction clauses 'scan_clauses' and targetlist 'tlist'.
 *    返回索引掃描計劃
 *
 * We use this for both plain IndexScans and IndexOnlyScans, because the
 * qual preprocessing work is the same for both.  Note that the caller tells
 * us which to build --- we don't look at best_path->path.pathtype, because
 * create_bitmap_subplan needs to be able to override the prior decision.
 * 此過程用于普通的indexscan和indexonlyscan,因為兩者的條件qual預(yù)處理工作是相同的。
 * 注意,調(diào)用者明確指示構(gòu)建哪個——不需要查看best_path->path.pathtype,
 * 因為create_bitmap_subplan需要能夠覆蓋之前的決定。
 */
static Scan *
create_indexscan_plan(PlannerInfo *root,
                      IndexPath *best_path,
                      List *tlist,
                      List *scan_clauses,
                      bool indexonly)
{
    Scan       *scan_plan;
    List       *indexquals = best_path->indexquals;
    List       *indexorderbys = best_path->indexorderbys;
    Index       baserelid = best_path->path.parent->relid;
    Oid         indexoid = best_path->indexinfo->indexoid;
    List       *qpqual;
    List       *stripped_indexquals;
    List       *fixed_indexquals;
    List       *fixed_indexorderbys;
    List       *indexorderbyops = NIL;
    ListCell   *l;

    /* it should be a base rel... */
    //基本關(guān)系
    Assert(baserelid > 0);
    Assert(best_path->path.parent->rtekind == RTE_RELATION);

    /*
     * Build "stripped" indexquals structure (no RestrictInfos) to pass to
     * executor as indexqualorig
     * 構(gòu)建"stripped"索引約束條件結(jié)構(gòu)(非RestrictInfos),作為執(zhí)行器的調(diào)用參數(shù)indexqualorig
     */
    stripped_indexquals = get_actual_clauses(indexquals);

    /*
     * The executor needs a copy with the indexkey on the left of each clause
     * and with index Vars substituted for table ones.
     * 執(zhí)行器需要在每個子句的左邊加上indexkey,并用索引變量替換表變量。
     */
    fixed_indexquals = fix_indexqual_references(root, best_path);

    /*
     * Likewise fix up index attr references in the ORDER BY expressions.
     * 同樣,修正ORDER BY 表達式的索引屬性attr引用。
     */
    fixed_indexorderbys = fix_indexorderby_references(root, best_path);

    /*
     * The qpqual list must contain all restrictions not automatically handled
     * by the index, other than pseudoconstant clauses which will be handled
     * by a separate gating plan node.  All the predicates in the indexquals
     * will be checked (either by the index itself, or by nodeIndexscan.c),
     * but if there are any "special" operators involved then they must be
     * included in qpqual.  The upshot is that qpqual must contain
     * scan_clauses minus whatever appears in indexquals.
     * qpqual鏈表必須包含索引未處理的其他限制條件,偽常量子句除外,偽常量子句將由單獨的gating計劃節(jié)點處理。
     * indexquals中的所有謂詞都將被檢查(可以通過索引本身檢查,也可以通過nodeIndexscan.c檢查),
     * 但是如果涉及到任何“特殊”運算符,那么它們必須包含在qpqual中。
     * 結(jié)果是,qpqual必須包含scan_clause,除去indexquals中出現(xiàn)的任何內(nèi)容。
     * 
     * In normal cases simple pointer equality checks will be enough to spot
     * duplicate RestrictInfos, so we try that first.
     * 在通常情況下,簡單的指針相等檢查將足以發(fā)現(xiàn)雙重的RestrictInfos,因此首先執(zhí)行此檢查操作。
     *
     * Another common case is that a scan_clauses entry is generated from the
     * same EquivalenceClass as some indexqual, and is therefore redundant
     * with it, though not equal.  (This happens when indxpath.c prefers a
     * different derived equality than what generate_join_implied_equalities
     * picked for a parameterized scan's ppi_clauses.)
     * 另一種常見的情況是scan_clauses entry是由與indexqual相同的EC生成的,因此盡管不相等但它是多余的。
     * (這發(fā)生在indxpath.c與為參數(shù)化掃描的ppi_clauses與generate_join_implied_equalities選擇的是不同的派生等式時)。
     *
     * In some situations (particularly with OR'd index conditions) we may
     * have scan_clauses that are not equal to, but are logically implied by,
     * the index quals; so we also try a predicate_implied_by() check to see
     * if we can discard quals that way.  (predicate_implied_by assumes its
     * first input contains only immutable functions, so we have to check
     * that.)
     * 在某些情況下(特別是在索引條件下),可能有scan_clauses,雖然不等價,但邏輯上由索引quals表示;
     * 因此,我們還嘗試使用predicate_implied_by()檢驗是否這樣丟棄quals。
     * (predicate_implied_by假設(shè)它的第一個輸入只包含不可變函數(shù),所以必須檢查它。)
     *
     * Note: if you change this bit of code you should also look at
     * extract_nonindex_conditions() in costsize.c.
     * 注意:如果需要這部分代碼,需要檢查costsize.c中的extract_nonindex_conditions()函數(shù)
     */
    qpqual = NIL;
    foreach(l, scan_clauses)//遍歷scan_clauses鏈表
    {
        RestrictInfo *rinfo = lfirst_node(RestrictInfo, l);

        if (rinfo->pseudoconstant)
            continue;           /* 不理會pseudoconstants;we may drop pseudoconstants here */
        if (list_member_ptr(indexquals, rinfo))
            continue;           /* 重復(fù)不處理;simple duplicate */
        if (is_redundant_derived_clause(rinfo, indexquals))
            continue;           /* 從EC中派生的不處理;derived from same EquivalenceClass */
        if (!contain_mutable_functions((Node *) rinfo->clause) &&
            predicate_implied_by(list_make1(rinfo->clause), indexquals, false))
            continue;           /* 通過indexquals可隱式證明;provably implied by indexquals */
        qpqual = lappend(qpqual, rinfo);
    }

    /* Sort clauses into best execution order */
    //條件排序
    qpqual = order_qual_clauses(root, qpqual);

    /* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
    //規(guī)約
    qpqual = extract_actual_clauses(qpqual, false);

    /*
     * We have to replace any outer-relation variables with nestloop params in
     * the indexqualorig, qpqual, and indexorderbyorig expressions.  A bit
     * annoying to have to do this separately from the processing in
     * fix_indexqual_references --- rethink this when generalizing the inner
     * indexscan support.  But note we can't really do this earlier because
     * it'd break the comparisons to predicates above ... (or would it?  Those
     * wouldn't have outer refs)
     * 我們必須用indexqualorig、qpqual和indexorderbyorig表達式中的nestloop參數(shù)替換任何外關(guān)系變量。
     * 與fix_indexqual_references中的處理分開來完成這一工作有點煩人——在泛化內(nèi)部indexscan支持時重新考慮這一點。
     * 但是請注意,不能提前做這個事情,因為它會打斷與上面謂詞的比較……(可能發(fā)生嗎?因為它們沒有外部依賴)
     */
    if (best_path->path.param_info)//存在參數(shù)信息,使用內(nèi)嵌循環(huán)參數(shù)替換
    {
        stripped_indexquals = (List *)
            replace_nestloop_params(root, (Node *) stripped_indexquals);
        qpqual = (List *)
            replace_nestloop_params(root, (Node *) qpqual);
        indexorderbys = (List *)
            replace_nestloop_params(root, (Node *) indexorderbys);
    }

    /*
     * If there are ORDER BY expressions, look up the sort operators for their
     * result datatypes.
     * //存在ORDER BY表達式
     */
    if (indexorderbys)
    {
        ListCell   *pathkeyCell,
                   *exprCell;

        /*
         * PathKey contains OID of the btree opfamily we're sorting by, but
         * that's not quite enough because we need the expression's datatype
         * to look up the sort operator in the operator family.
         * PathKey已經(jīng)包含我們正在排序的btree opfamily的OID,
         * 但這還不足夠,因為需要表達式的數(shù)據(jù)類型來查找操作符家族中的sort操作符。
         */
        Assert(list_length(best_path->path.pathkeys) == list_length(indexorderbys));
        forboth(pathkeyCell, best_path->path.pathkeys, exprCell, indexorderbys)
        {
            PathKey    *pathkey = (PathKey *) lfirst(pathkeyCell);
            Node       *expr = (Node *) lfirst(exprCell);
            Oid         exprtype = exprType(expr);
            Oid         sortop;

            /* Get sort operator from opfamily */
            sortop = get_opfamily_member(pathkey->pk_opfamily,
                                         exprtype,
                                         exprtype,
                                         pathkey->pk_strategy);
            if (!OidIsValid(sortop))
                elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
                     pathkey->pk_strategy, exprtype, exprtype, pathkey->pk_opfamily);
            indexorderbyops = lappend_oid(indexorderbyops, sortop);
        }
    }

    /* Finally ready to build the plan node */
    //OK,下面可以構(gòu)建計劃節(jié)點了
    if (indexonly)
        scan_plan = (Scan *) make_indexonlyscan(tlist,
                                                qpqual,
                                                baserelid,
                                                indexoid,
                                                fixed_indexquals,
                                                fixed_indexorderbys,
                                                best_path->indexinfo->indextlist,
                                                best_path->indexscandir);
    else
        scan_plan = (Scan *) make_indexscan(tlist,
                                            qpqual,
                                            baserelid,
                                            indexoid,
                                            fixed_indexquals,
                                            stripped_indexquals,
                                            fixed_indexorderbys,
                                            indexorderbys,
                                            indexorderbyops,
                                            best_path->indexscandir);

    copy_generic_path_info(&scan_plan->plan, &best_path->path);

    return scan_plan;
}


//------------------------------------- make_indexscan/make_indexonlyscan
static IndexScan *
make_indexscan(List *qptlist,
               List *qpqual,
               Index scanrelid,
               Oid indexid,
               List *indexqual,
               List *indexqualorig,
               List *indexorderby,
               List *indexorderbyorig,
               List *indexorderbyops,
               ScanDirection indexscandir)
{
    IndexScan  *node = makeNode(IndexScan);
    Plan       *plan = &node->scan.plan;

    plan->targetlist = qptlist;
    plan->qual = qpqual;
    plan->lefttree = NULL;
    plan->righttree = NULL;
    node->scan.scanrelid = scanrelid;
    node->indexid = indexid;
    node->indexqual = indexqual;
    node->indexqualorig = indexqualorig;
    node->indexorderby = indexorderby;
    node->indexorderbyorig = indexorderbyorig;
    node->indexorderbyops = indexorderbyops;
    node->indexorderdir = indexscandir;

    return node;
}

static IndexOnlyScan *
make_indexonlyscan(List *qptlist,
                   List *qpqual,
                   Index scanrelid,
                   Oid indexid,
                   List *indexqual,
                   List *indexorderby,
                   List *indextlist,
                   ScanDirection indexscandir)
{
    IndexOnlyScan *node = makeNode(IndexOnlyScan);
    Plan       *plan = &node->scan.plan;

    plan->targetlist = qptlist;
    plan->qual = qpqual;
    plan->lefttree = NULL;
    plan->righttree = NULL;
    node->scan.scanrelid = scanrelid;
    node->indexid = indexid;
    node->indexqual = indexqual;
    node->indexorderby = indexorderby;
    node->indextlist = indextlist;
    node->indexorderdir = indexscandir;

    return node;
}

三、跟蹤分析

測試腳本如下

testdb=# explain select dw.*,grjf.grbh,grjf.xm,grjf.ny,grjf.je 
testdb-# from t_dwxx dw,lateral (select gr.grbh,gr.xm,jf.ny,jf.je 
testdb(#                         from t_grxx gr inner join t_jfxx jf 
testdb(#                                        on gr.dwbh = dw.dwbh 
testdb(#                                           and gr.grbh = jf.grbh) grjf
testdb-# where dw.dwbh in ('1001','1002')
testdb-# order by dw.dwbh;
                                          QUERY PLAN                                               
-------------------------------------------------------------------------------------------------
 Sort  (cost=2010.12..2010.17 rows=20 width=47)
   Sort Key: dw.dwbh
   ->  Nested Loop  (cost=14.24..2009.69 rows=20 width=47)
         ->  Hash Join  (cost=13.95..2002.56 rows=20 width=32)
               Hash Cond: ((gr.dwbh)::text = (dw.dwbh)::text)
               ->  Seq Scan on t_grxx gr  (cost=0.00..1726.00 rows=100000 width=16)
               ->  Hash  (cost=13.92..13.92 rows=2 width=20)
                     ->  Index Scan using t_dwxx_pkey on t_dwxx dw  (cost=0.29..13.92 rows=2 width=20)
                           Index Cond: ((dwbh)::text = ANY ('{1001,1002}'::text[]))
         ->  Index Scan using idx_t_jfxx_grbh on t_jfxx jf  (cost=0.29..0.35 rows=1 width=20)
               Index Cond: ((grbh)::text = (gr.grbh)::text)
(11 rows)

啟動gdb,設(shè)置斷點,進入函數(shù)create_scan_plan

(gdb) b create_scan_plan
Breakpoint 1 at 0x7b7b78: file createplan.c, line 514.
(gdb) c
Continuing.

Breakpoint 1, create_scan_plan (root=0x2d36d00, best_path=0x2d57ad0, flags=0) at createplan.c:514
514     RelOptInfo *rel = best_path->parent;

pathtype為T_SeqScan

(gdb) p best_path->pathtype
$1 = T_SeqScan

進入相應(yīng)的分支,約束條件直接使用Relation的限制條件

(gdb) n
532     switch (best_path->pathtype)
(gdb) 
539             scan_clauses = rel->baserestrictinfo;
(gdb) 
540             break;

非索引快速掃描,構(gòu)建目標投影列

(gdb) n
559     if (gating_clauses)
(gdb) 
572     if (flags == CP_IGNORE_TLIST)
(gdb) 
576     else if (use_physical_tlist(root, best_path, flags))
(gdb) 
578         if (best_path->pathtype == T_IndexOnlyScan)
(gdb) 
592             tlist = build_physical_tlist(root, rel);
(gdb) 
593             if (tlist == NIL)
(gdb) 
601                 if (flags & CP_LABEL_TLIST)
(gdb) 
(gdb) p *tlist
$4 = {type = T_List, length = 5, head = 0x2d89440, tail = 0x2d897d8}
(gdb) p *(Node *)tlist->head->data.ptr_value
$5 = {type = T_TargetEntry}
(gdb) p *(TargetEntry *)tlist->head->data.ptr_value
$6 = {xpr = {type = T_TargetEntry}, expr = 0x2d89390, resno = 1, resname = 0x0, ressortgroupref = 0, resorigtbl = 0, 
  resorigcol = 0, resjunk = false}

根據(jù)pathtype進入相應(yīng)的處理邏輯,調(diào)用函數(shù)create_seqscan_plan

(gdb) n
614             plan = (Plan *) create_seqscan_plan(root,
(gdb) step
create_seqscan_plan (root=0x2d36d00, best_path=0x2d57ad0, tlist=0x2d89468, scan_clauses=0x0) at createplan.c:2444
2444        Index       scan_relid = best_path->parent->relid;

構(gòu)建掃描條件,為NULL

2451        scan_clauses = order_qual_clauses(root, scan_clauses);
(gdb) 
2454        scan_clauses = extract_actual_clauses(scan_clauses, false);
(gdb) 
2457        if (best_path->param_info)
(gdb) 
(gdb) p *scan_clauses
Cannot access memory at address 0x0

生成SeqScan Plan節(jié)點,并把啟動成本/總成本等相關(guān)信息拷貝到該Plan中

(gdb) n
2463        scan_plan = make_seqscan(tlist,
(gdb) 
2467        copy_generic_path_info(&scan_plan->plan, best_path);

完成創(chuàng)建,返回Plan

(gdb) n
2469        return scan_plan;
(gdb) p *scan_plan
$1 = {plan = {type = T_SeqScan, startup_cost = 0, total_cost = 1726, plan_rows = 100000, plan_width = 16, 
    parallel_aware = false, parallel_safe = true, plan_node_id = 0, targetlist = 0x2db4e38, qual = 0x0, lefttree = 0x0, 
    righttree = 0x0, initPlan = 0x0, extParam = 0x0, allParam = 0x0}, scanrelid = 3}

下面跟蹤分析create_indexscan_plan函數(shù)
設(shè)置斷點,進入函數(shù)

(gdb) b create_indexscan_plan
Breakpoint 2 at 0x7badad: file createplan.c, line 2536.
(gdb) c
Continuing.

Breakpoint 1, create_scan_plan (root=0x2d50a00, best_path=0x2da9e98, flags=2) at createplan.c:514
514     RelOptInfo *rel = best_path->parent;
(gdb) c
Continuing.

Breakpoint 2, create_indexscan_plan (root=0x2d50a00, best_path=0x2da9e98, tlist=0x2db5250, scan_clauses=0x2da8998, 
    indexonly=false) at createplan.c:2536
2536        List       *indexquals = best_path->indexquals;
(gdb) 

賦值并獲取相關(guān)信息

2536        List       *indexquals = best_path->indexquals;
(gdb) n
2537        List       *indexorderbys = best_path->indexorderbys;
(gdb) 
2538        Index       baserelid = best_path->path.parent->relid;
(gdb) 
2539        Oid         indexoid = best_path->indexinfo->indexoid;
(gdb) 
2544        List       *indexorderbyops = NIL;
(gdb) 
2548        Assert(baserelid > 0);
(gdb) 
2549        Assert(best_path->path.parent->rtekind == RTE_RELATION);
(gdb) 
2555        stripped_indexquals = get_actual_clauses(indexquals);
(gdb) n
2561        fixed_indexquals = fix_indexqual_references(root, best_path);
(gdb) 
2566        fixed_indexorderbys = fix_indexorderby_references(root, best_path);
(gdb) 
2596        qpqual = NIL;
(gdb) 
(gdb) p baserelid
$3 = 1
(gdb) p indexoid
$4 = 16738
#t_dwxx_pkey
testdb=# select relname from pg_class where oid=16738;
   relname   
-------------
 t_dwxx_pkey
(1 row)

遍歷掃描條件

2597        foreach(l, scan_clauses)
(gdb) p *scan_clauses
$5 = {type = T_List, length = 1, head = 0x2da8970, tail = 0x2da8970}

重復(fù)的約束條件,不需要處理

2603            if (list_member_ptr(indexquals, rinfo))
(gdb) 
2604                continue;           /* simple duplicate */

對條件進行排序&規(guī)約

2614        qpqual = order_qual_clauses(root, qpqual);
(gdb) n
2617        qpqual = extract_actual_clauses(qpqual, false);
(gdb) n
2628        if (best_path->path.param_info)
(gdb) p *qpqual
Cannot access memory at address 0x0

創(chuàng)建IndexScan節(jié)點

(gdb) n
2642        if (indexorderbys)
(gdb) 
2673        if (indexonly)
(gdb) 
2683            scan_plan = (Scan *) make_indexscan(tlist,
(gdb) 
2694        copy_generic_path_info(&scan_plan->plan, &best_path->path);
(gdb) 
2696        return scan_plan;
(gdb) 
2697    }
(gdb) p *scan_plan
$6 = {plan = {type = T_IndexScan, startup_cost = 0.28500000000000003, total_cost = 13.924222117799655, plan_rows = 2, 
    plan_width = 20, parallel_aware = false, parallel_safe = true, plan_node_id = 0, targetlist = 0x2db5250, qual = 0x0, 
    lefttree = 0x0, righttree = 0x0, initPlan = 0x0, extParam = 0x0, allParam = 0x0}, scanrelid = 1}
(gdb) 

回到create_scan_plan

(gdb) n
create_scan_plan (root=0x2d50a00, best_path=0x2da9e98, flags=2) at createplan.c:633
633             break;
(gdb) 
732     if (gating_clauses)
(gdb) 
735     return plan;
(gdb) 
736 }

調(diào)用完成.

四、參考資料

createplan.c
PG Document:Query Planning

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI