get_matching_partitions函數(shù)怎么用,小編覺(jué)得挺實(shí)用的,因此分享給大家做個(gè)參考,希?..."/>
溫馨提示×

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

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

PostgreSQL中prune_append_rel_partitions->get_matching_partitions函數(shù)怎么用

發(fā)布時(shí)間:2021-11-10 14:58:37 來(lái)源:億速云 閱讀:81 作者:小新 欄目:關(guān)系型數(shù)據(jù)庫(kù)

這篇文章將為大家詳細(xì)講解有關(guān)PostgreSQL中prune_append_rel_partitions->get_matching_partitions函數(shù)怎么用,小編覺(jué)得挺實(shí)用的,因此分享給大家做個(gè)參考,希望大家閱讀完這篇文章后可以有所收獲。

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

PartitionScheme
分區(qū)方案,根據(jù)設(shè)計(jì),分區(qū)方案只包含分區(qū)方法的一般屬性(列表與范圍、分區(qū)列的數(shù)量和每個(gè)分區(qū)列的類型信息),而不包含特定的分區(qū)邊界信息。

/*
 * If multiple relations are partitioned the same way, all such partitions
 * will have a pointer to the same PartitionScheme.  A list of PartitionScheme
 * objects is attached to the PlannerInfo.  By design, the partition scheme
 * incorporates only the general properties of the partition method (LIST vs.
 * RANGE, number of partitioning columns and the type information for each)
 * and not the specific bounds.
 * 如果多個(gè)關(guān)系以相同的方式分區(qū),那么所有這些分區(qū)都將具有指向相同PartitionScheme的指針。
 * PartitionScheme對(duì)象的鏈表附加到PlannerInfo中。
 * 根據(jù)設(shè)計(jì),分區(qū)方案只包含分區(qū)方法的一般屬性(列表與范圍、分區(qū)列的數(shù)量和每個(gè)分區(qū)列的類型信息),
 *   而不包含特定的界限。
 *
 * We store the opclass-declared input data types instead of the partition key
 * datatypes since the former rather than the latter are used to compare
 * partition bounds. Since partition key data types and the opclass declared
 * input data types are expected to be binary compatible (per ResolveOpClass),
 * both of those should have same byval and length properties.
 * 我們存儲(chǔ)opclass-declared的輸入數(shù)據(jù)類型,而不是分區(qū)鍵數(shù)據(jù)類型,
 *   因?yàn)榍罢哂糜诒容^分區(qū)邊界,而不是后者。
 * 由于分區(qū)鍵數(shù)據(jù)類型和opclass-declared的輸入數(shù)據(jù)類型預(yù)期是二進(jìn)制兼容的(每個(gè)ResolveOpClass),
 *   所以它們應(yīng)該具有相同的byval和length屬性。
 */
typedef struct PartitionSchemeData
{
    char        strategy;       /* 分區(qū)策略;partition strategy */
    int16       partnatts;      /* 分區(qū)屬性個(gè)數(shù);number of partition attributes */
    Oid        *partopfamily;   /* 操作符族OIDs;OIDs of operator families */
    Oid        *partopcintype;  /* opclass聲明的輸入數(shù)據(jù)類型的OIDs;OIDs of opclass declared input data types */
    Oid        *partcollation;  /* 分區(qū)排序規(guī)則OIDs;OIDs of partitioning collations */

    /* Cached information about partition key data types. */
    //緩存有關(guān)分區(qū)鍵數(shù)據(jù)類型的信息。
    int16      *parttyplen;
    bool       *parttypbyval;

    /* Cached information about partition comparison functions. */
    //緩存有關(guān)分區(qū)比較函數(shù)的信息。
    FmgrInfo   *partsupfunc;
}           PartitionSchemeData;

typedef struct PartitionSchemeData *PartitionScheme;

PartitionPruneXXX
執(zhí)行Prune期間需要使用的數(shù)據(jù)結(jié)構(gòu),包括PartitionPruneStep/PartitionPruneStepOp/PartitionPruneCombineOp/PartitionPruneStepCombine

/*
 * Abstract Node type for partition pruning steps (there are no concrete
 * Nodes of this type).
 * 用于分區(qū)修剪步驟pruning的抽象節(jié)點(diǎn)類型(沒(méi)有這種類型的具體節(jié)點(diǎn))。
 * 
 * step_id is the global identifier of the step within its pruning context.
 * step_id是步驟在其修剪pruning上下文中的全局標(biāo)識(shí)符。
 */
