溫馨提示×

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

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

PostgreSQL中哪個(gè)函數(shù)為heap tuple找到合適的分區(qū)

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

本篇內(nèi)容介紹了“PostgreSQL中哪個(gè)函數(shù)為heap tuple找到合適的分區(qū)”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

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

ModifyTable
ModifyTable Node
通過(guò)插入、更新或刪除,將子計(jì)劃生成的行應(yīng)用到結(jié)果表。

/* ----------------
 *   ModifyTable node -
 *      Apply rows produced by subplan(s) to result table(s),
 *      by inserting, updating, or deleting.
 *      通過(guò)插入、更新或刪除,將子計(jì)劃生成的行應(yīng)用到結(jié)果表。
 *
 * If the originally named target table is a partitioned table, both
 * nominalRelation and rootRelation contain the RT index of the partition
 * root, which is not otherwise mentioned in the plan.  Otherwise rootRelation
 * is zero.  However, nominalRelation will always be set, as it's the rel that
 * EXPLAIN should claim is the INSERT/UPDATE/DELETE target.
 * 如果最初命名的目標(biāo)表是分區(qū)表,則nominalRelation和rootRelation都包含分區(qū)根的RT索引,計(jì)劃中沒(méi)有另外提到這個(gè)索引。
 * 否則,根關(guān)系為零。但是,總是會(huì)設(shè)置名義關(guān)系,nominalRelation因?yàn)镋XPLAIN應(yīng)該聲明的rel是INSERT/UPDATE/DELETE目標(biāo)關(guān)系。
 * 
 * Note that rowMarks and epqParam are presumed to be valid for all the
 * subplan(s); they can't contain any info that varies across subplans.
 * 注意,rowMarks和epqParam被假定對(duì)所有子計(jì)劃有效;
 * 它們不能包含任何在子計(jì)劃中變化的信息。
 * ----------------
 */
typedef struct ModifyTable
{
    Plan        plan;
    CmdType     operation;      /* 操作類(lèi)型;INSERT, UPDATE, or DELETE */
    bool        canSetTag;      /* 是否需要設(shè)置tag?do we set the command tag/es_processed? */
    Index       nominalRelation;    /* 用于EXPLAIN的父RT索引;Parent RT index for use of EXPLAIN */
    Index       rootRelation;   /* 根Root RT索引(如目標(biāo)為分區(qū)表);Root RT index, if target is partitioned */
    bool        partColsUpdated;    /* 更新了層次結(jié)構(gòu)中的分區(qū)關(guān)鍵字;some part key in hierarchy updated */
    List       *resultRelations;    /* RT索引的整型鏈表;integer list of RT indexes */
    int         resultRelIndex; /* 計(jì)劃鏈表中第一個(gè)resultRel的索引;index of first resultRel in plan's list */
    int         rootResultRelIndex; /* 分區(qū)表根索引;index of the partitioned table root */
    List       *plans;          /* 生成源數(shù)據(jù)的計(jì)劃鏈表;plan(s) producing source data */
    List       *withCheckOptionLists;   /* 每一個(gè)目標(biāo)表均具備的WCO鏈表;per-target-table WCO lists */
    List       *returningLists; /* 每一個(gè)目標(biāo)表均具備的RETURNING鏈表;per-target-table RETURNING tlists */
    List       *fdwPrivLists;   /* 每一個(gè)目標(biāo)表的FDW私有數(shù)據(jù)鏈表;per-target-table FDW private data lists */
    Bitmapset  *fdwDirectModifyPlans;   /* FDW DM計(jì)劃索引位圖;indices of FDW DM plans */
    List       *rowMarks;       /* rowMarks鏈表;PlanRowMarks (non-locking only) */
    int         epqParam;       /* EvalPlanQual再解析使用的參數(shù)ID;ID of Param for EvalPlanQual re-eval */
    OnConflictAction onConflictAction;  /* ON CONFLICT action */
    List       *arbiterIndexes; /* 沖突仲裁器索引表;List of ON CONFLICT arbiter index OIDs  */
    List       *onConflictSet;  /* SET for INSERT ON CONFLICT DO UPDATE */
    Node       *onConflictWhere;    /* WHERE for ON CONFLICT UPDATE */
    Index       exclRelRTI;     /* RTI of the EXCLUDED pseudo relation */
    List       *exclRelTlist;   /* 已排除偽關(guān)系的投影列鏈表;tlist of the EXCLUDED pseudo relation */
} ModifyTable;

ResultRelInfo
ResultRelInfo結(jié)構(gòu)體
每當(dāng)更新一個(gè)現(xiàn)有的關(guān)系時(shí),我們必須更新關(guān)系上的索引,也許還需要觸發(fā)觸發(fā)器。ResultRelInfo保存關(guān)于結(jié)果關(guān)系所需的所有信息,包括索引。

