diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index e0aeaa6909..9d86b8dbe7 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -6161,6 +6161,8 @@ get_actual_variable_endpoint(Relation heapRel, Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; MemoryContext oldcontext; + uint32 visited_heap_pages = 0; + BlockNumber last_heap_block = InvalidBlockNumber; /* * We use the index-only-scan machinery for this. With mostly-static @@ -6213,13 +6215,41 @@ get_actual_variable_endpoint(Relation heapRel, /* 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(); + + /* + * Use a simple test to see if we read a new block. + * This clearly isn't accurate for all access patterns + * but the only user of this information is the + * simple heuristic below. + */ + if (block != last_heap_block) + visited_heap_pages++; + + /* + * Apply a simple heuristic to avoid time-consuming + * scans through dead entries, if they exist. + * If we exceed the limit, simply break and return, + * which causes us to use the default selectivity. + */ +#define VISITED_PAGES_LIMIT 100 + 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);