typedef struct PartitionPruneStep
{
    NodeTag     type;
    int         step_id;
} PartitionPruneStep;

 /*
 * PartitionPruneStepOp - Information to prune using a set of mutually AND'd
 *                          OpExpr clauses
 * PartitionPruneStepOp - 使用一組AND操作的OpExpr條件子句進(jìn)行修剪prune的信息
 *
 * This contains information extracted from up to partnatts OpExpr clauses,
 * where partnatts is the number of partition key columns.  'opstrategy' is the
 * strategy of the operator in the clause matched to the last partition key.
 * 'exprs' contains expressions which comprise the lookup key to be passed to
 * the partition bound search function.  'cmpfns' contains the OIDs of
 * comparison functions used to compare aforementioned expressions with
 * partition bounds.  Both 'exprs' and 'cmpfns' contain the same number of
 * items, up to partnatts items.
 * 它包含從partnatts OpExpr子句中提取的信息,
 *   其中partnatts是分區(qū)鍵列的數(shù)量。
 * “opstrategy”是子句中與最后一個(gè)分區(qū)鍵匹配的操作符的策略。
 * 'exprs'包含一些表達(dá)式,這些表達(dá)式包含要傳遞給分區(qū)綁定搜索函數(shù)的查找鍵。
 * “cmpfns”包含用于比較上述表達(dá)式與分區(qū)邊界的比較函數(shù)的OIDs。
 * “exprs”和“cmpfns”包含相同數(shù)量的條目,最多包含partnatts個(gè)條目。
 *
 * Once we find the offset of a partition bound using the lookup key, we
 * determine which partitions to include in the result based on the value of
 * 'opstrategy'.  For example, if it were equality, we'd return just the
 * partition that would contain that key or a set of partitions if the key
 * didn't consist of all partitioning columns.  For non-equality strategies,
 * we'd need to include other partitions as appropriate.
 * 一旦我們使用查找鍵找到分區(qū)綁定的偏移量,
 *   我們將根據(jù)“opstrategy”的值確定在結(jié)果中包含哪些分區(qū)。
 * 例如,如果它是相等的,我們只返回包含該鍵的分區(qū),或者如果該鍵不包含所有分區(qū)列,
 *  則返回一組分區(qū)。
 * 對(duì)于非等值的情況,需要適當(dāng)?shù)匕ㄆ渌謪^(qū)。
 *
 * 'nullkeys' is the set containing the offset of the partition keys (0 to
 * partnatts - 1) that were matched to an IS NULL clause.  This is only
 * considered for hash partitioning as we need to pass which keys are null
 * to the hash partition bound search function.  It is never possible to
 * have an expression be present in 'exprs' for a given partition key and
 * the corresponding bit set in 'nullkeys'.
 * 'nullkeys'是包含與is NULL子句匹配的分區(qū)鍵(0到partnatts - 1)偏移量的集合。
 * 這只適用于哈希分區(qū),因?yàn)槲覀冃枰獙⒛男╂I為null傳遞給哈希分區(qū)綁定搜索函數(shù)。
 * 對(duì)于給定的分區(qū)鍵和“nullkeys”中設(shè)置的相應(yīng)bit,不可能在“exprs”中出現(xiàn)表達(dá)式。
 */
typedef struct PartitionPruneStepOp
{
    PartitionPruneStep step;

    StrategyNumber opstrategy;
    List       *exprs;
    List       *cmpfns;
    Bitmapset  *nullkeys;
} PartitionPruneStepOp;

/*
 * PartitionPruneStepCombine - Information to prune using a BoolExpr clause
 * PartitionPruneStepCombine - 使用BoolExpr條件prune的信息
 *
 * For BoolExpr clauses, we combine the set of partitions determined for each
 * of the argument clauses.
 * 對(duì)于BoolExpr子句,我們?yōu)槊總€(gè)參數(shù)子句確定的分區(qū)集進(jìn)行組合。
 */
typedef enum PartitionPruneCombineOp
{
    PARTPRUNE_COMBINE_UNION,
    PARTPRUNE_COMBINE_INTERSECT
} PartitionPruneCombineOp;

typedef struct PartitionPruneStepCombine
{
    PartitionPruneStep step;

    PartitionPruneCombineOp combineOp;
    List       *source_stepids;
} PartitionPruneStepCombine;


/* The result of performing one PartitionPruneStep */
//執(zhí)行PartitionPruneStep步驟后的結(jié)果
typedef struct PruneStepResult
{
    /*
     * The offsets of bounds (in a table's boundinfo) whose partition is
     * selected by the pruning step.
     * 被pruning步驟選中的分區(qū)邊界(在數(shù)據(jù)表boundinfo中)偏移
     */
    Bitmapset  *bound_offsets;

    bool        scan_default;   /* 是否掃描默認(rèn)分區(qū)? Scan the default partition? */
    bool        scan_null;      /* 是否為NULL值掃描分區(qū)? Scan the partition for NULL values? */
} PruneStepResult;

二、源碼解讀

get_matching_partitions函數(shù)確定在分區(qū)pruning后仍然"存活"的分區(qū)。

 /*
 * get_matching_partitions
 *      Determine partitions that survive partition pruning
 *      確定在分區(qū)修剪pruning后仍然存在的分區(qū).
 *
 * Returns a Bitmapset of the RelOptInfo->part_rels indexes of the surviving
 * partitions.
 * 返回pruning后仍存在的分區(qū)的RelOptInfo->part_rels索引位圖集。
 */