/*
 * ResultRelInfo
 * ResultRelInfo結(jié)構(gòu)體
 *
 * Whenever we update an existing relation, we have to update indexes on the
 * relation, and perhaps also fire triggers.  ResultRelInfo holds all the
 * information needed about a result relation, including indexes.
 * 每當(dāng)更新一個(gè)現(xiàn)有的關(guān)系時(shí),我們必須更新關(guān)系上的索引,也許還需要觸發(fā)觸發(fā)器。
 * ResultRelInfo保存關(guān)于結(jié)果關(guān)系所需的所有信息,包括索引。
 * 
 * Normally, a ResultRelInfo refers to a table that is in the query's
 * range table; then ri_RangeTableIndex is the RT index and ri_RelationDesc
 * is just a copy of the relevant es_relations[] entry.  But sometimes,
 * in ResultRelInfos used only for triggers, ri_RangeTableIndex is zero
 * and ri_RelationDesc is a separately-opened relcache pointer that needs
 * to be separately closed.  See ExecGetTriggerResultRel.
 * 通常,ResultRelInfo是指查詢(xún)范圍表中的表;
 * ri_RangeTableIndex是RT索引,而ri_RelationDesc只是相關(guān)es_relations[]條目的副本。
 * 但有時(shí),在只用于觸發(fā)器的ResultRelInfos中,ri_RangeTableIndex為零(NULL),
 *   而ri_RelationDesc是一個(gè)需要單獨(dú)關(guān)閉單獨(dú)打開(kāi)的relcache指針。
 *   具體可參考ExecGetTriggerResultRel結(jié)構(gòu)體。
 */
typedef struct ResultRelInfo
{
    NodeTag     type;

    /* result relation's range table index, or 0 if not in range table */
    //RTE索引
    Index       ri_RangeTableIndex;

    /* relation descriptor for result relation */
    //結(jié)果/目標(biāo)relation的描述符
    Relation    ri_RelationDesc;

    /* # of indices existing on result relation */
    //目標(biāo)關(guān)系中索引數(shù)目
    int         ri_NumIndices;

    /* array of relation descriptors for indices */
    //索引的關(guān)系描述符數(shù)組(索引視為一個(gè)relation)
    RelationPtr ri_IndexRelationDescs;

    /* array of key/attr info for indices */
    //索引的鍵/屬性數(shù)組
    IndexInfo **ri_IndexRelationInfo;

    /* triggers to be fired, if any */
    //觸發(fā)的索引
    TriggerDesc *ri_TrigDesc;

    /* cached lookup info for trigger functions */
    //觸發(fā)器函數(shù)(緩存)
    FmgrInfo   *ri_TrigFunctions;

    /* array of trigger WHEN expr states */
    //WHEN表達(dá)式狀態(tài)的觸發(fā)器數(shù)組
    ExprState **ri_TrigWhenExprs;

    /* optional runtime measurements for triggers */
    //可選的觸發(fā)器運(yùn)行期度量器
    Instrumentation *ri_TrigInstrument;

    /* FDW callback functions, if foreign table */
    //FDW回調(diào)函數(shù)
    struct FdwRoutine *ri_FdwRoutine;

    /* available to save private state of FDW */
    //可用于存儲(chǔ)FDW的私有狀態(tài)
    void       *ri_FdwState;

    /* true when modifying foreign table directly */
    //直接更新FDW時(shí)為T(mén)
    bool        ri_usesFdwDirectModify;

    /* list of WithCheckOption's to be checked */
    //WithCheckOption鏈表
    List       *ri_WithCheckOptions;

    /* list of WithCheckOption expr states */
    //WithCheckOption表達(dá)式鏈表
    List       *ri_WithCheckOptionExprs;

    /* array of constraint-checking expr states */
    //約束檢查表達(dá)式狀態(tài)數(shù)組
    ExprState **ri_ConstraintExprs;

    /* for removing junk attributes from tuples */
    //用于從元組中刪除junk屬性
    JunkFilter *ri_junkFilter;

    /* list of RETURNING expressions */
    //RETURNING表達(dá)式鏈表
    List       *ri_returningList;

    /* for computing a RETURNING list */
    //用于計(jì)算RETURNING鏈表
    ProjectionInfo *ri_projectReturning;

    /* list of arbiter indexes to use to check conflicts */
    //用于檢查沖突的仲裁器索引的列表
    List       *ri_onConflictArbiterIndexes;

    /* ON CONFLICT evaluation state */
    //ON CONFLICT解析狀態(tài)
    OnConflictSetState *ri_onConflict;

    /* partition check expression */
    //分區(qū)檢查表達(dá)式鏈表
    List       *ri_PartitionCheck;

    /* partition check expression state */
    //分區(qū)檢查表達(dá)式狀態(tài)
    ExprState  *ri_PartitionCheckExpr;

    /* relation descriptor for root partitioned table */
    //分區(qū)root根表描述符
    Relation    ri_PartitionRoot;

    /* Additional information specific to partition tuple routing */
    //額外的分區(qū)元組路由信息
    struct PartitionRoutingInfo *ri_PartitionInfo;
} ResultRelInfo;

PartitionRoutingInfo
PartitionRoutingInfo結(jié)構(gòu)體
分區(qū)路由信息,用于將元組路由到表分區(qū)的結(jié)果關(guān)系信息。

/*
 * PartitionRoutingInfo
 * PartitionRoutingInfo - 分區(qū)路由信息
 * 
 * Additional result relation information specific to routing tuples to a
 * table partition.
 * 用于將元組路由到表分區(qū)的結(jié)果關(guān)系信息。
 */
