PostgreSQL Source Code git master
Loading...
Searching...
No Matches
heapam_indexscan.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * heapam_indexscan.c
4 * heap table plain index scan and index-only scan code
5 *
6 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
8 *
9 *
10 * IDENTIFICATION
11 * src/backend/access/heap/heapam_indexscan.c
12 *
13 *-------------------------------------------------------------------------
14 */
15#include "postgres.h"
16
17#include "access/heapam.h"
18#include "access/relscan.h"
19#include "storage/predicate.h"
20
21
22/* ------------------------------------------------------------------------
23 * Index Scan Callbacks for heap AM
24 * ------------------------------------------------------------------------
25 */
26
29{
31
32 hscan->xs_base.rel = rel;
33 hscan->xs_base.flags = flags;
34 hscan->xs_cbuf = InvalidBuffer;
35 hscan->xs_blk = InvalidBlockNumber;
36 hscan->xs_vmbuffer = InvalidBuffer;
37
38 return &hscan->xs_base;
39}
40
41void
43{
44 /*
45 * Resets are a no-op.
46 *
47 * Deliberately avoid dropping pins now held in xs_cbuf and xs_vmbuffer.
48 * This saves cycles during certain tight nested loop joins (it can avoid
49 * repeated pinning and unpinning of the same buffer across rescans).
50 */
51}
52
53void
55{
57
58 /* drop pin if there's a pinned heap page */
59 if (BufferIsValid(hscan->xs_cbuf))
60 ReleaseBuffer(hscan->xs_cbuf);
61
62 /* drop pin if there's a pinned visibility map page */
63 if (BufferIsValid(hscan->xs_vmbuffer))
64 ReleaseBuffer(hscan->xs_vmbuffer);
65
66 pfree(hscan);
67}
68
69/*
70 * heap_hot_search_buffer - search HOT chain for tuple satisfying snapshot
71 *
72 * On entry, *tid is the TID of a tuple (either a simple tuple, or the root
73 * of a HOT chain), and buffer is the buffer holding this tuple. We search
74 * for the first chain member satisfying the given snapshot. If one is
75 * found, we update *tid to reference that tuple's offset number, and
76 * return true. If no match, return false without modifying *tid.
77 *
78 * heapTuple is a caller-supplied buffer. When a match is found, we return
79 * the tuple here, in addition to updating *tid. If no match is found, the
80 * contents of this buffer on return are undefined.
81 *
82 * If all_dead is not NULL, we check non-visible tuples to see if they are
83 * globally dead; *all_dead is set true if all members of the HOT chain
84 * are vacuumable, false if not.
85 *
86 * Unlike heap_fetch, the caller must already have pin and (at least) share
87 * lock on the buffer; it is still pinned/locked at exit.
88 */
89bool
92 bool *all_dead, bool first_call)
93{
94 Page page = BufferGetPage(buffer);
96 BlockNumber blkno;
97 OffsetNumber offnum;
98 bool at_chain_start;
99 bool valid;
100 bool skip;
101 GlobalVisState *vistest = NULL;
102
103 /* If this is not the first call, previous call returned a (live!) tuple */
104 if (all_dead)
106
107 blkno = ItemPointerGetBlockNumber(tid);
108 offnum = ItemPointerGetOffsetNumber(tid);
110 skip = !first_call;
111
112 /* XXX: we should assert that a snapshot is pushed or registered */
114 Assert(BufferGetBlockNumber(buffer) == blkno);
115
116 /* Scan through possible multiple members of HOT-chain */
117 for (;;)
118 {
119 ItemId lp;
120
121 /* check for bogus TID */
123 break;
124
125 lp = PageGetItemId(page, offnum);
126
127 /* check for unused, dead, or redirected items */
128 if (!ItemIdIsNormal(lp))
129 {
130 /* We should only see a redirect at start of chain */
132 {
133 /* Follow the redirect */
134 offnum = ItemIdGetRedirect(lp);
135 at_chain_start = false;
136 continue;
137 }
138 /* else must be end of chain */
139 break;
140 }
141
142 /*
143 * Update heapTuple to point to the element of the HOT chain we're
144 * currently investigating. Having t_self set correctly is important
145 * because the SSI checks and the *Satisfies routine for historical
146 * MVCC snapshots need the correct tid to decide about the visibility.
147 */
148 heapTuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
149 heapTuple->t_len = ItemIdGetLength(lp);
150 heapTuple->t_tableOid = RelationGetRelid(relation);
151 ItemPointerSet(&heapTuple->t_self, blkno, offnum);
152
153 /*
154 * Shouldn't see a HEAP_ONLY tuple at chain start.
155 */
157 break;
158
159 /*
160 * The xmin should match the previous xmax value, else chain is
161 * broken.
162 */
166 break;
167
168 /*
169 * When first_call is true (and thus, skip is initially false) we'll
170 * return the first tuple we find. But on later passes, heapTuple
171 * will initially be pointing to the tuple we returned last time.
172 * Returning it again would be incorrect (and would loop forever), so
173 * we skip it and return the next match we find.
174 */
175 if (!skip)
176 {
177 /* If it's visible per the snapshot, we must return it */
178 valid = HeapTupleSatisfiesVisibility(heapTuple, snapshot, buffer);
180 buffer, snapshot);
181
182 if (valid)
183 {
184 ItemPointerSetOffsetNumber(tid, offnum);
185 PredicateLockTID(relation, &heapTuple->t_self, snapshot,
187 if (all_dead)
188 *all_dead = false;
189 return true;
190 }
191 }
192 skip = false;
193
194 /*
195 * If we can't see it, maybe no one else can either. At caller
196 * request, check whether all chain members are dead to all
197 * transactions.
198 *
199 * Note: if you change the criterion here for what is "dead", fix the
200 * planner's get_actual_variable_range() function to match.
201 */
202 if (all_dead && *all_dead)
203 {
204 if (!vistest)
205 vistest = GlobalVisTestFor(relation);
206
207 if (!HeapTupleIsSurelyDead(heapTuple, vistest))
208 *all_dead = false;
209 }
210
211 /*
212 * Check to see if HOT chain continues past this tuple; if so fetch
213 * the next offnum and loop around.
214 */
216 {
217 Assert(ItemPointerGetBlockNumber(&heapTuple->t_data->t_ctid) ==
218 blkno);
219 offnum = ItemPointerGetOffsetNumber(&heapTuple->t_data->t_ctid);
220 at_chain_start = false;
222 }
223 else
224 break; /* end of chain */
225
226 }
227
228 return false;
229}
230
231bool
233 ItemPointer tid,
234 Snapshot snapshot,
235 TupleTableSlot *slot,
236 bool *heap_continue, bool *all_dead)
237{
240 bool got_heap_tuple;
241
243
244 /* We can skip the buffer-switching logic if we're on the same page. */
245 if (hscan->xs_blk != ItemPointerGetBlockNumber(tid))
246 {
248
249 /* Remember this buffer's block number for next time */
250 hscan->xs_blk = ItemPointerGetBlockNumber(tid);
251
252 if (BufferIsValid(hscan->xs_cbuf))
253 ReleaseBuffer(hscan->xs_cbuf);
254
255 hscan->xs_cbuf = ReadBuffer(hscan->xs_base.rel, hscan->xs_blk);
256
257 /*
258 * Prune page when it is pinned for the first time
259 */
260 heap_page_prune_opt(hscan->xs_base.rel, hscan->xs_cbuf,
261 &hscan->xs_vmbuffer,
262 hscan->xs_base.flags & SO_HINT_REL_READ_ONLY);
263 }
264
265 Assert(BufferGetBlockNumber(hscan->xs_cbuf) == hscan->xs_blk);
266 Assert(hscan->xs_blk == ItemPointerGetBlockNumber(tid));
267
268 /* Obtain share-lock on the buffer so we can examine visibility */
271 hscan->xs_base.rel,
272 hscan->xs_cbuf,
273 snapshot,
274 &bslot->base.tupdata,
275 all_dead,
276 !*heap_continue);
277 bslot->base.tupdata.t_self = *tid;
279
280 if (got_heap_tuple)
281 {
282 /*
283 * Only in a non-MVCC snapshot can more than one member of the HOT
284 * chain be visible.
285 */
286 *heap_continue = !IsMVCCLikeSnapshot(snapshot);
287
288 slot->tts_tableOid = RelationGetRelid(scan->rel);
289 ExecStoreBufferHeapTuple(&bslot->base.tupdata, slot, hscan->xs_cbuf);
290 }
291 else
292 {
293 /* We've reached the end of the HOT chain. */
294 *heap_continue = false;
295 }
296
297 return got_heap_tuple;
298}
uint32 BlockNumber
Definition block.h:31
#define InvalidBlockNumber
Definition block.h:33
int Buffer
Definition buf.h:23
#define InvalidBuffer
Definition buf.h:25
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition bufmgr.c:4446
void ReleaseBuffer(Buffer buffer)
Definition bufmgr.c:5586
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition bufmgr.c:879
static Page BufferGetPage(Buffer buffer)
Definition bufmgr.h:468
@ BUFFER_LOCK_SHARE
Definition bufmgr.h:212
@ BUFFER_LOCK_UNLOCK
Definition bufmgr.h:207
static void LockBuffer(Buffer buffer, BufferLockMode mode)
Definition bufmgr.h:334
static bool BufferIsValid(Buffer bufnum)
Definition bufmgr.h:419
static ItemId PageGetItemId(Page page, OffsetNumber offsetNumber)
Definition bufpage.h:268
static void * PageGetItem(PageData *page, const ItemIdData *itemId)
Definition bufpage.h:378
PageData * Page
Definition bufpage.h:81
static OffsetNumber PageGetMaxOffsetNumber(const PageData *page)
Definition bufpage.h:396
#define Assert(condition)
Definition c.h:943
uint32_t uint32
Definition c.h:624
uint32 TransactionId
Definition c.h:736
TupleTableSlot * ExecStoreBufferHeapTuple(HeapTuple tuple, TupleTableSlot *slot, Buffer buffer)
#define palloc0_object(type)
Definition fe_memutils.h:75
void HeapCheckForSerializableConflictOut(bool visible, Relation relation, HeapTuple tuple, Buffer buffer, Snapshot snapshot)
Definition heapam.c:9154
void heapam_index_fetch_end(IndexFetchTableData *scan)
bool heapam_index_fetch_tuple(struct IndexFetchTableData *scan, ItemPointer tid, Snapshot snapshot, TupleTableSlot *slot, bool *heap_continue, bool *all_dead)
bool heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer, Snapshot snapshot, HeapTuple heapTuple, bool *all_dead, bool first_call)
IndexFetchTableData * heapam_index_fetch_begin(Relation rel, uint32 flags)
void heapam_index_fetch_reset(IndexFetchTableData *scan)
bool HeapTupleSatisfiesVisibility(HeapTuple htup, Snapshot snapshot, Buffer buffer)
bool HeapTupleIsSurelyDead(HeapTuple htup, GlobalVisState *vistest)
HeapTupleHeaderData * HeapTupleHeader
Definition htup.h:23
static bool HeapTupleIsHotUpdated(const HeapTupleData *tuple)
static bool HeapTupleIsHeapOnly(const HeapTupleData *tuple)
static TransactionId HeapTupleHeaderGetXmin(const HeapTupleHeaderData *tup)
static TransactionId HeapTupleHeaderGetUpdateXid(const HeapTupleHeaderData *tup)
#define ItemIdGetLength(itemId)
Definition itemid.h:59
#define ItemIdIsNormal(itemId)
Definition itemid.h:99
#define ItemIdGetRedirect(itemId)
Definition itemid.h:78
#define ItemIdIsRedirected(itemId)
Definition itemid.h:106
static void ItemPointerSet(ItemPointerData *pointer, BlockNumber blockNumber, OffsetNumber offNum)
Definition itemptr.h:135
static void ItemPointerSetOffsetNumber(ItemPointerData *pointer, OffsetNumber offsetNumber)
Definition itemptr.h:158
static OffsetNumber ItemPointerGetOffsetNumber(const ItemPointerData *pointer)
Definition itemptr.h:124
static BlockNumber ItemPointerGetBlockNumber(const ItemPointerData *pointer)
Definition itemptr.h:103
void pfree(void *pointer)
Definition mcxt.c:1616
uint16 OffsetNumber
Definition off.h:24
static const struct exclude_list_item skip[]
void PredicateLockTID(Relation relation, const ItemPointerData *tid, Snapshot snapshot, TransactionId tuple_xid)
Definition predicate.c:2551
static int fb(int x)
GlobalVisState * GlobalVisTestFor(Relation rel)
Definition procarray.c:4106
void heap_page_prune_opt(Relation relation, Buffer buffer, Buffer *vmbuffer, bool rel_read_only)
Definition pruneheap.c:271
#define RelationGetRelid(relation)
Definition rel.h:514
TransactionId RecentXmin
Definition snapmgr.c:160
#define IsMVCCLikeSnapshot(snapshot)
Definition snapmgr.h:74
@ SO_HINT_REL_READ_ONLY
Definition tableam.h:71
#define InvalidTransactionId
Definition transam.h:31
#define TransactionIdEquals(id1, id2)
Definition transam.h:43
#define TransactionIdIsValid(xid)
Definition transam.h:41
#define TTS_IS_BUFFERTUPLE(slot)
Definition tuptable.h:256