Bitmapset *
get_matching_partitions(PartitionPruneContext *context, List *pruning_steps)
{
    Bitmapset  *result;
    int         num_steps = list_length(pruning_steps),
                i;
    PruneStepResult **results,
               *final_result;
    ListCell   *lc;

    /* If there are no pruning steps then all partitions match. */
    //沒(méi)有pruning步驟,則視為保留所有分區(qū)
    if (num_steps == 0)
    {
        Assert(context->nparts > 0);
        return bms_add_range(NULL, 0, context->nparts - 1);
    }

    /*
     * Allocate space for individual pruning steps to store its result.  Each
     * slot will hold a PruneStepResult after performing a given pruning step.
     * Later steps may use the result of one or more earlier steps.  The
     * result of applying all pruning steps is the value contained in the slot
     * of the last pruning step.
     * 為單個(gè)修剪步驟分配空間來(lái)存儲(chǔ)結(jié)果。
     * 每個(gè)slot將持有pruning后,執(zhí)行一個(gè)給定的pruning步驟。
     * 后面的步驟可以使用前面一個(gè)或多個(gè)步驟的結(jié)果。
     * 應(yīng)用所有步驟的結(jié)果是最后一個(gè)步驟的slot中包含的值。
     */
    results = (PruneStepResult **)
        palloc0(num_steps * sizeof(PruneStepResult *));
    foreach(lc, pruning_steps)//遍歷步驟
    {
        PartitionPruneStep *step = lfirst(lc);

        switch (nodeTag(step))
        {
            case T_PartitionPruneStepOp:
                results[step->step_id] =
                    perform_pruning_base_step(context,
                                              (PartitionPruneStepOp *) step);//執(zhí)行pruning基礎(chǔ)步驟
                break;

            case T_PartitionPruneStepCombine:
                results[step->step_id] =
                    perform_pruning_combine_step(context,
                                                 (PartitionPruneStepCombine *) step,
                                                 results);//執(zhí)行pruning組合步驟
                break;

            default:
                elog(ERROR, "invalid pruning step type: %d",
                     (int) nodeTag(step));
        }
    }

    /*
     * At this point we know the offsets of all the datums whose corresponding
     * partitions need to be in the result, including special null-accepting
     * and default partitions.  Collect the actual partition indexes now.
     * 到目前為止,我們已經(jīng)知道結(jié)果中需要的相應(yīng)分區(qū)的所有數(shù)據(jù)的偏移量,
     *   包括特殊的接受null的分區(qū)和默認(rèn)分區(qū)。
     * 現(xiàn)在收集實(shí)際的分區(qū)索引。
     */
    final_result = results[num_steps - 1];//最終結(jié)果
    Assert(final_result != NULL);
    i = -1;
    result = NULL;
    while ((i = bms_next_member(final_result->bound_offsets, i)) >= 0)
    {
        int         partindex = context->boundinfo->indexes[i];//分區(qū)編號(hào)

        /*
         * In range and hash partitioning cases, some slots may contain -1,
         * indicating that no partition has been defined to accept a given
         * range of data or for a given remainder, respectively. The default
         * partition, if any, in case of range partitioning, will be added to
         * the result, because the specified range still satisfies the query's
         * conditions.
         * 范圍分區(qū)和散列分區(qū),一些slot可能包含-1,
         *   這表示沒(méi)有定義接受給定范圍的數(shù)據(jù)或給定余數(shù)的分區(qū)。
         * 在范圍分區(qū)的情況下,默認(rèn)分區(qū)(如果有的話)將被添加到結(jié)果中,
         *   因?yàn)橹付ǖ姆秶匀粷M足查詢的條件。
         */
        if (partindex >= 0)
            result = bms_add_member(result, partindex);
    }

    /* Add the null and/or default partition if needed and if present. */
    //如果需要,添加NULL和/或默認(rèn)分區(qū)。
    if (final_result->scan_null)
    {
        Assert(context->strategy == PARTITION_STRATEGY_LIST);
        Assert(partition_bound_accepts_nulls(context->boundinfo));
        result = bms_add_member(result, context->boundinfo->null_index);
    }
    if (final_result->scan_default)
    {
        Assert(context->strategy == PARTITION_STRATEGY_LIST ||
               context->strategy == PARTITION_STRATEGY_RANGE);
        Assert(partition_bound_has_default(context->boundinfo));
        result = bms_add_member(result, context->boundinfo->default_index);
    }

    return result;
}


 /*
 * perform_pruning_base_step
 *      Determines the indexes of datums that satisfy conditions specified in
 *      'opstep'.
 *      確定滿足“opstep”中指定條件的數(shù)據(jù)索引。
 *
 * Result also contains whether special null-accepting and/or default
 * partition need to be scanned.
 * 結(jié)果還包含是否需要掃描特殊的可接受null和/或默認(rèn)分區(qū)。
 */