typedef struct PartitionRoutingInfo
{
    /*
     * Map for converting tuples in root partitioned table format into
     * partition format, or NULL if no conversion is required.
     * 映射,用于將根分區(qū)表格式的元組轉(zhuǎn)換為分區(qū)格式,如果不需要轉(zhuǎn)換,則轉(zhuǎn)換為NULL。
     */
    TupleConversionMap *pi_RootToPartitionMap;

    /*
     * Map for converting tuples in partition format into the root partitioned
     * table format, or NULL if no conversion is required.
     * 映射,用于將分區(qū)格式的元組轉(zhuǎn)換為根分區(qū)表格式,如果不需要轉(zhuǎn)換,則轉(zhuǎn)換為NULL。
     */
    TupleConversionMap *pi_PartitionToRootMap;

    /*
     * Slot to store tuples in partition format, or NULL when no translation
     * is required between root and partition.
     * 以分區(qū)格式存儲(chǔ)元組的slot.在根分區(qū)和分區(qū)之間不需要轉(zhuǎn)換時(shí)為NULL。
     */
    TupleTableSlot *pi_PartitionTupleSlot;
} PartitionRoutingInfo;

TupleConversionMap
TupleConversionMap結(jié)構(gòu)體,用于存儲(chǔ)元組轉(zhuǎn)換映射信息.

typedef struct TupleConversionMap
{
    TupleDesc   indesc;         /* 源行類(lèi)型的描述符;tupdesc for source rowtype */
    TupleDesc   outdesc;        /* 結(jié)果行類(lèi)型的描述符;tupdesc for result rowtype */
    AttrNumber *attrMap;        /* 輸入字段的索引信息,0表示NULL;indexes of input fields, or 0 for null */
    Datum      *invalues;       /* 析構(gòu)源數(shù)據(jù)的工作空間;workspace for deconstructing source */
    bool       *inisnull;       //是否為NULL標(biāo)記數(shù)組
    Datum      *outvalues;      /* 構(gòu)造結(jié)果的工作空間;workspace for constructing result */
    bool       *outisnull;      //null標(biāo)記
} TupleConversionMap;

二、源碼解讀

ExecFindPartition函數(shù)在以父節(jié)點(diǎn)為根的分區(qū)樹(shù)中為包含在*slot中的元組找到目標(biāo)分區(qū)(葉子分區(qū))

/*
 * ExecFindPartition -- Find a leaf partition in the partition tree rooted
 * at parent, for the heap tuple contained in *slot
 * ExecFindPartition —— 在以父節(jié)點(diǎn)為根的分區(qū)樹(shù)中為包含在*slot中的堆元組找到目標(biāo)分區(qū)(葉子分區(qū))
 * 
 * estate must be non-NULL; we'll need it to compute any expressions in the
 * partition key(s)
 * estate不能為NULL;需要使用它計(jì)算分區(qū)鍵上的表達(dá)式
 *
 * If no leaf partition is found, this routine errors out with the appropriate
 * error message, else it returns the leaf partition sequence number
 * as an index into the array of (ResultRelInfos of) all leaf partitions in
 * the partition tree.
 * 如果沒(méi)有找到目標(biāo)分區(qū),則此例程將輸出適當(dāng)?shù)腻e(cuò)誤消息,
 *   否則它將分區(qū)樹(shù)中所有葉子分區(qū)的數(shù)組(ResultRelInfos)的目標(biāo)分區(qū)序列號(hào)作為索引返回。
 */
