diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c index fe80b8b0ba..b6fb128232 100644 --- a/src/backend/access/index/indexam.c +++ b/src/backend/access/index/indexam.c @@ -278,6 +278,9 @@ index_beginscan_internal(Relation indexRelation, scan->parallel_scan = pscan; scan->xs_temp_snap = temp_snap; + scan->xs_page_limit = 0; + scan->xs_pages_visited = 0; + return scan; } @@ -311,6 +314,9 @@ index_rescan(IndexScanDesc scan, scan->kill_prior_tuple = false; /* for safety */ scan->xs_heap_continue = false; + scan->xs_page_limit = 0; + scan->xs_pages_visited = 0; + scan->indexRelation->rd_indam->amrescan(scan, keys, nkeys, orderbys, norderbys); } diff --git a/src/backend/access/nbtree/nbtsearch.c b/src/backend/access/nbtree/nbtsearch.c index c74543bfde..6f15170a4d 100644 --- a/src/backend/access/nbtree/nbtsearch.c +++ b/src/backend/access/nbtree/nbtsearch.c @@ -2001,6 +2001,8 @@ _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir) BTScanPosInvalidate(so->currPos); return false; } + if (unlikely(scan->xs_page_limit > 0) && ++scan->xs_pages_visited > scan->xs_page_limit) + return false; /* check for interrupts while we're not holding any buffer lock */ CHECK_FOR_INTERRUPTS(); /* step right one page */ diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index d597b7e81f..506794857a 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -6145,6 +6145,8 @@ get_actual_variable_endpoint(Relation heapRel, Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; MemoryContext oldcontext; + int visited_heap_pages = 0; + BlockNumber last_heap_block = InvalidBlockNumber; /* * We use the index-only-scan machinery for this. With mostly-static @@ -6194,16 +6196,33 @@ get_actual_variable_endpoint(Relation heapRel, index_scan->xs_want_itup = true; index_rescan(index_scan, scankeys, 1, NULL, 0); + /* Don't index scan forever; correctness is not the issue here */ +#define VISITED_PAGES_LIMIT 50 + index_scan->xs_page_limit = VISITED_PAGES_LIMIT; + /* Fetch first/next tuple in specified direction */ while ((tid = index_getnext_tid(index_scan, indexscandir)) != NULL) { + BlockNumber block = ItemPointerGetBlockNumber(tid); + if (!VM_ALL_VISIBLE(heapRel, - ItemPointerGetBlockNumber(tid), + block, &vmbuffer)) { /* Rats, we have to visit the heap to check visibility */ if (!index_fetch_heap(index_scan, tableslot)) - continue; /* no visible tuple, try next index entry */ + { + CHECK_FOR_INTERRUPTS(); + if (block != last_heap_block) + visited_heap_pages++; + if (visited_heap_pages > VISITED_PAGES_LIMIT) + break; + else + { + last_heap_block = block; + continue; /* no visible tuple, try next index entry */ + } + } /* We don't actually need the heap tuple for anything */ ExecClearTuple(tableslot); diff --git a/src/include/access/relscan.h b/src/include/access/relscan.h index 53a93ccbe7..16c6e4627d 100644 --- a/src/include/access/relscan.h +++ b/src/include/access/relscan.h @@ -124,6 +124,9 @@ typedef struct IndexScanDescData bool xs_want_itup; /* caller requests index tuples */ bool xs_temp_snap; /* unregister snapshot at scan end? */ + uint32 xs_page_limit; /* limit on num pages in scan, or 0=no limit */ + uint32 xs_pages_visited; /* current num pages visited in scan */ + /* signaling to index AM about killing index tuples */ bool kill_prior_tuple; /* last-returned tuple is dead */ bool ignore_killed_tuples; /* do not return killed entries */