static PruneStepResult *
perform_pruning_base_step(PartitionPruneContext *context,
                          PartitionPruneStepOp *opstep)
{
    ListCell   *lc1,
               *lc2;
    int         keyno,
                nvalues;
    Datum       values[PARTITION_MAX_KEYS];
    FmgrInfo   *partsupfunc;
    int         stateidx;

    /*
     * There better be the same number of expressions and compare functions.
     * 最好有相同數(shù)量的表達(dá)式和比較函數(shù)。
     */
    Assert(list_length(opstep->exprs) == list_length(opstep->cmpfns));

    nvalues = 0;
    lc1 = list_head(opstep->exprs);
    lc2 = list_head(opstep->cmpfns);

    /*
     * Generate the partition lookup key that will be used by one of the
     * get_matching_*_bounds functions called below.
     * 生成將由下面調(diào)用的get_matching_*_bounds函數(shù)使用的分區(qū)查找鍵。
     */
    for (keyno = 0; keyno < context->partnatts; keyno++)
    {
        /*
         * For hash partitioning, it is possible that values of some keys are
         * not provided in operator clauses, but instead the planner found
         * that they appeared in a IS NULL clause.
         * 對(duì)于哈希分區(qū),操作符子句中可能沒(méi)有提供某些鍵的值,
         *   但是計(jì)劃器發(fā)現(xiàn)它們出現(xiàn)在is NULL子句中。
         */
        if (bms_is_member(keyno, opstep->nullkeys))
            continue;

        /*
         * For range partitioning, we must only perform pruning with values
         * for either all partition keys or a prefix thereof.
         * 對(duì)于范圍分區(qū),我們必須只對(duì)所有分區(qū)鍵或其前綴執(zhí)行值修剪pruning。
         */
        if (keyno > nvalues && context->strategy == PARTITION_STRATEGY_RANGE)
            break;

        if (lc1 != NULL)//步驟的條件表達(dá)式不為NULL
        {
            Expr       *expr;
            Datum       datum;
            bool        isnull;

            expr = lfirst(lc1);
            stateidx = PruneCxtStateIdx(context->partnatts,
                                        opstep->step.step_id, keyno);
            if (partkey_datum_from_expr(context, expr, stateidx,
                                        &datum, &isnull))
            {
                Oid         cmpfn;

                /*
                 * Since we only allow strict operators in pruning steps, any
                 * null-valued comparison value must cause the comparison to
                 * fail, so that no partitions could match.
                 * 由于我們只允許在修剪pruning步驟中使用嚴(yán)格的操作符,
                 *   任何空值比較值都必須導(dǎo)致比較失敗,這樣就沒(méi)有分區(qū)能夠匹配。
                 */
                if (isnull)
                {
                    PruneStepResult *result;

                    result = (PruneStepResult *) palloc(sizeof(PruneStepResult));
                    result->bound_offsets = NULL;
                    result->scan_default = false;
                    result->scan_null = false;

                    return result;
                }

                /* Set up the stepcmpfuncs entry, unless we already did */
                //配置stepcmpfuncs(步驟比較函數(shù))入口
                cmpfn = lfirst_oid(lc2);
                Assert(OidIsValid(cmpfn));
                if (cmpfn != context->stepcmpfuncs[stateidx].fn_oid)
                {
                    /*
                     * If the needed support function is the same one cached
                     * in the relation's partition key, copy the cached
                     * FmgrInfo.  Otherwise (i.e., when we have a cross-type
                     * comparison), an actual lookup is required.
                     * 如果所需的支持函數(shù)與關(guān)系分區(qū)鍵緩存的支持函數(shù)相同,
                     *   則復(fù)制緩存的FmgrInfo。
                     * 否則(比如存在一個(gè)跨類型比較時(shí)),需要實(shí)際的查找。
                     */
                    if (cmpfn == context->partsupfunc[keyno].fn_oid)
                        fmgr_info_copy(&context->stepcmpfuncs[stateidx],
                                       &context->partsupfunc[keyno],
                                       context->ppccontext);
                    else
                        fmgr_info_cxt(cmpfn, &context->stepcmpfuncs[stateidx],
                                      context->ppccontext);
                }

                values[keyno] = datum;
                nvalues++;
            }

            lc1 = lnext(lc1);
            lc2 = lnext(lc2);
        }
    }

    /*
     * Point partsupfunc to the entry for the 0th key of this step; the
     * additional support functions, if any, follow consecutively.
     * 將partsupfunc指向此步驟第0個(gè)鍵的條目;
     *   附加的支持功能(如果有的話)是連續(xù)的。
     */
    stateidx = PruneCxtStateIdx(context->partnatts, opstep->step.step_id, 0);
    partsupfunc = &context->stepcmpfuncs[stateidx];

    switch (context->strategy)
    {
        case PARTITION_STRATEGY_HASH:
            return get_matching_hash_bounds(context,
                                            opstep->opstrategy,
                                            values, nvalues,
                                            partsupfunc,
                                            opstep->nullkeys);

        case PARTITION_STRATEGY_LIST:
            return get_matching_list_bounds(context,
                                            opstep->opstrategy,
                                            values[0], nvalues,
                                            &partsupfunc[0],
                                            opstep->nullkeys);

        case PARTITION_STRATEGY_RANGE:
            return get_matching_range_bounds(context,
                                             opstep->opstrategy,
                                             values, nvalues,
                                             partsupfunc,
                                             opstep->nullkeys);

        default:
            elog(ERROR, "unexpected partition strategy: %d",
                 (int) context->strategy);
            break;
    }

    return NULL;
}

/*
 * perform_pruning_combine_step
 *      Determines the indexes of datums obtained by combining those given
 *      by the steps identified by cstep->source_stepids using the specified
 *      combination method
 *      使用指定的組合方法將cstep->source_stepids標(biāo)識(shí)的步驟組合在一起得到數(shù)據(jù)索引
 *
 * Since cstep may refer to the result of earlier steps, we also receive
 * step_results here.
 * 因?yàn)閏step可能引用前面步驟的結(jié)果,所以在這里也會(huì)收到step_results。
 */
static PruneStepResult *
perform_pruning_combine_step(PartitionPruneContext *context,
                             PartitionPruneStepCombine *cstep,
                             PruneStepResult **step_results)
{
    ListCell   *lc1;
    PruneStepResult *result = NULL;
    bool        firststep;

    /*
     * A combine step without any source steps is an indication to not perform
     * any partition pruning, we just return all partitions.
     * 如無(wú)源步驟,則不執(zhí)行分區(qū)pruning,返回所有分區(qū)
     */
    result = (PruneStepResult *) palloc0(sizeof(PruneStepResult));
    if (list_length(cstep->source_stepids) == 0)
    {
        PartitionBoundInfo boundinfo = context->boundinfo;

        result->bound_offsets = bms_add_range(NULL, 0, boundinfo->ndatums - 1);
        result->scan_default = partition_bound_has_default(boundinfo);
        result->scan_null = partition_bound_accepts_nulls(boundinfo);
        return result;
    }

    switch (cstep->combineOp)//根據(jù)組合操作類型確定相應(yīng)邏輯
    {
        case PARTPRUNE_COMBINE_UNION://PARTPRUNE_COMBINE_UNION
            foreach(lc1, cstep->source_stepids)
            {
                int         step_id = lfirst_int(lc1);
                PruneStepResult *step_result;

                /*
                 * step_results[step_id] must contain a valid result, which is
                 * confirmed by the fact that cstep's step_id is greater than
                 * step_id and the fact that results of the individual steps
                 * are evaluated in sequence of their step_ids.
                 * step_results[step_id]必須包含一個(gè)有效的結(jié)果,
                 *   cstep的step_id大于step_id,并且各個(gè)步驟的結(jié)果按其step_id的順序計(jì)算,
                 *   這一點(diǎn)是可以確認(rèn)的。
                 */
                if (step_id >= cstep->step.step_id)
                    elog(ERROR, "invalid pruning combine step argument");
                step_result = step_results[step_id];
                Assert(step_result != NULL);

                /* Record any additional datum indexes from this step */
                //記錄從該步驟產(chǎn)生的偏移索引
                result->bound_offsets = bms_add_members(result->bound_offsets,
                                                        step_result->bound_offsets);

                /* Update whether to scan null and default partitions. */
                //更新掃描null/default分區(qū)的標(biāo)記
                if (!result->scan_null)
                    result->scan_null = step_result->scan_null;
                if (!result->scan_default)
                    result->scan_default = step_result->scan_default;
            }
            break;

        case PARTPRUNE_COMBINE_INTERSECT://PARTPRUNE_COMBINE_INTERSECT
            firststep = true;
            foreach(lc1, cstep->source_stepids)
            {
                int         step_id = lfirst_int(lc1);
                PruneStepResult *step_result;

                if (step_id >= cstep->step.step_id)
                    elog(ERROR, "invalid pruning combine step argument");
                step_result = step_results[step_id];
                Assert(step_result != NULL);

                if (firststep)//第一個(gè)步驟
                {
                    /* Copy step's result the first time. */
                  //第一次,拷貝步驟的結(jié)果
                    result->bound_offsets =
                        bms_copy(step_result->bound_offsets);
                    result->scan_null = step_result->scan_null;
                    result->scan_default = step_result->scan_default;
                    firststep = false;
                }
                else
                {
                    /* Record datum indexes common to both steps */
                    //記錄其他步驟產(chǎn)生的索引
                    result->bound_offsets =
                        bms_int_members(result->bound_offsets,
                                        step_result->bound_offsets);

                    /* Update whether to scan null and default partitions. */
                    //更新掃描null/default分區(qū)的標(biāo)記
                    if (result->scan_null)
                        result->scan_null = step_result->scan_null;
                    if (result->scan_default)
                        result->scan_default = step_result->scan_default;
                }
            }
            break;
    }

    return result;
}