int
ExecFindPartition(ResultRelInfo *resultRelInfo, PartitionDispatch *pd,
                  TupleTableSlot *slot, EState *estate)
{
    int         result;//結(jié)果索引號(hào)
    Datum       values[PARTITION_MAX_KEYS];//值類(lèi)型Datum
    bool        isnull[PARTITION_MAX_KEYS];//是否null?
    Relation    rel;//關(guān)系
    PartitionDispatch dispatch;//
    ExprContext *ecxt = GetPerTupleExprContext(estate);//表達(dá)式上下文
    TupleTableSlot *ecxt_scantuple_old = ecxt->ecxt_scantuple;//原tuple slot
    TupleTableSlot *myslot = NULL;//臨時(shí)變量
    MemoryContext   oldcxt;//原內(nèi)存上下文
    HeapTuple       tuple;//tuple

    /* use per-tuple context here to avoid leaking memory */
    //使用每個(gè)元組上下文來(lái)避免內(nèi)存泄漏
    oldcxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));

    /*
     * First check the root table's partition constraint, if any.  No point in
     * routing the tuple if it doesn't belong in the root table itself.
     * 首先檢查根表的分區(qū)約束(如果有的話(huà))。如果元組不屬于根表本身,則沒(méi)有必要路由它。
     */
    if (resultRelInfo->ri_PartitionCheck)
        ExecPartitionCheck(resultRelInfo, slot, estate, true);

    /* start with the root partitioned table */
    //從root分區(qū)表開(kāi)始
    tuple = ExecFetchSlotTuple(slot);//獲取tuple
    dispatch = pd[0];//root
    while (true)
    {
        PartitionDesc partdesc;//分區(qū)描述符
        TupleConversionMap *map = dispatch->tupmap;//轉(zhuǎn)換映射
        int         cur_index = -1;//當(dāng)前索引

        rel = dispatch->reldesc;//relation
        partdesc = RelationGetPartitionDesc(rel);//獲取rel描述符

        /*
         * Convert the tuple to this parent's layout, if different from the
         * current relation.
         * 如果元組與當(dāng)前關(guān)系不同,則將tuple轉(zhuǎn)換為parent's layout。
         */
        myslot = dispatch->tupslot;
        if (myslot != NULL && map != NULL)
        {
            tuple = do_convert_tuple(tuple, map);
            ExecStoreTuple(tuple, myslot, InvalidBuffer, true);
            slot = myslot;
        }

        /*
         * Extract partition key from tuple. Expression evaluation machinery
         * that FormPartitionKeyDatum() invokes expects ecxt_scantuple to
         * point to the correct tuple slot.  The slot might have changed from
         * what was used for the parent table if the table of the current
         * partitioning level has different tuple descriptor from the parent.
         * So update ecxt_scantuple accordingly.
         * 從元組中提取分區(qū)鍵。
         * FormPartitionKeyDatum()調(diào)用的表達(dá)式計(jì)算機(jī)制期望ecxt_scantuple指向正確的元組slot。
         * 如果當(dāng)前分區(qū)級(jí)別的表與父表具有不同的元組描述符,那么slot可能已經(jīng)改變了父表使用的slot。
         * 因此相應(yīng)地更新ecxt_scantuple。
         */
        ecxt->ecxt_scantuple = slot;
        FormPartitionKeyDatum(dispatch, slot, estate, values, isnull);

        /*
         * Nothing for get_partition_for_tuple() to do if there are no
         * partitions to begin with.
         * 如無(wú)分區(qū),則退出(無(wú)需調(diào)用get_partition_for_tuple)
         */
        if (partdesc->nparts == 0)
        {
            result = -1;
            break;
        }
        //調(diào)用get_partition_for_tuple
        cur_index = get_partition_for_tuple(rel, values, isnull);

        /*
         * cur_index < 0 means we failed to find a partition of this parent.
         * cur_index >= 0 means we either found the leaf partition, or the
         * next parent to find a partition of.
         * cur_index < 0表示未能找到該父節(jié)點(diǎn)的分區(qū)。
         * cur_index >= 0表示要么找到葉子分區(qū),要么找到下一個(gè)父分區(qū)。
         */
        if (cur_index < 0)
        {
            result = -1;
            break;//找不到,退出
        }
        else if (dispatch->indexes[cur_index] >= 0)
        {
            result = dispatch->indexes[cur_index];
            /* success! */
            break;//找到了,退出循環(huán)
        }
        else
        {
            /* move down one level */
            //移到下一層查找
            dispatch = pd[-dispatch->indexes[cur_index]];

            /*
             * Release the dedicated slot, if it was used.  Create a copy of
             * the tuple first, for the next iteration.
             */
            if (slot == myslot)
            {
                tuple = ExecCopySlotTuple(myslot);
                ExecClearTuple(myslot);
            }
        }
    }

    /* Release the tuple in the lowest parent's dedicated slot. */
     //釋放位于最低父級(jí)的專(zhuān)用的slot相對(duì)應(yīng)的元組。
    if (slot == myslot)
        ExecClearTuple(myslot);

    /* A partition was not found. */
    //找不到partition
    if (result < 0)
    {
        char       *val_desc;

        val_desc = ExecBuildSlotPartitionKeyDescription(rel,
                                                        values, isnull, 64);
        Assert(OidIsValid(RelationGetRelid(rel)));
        ereport(ERROR,
                (errcode(ERRCODE_CHECK_VIOLATION),
                 errmsg("no partition of relation \"%s\" found for row",
                        RelationGetRelationName(rel)),
                 val_desc ? errdetail("Partition key of the failing row contains %s.", val_desc) : 0));
    }

    MemoryContextSwitchTo(oldcxt);
    ecxt->ecxt_scantuple = ecxt_scantuple_old;

    return result;
}



/*
 * get_partition_for_tuple
 *      Finds partition of relation which accepts the partition key specified
 *      in values and isnull
 * get_partition_for_tuple
 *      查找參數(shù)為values和isnull中指定分區(qū)鍵的關(guān)系分區(qū)
 *
 * Return value is index of the partition (>= 0 and < partdesc->nparts) if one
 * found or -1 if none found.
 * 返回值是分區(qū)的索引(>= 0和< partdesc->nparts),
 *   如果找到一個(gè)分區(qū),則返回值;如果沒(méi)有找到,則返回值為-1。
 */
