code for Global-Partition-Index feature

Signed-off-by: xiliu <xiliu_h@163.com>
This commit is contained in:
xiliu
2020-08-25 15:10:14 +08:00
parent 339cd59f26
commit c040d78287
157 changed files with 12502 additions and 939 deletions

View File

@ -1582,6 +1582,7 @@ void InitResultRelInfo(ResultRelInfo *resultRelInfo, Relation resultRelationDesc
resultRelInfo->ri_RangeTableIndex = resultRelationIndex;
resultRelInfo->ri_RelationDesc = resultRelationDesc;
resultRelInfo->ri_NumIndices = 0;
resultRelInfo->ri_ContainGPI = false;
resultRelInfo->ri_IndexRelationDescs = NULL;
resultRelInfo->ri_IndexRelationInfo = NULL;
/* make a copy so as not to depend on relcache info not changing... */

View File

@ -1112,6 +1112,7 @@ void ExecOpenIndices(ResultRelInfo* resultRelInfo, bool speculative)
IndexInfo** indexInfoArray;
resultRelInfo->ri_NumIndices = 0;
resultRelInfo->ri_ContainGPI = false;
/* fast path if no indexes */
if (!RelationGetForm(resultRelation)->relhasindex)
@ -1155,6 +1156,12 @@ void ExecOpenIndices(ResultRelInfo* resultRelInfo, bool speculative)
index_close(indexDesc, RowExclusiveLock);
continue;
}
/* Check index whether is global parition index, and save */
if (RelationIsGlobalIndex(indexDesc)) {
resultRelInfo->ri_ContainGPI = true;
}
/* extract index key information from the index's pg_index info */
ii = BuildIndexInfo(indexDesc);
@ -1397,6 +1404,7 @@ List* ExecInsertIndexTuples(TupleTableSlot* slot, ItemPointer tupleid, EState* e
bool isnull[INDEX_MAX_KEYS];
Relation actualheap;
bool ispartitionedtable = false;
bool containGPI;
List* partitionIndexOidList = NIL;
/*
@ -1407,6 +1415,7 @@ List* ExecInsertIndexTuples(TupleTableSlot* slot, ItemPointer tupleid, EState* e
relationDescs = resultRelInfo->ri_IndexRelationDescs;
indexInfoArray = resultRelInfo->ri_IndexRelationInfo;
heapRelation = resultRelInfo->ri_RelationDesc;
containGPI = resultRelInfo->ri_ContainGPI;
/*
* We will use the EState's per-tuple context for evaluating predicates
@ -1427,7 +1436,8 @@ List* ExecInsertIndexTuples(TupleTableSlot* slot, ItemPointer tupleid, EState* e
if (p == NULL || p->pd_part == NULL) {
return NIL;
}
if (!p->pd_part->indisusable) {
/* If the global partition index is included, the index insertion process needs to continue */
if (!p->pd_part->indisusable && !containGPI) {
numIndices = 0;
}
} else {
@ -1437,6 +1447,15 @@ List* ExecInsertIndexTuples(TupleTableSlot* slot, ItemPointer tupleid, EState* e
if (bucketId != InvalidBktId) {
searchHBucketFakeRelation(estate->esfRelations, estate->es_query_cxt, actualheap, bucketId, actualheap);
}
/* Partition create in current transaction, set partition and rel reloption wait_clean_gpi */
if (RelationCreateInCurrXact(actualheap) && containGPI && !PartitionEnableWaitCleanGpi(p)) {
/* partition create not set wait_clean_gpi, must use update, and we ensure no concurrency */
PartitionSetWaitCleanGpi(RelationGetRelid(actualheap), true, false);
/* Partitioned create set wait_clean_gpi=n, and we want save it, so just use inplace */
PartitionedSetWaitCleanGpi(RelationGetRelationName(heapRelation), RelationGetRelid(heapRelation), true, true);
}
/*
* for each index, form and insert the index tuple
*/
@ -1461,28 +1480,36 @@ List* ExecInsertIndexTuples(TupleTableSlot* slot, ItemPointer tupleid, EState* e
continue;
}
if (ispartitionedtable) {
partitionedindexid = RelationGetRelid(indexRelation);
if (!PointerIsValid(partitionIndexOidList)) {
partitionIndexOidList = PartitionGetPartIndexList(p);
// no local indexes available
if (!PointerIsValid(partitionIndexOidList)) {
return NIL;
if (ispartitionedtable && !RelationIsGlobalIndex(indexRelation)) {
/* The GPI index insertion is the same as that of a common table */
if (RelationIsGlobalIndex(indexRelation)) {
if (indexRelation->rd_index->indisusable == false) {
continue;
}
actualindex = indexRelation;
} else {
partitionedindexid = RelationGetRelid(indexRelation);
if (!PointerIsValid(partitionIndexOidList)) {
partitionIndexOidList = PartitionGetPartIndexList(p);
// no local indexes available
if (!PointerIsValid(partitionIndexOidList)) {
return NIL;
}
}
}
indexpartitionid = searchPartitionIndexOid(partitionedindexid, partitionIndexOidList);
indexpartitionid = searchPartitionIndexOid(partitionedindexid, partitionIndexOidList);
searchFakeReationForPartitionOid(estate->esfRelations,
estate->es_query_cxt,
indexRelation,
indexpartitionid,
actualindex,
indexpartition,
RowExclusiveLock);
// skip unusable index
if (false == indexpartition->pd_part->indisusable) {
continue;
searchFakeReationForPartitionOid(estate->esfRelations,
estate->es_query_cxt,
indexRelation,
indexpartitionid,
actualindex,
indexpartition,
RowExclusiveLock);
// skip unusable index
if (false == indexpartition->pd_part->indisusable) {
continue;
}
}
} else {
actualindex = indexRelation;
@ -1617,7 +1644,7 @@ bool check_violation(Relation heap, Relation index, IndexInfo* indexInfo, ItemPo
Oid* constr_procs = indexInfo->ii_ExclusionProcs;
uint16* constr_strats = indexInfo->ii_ExclusionStrats;
Oid* index_collations = index->rd_indcollation;
int index_natts = index->rd_index->indnatts;
int indnkeyatts = IndexRelationGetNumberOfKeyAttributes(index);
IndexScanDesc index_scan;
HeapTuple tup;
ScanKeyData scankeys[INDEX_MAX_KEYS];
@ -1633,7 +1660,7 @@ bool check_violation(Relation heap, Relation index, IndexInfo* indexInfo, ItemPo
* If any of the input values are NULL, the constraint check is assumed to
* pass (i.e., we assume the operators are strict).
*/
for (i = 0; i < index_natts; i++) {
for (i = 0; i < indnkeyatts; i++) {
if (isnull[i]) {
return true;
}
@ -1652,7 +1679,7 @@ bool check_violation(Relation heap, Relation index, IndexInfo* indexInfo, ItemPo
*/
InitDirtySnapshot(DirtySnapshot);
for (i = 0; i < index_natts; i++) {
for (i = 0; i < indnkeyatts; i++) {
ScanKeyEntryInitialize(
&scankeys[i], 0, i + 1, constr_strats[i], InvalidOid, index_collations[i], constr_procs[i], values[i]);
}
@ -1677,8 +1704,8 @@ bool check_violation(Relation heap, Relation index, IndexInfo* indexInfo, ItemPo
retry:
conflict = false;
found_self = false;
index_scan = index_beginscan(heap, index, &DirtySnapshot, index_natts, 0);
index_rescan(index_scan, scankeys, index_natts, NULL, 0);
index_scan = index_beginscan(heap, index, &DirtySnapshot, indnkeyatts, 0);
index_rescan(index_scan, scankeys, indnkeyatts, NULL, 0);
while ((tup = index_getnext(index_scan, ForwardScanDirection)) != NULL) {
TransactionId xwait;
@ -1795,10 +1822,10 @@ retry:
static bool index_recheck_constraint(
Relation index, Oid* constr_procs, Datum* existing_values, const bool* existing_isnull, Datum* new_values)
{
int index_natts = index->rd_index->indnatts;
int indnkeyatts = IndexRelationGetNumberOfKeyAttributes(index);
int i;
for (i = 0; i < index_natts; i++) {
for (i = 0; i < indnkeyatts; i++) {
/* Assume the exclusion operators are strict */
if (existing_isnull[i]) {
return false;

View File

@ -125,6 +125,18 @@ Node* MultiExecBitmapAnd(BitmapAndState* node)
if (result == NULL) {
result = subresult; /* first subplan */
} else {
/*
* If the global tbm intersect with non-global tbm,
* set the final result to non-global tbm.
*
* Notes: This scenario means that the two filter criteria used in the where
* condition of the sql statement, one uses the local partitioned index and
* the other uses the global partitioned index
*/
if (tbm_is_global(result) != tbm_is_global(subresult)) {
tbm_set_global(result, false);
}
tbm_intersect(result, subresult);
tbm_free(subresult);
}

View File

@ -58,6 +58,12 @@ static void bitgetpage(HeapScanDesc scan, TBMIterateResult* tbmres);
static void ExecInitPartitionForBitmapHeapScan(BitmapHeapScanState* scanstate, EState* estate);
static void ExecInitNextPartitionForBitmapHeapScan(BitmapHeapScanState* node);
/* This struct is used for partition switch while prefetch pages */
typedef struct PrefetchNode {
BlockNumber blockNum;
Oid partOid;
} PrefetchNode;
void BitmapHeapFree(BitmapHeapScanState* node)
{
if (node->tbmiterator != NULL) {
@ -176,6 +182,19 @@ static TupleTableSlot* BitmapHeapTblNext(BitmapHeapScanState* node)
break;
}
/* Check whether switch partition-fake-rel, use rd_rel save */
if (BitmapNodeNeedSwitchPartRel(node)) {
GPISetCurrPartOid(node->gpi_scan, node->tbmres->partitionOid);
if (!GPIGetNextPartRelation(node->gpi_scan, CurrentMemoryContext, AccessShareLock)) {
/* If the current partition is invalid, the next page is directly processed */
tbmres = NULL;
continue;
} else {
scan->rs_rd = node->gpi_scan->fakePartRelation;
scan->rs_nblocks = RelationGetNumberOfBlocks(scan->rs_rd);
}
}
#ifdef USE_PREFETCH
if (node->prefetch_pages > 0) {
/* The main iterator has closed the distance by one page */
@ -281,11 +300,17 @@ static TupleTableSlot* BitmapHeapTblNext(BitmapHeapScanState* node)
{
BlockNumber* blockList = NULL;
BlockNumber* blockListPtr = NULL;
PrefetchNode* prefetchNode = NULL;
PrefetchNode* prefetchNodePtr = NULL;
int prefetchNow = 0;
int prefetchWindow = node->prefetch_target - node->prefetch_pages;
/* We expect to prefetch at most prefetchWindow pages */
if (prefetchWindow > 0) {
if (tbm_is_global(tbm)) {
prefetchNode = (PrefetchNode*)malloc(sizeof(PrefetchNode) * prefetchWindow);
prefetchNodePtr = prefetchNode;
}
blockList = (BlockNumber*)palloc(sizeof(BlockNumber) * prefetchWindow);
blockListPtr = blockList;
}
@ -299,7 +324,12 @@ static TupleTableSlot* BitmapHeapTblNext(BitmapHeapScanState* node)
break;
}
node->prefetch_pages++;
/* we use PrefetchNode here to store relations between blockno and partition Oid */
if (tbm_is_global(tbm)) {
prefetchNodePtr->blockNum = tbmpre->blockno;
prefetchNodePtr->partOid = tbmpre->partitionOid;
prefetchNodePtr++;
}
/* For Async Direct I/O we accumulate a list and send it */
*blockListPtr++ = tbmpre->blockno;
prefetchNow++;
@ -307,17 +337,51 @@ static TupleTableSlot* BitmapHeapTblNext(BitmapHeapScanState* node)
/* Send the list we generated and free it */
if (prefetchNow) {
PageListPrefetch(scan->rs_rd, MAIN_FORKNUM, blockList, prefetchNow, 0, 0);
if (tbm_is_global(tbm)) {
/*
* we must save part Oid before switch relation, and recover it after prefetch.
* The reason for this is to assure correctness while getting a new tbmres.
*/
Oid oldOid = GPIGetCurrPartOid(node->gpi_scan);
int blkCount = 0;
Oid prevOid = prefetchNode[0].partOid;
for (int i = 0; i < prefetchNow; i++) {
if (prefetchNode[i].partOid == prevOid) {
blockList[blkCount++] = prefetchNode[i].blockNum;
} else {
GPISetCurrPartOid(node->gpi_scan, prevOid);
if (GPIGetNextPartRelation(node->gpi_scan, CurrentMemoryContext, AccessShareLock)) {
PageListPrefetch(
node->gpi_scan->fakePartRelation, MAIN_FORKNUM, blockList, blkCount, 0, 0);
}
blkCount = 0;
prevOid = prefetchNode[i].partOid;
blockList[blkCount++] = prefetchNode[i].blockNum;
}
}
GPISetCurrPartOid(node->gpi_scan, prevOid);
if (GPIGetNextPartRelation(node->gpi_scan, CurrentMemoryContext, AccessShareLock)) {
PageListPrefetch(node->gpi_scan->fakePartRelation, MAIN_FORKNUM, blockList, blkCount, 0, 0);
}
/* recover old oid after prefetch switch */
GPISetCurrPartOid(node->gpi_scan, oldOid);
} else {
PageListPrefetch(scan->rs_rd, MAIN_FORKNUM, blockList, prefetchNow, 0, 0);
}
}
if (prefetchWindow > 0) {
pfree_ext(blockList);
if (tbm_is_global(tbm)) {
pfree_ext(prefetchNode);
}
}
}
ADIO_ELSE()
{
Oid oldOid = GPIGetCurrPartOid(node->gpi_scan);
while (node->prefetch_pages < node->prefetch_target) {
TBMIterateResult* tbmpre = tbm_iterate(prefetch_iterator);
Relation prefetchRel = scan->rs_rd;
if (tbmpre == NULL) {
/* No more pages to prefetch */
tbm_end_iterate(prefetch_iterator);
@ -325,10 +389,21 @@ static TupleTableSlot* BitmapHeapTblNext(BitmapHeapScanState* node)
break;
}
node->prefetch_pages++;
if (tbm_is_global(node->tbm) && GPIScanCheckPartOid(node->gpi_scan, tbmpre->partitionOid)) {
GPISetCurrPartOid(node->gpi_scan, tbmpre->partitionOid);
if (!GPIGetNextPartRelation(node->gpi_scan, CurrentMemoryContext, AccessShareLock)) {
/* If the current partition is invalid, the next page is directly processed */
tbmpre = NULL;
continue;
} else {
prefetchRel = node->gpi_scan->fakePartRelation;
}
}
/* For posix_fadvise() we just send the one request */
PrefetchBuffer(scan->rs_rd, MAIN_FORKNUM, tbmpre->blockno);
PrefetchBuffer(prefetchRel, MAIN_FORKNUM, tbmpre->blockno);
}
/* recover old oid after prefetch switch */
GPISetCurrPartOid(node->gpi_scan, oldOid);
}
ADIO_END();
}
@ -542,9 +617,9 @@ void ExecReScanBitmapHeapScan(BitmapHeapScanState* node)
*/
abs_tbl_endscan(node->ss.ss_currentScanDesc);
/* switch to next partition for scan */
ExecInitNextPartitionForBitmapHeapScan(node);
} else {
/* switch to next partition for scan */
ExecInitNextPartitionForBitmapHeapScan(node);
} else {
/* rescan to release any page pin */
abs_tbl_rescan(node->ss.ss_currentScanDesc, NULL);
}
@ -598,20 +673,26 @@ void ExecEndBitmapHeapScan(BitmapHeapScanState* node)
if (node->ss.ss_currentScanDesc != NULL) {
abs_tbl_endscan(node->ss.ss_currentScanDesc);
}
if (node->gpi_scan != NULL) {
GPIScanEnd(node->gpi_scan);
}
/* close heap scan */
if (node->ss.isPartTbl && PointerIsValid(node->ss.partitions)) {
/* close table partition */
Assert(node->ss.ss_currentPartition);
releaseDummyRelation(&(node->ss.ss_currentPartition));
Assert(node->ss.ss_currentPartition);
releaseDummyRelation(&(node->ss.ss_currentPartition));
releasePartitionList(node->ss.ss_currentRelation, &(node->ss.partitions), NoLock);
releasePartitionList(node->ss.ss_currentRelation, &(node->ss.partitions), NoLock);
}
/*
* close the heap relation.
*/
ExecCloseScanRelation(relation);
}
static inline void InitBitmapHeapScanNextMtd(BitmapHeapScanState* bmstate)
{
@ -659,6 +740,9 @@ BitmapHeapScanState* ExecInitBitmapHeapScan(BitmapHeapScan* node, EState* estate
scanstate->ss.currentSlot = 0;
scanstate->ss.partScanDirection = node->scan.partScanDirection;
/* initilize Global partition index scan information */
GPIScanInit(&scanstate->gpi_scan);
/*
* Miscellaneous initialization
*
@ -687,6 +771,7 @@ BitmapHeapScanState* ExecInitBitmapHeapScan(BitmapHeapScan* node, EState* estate
currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid);
scanstate->ss.ss_currentRelation = currentRelation;
scanstate->gpi_scan->parentRelation = currentRelation;
InitBitmapHeapScanNextMtd(scanstate);
/*

View File

@ -92,6 +92,11 @@ Node* MultiExecBitmapIndexScan(BitmapIndexScanState* node)
} else {
/* XXX should we use less than u_sess->attr.attr_memory.work_mem for this? */
tbm = tbm_create(u_sess->attr.attr_memory.work_mem * 1024L);
/* If bitmapscan uses global partition index, set tbm to global */
if (RelationIsGlobalIndex(node->biss_RelationDesc)) {
tbm_set_global(tbm, true);
}
}
if (hbkt_idx_need_switch_bkt(scandesc, node->ss.ps.hbktScanSlot.currSlot)) {

View File

@ -126,6 +126,10 @@ Node* MultiExecBitmapOr(BitmapOrState* node)
if (result == NULL) {
/* XXX should we use less than u_sess->attr.attr_memory.work_mem for this? */
result = tbm_create(u_sess->attr.attr_memory.work_mem * 1024L);
/* If bitmapscan uses global partition index, set tbm to global */
if (RelationIsGlobalIndex(((BitmapIndexScanState*)subnode)->biss_RelationDesc)) {
tbm_set_global(result, true);
}
}
((BitmapIndexScanState*)subnode)->biss_result = result;
@ -148,6 +152,12 @@ Node* MultiExecBitmapOr(BitmapOrState* node)
if (result == NULL) {
result = subresult; /* first subplan */
} else {
if (tbm_is_global(result) != tbm_is_global(subresult)) {
ereport(ERROR,
(errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),
errmsg(
"do not support bitmap index scan for global index and local index simultaneously.")));
}
tbm_union(result, subresult);
tbm_free(subresult);
}

View File

@ -97,6 +97,15 @@ static TupleTableSlot* IndexOnlyNext(IndexOnlyScanState* node)
* reading the TID; and (2) is satisfied by the acquisition of the
* buffer content lock in order to insert the TID.
*/
if (IndexScanNeedSwitchPartRel(indexScan)) {
/*
* Change the heapRelation in indexScanDesc to Partition Relation of current index
*/
if (!GPIGetNextPartRelation(indexScan->xs_gpi_scan, CurrentMemoryContext, AccessShareLock)) {
continue;
}
indexScan->heapRelation = indexScan->xs_gpi_scan->fakePartRelation;
}
if (!visibilitymap_test(indexScan->heapRelation, ItemPointerGetBlockNumber(tid), &node->ioss_VMBuffer)) {
/*
* Rats, we have to visit the heap to check visibility.
@ -286,16 +295,16 @@ void ExecReScanIndexOnlyScan(IndexOnlyScanState* node)
if (!PointerIsValid(node->ss.partitions)) {
return;
}
Assert(PointerIsValid(node->ioss_ScanDesc));
Assert(PointerIsValid(node->ioss_ScanDesc));
abs_idx_endscan(node->ioss_ScanDesc);
/* initialize to scan the next partition */
ExecInitNextIndexPartitionForIndexScanOnly(node);
/* initialize to scan the next partition */
ExecInitNextIndexPartitionForIndexScanOnly(node);
ExecScanReScan(&node->ss);
/*
* give up rescaning the index if there is no partition to scan
*/
return;
/*
* give up rescaning the index if there is no partition to scan
*/
return;
}
}

View File

@ -41,7 +41,6 @@
#include "gstrace/executer_gstrace.h"
static TupleTableSlot* IndexNext(IndexScanState* node);
static void ExecInitNextPartitionForIndexScan(IndexScanState* node);
/* ----------------------------------------------------------------
@ -818,7 +817,9 @@ void ExecIndexBuildScanKeys(PlanState* plan_state, Relation index, List* quals,
Expr* leftop = NULL; /* expr on lhs of operator */
Expr* rightop = NULL; /* expr on rhs ... */
AttrNumber varattno; /* att number used in scan */
int indnkeyatts;
indnkeyatts = IndexRelationGetNumberOfKeyAttributes(index);
if (IsA(clause, OpExpr)) {
/* indexkey op const or indexkey op expression */
uint32 flags = 0;
@ -839,7 +840,7 @@ void ExecIndexBuildScanKeys(PlanState* plan_state, Relation index, List* quals,
(errcode(ERRCODE_INDEX_CORRUPTED), errmsg("indexqual for OpExpr doesn't have key on left side")));
varattno = ((Var*)leftop)->varattno;
if (varattno < 1 || varattno > index->rd_index->indnatts)
if (varattno < 1 || varattno > indnkeyatts)
ereport(ERROR,
(errcode(ERRCODE_INDEX_CORRUPTED),
errmsg("bogus index qualification for OpExpr, attribute number is %d.", varattno)));
@ -1050,7 +1051,7 @@ void ExecIndexBuildScanKeys(PlanState* plan_state, Relation index, List* quals,
errmsg("indexqual for ScalarArray doesn't have key on left side")));
varattno = ((Var*)leftop)->varattno;
if (varattno < 1 || varattno > index->rd_index->indnatts)
if (varattno < 1 || varattno > indnkeyatts)
ereport(ERROR,
(errcode(ERRCODE_INDEX_CORRUPTED),
errmsg("bogus index qualification for ScalarArray, attribute number is %d.", varattno)));

View File

@ -1405,6 +1405,10 @@ bool UpdateFusion::execute(long max_rows, char* completionTag)
while ((oldtup = m_scan->getTuple()) != NULL) {
if (RelationIsPartitioned(m_scan->m_rel)) {
rel = m_scan->getCurrentRel();
}
CHECK_FOR_INTERRUPTS();
HTSU_Result result;
ItemPointerData update_ctid;
@ -1589,6 +1593,10 @@ bool DeleteFusion::execute(long max_rows, char* completionTag)
m_tupDesc = RelationGetDescr(rel);
while ((oldtup = m_scan->getTuple()) != NULL) {
if (RelationIsPartitioned(m_scan->m_rel)) {
rel = m_scan->getCurrentRel();
}
HTSU_Result result;
ItemPointerData update_ctid;
TransactionId update_xmax;
@ -1806,6 +1814,10 @@ bool SelectForUpdateFusion::execute(long max_rows, char* completionTag)
}
while (nprocessed < (unsigned long)get_rows && (tuple = m_scan->getTuple()) != NULL) {
if (RelationIsPartitioned(m_scan->m_rel)) {
rel = m_scan->getCurrentRel();
}
CHECK_FOR_INTERRUPTS();
heap_deform_tuple(tuple, RelationGetDescr(rel), m_values, m_isnull);

View File

@ -295,6 +295,19 @@ bool IndexFusion::EpqCheck(Datum* values, const bool* isnull)
return true;
}
Relation IndexFusion::getCurrentRel()
{
IndexScanDesc indexScan = GetIndexScanDesc(m_scandesc);
if (indexScan->xs_gpi_scan) {
return indexScan->xs_gpi_scan->fakePartRelation;
} else {
ereport(ERROR,
(errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),
errmsg("partitioned relation dose not use global partition index")));
return NULL;
}
}
void IndexFusion::setAttrNo()
{
ListCell* lc = NULL;
@ -326,7 +339,6 @@ IndexScanFusion::IndexScanFusion(IndexScan* node, PlannedStmt* planstmt, ParamLi
m_node = node;
m_keyInit = false;
m_keyNum = list_length(node->indexqual);
;
m_scanKeys = (ScanKey)palloc0(m_keyNum * sizeof(ScanKeyData));
/* init params */
@ -594,6 +606,15 @@ TupleTableSlot* IndexOnlyScanFusion::getTupleSlot()
while ((tid = abs_idx_getnext_tid(m_scandesc, *m_direction)) != NULL) {
HeapTuple tuple = NULL;
IndexScanDesc indexdesc = GetIndexScanDesc(m_scandesc);
if (IndexScanNeedSwitchPartRel(indexdesc)) {
/*
* Change the heapRelation in indexScanDesc to Partition Relation of current index
*/
if (!GPIGetNextPartRelation(indexdesc->xs_gpi_scan, CurrentMemoryContext, AccessShareLock)) {
continue;
}
indexdesc->heapRelation = indexdesc->xs_gpi_scan->fakePartRelation;
}
if (!visibilitymap_test(indexdesc->heapRelation, ItemPointerGetBlockNumber(tid), &m_VMBuffer)) {
tuple = index_fetch_heap(indexdesc);
if (tuple == NULL) {