/*
 * get_matching_hash_bounds
 *      Determine offset of the hash bound matching the specified values,
 *      considering that all the non-null values come from clauses containing
 *      a compatible hash equality operator and any keys that are null come
 *      from an IS NULL clause.
 *      考慮到所有非空值都來(lái)自包含兼容的哈希相等操作符的子句,
 *        而所有空鍵都來(lái)自IS NULL子句,因此確定匹配指定值的哈希邊界的偏移量。
 *
 * Generally this function will return a single matching bound offset,
 * although if a partition has not been setup for a given modulus then we may
 * return no matches.  If the number of clauses found don't cover the entire
 * partition key, then we'll need to return all offsets.
 * 通常,這個(gè)函數(shù)會(huì)返回一個(gè)匹配的邊界偏移量,
 *   但是如果沒(méi)有為給定的模數(shù)設(shè)置分區(qū),則可能不返回匹配值。
 * 如果找到的子句數(shù)量不包含整個(gè)分區(qū)鍵,那么我們需要返回所有偏移量。
 *
 * 'opstrategy' if non-zero must be HTEqualStrategyNumber.
 *  opstrategy - 如非0,則為HTEqualStrategyNumber
 *
 * 'values' contains Datums indexed by the partition key to use for pruning.
 *  values 包含用于分區(qū)鍵執(zhí)行pruning的數(shù)據(jù)值
 *
 * 'nvalues', the number of Datums in the 'values' array.
 *  nvalues values數(shù)組大小
 *
 * 'partsupfunc' contains partition hashing functions that can produce correct
 * hash for the type of the values contained in 'values'.
 *  partsupfunc 存儲(chǔ)分區(qū)hash函數(shù),可以為values產(chǎn)生hash值
 * 
 * 'nullkeys' is the set of partition keys that are null.
 *  nullkeys 為null的分區(qū)鍵值集合
 */
static PruneStepResult *
get_matching_hash_bounds(PartitionPruneContext *context,
                         StrategyNumber opstrategy, Datum *values, int nvalues,
                         FmgrInfo *partsupfunc, Bitmapset *nullkeys)
{
    PruneStepResult *result = (PruneStepResult *) palloc0(sizeof(PruneStepResult));
    PartitionBoundInfo boundinfo = context->boundinfo;
    int        *partindices = boundinfo->indexes;
    int         partnatts = context->partnatts;
    bool        isnull[PARTITION_MAX_KEYS];
    int         i;
    uint64      rowHash;
    int         greatest_modulus;

    Assert(context->strategy == PARTITION_STRATEGY_HASH);

    /*
     * For hash partitioning we can only perform pruning based on equality
     * clauses to the partition key or IS NULL clauses.  We also can only
     * prune if we got values for all keys.
     * 對(duì)于Hash分區(qū),只能基于等值條件語(yǔ)句或者是IS NULL條件進(jìn)行pruning.
     * 當(dāng)然,如果可以拿到所有鍵的值,也可以執(zhí)行prune
     */
    if (nvalues + bms_num_members(nullkeys) == partnatts)
    {
        /*
         * If there are any values, they must have come from clauses
         * containing an equality operator compatible with hash partitioning.
         * 如存在values,那么這些值必須從包含一個(gè)與hash分區(qū)兼容的等值操作符的條件語(yǔ)句而來(lái)
         */
        Assert(opstrategy == HTEqualStrategyNumber || nvalues == 0);

        for (i = 0; i < partnatts; i++)
            isnull[i] = bms_is_member(i, nullkeys);

        greatest_modulus = get_hash_partition_greatest_modulus(boundinfo);
        rowHash = compute_partition_hash_value(partnatts, partsupfunc,
                                               values, isnull);

        if (partindices[rowHash % greatest_modulus] >= 0)
            result->bound_offsets =
                bms_make_singleton(rowHash % greatest_modulus);
    }
    else
    {
        /* Getting here means at least one hash partition exists. */
        //程序執(zhí)行到這里,意味著至少存在一個(gè)hash分區(qū)
        Assert(boundinfo->ndatums > 0);
        result->bound_offsets = bms_add_range(NULL, 0,
                                              boundinfo->ndatums - 1);
    }

    /*
     * There is neither a special hash null partition or the default hash
     * partition.
     * 要么存在一個(gè)特別的hash null分區(qū),要么是默認(rèn)的hash分區(qū)
     */
    result->scan_null = result->scan_default = false;

    return result;
}