static int
get_partition_for_tuple(Relation relation, Datum *values, bool *isnull)
{
    int         bound_offset;
    int         part_index = -1;
    PartitionKey key = RelationGetPartitionKey(relation);
    PartitionDesc partdesc = RelationGetPartitionDesc(relation);
    PartitionBoundInfo boundinfo = partdesc->boundinfo;

    /* Route as appropriate based on partitioning strategy. */
    //基于分區(qū)的策略進(jìn)行路由
    switch (key->strategy)
    {
        case PARTITION_STRATEGY_HASH://HASH分區(qū)
            {
                int         greatest_modulus;
                uint64      rowHash;

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

                part_index = boundinfo->indexes[rowHash % greatest_modulus];
            }
            break;

        case PARTITION_STRATEGY_LIST://列表分區(qū)
            if (isnull[0])
            {
                if (partition_bound_accepts_nulls(boundinfo))
                    part_index = boundinfo->null_index;
            }
            else
            {
                bool        equal = false;

                bound_offset = partition_list_bsearch(key->partsupfunc,
                                                      key->partcollation,
                                                      boundinfo,
                                                      values[0], &equal);
                if (bound_offset >= 0 && equal)
                    part_index = boundinfo->indexes[bound_offset];
            }
            break;

        case PARTITION_STRATEGY_RANGE://范圍分區(qū)
            {
                bool        equal = false,
                            range_partkey_has_null = false;
                int         i;

                /*
                 * No range includes NULL, so this will be accepted by the
                 * default partition if there is one, and otherwise rejected.
                 * 任何范圍都不包含NULL值,因此默認(rèn)分區(qū)將接受該值(如果存在),否則將拒絕該值。
                 */
                for (i = 0; i < key->partnatts; i++)
                {
                    if (isnull[i])
                    {
                        range_partkey_has_null = true;
                        break;
                    }
                }

                if (!range_partkey_has_null)
                {
                    bound_offset = partition_range_datum_bsearch(key->partsupfunc,
                                                                 key->partcollation,
                                                                 boundinfo,
                                                                 key->partnatts,
                                                                 values,
                                                                 &equal);

                    /*
                     * The bound at bound_offset is less than or equal to the
                     * tuple value, so the bound at offset+1 is the upper
                     * bound of the partition we're looking for, if there
                     * actually exists one.
                     * bound_offset的邊界小于或等于元組值,所以offset+1的邊界是我們要找的分區(qū)的上界,如存在的話(huà)。
                     */
                    part_index = boundinfo->indexes[bound_offset + 1];
                }
            }
            break;

        default:
            elog(ERROR, "unexpected partition strategy: %d",
                 (int) key->strategy);//暫不支持其他分區(qū)
    }

    /*
     * part_index < 0 means we failed to find a partition of this parent. Use
     * the default partition, if there is one.
     * part_index < 0表示沒(méi)有找到這個(gè)父節(jié)點(diǎn)的分區(qū)。如存在分區(qū),則使用默認(rèn)分區(qū)。
     */
    if (part_index < 0)
        part_index = boundinfo->default_index;

    return part_index;
}

依賴(lài)的函數(shù)

/*
 * get_hash_partition_greatest_modulus
 *
 * Returns the greatest modulus of the hash partition bound. The greatest
 * modulus will be at the end of the datums array because hash partitions are
 * arranged in the ascending order of their moduli and remainders.
 * 返回哈希分區(qū)邊界的最大模。
 * 最大模量將位于datums數(shù)組的末尾,因?yàn)楣7謪^(qū)按照它們的模塊和余數(shù)的升序排列。
 */
int
get_hash_partition_greatest_modulus(PartitionBoundInfo bound)
{
    Assert(bound && bound->strategy == PARTITION_STRATEGY_HASH);
    Assert(bound->datums && bound->ndatums > 0);
    Assert(DatumGetInt32(bound->datums[bound->ndatums - 1][0]) > 0);

    return DatumGetInt32(bound->datums[bound->ndatums - 1][0]);
}

/*
 * compute_partition_hash_value
 * 
 * Compute the hash value for given partition key values.
 * 給定分區(qū)鍵值,計(jì)算相應(yīng)的Hash值
 */
uint64
compute_partition_hash_value(int partnatts, FmgrInfo *partsupfunc,
                             Datum *values, bool *isnull)
{
    int         i;
    uint64      rowHash = 0;//返回結(jié)果
    Datum       seed = UInt64GetDatum(HASH_PARTITION_SEED);

    for (i = 0; i < partnatts; i++)
    {
        /* Nulls are just ignored */
        if (!isnull[i])
        {
            //不為NULL
            Datum       hash;

            Assert(OidIsValid(partsupfunc[i].fn_oid));

            /*
             * Compute hash for each datum value by calling respective
             * datatype-specific hash functions of each partition key
             * attribute.
             * 通過(guò)調(diào)用每個(gè)分區(qū)鍵屬性的特定于數(shù)據(jù)類(lèi)型的哈希函數(shù),計(jì)算每個(gè)數(shù)據(jù)值的哈希值。
             */
            hash = FunctionCall2(&partsupfunc[i], values[i], seed);

            /* Form a single 64-bit hash value */
            //組合成一個(gè)單獨(dú)的64bit哈希值
            rowHash = hash_combine64(rowHash, DatumGetUInt64(hash));
        }
    }

    return rowHash;
}


/*
 * Combine two 64-bit hash values, resulting in another hash value, using the
 * same kind of technique as hash_combine().  Testing shows that this also
 * produces good bit mixing.
 * 使用與hash_combine()相同的技術(shù)組合兩個(gè)64位哈希值,生成另一個(gè)哈希值。
 * 測(cè)試表明,該方法也能產(chǎn)生良好的混合效果。
 */
static inline uint64
hash_combine64(uint64 a, uint64 b)
{
    /* 0x49a0f4dd15e5a8e3 is 64bit random data */
    a ^= b + UINT64CONST(0x49a0f4dd15e5a8e3) + (a << 54) + (a >> 7);
    return a;
}

//兩個(gè)參數(shù)的函數(shù)調(diào)用宏定義
#define FunctionCall2(flinfo, arg1, arg2) \
   FunctionCall2Coll(flinfo, InvalidOid, arg1, arg2)

三、跟蹤分析

測(cè)試腳本如下

-- Hash Partition
drop table if exists t_hash_partition;
create table t_hash_partition (c1 int not null,c2  varchar(40),c3 varchar(40)) partition by hash(c1);
create table t_hash_partition_1 partition of t_hash_partition for values with (modulus 6,remainder 0);
create table t_hash_partition_2 partition of t_hash_partition for values with (modulus 6,remainder 1);
create table t_hash_partition_3 partition of t_hash_partition for values with (modulus 6,remainder 2);
create table t_hash_partition_4 partition of t_hash_partition for values with (modulus 6,remainder 3);
create table t_hash_partition_5 partition of t_hash_partition for values with (modulus 6,remainder 4);
create table t_hash_partition_6 partition of t_hash_partition for values with (modulus 6,remainder 5);

insert into t_hash_partition(c1,c2,c3) VALUES(0,'HASH0','HAHS0');

啟動(dòng)gdb,設(shè)置斷點(diǎn),進(jìn)入ExecFindPartition

(gdb) b ExecFindPartition
Breakpoint 1 at 0x6e19e7: file execPartition.c, line 227.
(gdb) c
Continuing.

Breakpoint 1, ExecFindPartition (resultRelInfo=0x14299a8, pd=0x142ae58, slot=0x142a140, estate=0x1429758)
    at execPartition.c:227
227     ExprContext *ecxt = GetPerTupleExprContext(estate);

初始化變量,切換內(nèi)存上下文

227     ExprContext *ecxt = GetPerTupleExprContext(estate);
(gdb) n
228     TupleTableSlot *ecxt_scantuple_old = ecxt->ecxt_scantuple;
(gdb) 
229     TupleTableSlot *myslot = NULL;
(gdb) 
234     oldcxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
(gdb) p ecxt_scantuple_old
$1 = (TupleTableSlot *) 0x0

提取tuple,獲取dispatch

(gdb) n
244     tuple = ExecFetchSlotTuple(slot);
(gdb) 
245     dispatch = pd[0];
(gdb) n
249         TupleConversionMap *map = dispatch->tupmap;
(gdb) p *tuple
$2 = {t_len = 40, t_self = {ip_blkid = {bi_hi = 65535, bi_lo = 65535}, ip_posid = 0}, t_tableOid = 0, t_data = 0x142b158}
(gdb)

查看分發(fā)器dispatch信息

(gdb) p *dispatch
$3 = {reldesc = 0x7fbfa6900950, key = 0x1489860, keystate = 0x0, partdesc = 0x149b130, tupslot = 0x0, tupmap = 0x0, 
  indexes = 0x142ade8}
(gdb) p *dispatch->reldesc
$4 = {rd_node = {spcNode = 1663, dbNode = 16402, relNode = 16986}, rd_smgr = 0x0, rd_refcnt = 1, rd_backend = -1, 
  rd_islocaltemp = false, rd_isnailed = false, rd_isvalid = true, rd_indexvalid = 0 '\000', rd_statvalid = false, 
  rd_createSubid = 0, rd_newRelfilenodeSubid = 0, rd_rel = 0x7fbfa6900b68, rd_att = 0x7fbfa6900c80, rd_id = 16986, 
  rd_lockInfo = {lockRelId = {relId = 16986, dbId = 16402}}, rd_rules = 0x0, rd_rulescxt = 0x0, trigdesc = 0x0, 
  rd_rsdesc = 0x0, rd_fkeylist = 0x0, rd_fkeyvalid = false, rd_partkeycxt = 0x1489710, rd_partkey = 0x1489860, 
  rd_pdcxt = 0x149afe0, rd_partdesc = 0x149b130, rd_partcheck = 0x0, rd_indexlist = 0x0, rd_oidindex = 0, rd_pkindex = 0, 
  rd_replidindex = 0, rd_statlist = 0x0, rd_indexattr = 0x0, rd_projindexattr = 0x0, rd_keyattr = 0x0, rd_pkattr = 0x0, 
  rd_idattr = 0x0, rd_projidx = 0x0, rd_pubactions = 0x0, rd_options = 0x0, rd_index = 0x0, rd_indextuple = 0x0, 
  rd_amhandler = 0, rd_indexcxt = 0x0, rd_amroutine = 0x0, rd_opfamily = 0x0, rd_opcintype = 0x0, rd_support = 0x0, 
  rd_supportinfo = 0x0, rd_indoption = 0x0, rd_indexprs = 0x0, rd_indpred = 0x0, rd_exclops = 0x0, rd_exclprocs = 0x0, 
  rd_exclstrats = 0x0, rd_amcache = 0x0, rd_indcollation = 0x0, rd_fdwroutine = 0x0, rd_toastoid = 0, pgstat_info = 0x0}