三、跟蹤分析

測(cè)試腳本如下

testdb=# explain verbose select * from t_hash_partition where c1 = 1 OR c1 = 2;
                                     QUERY PLAN                                      
-------------------------------------------------------------------------------------
 Append  (cost=0.00..30.53 rows=6 width=200)
   ->  Seq Scan on public.t_hash_partition_1  (cost=0.00..15.25 rows=3 width=200)
         Output: t_hash_partition_1.c1, t_hash_partition_1.c2, t_hash_partition_1.c3
         Filter: ((t_hash_partition_1.c1 = 1) OR (t_hash_partition_1.c1 = 2))
   ->  Seq Scan on public.t_hash_partition_3  (cost=0.00..15.25 rows=3 width=200)
         Output: t_hash_partition_3.c1, t_hash_partition_3.c2, t_hash_partition_3.c3
         Filter: ((t_hash_partition_3.c1 = 1) OR (t_hash_partition_3.c1 = 2))
(7 rows)

啟動(dòng)gdb,設(shè)置斷點(diǎn)

(gdb) b get_matching_partitions
Breakpoint 1 at 0x804d3b: file partprune.c, line 619.
(gdb) c
Continuing.

Breakpoint 1, get_matching_partitions (context=0x7fff4a5e3930, pruning_steps=0x14d5300) at partprune.c:619
619     int         num_steps = list_length(pruning_steps),
(gdb) n
626     if (num_steps == 0)

查看輸入?yún)?shù),pruning_steps是有3個(gè)ITEM的鏈表

(gdb) p *pruning_steps
$1 = {type = T_List, length = 3, head = 0x14d52d8, tail = 0x14d58d0}

pruning_steps的3個(gè)ITEM類型分別是PartitionPruneStepOp/PartitionPruneStepOp/PartitionPruneStepCombine
第1和2個(gè)ITEM的expr是Const,即常量1和2

(gdb) p *(Node *)pruning_steps->head->data.ptr_value
$2 = {type = T_PartitionPruneStepOp} -->Node類型
(gdb) p *(PartitionPruneStepOp *)pruning_steps->head->data.ptr_value
$3 = {step = {type = T_PartitionPruneStepOp, step_id = 0}, opstrategy = 1, exprs = 0x14d52a0, cmpfns = 0x14d5240, 
  nullkeys = 0x0}
(gdb) set $ppso=(PartitionPruneStepOp *)pruning_steps->head->data.ptr_value
(gdb) p *$ppso->exprs
$4 = {type = T_List, length = 1, head = 0x14d5278, tail = 0x14d5278}
(gdb) p *(Node *)$ppso->exprs->head->data.ptr_value
$5 = {type = T_Const}
(gdb) p *(Const *)$ppso->exprs->head->data.ptr_value
$6 = {xpr = {type = T_Const}, consttype = 23, consttypmod = -1, constcollid = 0, constlen = 4, constvalue = 1, 
  constisnull = false, constbyval = true, location = 42} -->第1個(gè)步驟的表達(dá)式,常量1
(gdb) set $ppso=(PartitionPruneStepOp *)pruning_steps->head->next->data.ptr_value
(gdb) p *(Const *)$ppso->exprs->head->data.ptr_value
$7 = {xpr = {type = T_Const}, consttype = 23, consttypmod = -1, constcollid = 0, constlen = 4, constvalue = 2, 
  constisnull = false, constbyval = true, location = 52}  -->第2個(gè)步驟的表達(dá)式,常量2
(gdb) p *(Node *)pruning_steps->head->next->next->data.ptr_value
$8 = {type = T_PartitionPruneStepCombine}
(gdb) set $ppsc=(PartitionPruneStepCombine *)pruning_steps->head->next->next->data.ptr_value
(gdb) p *$ppsc
$9 = {step = {type = T_PartitionPruneStepCombine, step_id = 2}, combineOp = PARTPRUNE_COMBINE_UNION, 
  source_stepids = 0x14d5480}  -->第3個(gè)步驟,組合操作是PARTPRUNE_COMBINE_UNION

為步驟結(jié)果分配內(nèi)存

(gdb) n
640         palloc0(num_steps * sizeof(PruneStepResult *));
(gdb) p num_steps
$10 = 3
(gdb) n
639     results = (PruneStepResult **)
(gdb) 
641     foreach(lc, pruning_steps)

開(kāi)始遍歷pruning步驟,進(jìn)入perform_pruning_base_step函數(shù)

(gdb) 
643         PartitionPruneStep *step = lfirst(lc);
(gdb) 
645         switch (nodeTag(step))
(gdb) 
648                 results[step->step_id] =
(gdb) step
649                     perform_pruning_base_step(context,
(gdb) 
perform_pruning_base_step (context=0x7fff4a5e3930, opstep=0x14d4e98) at partprune.c:2995
2995        Assert(list_length(opstep->exprs) == list_length(opstep->cmpfns));

perform_pruning_base_step->查看輸入?yún)?shù),比較函數(shù)是OID=425的函數(shù)

(gdb) p *opstep->cmpfns
$12 = {type = T_OidList, length = 1, head = 0x14d5218, tail = 0x14d5218}
(gdb) p opstep->cmpfns->head->data.oid_value
$16 = 425
(gdb) n
2998        lc1 = list_head(opstep->exprs);
(gdb) 
2999        lc2 = list_head(opstep->cmpfns);
(gdb)

perform_pruning_base_step->遍歷分區(qū)鍵

(gdb) 
3005        for (keyno = 0; keyno < context->partnatts; keyno++)
(gdb) 
3012            if (bms_is_member(keyno, opstep->nullkeys)) -->沒(méi)有null鍵
(gdb) 
3019            if (keyno > nvalues && context->strategy == PARTITION_STRATEGY_RANGE) -->nvalues為0
(gdb) p nvalues
$17 = 0
(gdb) n
3022            if (lc1 != NULL)
(gdb) 
3028                expr = lfirst(lc1); -->獲取步驟中的表達(dá)式,其實(shí)是常量1
(gdb) 
3029                stateidx = PruneCxtStateIdx(context->partnatts, -->stateidx為0
(gdb) p *expr
$18 = {type = T_Const}
(gdb) p *(Const *)expr
$19 = {xpr = {type = T_Const}, consttype = 23, consttypmod = -1, constcollid = 0, constlen = 4, constvalue = 1, 
  constisnull = false, constbyval = true, location = 42}
(gdb) n
3031                if (partkey_datum_from_expr(context, expr, stateidx,
(gdb) p stateidx
$20 = 0
(gdb) n
3041                    if (isnull) -->非NULL
(gdb) p isnull
$21 = false

perform_pruning_base_step->獲取比較函數(shù)進(jìn)行處理

(gdb) n
3054                    cmpfn = lfirst_oid(lc2); --> OID=425
(gdb) 
3055                    Assert(OidIsValid(cmpfn));
(gdb) 
3056                    if (cmpfn != context->stepcmpfuncs[stateidx].fn_oid) -->fn_oid為0
(gdb) 
3064                        if (cmpfn == context->partsupfunc[keyno].fn_oid) -->fn_oid為425
(gdb) p context->stepcmpfuncs[stateidx].fn_oid
$22 = 0
(gdb) p context->partsupfunc[keyno].fn_oid
$23 = 425
(gdb) n
3065                            fmgr_info_copy(&context->stepcmpfuncs[stateidx],
(gdb) 
3066                                           &context->partsupfunc[keyno],
(gdb) 
3065                            fmgr_info_copy(&context->stepcmpfuncs[stateidx],
(gdb) 
3066                                           &context->partsupfunc[keyno],
(gdb) 
3065                            fmgr_info_copy(&context->stepcmpfuncs[stateidx], --> 拷貝函數(shù)
(gdb) 
3073                    values[keyno] = datum;-->設(shè)置值
(gdb) p datum
$24 = 1
(gdb) n
3074                    nvalues++;
(gdb) 
3077                lc1 = lnext(lc1);
(gdb) 
3078                lc2 = lnext(lc2);
(gdb)

perform_pruning_base_step->完成分區(qū)鍵遍歷

(gdb) n
3005        for (keyno = 0; keyno < context->partnatts; keyno++)
(gdb) 
3086        stateidx = PruneCxtStateIdx(context->partnatts, opstep->step.step_id, 0);
(gdb) 
3087        partsupfunc = &context->stepcmpfuncs[stateidx];
(gdb) 
3089        switch (context->strategy)
(gdb) 
3092                return get_matching_hash_bounds(context,
(gdb)

perform_pruning_base_step->進(jìn)入get_matching_hash_bounds函數(shù)

(gdb) 
3092                return get_matching_hash_bounds(context,
(gdb) step
3093                                                opstep->opstrategy,
(gdb) 
3092                return get_matching_hash_bounds(context,
(gdb) 
get_matching_hash_bounds (context=0x7fff4a5e3930, opstrategy=1, values=0x7fff4a5e3750, nvalues=1, partsupfunc=0x14d3068, 
    nullkeys=0x0) at partprune.c:2156
2156        PruneStepResult *result = (PruneStepResult *) palloc0(sizeof(PruneStepResult));
(gdb)

get_matching_hash_bounds->變量賦值

(gdb) n
2157        PartitionBoundInfo boundinfo = context->boundinfo;
(gdb) 
2158        int        *partindices = boundinfo->indexes;
(gdb) 
2159        int         partnatts = context->partnatts;
(gdb) 
2165        Assert(context->strategy == PARTITION_STRATEGY_HASH);
(gdb) 
2172        if (nvalues + bms_num_members(nullkeys) == partnatts)
(gdb)

get_matching_hash_bounds->分區(qū)邊界信息,共有6個(gè)分區(qū),Index分別是0-5

(gdb) p boundinfo
$25 = (PartitionBoundInfo) 0x14d32a0
(gdb) p *boundinfo
$26 = {strategy = 104 'h', ndatums = 6, datums = 0x14d32f8, kind = 0x0, indexes = 0x14d2e00, null_index = -1, 
  default_index = -1}
(gdb) p *boundinfo->datums
$27 = (Datum *) 0x14d3350
(gdb) p **boundinfo->datums
$28 = 6
(gdb) p **boundinfo->indexes
Cannot access memory at address 0x0
(gdb) p *boundinfo->indexes
$29 = 0
(gdb) p boundinfo->indexes[0]
$30 = 0
(gdb) p boundinfo->indexes[1]
$31 = 1
(gdb) p boundinfo->indexes[5]
$32 = 5
(gdb)

get_matching_hash_bounds->分區(qū)索引和分區(qū)鍵數(shù)

(gdb) p *partindices
$33 = 0
(gdb) p partnatts
$34 = 1
(gdb) 
(gdb) p nvalues
$35 = 1
(gdb) p bms_num_members(nullkeys)
$36 = 0

get_matching_hash_bounds->遍歷分區(qū)鍵,判斷值是否落在分區(qū)中

(gdb) n
2178            Assert(opstrategy == HTEqualStrategyNumber || nvalues == 0);
(gdb) 
2180            for (i = 0; i < partnatts; i++)
(gdb) 
2181                isnull[i] = bms_is_member(i, nullkeys);
(gdb) 
2180            for (i = 0; i < partnatts; i++)
(gdb) 
2183            greatest_modulus = get_hash_partition_greatest_modulus(boundinfo);
(gdb) 
2184            rowHash = compute_partition_hash_value(partnatts, partsupfunc,
(gdb) 
2187            if (partindices[rowHash % greatest_modulus] >= 0)

get_matching_hash_bounds->

(gdb) p values
$43 = (Datum *) 0x7fff4a5e3750
(gdb) p *values
$44 = 1 --> 約束條件
(gdb) p isnull[0]
$38 = false -->不為NULL
(gdb) p greatest_modulus
$39 = 6 -->6個(gè)分區(qū)
(gdb) p rowHash
$40 = 11274504255086170040 -->values算出的Hash值
(gdb) p rowHash % greatest_modulus
$41 = 2 --> 所在的分區(qū)
(gdb) p partindices[2]
$42 = 2 -->存在該分區(qū)
(gdb)

get_matching_hash_bounds->返回結(jié)果(bound_offsets->words=4,即編號(hào)2)

(gdb) n
2189                    bms_make_singleton(rowHash % greatest_modulus);
(gdb) 
2188                result->bound_offsets =
(gdb) 
2203        result->scan_null = result->scan_default = false;
(gdb) 
2205        return result;
(gdb) 
2206    }
(gdb) p *result
$45 = {bound_offsets = 0x14d59b8, scan_default = false, scan_null = false}
(gdb) p *result->bound_offsets
$46 = {nwords = 1, words = 0x14d59bc}
(gdb) p *result->bound_offsets->words
$47 = 4
(gdb)

回到get_matching_partitions

(gdb) n
perform_pruning_base_step (context=0x7fff4a5e3930, opstep=0x14d4e98) at partprune.c:3119
3119    }
(gdb) 
get_matching_partitions (context=0x7fff4a5e3930, pruning_steps=0x14d5300) at partprune.c:648
648                 results[step->step_id] =
(gdb) 
651                 break;
(gdb) 
641     foreach(lc, pruning_steps)
(gdb) 
643         PartitionPruneStep *step = lfirst(lc);
(gdb) 
645         switch (nodeTag(step))
(gdb) p

繼續(xù)執(zhí)行步驟,進(jìn)入perform_pruning_combine_step

654                 results[step->step_id] =
(gdb) 
655                     perform_pruning_combine_step(context,
(gdb) step
perform_pruning_combine_step (context=0x7fff4a5e3930, cstep=0x14d5898, step_results=0x14d5958) at partprune.c:3136
3136        PruneStepResult *result = NULL;
(gdb)

perform_pruning_combine_step->進(jìn)入PARTPRUNE_COMBINE_UNION處理邏輯

(gdb) n
3143        result = (PruneStepResult *) palloc0(sizeof(PruneStepResult));
(gdb) 
3144        if (list_length(cstep->source_stepids) == 0)
(gdb) 
3154        switch (cstep->combineOp)
(gdb) 
3157                foreach(lc1, cstep->source_stepids)
(gdb) 
(gdb) p *cstep
$49 = {step = {type = T_PartitionPruneStepCombine, step_id = 2}, combineOp = PARTPRUNE_COMBINE_UNION, 
  source_stepids = 0x14d5480}

perform_pruning_combine_step->遍歷組合步驟的源步驟 cstep->source_stepids,合并這些步驟的結(jié)果

(gdb) n
3159                    int         step_id = lfirst_int(lc1);
...
(gdb) 
3174                    result->bound_offsets = bms_add_members(result->bound_offsets,
(gdb) 
3178                    if (!result->scan_null)
(gdb) 
3179                        result->scan_null = step_result->scan_null;
(gdb) 
3180                    if (!result->scan_default)
(gdb) 
3181                        result->scan_default = step_result->scan_default;
(gdb) 
3157                foreach(lc1, cstep->source_stepids)
(gdb) 
3183                break;
(gdb) 
3223        return result;
(gdb)

perform_pruning_combine_step->最終結(jié)果

(gdb) p *result
$54 = {bound_offsets = 0x14d5a48, scan_default = false, scan_null = false}
(gdb) p *result->bound_offsets
$55 = {nwords = 1, words = 0x14d5a4c}
(gdb) p *result->bound_offsets->words
$56 = 5

perform_pruning_combine_step->回到get_matching_partitions

(gdb) n
3224    }
(gdb) 
get_matching_partitions (context=0x7fff4a5e3930, pruning_steps=0x14d5300) at partprune.c:654
654                 results[step->step_id] =

完成所有步驟的處理

(gdb) n
658                 break;
(gdb) 
641     foreach(lc, pruning_steps)
(gdb) n
671     final_result = results[num_steps - 1];
(gdb) 
672     Assert(final_result != NULL);
(gdb)

構(gòu)造結(jié)果位圖集

...
675     while ((i = bms_next_member(final_result->bound_offsets, i)) >= 0)
(gdb) n
677         int         partindex = context->boundinfo->indexes[i];
(gdb) 
687         if (partindex >= 0)
(gdb) 
688             result = bms_add_member(result, partindex);
(gdb)

完成調(diào)用

gdb) 
675     while ((i = bms_next_member(final_result->bound_offsets, i)) >= 0)
(gdb) 
692     if (final_result->scan_null)
(gdb) 
698     if (final_result->scan_default)
(gdb) 
706     return result;
(gdb) 
707 }
(gdb)

關(guān)于“PostgreSQL中prune_append_rel_partitions->get_matching_partitions函數(shù)怎么用”這篇文章就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,使各位可以學(xué)到更多知識(shí),如果覺(jué)得文章不錯(cuò),請(qǐng)把它分享出去讓更多的人看到。

向AI問(wèn)一下細(xì)節(jié)

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

AI