----------------------------------------------------------------------------  
testdb=# select relname from pg_class where oid=16986;
     relname      
------------------
 t_hash_partition -->hash分區(qū)表
(1 row)
----------------------------------------------------------------------------  
(gdb) p *dispatch->key
$5 = {strategy = 104 'h', partnatts = 1, partattrs = 0x14898f8, partexprs = 0x0, partopfamily = 0x1489918, 
  partopcintype = 0x1489938, partsupfunc = 0x1489958, partcollation = 0x14899b0, parttypid = 0x14899d0, 
  parttypmod = 0x14899f0, parttyplen = 0x1489a10, parttypbyval = 0x1489a30, 
  parttypalign = 0x1489a50 "i~\177\177\177\177\177\177\b", parttypcoll = 0x1489a70}
(gdb) p *dispatch->partdesc
$6 = {nparts = 6, oids = 0x149b168, boundinfo = 0x149b1a0}
(gdb) p *dispatch->partdesc->boundinfo
$8 = {strategy = 104 'h', ndatums = 6, datums = 0x149b1f8, kind = 0x0, indexes = 0x149b288, null_index = -1, 
  default_index = -1}
(gdb) p *dispatch->partdesc->boundinfo->datums
$9 = (Datum *) 0x149b2c0
(gdb) p **dispatch->partdesc->boundinfo->datums
$10 = 6
(gdb) p *dispatch->indexes
$15 = 0

分區(qū)描述符中的oids(分別對(duì)應(yīng)t_hash_partition_1->6)

(gdb) p dispatch->partdesc->oids[0]
$11 = 16989
(gdb) p dispatch->partdesc->oids[1]
$12 = 16992
...
(gdb) p dispatch->partdesc->oids[5]
$13 = 17004

索引信息

(gdb) p dispatch->indexes[0]
$16 = 0
...
(gdb) p dispatch->indexes[5]
$18 = 5

設(shè)置當(dāng)前索引(-1),獲取relation信息,獲取分區(qū)描述符

(gdb) n
250         int         cur_index = -1;
(gdb) 
252         rel = dispatch->reldesc;
(gdb) 
253         partdesc = RelationGetPartitionDesc(rel);
(gdb) 
259         myslot = dispatch->tupslot;
(gdb) p *partdesc
$19 = {nparts = 6, oids = 0x149b168, boundinfo = 0x149b1a0}
(gdb)

myslot為NULL

(gdb) n
260         if (myslot != NULL && map != NULL)
(gdb) p myslot
$20 = (TupleTableSlot *) 0x0

從元組中提取分區(qū)鍵

(gdb) n
275         ecxt->ecxt_scantuple = slot;
(gdb) 
276         FormPartitionKeyDatum(dispatch, slot, estate, values, isnull);
(gdb) 
282         if (partdesc->nparts == 0)
(gdb) p *partdesc
$21 = {nparts = 6, oids = 0x149b168, boundinfo = 0x149b1a0}
(gdb) p *slot
$22 = {type = T_TupleTableSlot, tts_isempty = false, tts_shouldFree = true, tts_shouldFreeMin = false, tts_slow = false, 
  tts_tuple = 0x142b140, tts_tupleDescriptor = 0x1429f28, tts_mcxt = 0x1429640, tts_buffer = 0, tts_nvalid = 1, 
  tts_values = 0x142a1a0, tts_isnull = 0x142a1b8, tts_mintuple = 0x0, tts_minhdr = {t_len = 0, t_self = {ip_blkid = {
        bi_hi = 0, bi_lo = 0}, ip_posid = 0}, t_tableOid = 0, t_data = 0x0}, tts_off = 4, tts_fixedTupleDescriptor = true}
(gdb) p values
$23 = {0, 7152626, 21144656, 21144128, 7141053, 21143088, 21144128, 16372128, 140722434628688, 0, 0, 0, 21143872, 
  140722434628736, 140461078524324, 21141056, 21144128, 0, 21143088, 21141056, 7152279, 0, 7421941, 21141056, 21143088, 
  21614576, 140722434628800, 7422189, 21143872, 140722434628839, 21143088, 21144128}
(gdb) p isnull
$24 = {false, 91, 186, 126, 252, 127, false, false, 208, 166, 71, false, false, false, false, false, 2, 
  false <repeats 15 times>}
(gdb) p *estate
$25 = {type = T_EState, es_direction = ForwardScanDirection, es_snapshot = 0x1451ee0, es_crosscheck_snapshot = 0x0, 
  es_range_table = 0x14a71c0, es_plannedstmt = 0x14a72b8, 
  es_sourceText = 0x13acec8 "insert into t_hash_partition(c1,c2,c3) VALUES(0,'HASH0','HAHS0');", es_junkFilter = 0x0, 
  es_output_cid = 0, es_result_relations = 0x14299a8, es_num_result_relations = 1, es_result_relation_info = 0x14299a8, 
  es_root_result_relations = 0x0, es_num_root_result_relations = 0, es_tuple_routing_result_relations = 0x0, 
  es_trig_target_relations = 0x0, es_trig_tuple_slot = 0x142afc0, es_trig_oldtup_slot = 0x0, es_trig_newtup_slot = 0x0, 
  es_param_list_info = 0x0, es_param_exec_vals = 0x1429970, es_queryEnv = 0x0, es_query_cxt = 0x1429640, 
  es_tupleTable = 0x142a200, es_rowMarks = 0x0, es_processed = 0, es_lastoid = 0, es_top_eflags = 0, es_instrument = 0, 
  es_finished = false, es_exprcontexts = 0x1429ef0, es_subplanstates = 0x0, es_auxmodifytables = 0x0, 
  es_per_tuple_exprcontext = 0x142b080, es_epqTuple = 0x0, es_epqTupleSet = 0x0, es_epqScanDone = 0x0, 
  es_use_parallel_mode = false, es_query_dsa = 0x0, es_jit_flags = 0, es_jit = 0x0, es_jit_worker_instr = 0x0}
(gdb)

進(jìn)入get_partition_for_tuple函數(shù)

(gdb) n
288         cur_index = get_partition_for_tuple(rel, values, isnull);
(gdb) step
get_partition_for_tuple (relation=0x7fbfa6900950, values=0x7ffc7eba5bb0, isnull=0x7ffc7eba5b90) at execPartition.c:1139
1139        int         part_index = -1;
(gdb)

get_partition_for_tuple->獲取分區(qū)鍵

1139        int         part_index = -1;
(gdb) n
1140        PartitionKey key = RelationGetPartitionKey(relation);
(gdb) 
1141        PartitionDesc partdesc = RelationGetPartitionDesc(relation);
(gdb) p key
$26 = (PartitionKey) 0x1489860
(gdb) p *key
$27 = {strategy = 104 'h', partnatts = 1, partattrs = 0x14898f8, partexprs = 0x0, partopfamily = 0x1489918, 
  partopcintype = 0x1489938, partsupfunc = 0x1489958, partcollation = 0x14899b0, parttypid = 0x14899d0, 
  parttypmod = 0x14899f0, parttyplen = 0x1489a10, parttypbyval = 0x1489a30, 
  parttypalign = 0x1489a50 "i~\177\177\177\177\177\177\b", parttypcoll = 0x1489a70}

get_partition_for_tuple->獲取分區(qū)描述符&分區(qū)邊界信息

(gdb) n
1142        PartitionBoundInfo boundinfo = partdesc->boundinfo;
(gdb) 
1145        switch (key->strategy)
(gdb) p *partdesc
$28 = {nparts = 6, oids = 0x149b168, boundinfo = 0x149b1a0}
(gdb) p *boundinfo
$29 = {strategy = 104 'h', ndatums = 6, datums = 0x149b1f8, kind = 0x0, indexes = 0x149b288, null_index = -1, 
  default_index = -1}

get_partition_for_tuple->進(jìn)入Hash分區(qū)處理分支

(gdb) n
1152                    greatest_modulus = get_hash_partition_greatest_modulus(boundinfo);
(gdb) p key->strategy
$30 = 104 'h'

get_partition_for_tuple->計(jì)算模塊數(shù)&行hash值,獲得分區(qū)編號(hào)(index)

(gdb) n
1153                    rowHash = compute_partition_hash_value(key->partnatts,
(gdb) n
1157                    part_index = boundinfo->indexes[rowHash % greatest_modulus];
(gdb) 
1159                break;
(gdb) p part_index
$31 = 2
(gdb)

get_partition_for_tuple->返回

(gdb) n
1228        if (part_index < 0)
(gdb) 
1231        return part_index;
(gdb) 
1232    }
(gdb) 
ExecFindPartition (resultRelInfo=0x14299a8, pd=0x142ae58, slot=0x142a140, estate=0x1429758) at execPartition.c:295
295         if (cur_index < 0)
(gdb)

已取得分區(qū)信息(分區(qū)索引編號(hào)=2)

(gdb) n
300         else if (dispatch->indexes[cur_index] >= 0)
(gdb) 
302             result = dispatch->indexes[cur_index];
(gdb) p dispatch->indexes[cur_index]
$32 = 2
(gdb) n
304             break;
(gdb) 
324     if (slot == myslot)
(gdb) 
328     if (result < 0)
(gdb) 
342     MemoryContextSwitchTo(oldcxt);
(gdb) 
343     ecxt->ecxt_scantuple = ecxt_scantuple_old;
(gdb) 
345     return result;
(gdb)

完成函數(shù)調(diào)用

(gdb) n
346 }
(gdb) 
ExecPrepareTupleRouting (mtstate=0x1429ac0, estate=0x1429758, proute=0x142a7a8, targetRelInfo=0x14299a8, slot=0x142a140)
    at nodeModifyTable.c:1716
1716        Assert(partidx >= 0 && partidx < proute->num_partitions);

“PostgreSQL中哪個(gè)函數(shù)為heap tuple找到合適的分區(qū)”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

向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