Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * heapam_handler.c
4 : : * heap table access method code
5 : : *
6 : : * Portions Copyright (c) 1996-2024, 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_handler.c
12 : : *
13 : : *
14 : : * NOTES
15 : : * This files wires up the lower level heapam.c et al routines with the
16 : : * tableam abstraction.
17 : : *
18 : : *-------------------------------------------------------------------------
19 : : */
20 : : #include "postgres.h"
21 : :
22 : : #include "access/genam.h"
23 : : #include "access/heapam.h"
24 : : #include "access/heaptoast.h"
25 : : #include "access/multixact.h"
26 : : #include "access/rewriteheap.h"
27 : : #include "access/syncscan.h"
28 : : #include "access/tableam.h"
29 : : #include "access/tsmapi.h"
30 : : #include "access/visibilitymap.h"
31 : : #include "access/xact.h"
32 : : #include "catalog/catalog.h"
33 : : #include "catalog/index.h"
34 : : #include "catalog/storage.h"
35 : : #include "catalog/storage_xlog.h"
36 : : #include "commands/progress.h"
37 : : #include "executor/executor.h"
38 : : #include "miscadmin.h"
39 : : #include "pgstat.h"
40 : : #include "storage/bufmgr.h"
41 : : #include "storage/bufpage.h"
42 : : #include "storage/lmgr.h"
43 : : #include "storage/predicate.h"
44 : : #include "storage/procarray.h"
45 : : #include "storage/smgr.h"
46 : : #include "utils/builtins.h"
47 : : #include "utils/rel.h"
48 : :
49 : : static void reform_and_rewrite_tuple(HeapTuple tuple,
50 : : Relation OldHeap, Relation NewHeap,
51 : : Datum *values, bool *isnull, RewriteState rwstate);
52 : :
53 : : static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer,
54 : : HeapTuple tuple,
55 : : OffsetNumber tupoffset);
56 : :
57 : : static BlockNumber heapam_scan_get_blocks_done(HeapScanDesc hscan);
58 : :
59 : : static const TableAmRoutine heapam_methods;
60 : :
61 : :
62 : : /* ------------------------------------------------------------------------
63 : : * Slot related callbacks for heap AM
64 : : * ------------------------------------------------------------------------
65 : : */
66 : :
67 : : static const TupleTableSlotOps *
1861 andres@anarazel.de 68 :CBC 12537408 : heapam_slot_callbacks(Relation relation)
69 : : {
70 : 12537408 : return &TTSOpsBufferHeapTuple;
71 : : }
72 : :
73 : :
74 : : /* ------------------------------------------------------------------------
75 : : * Index Scan Callbacks for heap AM
76 : : * ------------------------------------------------------------------------
77 : : */
78 : :
79 : : static IndexFetchTableData *
80 : 11904907 : heapam_index_fetch_begin(Relation rel)
81 : : {
82 : 11904907 : IndexFetchHeapData *hscan = palloc0(sizeof(IndexFetchHeapData));
83 : :
84 : 11904907 : hscan->xs_base.rel = rel;
85 : 11904907 : hscan->xs_cbuf = InvalidBuffer;
86 : :
87 : 11904907 : return &hscan->xs_base;
88 : : }
89 : :
90 : : static void
91 : 21261884 : heapam_index_fetch_reset(IndexFetchTableData *scan)
92 : : {
93 : 21261884 : IndexFetchHeapData *hscan = (IndexFetchHeapData *) scan;
94 : :
95 [ + + ]: 21261884 : if (BufferIsValid(hscan->xs_cbuf))
96 : : {
97 : 10180854 : ReleaseBuffer(hscan->xs_cbuf);
98 : 10180854 : hscan->xs_cbuf = InvalidBuffer;
99 : : }
100 : 21261884 : }
101 : :
102 : : static void
103 : 11904164 : heapam_index_fetch_end(IndexFetchTableData *scan)
104 : : {
105 : 11904164 : IndexFetchHeapData *hscan = (IndexFetchHeapData *) scan;
106 : :
107 : 11904164 : heapam_index_fetch_reset(scan);
108 : :
109 : 11904164 : pfree(hscan);
110 : 11904164 : }
111 : :
112 : : static bool
113 : 16627506 : heapam_index_fetch_tuple(struct IndexFetchTableData *scan,
114 : : ItemPointer tid,
115 : : Snapshot snapshot,
116 : : TupleTableSlot *slot,
117 : : bool *call_again, bool *all_dead)
118 : : {
119 : 16627506 : IndexFetchHeapData *hscan = (IndexFetchHeapData *) scan;
120 : 16627506 : BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
121 : : bool got_heap_tuple;
122 : :
123 [ - + ]: 16627506 : Assert(TTS_IS_BUFFERTUPLE(slot));
124 : :
125 : : /* We can skip the buffer-switching logic if we're in mid-HOT chain. */
126 [ + + ]: 16627506 : if (!*call_again)
127 : : {
128 : : /* Switch to correct buffer if we don't have it already */
129 : 16555626 : Buffer prev_buf = hscan->xs_cbuf;
130 : :
131 : 16555626 : hscan->xs_cbuf = ReleaseAndReadBuffer(hscan->xs_cbuf,
132 : : hscan->xs_base.rel,
133 : : ItemPointerGetBlockNumber(tid));
134 : :
135 : : /*
136 : : * Prune page, but only if we weren't already on this page
137 : : */
138 [ + + ]: 16555623 : if (prev_buf != hscan->xs_cbuf)
139 : 11687684 : heap_page_prune_opt(hscan->xs_base.rel, hscan->xs_cbuf);
140 : : }
141 : :
142 : : /* Obtain share-lock on the buffer so we can examine visibility */
143 : 16627503 : LockBuffer(hscan->xs_cbuf, BUFFER_LOCK_SHARE);
144 : 16627503 : got_heap_tuple = heap_hot_search_buffer(tid,
145 : : hscan->xs_base.rel,
146 : : hscan->xs_cbuf,
147 : : snapshot,
148 : : &bslot->base.tupdata,
149 : : all_dead,
150 : 16627503 : !*call_again);
151 : 16627501 : bslot->base.tupdata.t_self = *tid;
152 : 16627501 : LockBuffer(hscan->xs_cbuf, BUFFER_LOCK_UNLOCK);
153 : :
154 [ + + ]: 16627501 : if (got_heap_tuple)
155 : : {
156 : : /*
157 : : * Only in a non-MVCC snapshot can more than one member of the HOT
158 : : * chain be visible.
159 : : */
160 [ + + + + ]: 10451807 : *call_again = !IsMVCCSnapshot(snapshot);
161 : :
162 : 10451807 : slot->tts_tableOid = RelationGetRelid(scan->rel);
163 : 10451807 : ExecStoreBufferHeapTuple(&bslot->base.tupdata, slot, hscan->xs_cbuf);
164 : : }
165 : : else
166 : : {
167 : : /* We've reached the end of the HOT chain. */
168 : 6175694 : *call_again = false;
169 : : }
170 : :
171 : 16627501 : return got_heap_tuple;
172 : : }
173 : :
174 : :
175 : : /* ------------------------------------------------------------------------
176 : : * Callbacks for non-modifying operations on individual tuples for heap AM
177 : : * ------------------------------------------------------------------------
178 : : */
179 : :
180 : : static bool
1847 181 : 312393 : heapam_fetch_row_version(Relation relation,
182 : : ItemPointer tid,
183 : : Snapshot snapshot,
184 : : TupleTableSlot *slot)
185 : : {
186 : 312393 : BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
187 : : Buffer buffer;
188 : :
189 [ - + ]: 312393 : Assert(TTS_IS_BUFFERTUPLE(slot));
190 : :
191 : 312393 : bslot->base.tupdata.t_self = *tid;
732 tgl@sss.pgh.pa.us 192 [ + + ]: 312393 : if (heap_fetch(relation, snapshot, &bslot->base.tupdata, &buffer, false))
193 : : {
194 : : /* store in slot, transferring existing pin */
1847 andres@anarazel.de 195 : 310507 : ExecStorePinnedBufferHeapTuple(&bslot->base.tupdata, slot, buffer);
196 : 310507 : slot->tts_tableOid = RelationGetRelid(relation);
197 : :
198 : 310507 : return true;
199 : : }
200 : :
201 : 1886 : return false;
202 : : }
203 : :
204 : : static bool
1794 205 : 292 : heapam_tuple_tid_valid(TableScanDesc scan, ItemPointer tid)
206 : : {
207 : 292 : HeapScanDesc hscan = (HeapScanDesc) scan;
208 : :
209 [ + - ]: 584 : return ItemPointerIsValid(tid) &&
210 [ + + ]: 292 : ItemPointerGetBlockNumber(tid) < hscan->rs_nblocks;
211 : : }
212 : :
213 : : static bool
1861 214 : 102471 : heapam_tuple_satisfies_snapshot(Relation rel, TupleTableSlot *slot,
215 : : Snapshot snapshot)
216 : : {
217 : 102471 : BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
218 : : bool res;
219 : :
220 [ - + ]: 102471 : Assert(TTS_IS_BUFFERTUPLE(slot));
221 [ - + ]: 102471 : Assert(BufferIsValid(bslot->buffer));
222 : :
223 : : /*
224 : : * We need buffer pin and lock to call HeapTupleSatisfiesVisibility.
225 : : * Caller should be holding pin, but not lock.
226 : : */
227 : 102471 : LockBuffer(bslot->buffer, BUFFER_LOCK_SHARE);
228 : 102471 : res = HeapTupleSatisfiesVisibility(bslot->base.tuple, snapshot,
229 : : bslot->buffer);
230 : 102471 : LockBuffer(bslot->buffer, BUFFER_LOCK_UNLOCK);
231 : :
232 : 102471 : return res;
233 : : }
234 : :
235 : :
236 : : /* ----------------------------------------------------------------------------
237 : : * Functions for manipulations of physical tuples for heap AM.
238 : : * ----------------------------------------------------------------------------
239 : : */
240 : :
241 : : static void
1849 242 : 6898696 : heapam_tuple_insert(Relation relation, TupleTableSlot *slot, CommandId cid,
243 : : int options, BulkInsertState bistate)
244 : : {
245 : 6898696 : bool shouldFree = true;
246 : 6898696 : HeapTuple tuple = ExecFetchSlotHeapTuple(slot, true, &shouldFree);
247 : :
248 : : /* Update the tuple with table oid */
249 : 6898696 : slot->tts_tableOid = RelationGetRelid(relation);
250 : 6898696 : tuple->t_tableOid = slot->tts_tableOid;
251 : :
252 : : /* Perform the insertion, and copy the resulting ItemPointer */
253 : 6898696 : heap_insert(relation, tuple, cid, options, bistate);
254 : 6898681 : ItemPointerCopy(&tuple->t_self, &slot->tts_tid);
255 : :
256 [ + + ]: 6898681 : if (shouldFree)
257 : 1395216 : pfree(tuple);
258 : 6898681 : }
259 : :
260 : : static void
1842 261 : 2013 : heapam_tuple_insert_speculative(Relation relation, TupleTableSlot *slot,
262 : : CommandId cid, int options,
263 : : BulkInsertState bistate, uint32 specToken)
264 : : {
1849 265 : 2013 : bool shouldFree = true;
266 : 2013 : HeapTuple tuple = ExecFetchSlotHeapTuple(slot, true, &shouldFree);
267 : :
268 : : /* Update the tuple with table oid */
269 : 2013 : slot->tts_tableOid = RelationGetRelid(relation);
270 : 2013 : tuple->t_tableOid = slot->tts_tableOid;
271 : :
272 : 2013 : HeapTupleHeaderSetSpeculativeToken(tuple->t_data, specToken);
273 : 2013 : options |= HEAP_INSERT_SPECULATIVE;
274 : :
275 : : /* Perform the insertion, and copy the resulting ItemPointer */
276 : 2013 : heap_insert(relation, tuple, cid, options, bistate);
277 : 2013 : ItemPointerCopy(&tuple->t_self, &slot->tts_tid);
278 : :
279 [ + + ]: 2013 : if (shouldFree)
280 : 30 : pfree(tuple);
281 : 2013 : }
282 : :
283 : : static void
1842 284 : 2010 : heapam_tuple_complete_speculative(Relation relation, TupleTableSlot *slot,
285 : : uint32 specToken, bool succeeded)
286 : : {
1849 287 : 2010 : bool shouldFree = true;
288 : 2010 : HeapTuple tuple = ExecFetchSlotHeapTuple(slot, true, &shouldFree);
289 : :
290 : : /* adjust the tuple's state accordingly */
1797 291 [ + + ]: 2010 : if (succeeded)
1849 292 : 2005 : heap_finish_speculative(relation, &slot->tts_tid);
293 : : else
294 : 5 : heap_abort_speculative(relation, &slot->tts_tid);
295 : :
296 [ + + ]: 2010 : if (shouldFree)
297 : 30 : pfree(tuple);
298 : 2010 : }
299 : :
300 : : static TM_Result
301 : 860794 : heapam_tuple_delete(Relation relation, ItemPointer tid, CommandId cid,
302 : : Snapshot snapshot, Snapshot crosscheck, bool wait,
303 : : TM_FailureData *tmfd, bool changingPart)
304 : : {
305 : : /*
306 : : * Currently Deleting of index tuples are handled at vacuum, in case if
307 : : * the storage itself is cleaning the dead tuples by itself, it is the
308 : : * time to call the index tuple deletion also.
309 : : */
3 akorotkov@postgresql 310 : 860794 : return heap_delete(relation, tid, cid, crosscheck, wait, tmfd, changingPart);
311 : : }
312 : :
313 : :
314 : : static TM_Result
1849 andres@anarazel.de 315 : 188146 : heapam_tuple_update(Relation relation, ItemPointer otid, TupleTableSlot *slot,
316 : : CommandId cid, Snapshot snapshot, Snapshot crosscheck,
317 : : bool wait, TM_FailureData *tmfd,
318 : : LockTupleMode *lockmode, TU_UpdateIndexes *update_indexes)
319 : : {
320 : 188146 : bool shouldFree = true;
321 : 188146 : HeapTuple tuple = ExecFetchSlotHeapTuple(slot, true, &shouldFree);
322 : : TM_Result result;
323 : :
324 : : /* Update the tuple with table oid */
325 : 188146 : slot->tts_tableOid = RelationGetRelid(relation);
326 : 188146 : tuple->t_tableOid = slot->tts_tableOid;
327 : :
3 akorotkov@postgresql 328 : 188146 : result = heap_update(relation, otid, tuple, cid, crosscheck, wait,
329 : : tmfd, lockmode, update_indexes);
1849 andres@anarazel.de 330 : 188134 : ItemPointerCopy(&tuple->t_self, &slot->tts_tid);
331 : :
332 : : /*
333 : : * Decide whether new index entries are needed for the tuple
334 : : *
335 : : * Note: heap_update returns the tid (location) of the new tuple in the
336 : : * t_self field.
337 : : *
338 : : * If the update is not HOT, we must update all indexes. If the update is
339 : : * HOT, it could be that we updated summarized columns, so we either
340 : : * update only summarized indexes, or none at all.
341 : : */
391 tomas.vondra@postgre 342 [ + + ]: 188134 : if (result != TM_Ok)
343 : : {
344 [ - + ]: 154 : Assert(*update_indexes == TU_None);
345 : 154 : *update_indexes = TU_None;
346 : : }
347 [ + + ]: 187980 : else if (!HeapTupleIsHeapOnly(tuple))
348 [ - + ]: 123280 : Assert(*update_indexes == TU_All);
349 : : else
350 [ + + - + ]: 64700 : Assert((*update_indexes == TU_Summarizing) ||
351 : : (*update_indexes == TU_None));
352 : :
1849 andres@anarazel.de 353 [ + + ]: 188134 : if (shouldFree)
354 : 31944 : pfree(tuple);
355 : :
356 : 188134 : return result;
357 : : }
358 : :
359 : : static TM_Result
360 : 82555 : heapam_tuple_lock(Relation relation, ItemPointer tid, Snapshot snapshot,
361 : : TupleTableSlot *slot, CommandId cid, LockTupleMode mode,
362 : : LockWaitPolicy wait_policy, uint8 flags,
363 : : TM_FailureData *tmfd)
364 : : {
365 : 82555 : BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
366 : : TM_Result result;
367 : : Buffer buffer;
368 : 82555 : HeapTuple tuple = &bslot->base.tupdata;
369 : : bool follow_updates;
370 : :
371 : 82555 : follow_updates = (flags & TUPLE_LOCK_FLAG_LOCK_UPDATE_IN_PROGRESS) != 0;
372 : 82555 : tmfd->traversed = false;
373 : :
374 [ + - ]: 82555 : Assert(TTS_IS_BUFFERTUPLE(slot));
375 : :
376 : 82555 : tuple_lock_retry:
3 akorotkov@postgresql 377 : 82709 : tuple->t_self = *tid;
378 : 82709 : result = heap_lock_tuple(relation, tuple, cid, mode, wait_policy,
379 : : follow_updates, &buffer, tmfd);
380 : :
1849 andres@anarazel.de 381 [ + + ]: 82700 : if (result == TM_Updated &&
382 [ + + ]: 188 : (flags & TUPLE_LOCK_FLAG_FIND_LAST_VERSION))
383 : : {
384 : : /* Should not encounter speculative tuple on recheck */
377 akorotkov@postgresql 385 [ - + ]: 175 : Assert(!HeapTupleHeaderIsSpeculative(tuple->t_data));
386 : :
3 387 : 175 : ReleaseBuffer(buffer);
388 : :
1849 andres@anarazel.de 389 [ + - ]: 175 : if (!ItemPointerEquals(&tmfd->ctid, &tuple->t_self))
390 : : {
391 : : SnapshotData SnapshotDirty;
392 : : TransactionId priorXmax;
393 : :
394 : : /* it was updated, so look at the updated version */
395 : 175 : *tid = tmfd->ctid;
396 : : /* updated row should have xmin matching this xmax */
397 : 175 : priorXmax = tmfd->xmax;
398 : :
399 : : /* signal that a tuple later in the chain is getting locked */
400 : 175 : tmfd->traversed = true;
401 : :
402 : : /*
403 : : * fetch target tuple
404 : : *
405 : : * Loop here to deal with updated or busy tuples
406 : : */
407 : 175 : InitDirtySnapshot(SnapshotDirty);
408 : : for (;;)
409 : : {
410 [ + + ]: 201 : if (ItemPointerIndicatesMovedPartitions(tid))
411 [ + - ]: 9 : ereport(ERROR,
412 : : (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
413 : : errmsg("tuple to be locked was already moved to another partition due to concurrent update")));
414 : :
415 : 192 : tuple->t_self = *tid;
732 tgl@sss.pgh.pa.us 416 [ + + ]: 192 : if (heap_fetch(relation, &SnapshotDirty, tuple, &buffer, true))
417 : : {
418 : : /*
419 : : * If xmin isn't what we're expecting, the slot must have
420 : : * been recycled and reused for an unrelated tuple. This
421 : : * implies that the latest version of the row was deleted,
422 : : * so we need do nothing. (Should be safe to examine xmin
423 : : * without getting buffer's content lock. We assume
424 : : * reading a TransactionId to be atomic, and Xmin never
425 : : * changes in an existing tuple, except to invalid or
426 : : * frozen, and neither of those can match priorXmax.)
427 : : */
1849 andres@anarazel.de 428 [ + - - + ]: 163 : if (!TransactionIdEquals(HeapTupleHeaderGetXmin(tuple->t_data),
429 : : priorXmax))
430 : : {
1849 andres@anarazel.de 431 :UBC 0 : ReleaseBuffer(buffer);
432 : 0 : return TM_Deleted;
433 : : }
434 : :
435 : : /* otherwise xmin should not be dirty... */
1849 andres@anarazel.de 436 [ - + ]:CBC 163 : if (TransactionIdIsValid(SnapshotDirty.xmin))
1718 peter@eisentraut.org 437 [ # # ]:UBC 0 : ereport(ERROR,
438 : : (errcode(ERRCODE_DATA_CORRUPTED),
439 : : errmsg_internal("t_xmin %u is uncommitted in tuple (%u,%u) to be updated in table \"%s\"",
440 : : SnapshotDirty.xmin,
441 : : ItemPointerGetBlockNumber(&tuple->t_self),
442 : : ItemPointerGetOffsetNumber(&tuple->t_self),
443 : : RelationGetRelationName(relation))));
444 : :
445 : : /*
446 : : * If tuple is being updated by other transaction then we
447 : : * have to wait for its commit/abort, or die trying.
448 : : */
1849 andres@anarazel.de 449 [ + + ]:CBC 163 : if (TransactionIdIsValid(SnapshotDirty.xmax))
450 : : {
451 : 2 : ReleaseBuffer(buffer);
452 [ - + + - ]: 2 : switch (wait_policy)
453 : : {
1849 andres@anarazel.de 454 :UBC 0 : case LockWaitBlock:
455 : 0 : XactLockTableWait(SnapshotDirty.xmax,
456 : : relation, &tuple->t_self,
457 : : XLTW_FetchUpdated);
458 : 0 : break;
1849 andres@anarazel.de 459 :CBC 1 : case LockWaitSkip:
460 [ + - ]: 1 : if (!ConditionalXactLockTableWait(SnapshotDirty.xmax))
461 : : /* skip instead of waiting */
462 : 1 : return TM_WouldBlock;
1849 andres@anarazel.de 463 :UBC 0 : break;
1849 andres@anarazel.de 464 :CBC 1 : case LockWaitError:
465 [ + - ]: 1 : if (!ConditionalXactLockTableWait(SnapshotDirty.xmax))
466 [ + - ]: 1 : ereport(ERROR,
467 : : (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
468 : : errmsg("could not obtain lock on row in relation \"%s\"",
469 : : RelationGetRelationName(relation))));
1849 andres@anarazel.de 470 :UBC 0 : break;
471 : : }
472 : 0 : continue; /* loop back to repeat heap_fetch */
473 : : }
474 : :
475 : : /*
476 : : * If tuple was inserted by our own transaction, we have
477 : : * to check cmin against cid: cmin >= current CID means
478 : : * our command cannot see the tuple, so we should ignore
479 : : * it. Otherwise heap_lock_tuple() will throw an error,
480 : : * and so would any later attempt to update or delete the
481 : : * tuple. (We need not check cmax because
482 : : * HeapTupleSatisfiesDirty will consider a tuple deleted
483 : : * by our transaction dead, regardless of cmax.) We just
484 : : * checked that priorXmax == xmin, so we can test that
485 : : * variable instead of doing HeapTupleHeaderGetXmin again.
486 : : */
1849 andres@anarazel.de 487 [ + + + - ]:CBC 168 : if (TransactionIdIsCurrentTransactionId(priorXmax) &&
488 : 7 : HeapTupleHeaderGetCmin(tuple->t_data) >= cid)
489 : : {
1834 490 : 7 : tmfd->xmax = priorXmax;
491 : :
492 : : /*
493 : : * Cmin is the problematic value, so store that. See
494 : : * above.
495 : : */
496 : 7 : tmfd->cmax = HeapTupleHeaderGetCmin(tuple->t_data);
1849 497 : 7 : ReleaseBuffer(buffer);
1834 498 : 7 : return TM_SelfModified;
499 : : }
500 : :
501 : : /*
502 : : * This is a live tuple, so try to lock it again.
503 : : */
3 akorotkov@postgresql 504 : 154 : ReleaseBuffer(buffer);
1849 andres@anarazel.de 505 : 154 : goto tuple_lock_retry;
506 : : }
507 : :
508 : : /*
509 : : * If the referenced slot was actually empty, the latest
510 : : * version of the row must have been deleted, so we need do
511 : : * nothing.
512 : : */
513 [ - + ]: 29 : if (tuple->t_data == NULL)
514 : : {
3 akorotkov@postgresql 515 [ # # ]:UBC 0 : Assert(!BufferIsValid(buffer));
1849 andres@anarazel.de 516 : 0 : return TM_Deleted;
517 : : }
518 : :
519 : : /*
520 : : * As above, if xmin isn't what we're expecting, do nothing.
521 : : */
1849 andres@anarazel.de 522 [ + - - + ]:CBC 29 : if (!TransactionIdEquals(HeapTupleHeaderGetXmin(tuple->t_data),
523 : : priorXmax))
524 : : {
732 tgl@sss.pgh.pa.us 525 :UBC 0 : ReleaseBuffer(buffer);
1849 andres@anarazel.de 526 : 0 : return TM_Deleted;
527 : : }
528 : :
529 : : /*
530 : : * If we get here, the tuple was found but failed
531 : : * SnapshotDirty. Assuming the xmin is either a committed xact
532 : : * or our own xact (as it certainly should be if we're trying
533 : : * to modify the tuple), this must mean that the row was
534 : : * updated or deleted by either a committed xact or our own
535 : : * xact. If it was deleted, we can ignore it; if it was
536 : : * updated then chain up to the next version and repeat the
537 : : * whole process.
538 : : *
539 : : * As above, it should be safe to examine xmax and t_ctid
540 : : * without the buffer content lock, because they can't be
541 : : * changing. We'd better hold a buffer pin though.
542 : : */
1849 andres@anarazel.de 543 [ + + ]:CBC 29 : if (ItemPointerEquals(&tuple->t_self, &tuple->t_data->t_ctid))
544 : : {
545 : : /* deleted, so forget about it */
732 tgl@sss.pgh.pa.us 546 : 3 : ReleaseBuffer(buffer);
1849 andres@anarazel.de 547 : 3 : return TM_Deleted;
548 : : }
549 : :
550 : : /* updated, so look at the updated row */
551 : 26 : *tid = tuple->t_data->t_ctid;
552 : : /* updated row should have xmin matching this xmax */
553 [ + - + + : 26 : priorXmax = HeapTupleHeaderGetUpdateXid(tuple->t_data);
+ - ]
732 tgl@sss.pgh.pa.us 554 : 26 : ReleaseBuffer(buffer);
555 : : /* loop back to fetch next in chain */
556 : : }
557 : : }
558 : : else
559 : : {
560 : : /* tuple was deleted, so give up */
1849 andres@anarazel.de 561 :UBC 0 : return TM_Deleted;
562 : : }
563 : : }
564 : :
1849 andres@anarazel.de 565 :CBC 82525 : slot->tts_tableOid = RelationGetRelid(relation);
566 : 82525 : tuple->t_tableOid = slot->tts_tableOid;
567 : :
568 : : /* store in slot, transferring existing pin */
3 akorotkov@postgresql 569 : 82525 : ExecStorePinnedBufferHeapTuple(tuple, slot, buffer);
570 : :
1849 andres@anarazel.de 571 : 82525 : return result;
572 : : }
573 : :
574 : :
575 : : /* ------------------------------------------------------------------------
576 : : * DDL related callbacks for heap AM.
577 : : * ------------------------------------------------------------------------
578 : : */
579 : :
580 : : static void
648 rhaas@postgresql.org 581 : 29118 : heapam_relation_set_new_filelocator(Relation rel,
582 : : const RelFileLocator *newrlocator,
583 : : char persistence,
584 : : TransactionId *freezeXid,
585 : : MultiXactId *minmulti)
586 : : {
587 : : SMgrRelation srel;
588 : :
589 : : /*
590 : : * Initialize to the minimum XID that could put tuples in the table. We
591 : : * know that no xacts older than RecentXmin are still running, so that
592 : : * will do.
593 : : */
1844 andres@anarazel.de 594 : 29118 : *freezeXid = RecentXmin;
595 : :
596 : : /*
597 : : * Similarly, initialize the minimum Multixact to the first value that
598 : : * could possibly be stored in tuples in the table. Running transactions
599 : : * could reuse values from their local cache, so we are careful to
600 : : * consider all currently running multis.
601 : : *
602 : : * XXX this could be refined further, but is it worth the hassle?
603 : : */
604 : 29118 : *minmulti = GetOldestMultiXactId();
605 : :
648 rhaas@postgresql.org 606 : 29118 : srel = RelationCreateStorage(*newrlocator, persistence, true);
607 : :
608 : : /*
609 : : * If required, set up an init fork for an unlogged table so that it can
610 : : * be correctly reinitialized on restart. Recovery may remove it while
611 : : * replaying, for example, an XLOG_DBASE_CREATE* or XLOG_TBLSPC_CREATE
612 : : * record. Therefore, logging is necessary even if wal_level=minimal.
613 : : */
1812 andres@anarazel.de 614 [ + + ]: 29118 : if (persistence == RELPERSISTENCE_UNLOGGED)
615 : : {
1844 616 [ + + + - : 134 : Assert(rel->rd_rel->relkind == RELKIND_RELATION ||
- + ]
617 : : rel->rd_rel->relkind == RELKIND_MATVIEW ||
618 : : rel->rd_rel->relkind == RELKIND_TOASTVALUE);
1812 619 : 134 : smgrcreate(srel, INIT_FORKNUM, false);
648 rhaas@postgresql.org 620 : 134 : log_smgrcreate(newrlocator, INIT_FORKNUM);
621 : : }
622 : :
1812 andres@anarazel.de 623 : 29118 : smgrclose(srel);
1844 624 : 29118 : }
625 : :
626 : : static void
627 : 262 : heapam_relation_nontransactional_truncate(Relation rel)
628 : : {
629 : 262 : RelationTruncate(rel, 0);
630 : 262 : }
631 : :
632 : : static void
648 rhaas@postgresql.org 633 : 49 : heapam_relation_copy_data(Relation rel, const RelFileLocator *newrlocator)
634 : : {
635 : : SMgrRelation dstrel;
636 : :
637 : : /*
638 : : * Since we copy the file directly without looking at the shared buffers,
639 : : * we'd better first flush out any pages of the source relation that are
640 : : * in shared buffers. We assume no new changes will be made while we are
641 : : * holding exclusive lock on the rel.
642 : : */
1812 andres@anarazel.de 643 : 49 : FlushRelationBuffers(rel);
644 : :
645 : : /*
646 : : * Create and copy all forks of the relation, and schedule unlinking of
647 : : * old physical files.
648 : : *
649 : : * NOTE: any conflict in relfilenumber value will be caught in
650 : : * RelationCreateStorage().
651 : : */
62 heikki.linnakangas@i 652 :GNC 49 : dstrel = RelationCreateStorage(*newrlocator, rel->rd_rel->relpersistence, true);
653 : :
654 : : /* copy main fork */
1007 tgl@sss.pgh.pa.us 655 :CBC 49 : RelationCopyStorage(RelationGetSmgr(rel), dstrel, MAIN_FORKNUM,
1844 andres@anarazel.de 656 : 49 : rel->rd_rel->relpersistence);
657 : :
658 : : /* copy those extra forks that exist */
659 : 49 : for (ForkNumber forkNum = MAIN_FORKNUM + 1;
660 [ + + ]: 196 : forkNum <= MAX_FORKNUM; forkNum++)
661 : : {
1007 tgl@sss.pgh.pa.us 662 [ + + ]: 147 : if (smgrexists(RelationGetSmgr(rel), forkNum))
663 : : {
1844 andres@anarazel.de 664 : 6 : smgrcreate(dstrel, forkNum, false);
665 : :
666 : : /*
667 : : * WAL log creation if the relation is persistent, or this is the
668 : : * init fork of an unlogged relation.
669 : : */
1119 bruce@momjian.us 670 [ + + ]: 6 : if (RelationIsPermanent(rel) ||
1844 andres@anarazel.de 671 [ - + - - ]: 3 : (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
672 : : forkNum == INIT_FORKNUM))
648 rhaas@postgresql.org 673 : 3 : log_smgrcreate(newrlocator, forkNum);
1007 tgl@sss.pgh.pa.us 674 : 6 : RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum,
1844 andres@anarazel.de 675 : 6 : rel->rd_rel->relpersistence);
676 : : }
677 : : }
678 : :
679 : :
680 : : /* drop old relation, and close new one */
681 : 49 : RelationDropStorage(rel);
682 : 49 : smgrclose(dstrel);
683 : 49 : }
684 : :
685 : : static void
686 : 263 : heapam_relation_copy_for_cluster(Relation OldHeap, Relation NewHeap,
687 : : Relation OldIndex, bool use_sort,
688 : : TransactionId OldestXmin,
689 : : TransactionId *xid_cutoff,
690 : : MultiXactId *multi_cutoff,
691 : : double *num_tuples,
692 : : double *tups_vacuumed,
693 : : double *tups_recently_dead)
694 : : {
695 : : RewriteState rwstate;
696 : : IndexScanDesc indexScan;
697 : : TableScanDesc tableScan;
698 : : HeapScanDesc heapScan;
699 : : bool is_system_catalog;
700 : : Tuplesortstate *tuplesort;
701 : 263 : TupleDesc oldTupDesc = RelationGetDescr(OldHeap);
702 : 263 : TupleDesc newTupDesc = RelationGetDescr(NewHeap);
703 : : TupleTableSlot *slot;
704 : : int natts;
705 : : Datum *values;
706 : : bool *isnull;
707 : : BufferHeapTupleTableSlot *hslot;
1234 fujii@postgresql.org 708 : 263 : BlockNumber prev_cblock = InvalidBlockNumber;
709 : :
710 : : /* Remember if it's a system catalog */
1844 andres@anarazel.de 711 : 263 : is_system_catalog = IsSystemRelation(OldHeap);
712 : :
713 : : /*
714 : : * Valid smgr_targblock implies something already wrote to the relation.
715 : : * This may be harmless, but this function hasn't planned for it.
716 : : */
717 [ - + - - ]: 263 : Assert(RelationGetTargetBlock(NewHeap) == InvalidBlockNumber);
718 : :
719 : : /* Preallocate values/isnull arrays */
720 : 263 : natts = newTupDesc->natts;
721 : 263 : values = (Datum *) palloc(natts * sizeof(Datum));
722 : 263 : isnull = (bool *) palloc(natts * sizeof(bool));
723 : :
724 : : /* Initialize the rewrite operation */
1818 725 : 263 : rwstate = begin_heap_rewrite(OldHeap, NewHeap, OldestXmin, *xid_cutoff,
726 : : *multi_cutoff);
727 : :
728 : :
729 : : /* Set up sorting if wanted */
1844 730 [ + + ]: 263 : if (use_sort)
309 pg@bowt.ie 731 : 55 : tuplesort = tuplesort_begin_cluster(oldTupDesc, OldIndex,
732 : : maintenance_work_mem,
733 : : NULL, TUPLESORT_NONE);
734 : : else
1844 andres@anarazel.de 735 : 208 : tuplesort = NULL;
736 : :
737 : : /*
738 : : * Prepare to scan the OldHeap. To ensure we see recently-dead tuples
739 : : * that still need to be copied, we scan with SnapshotAny and use
740 : : * HeapTupleSatisfiesVacuum for the visibility test.
741 : : */
742 [ + + + + ]: 263 : if (OldIndex != NULL && !use_sort)
743 : 39 : {
1842 744 : 39 : const int ci_index[] = {
745 : : PROGRESS_CLUSTER_PHASE,
746 : : PROGRESS_CLUSTER_INDEX_RELID
747 : : };
748 : : int64 ci_val[2];
749 : :
750 : : /* Set phase and OIDOldIndex to columns */
1844 751 : 39 : ci_val[0] = PROGRESS_CLUSTER_PHASE_INDEX_SCAN_HEAP;
752 : 39 : ci_val[1] = RelationGetRelid(OldIndex);
753 : 39 : pgstat_progress_update_multi_param(2, ci_index, ci_val);
754 : :
755 : 39 : tableScan = NULL;
756 : 39 : heapScan = NULL;
757 : 39 : indexScan = index_beginscan(OldHeap, OldIndex, SnapshotAny, 0, 0);
758 : 39 : index_rescan(indexScan, NULL, 0, NULL, 0);
759 : : }
760 : : else
761 : : {
762 : : /* In scan-and-sort mode and also VACUUM FULL, set phase */
763 : 224 : pgstat_progress_update_param(PROGRESS_CLUSTER_PHASE,
764 : : PROGRESS_CLUSTER_PHASE_SEQ_SCAN_HEAP);
765 : :
766 : 224 : tableScan = table_beginscan(OldHeap, SnapshotAny, 0, (ScanKey) NULL);
767 : 224 : heapScan = (HeapScanDesc) tableScan;
768 : 224 : indexScan = NULL;
769 : :
770 : : /* Set total heap blocks */
771 : 224 : pgstat_progress_update_param(PROGRESS_CLUSTER_TOTAL_HEAP_BLKS,
772 : 224 : heapScan->rs_nblocks);
773 : : }
774 : :
775 : 263 : slot = table_slot_create(OldHeap, NULL);
776 : 263 : hslot = (BufferHeapTupleTableSlot *) slot;
777 : :
778 : : /*
779 : : * Scan through the OldHeap, either in OldIndex order or sequentially;
780 : : * copy each tuple into the NewHeap, or transiently to the tuplesort
781 : : * module. Note that we don't bother sorting dead tuples (they won't get
782 : : * to the new table anyway).
783 : : */
784 : : for (;;)
785 : 390170 : {
786 : : HeapTuple tuple;
787 : : Buffer buf;
788 : : bool isdead;
789 : :
790 [ - + ]: 390433 : CHECK_FOR_INTERRUPTS();
791 : :
792 [ + + ]: 390433 : if (indexScan != NULL)
793 : : {
794 [ + + ]: 93 : if (!index_getnext_slot(indexScan, ForwardScanDirection, slot))
795 : 39 : break;
796 : :
797 : : /* Since we used no scan keys, should never need to recheck */
798 [ - + ]: 54 : if (indexScan->xs_recheck)
1844 andres@anarazel.de 799 [ # # ]:UBC 0 : elog(ERROR, "CLUSTER does not support lossy index conditions");
800 : : }
801 : : else
802 : : {
1844 andres@anarazel.de 803 [ + + ]:CBC 390340 : if (!table_scan_getnextslot(tableScan, ForwardScanDirection, slot))
804 : : {
805 : : /*
806 : : * If the last pages of the scan were empty, we would go to
807 : : * the next phase while heap_blks_scanned != heap_blks_total.
808 : : * Instead, to ensure that heap_blks_scanned is equivalent to
809 : : * heap_blks_total after the table scan phase, this parameter
810 : : * is manually updated to the correct value when the table
811 : : * scan finishes.
812 : : */
1234 fujii@postgresql.org 813 : 224 : pgstat_progress_update_param(PROGRESS_CLUSTER_HEAP_BLKS_SCANNED,
814 : 224 : heapScan->rs_nblocks);
1844 andres@anarazel.de 815 : 224 : break;
816 : : }
817 : :
818 : : /*
819 : : * In scan-and-sort mode and also VACUUM FULL, set heap blocks
820 : : * scanned
821 : : *
822 : : * Note that heapScan may start at an offset and wrap around, i.e.
823 : : * rs_startblock may be >0, and rs_cblock may end with a number
824 : : * below rs_startblock. To prevent showing this wraparound to the
825 : : * user, we offset rs_cblock by rs_startblock (modulo rs_nblocks).
826 : : */
1234 fujii@postgresql.org 827 [ + + ]: 390116 : if (prev_cblock != heapScan->rs_cblock)
828 : : {
829 : 5461 : pgstat_progress_update_param(PROGRESS_CLUSTER_HEAP_BLKS_SCANNED,
830 : 5461 : (heapScan->rs_cblock +
831 : 5461 : heapScan->rs_nblocks -
832 : 5461 : heapScan->rs_startblock
833 : 5461 : ) % heapScan->rs_nblocks + 1);
834 : 5461 : prev_cblock = heapScan->rs_cblock;
835 : : }
836 : : }
837 : :
1844 andres@anarazel.de 838 : 390170 : tuple = ExecFetchSlotHeapTuple(slot, false, NULL);
839 : 390170 : buf = hslot->buffer;
840 : :
841 : 390170 : LockBuffer(buf, BUFFER_LOCK_SHARE);
842 : :
843 [ + + + + : 390170 : switch (HeapTupleSatisfiesVacuum(tuple, OldestXmin, buf))
+ - ]
844 : : {
845 : 14912 : case HEAPTUPLE_DEAD:
846 : : /* Definitely dead */
847 : 14912 : isdead = true;
848 : 14912 : break;
849 : 33301 : case HEAPTUPLE_RECENTLY_DEAD:
850 : 33301 : *tups_recently_dead += 1;
851 : : /* fall through */
852 : 375136 : case HEAPTUPLE_LIVE:
853 : : /* Live or recently dead, must copy it */
854 : 375136 : isdead = false;
855 : 375136 : break;
856 : 68 : case HEAPTUPLE_INSERT_IN_PROGRESS:
857 : :
858 : : /*
859 : : * Since we hold exclusive lock on the relation, normally the
860 : : * only way to see this is if it was inserted earlier in our
861 : : * own transaction. However, it can happen in system
862 : : * catalogs, since we tend to release write lock before commit
863 : : * there. Give a warning if neither case applies; but in any
864 : : * case we had better copy it.
865 : : */
866 [ + + ]: 68 : if (!is_system_catalog &&
867 [ + - - + ]: 10 : !TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple->t_data)))
1844 andres@anarazel.de 868 [ # # ]:UBC 0 : elog(WARNING, "concurrent insert in progress within table \"%s\"",
869 : : RelationGetRelationName(OldHeap));
870 : : /* treat as live */
1844 andres@anarazel.de 871 :CBC 68 : isdead = false;
872 : 68 : break;
873 : 54 : case HEAPTUPLE_DELETE_IN_PROGRESS:
874 : :
875 : : /*
876 : : * Similar situation to INSERT_IN_PROGRESS case.
877 : : */
878 [ + + ]: 54 : if (!is_system_catalog &&
879 [ + - - + : 15 : !TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetUpdateXid(tuple->t_data)))
- - - + ]
1844 andres@anarazel.de 880 [ # # ]:UBC 0 : elog(WARNING, "concurrent delete in progress within table \"%s\"",
881 : : RelationGetRelationName(OldHeap));
882 : : /* treat as recently dead */
1844 andres@anarazel.de 883 :CBC 54 : *tups_recently_dead += 1;
884 : 54 : isdead = false;
885 : 54 : break;
1844 andres@anarazel.de 886 :UBC 0 : default:
887 [ # # ]: 0 : elog(ERROR, "unexpected HeapTupleSatisfiesVacuum result");
888 : : isdead = false; /* keep compiler quiet */
889 : : break;
890 : : }
891 : :
1844 andres@anarazel.de 892 :CBC 390170 : LockBuffer(buf, BUFFER_LOCK_UNLOCK);
893 : :
894 [ + + ]: 390170 : if (isdead)
895 : : {
896 : 14912 : *tups_vacuumed += 1;
897 : : /* heap rewrite module still needs to see it... */
898 [ - + ]: 14912 : if (rewrite_heap_dead_tuple(rwstate, tuple))
899 : : {
900 : : /* A previous recently-dead tuple is now known dead */
1844 andres@anarazel.de 901 :UBC 0 : *tups_vacuumed += 1;
902 : 0 : *tups_recently_dead -= 1;
903 : : }
1844 andres@anarazel.de 904 :CBC 14912 : continue;
905 : : }
906 : :
907 : 375258 : *num_tuples += 1;
908 [ + + ]: 375258 : if (tuplesort != NULL)
909 : : {
910 : 273659 : tuplesort_putheaptuple(tuplesort, tuple);
911 : :
912 : : /*
913 : : * In scan-and-sort mode, report increase in number of tuples
914 : : * scanned
915 : : */
916 : 273659 : pgstat_progress_update_param(PROGRESS_CLUSTER_HEAP_TUPLES_SCANNED,
917 : 273659 : *num_tuples);
918 : : }
919 : : else
920 : : {
1842 921 : 101599 : const int ct_index[] = {
922 : : PROGRESS_CLUSTER_HEAP_TUPLES_SCANNED,
923 : : PROGRESS_CLUSTER_HEAP_TUPLES_WRITTEN
924 : : };
925 : : int64 ct_val[2];
926 : :
1844 927 : 101599 : reform_and_rewrite_tuple(tuple, OldHeap, NewHeap,
928 : : values, isnull, rwstate);
929 : :
930 : : /*
931 : : * In indexscan mode and also VACUUM FULL, report increase in
932 : : * number of tuples scanned and written
933 : : */
934 : 101599 : ct_val[0] = *num_tuples;
935 : 101599 : ct_val[1] = *num_tuples;
936 : 101599 : pgstat_progress_update_multi_param(2, ct_index, ct_val);
937 : : }
938 : : }
939 : :
940 [ + + ]: 263 : if (indexScan != NULL)
941 : 39 : index_endscan(indexScan);
942 [ + + ]: 263 : if (tableScan != NULL)
943 : 224 : table_endscan(tableScan);
944 [ + - ]: 263 : if (slot)
945 : 263 : ExecDropSingleTupleTableSlot(slot);
946 : :
947 : : /*
948 : : * In scan-and-sort mode, complete the sort, then read out all live tuples
949 : : * from the tuplestore and write them to the new relation.
950 : : */
951 [ + + ]: 263 : if (tuplesort != NULL)
952 : : {
1842 953 : 55 : double n_tuples = 0;
954 : :
955 : : /* Report that we are now sorting tuples */
1844 956 : 55 : pgstat_progress_update_param(PROGRESS_CLUSTER_PHASE,
957 : : PROGRESS_CLUSTER_PHASE_SORT_TUPLES);
958 : :
959 : 55 : tuplesort_performsort(tuplesort);
960 : :
961 : : /* Report that we are now writing new heap */
962 : 55 : pgstat_progress_update_param(PROGRESS_CLUSTER_PHASE,
963 : : PROGRESS_CLUSTER_PHASE_WRITE_NEW_HEAP);
964 : :
965 : : for (;;)
966 : 273659 : {
967 : : HeapTuple tuple;
968 : :
969 [ - + ]: 273714 : CHECK_FOR_INTERRUPTS();
970 : :
971 : 273714 : tuple = tuplesort_getheaptuple(tuplesort, true);
972 [ + + ]: 273714 : if (tuple == NULL)
973 : 55 : break;
974 : :
975 : 273659 : n_tuples += 1;
976 : 273659 : reform_and_rewrite_tuple(tuple,
977 : : OldHeap, NewHeap,
978 : : values, isnull,
979 : : rwstate);
980 : : /* Report n_tuples */
981 : 273659 : pgstat_progress_update_param(PROGRESS_CLUSTER_HEAP_TUPLES_WRITTEN,
982 : : n_tuples);
983 : : }
984 : :
985 : 55 : tuplesort_end(tuplesort);
986 : : }
987 : :
988 : : /* Write out any remaining tuples, and fsync if needed */
989 : 263 : end_heap_rewrite(rwstate);
990 : :
991 : : /* Clean up */
992 : 263 : pfree(values);
993 : 263 : pfree(isnull);
994 : 263 : }
995 : :
996 : : /*
997 : : * Prepare to analyze the next block in the read stream. Returns false if
998 : : * the stream is exhausted and true otherwise. The scan must have been started
999 : : * with SO_TYPE_ANALYZE option.
1000 : : *
1001 : : * This routine holds a buffer pin and lock on the heap page. They are held
1002 : : * until heapam_scan_analyze_next_tuple() returns false. That is until all the
1003 : : * items of the heap page are analyzed.
1004 : : */
1005 : : bool
6 tmunro@postgresql.or 1006 :GNC 68211 : heapam_scan_analyze_next_block(TableScanDesc scan, ReadStream *stream)
1007 : : {
1842 andres@anarazel.de 1008 :CBC 68211 : HeapScanDesc hscan = (HeapScanDesc) scan;
1009 : :
1010 : : /*
1011 : : * We must maintain a pin on the target page's buffer to ensure that
1012 : : * concurrent activity - e.g. HOT pruning - doesn't delete tuples out from
1013 : : * under us. It comes from the stream already pinned. We also choose to
1014 : : * hold sharelock on the buffer throughout --- we could release and
1015 : : * re-acquire sharelock for each tuple, but since we aren't doing much
1016 : : * work per tuple, the extra lock traffic is probably better avoided.
1017 : : */
6 tmunro@postgresql.or 1018 :GNC 68211 : hscan->rs_cbuf = read_stream_next_buffer(stream, NULL);
1019 [ + + ]: 68211 : if (!BufferIsValid(hscan->rs_cbuf))
1020 : 6819 : return false;
1021 : :
1842 andres@anarazel.de 1022 :CBC 61392 : LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_SHARE);
1023 : :
6 tmunro@postgresql.or 1024 :GNC 61392 : hscan->rs_cblock = BufferGetBlockNumber(hscan->rs_cbuf);
1025 : 61392 : hscan->rs_cindex = FirstOffsetNumber;
6 tmunro@postgresql.or 1026 :CBC 61392 : return true;
1027 : : }
1028 : :
1029 : : /*
1030 : : * Iterate over tuples in the block selected with
1031 : : * heapam_scan_analyze_next_block(). If a tuple that's suitable for sampling
1032 : : * is found, true is returned and a tuple is stored in `slot`. When no more
1033 : : * tuples for sampling, false is returned and the pin and lock acquired by
1034 : : * heapam_scan_analyze_next_block() are released.
1035 : : *
1036 : : * *liverows and *deadrows are incremented according to the encountered
1037 : : * tuples.
1038 : : */
1039 : : bool
1842 andres@anarazel.de 1040 : 5028971 : heapam_scan_analyze_next_tuple(TableScanDesc scan, TransactionId OldestXmin,
1041 : : double *liverows, double *deadrows,
1042 : : TupleTableSlot *slot)
1043 : : {
1044 : 5028971 : HeapScanDesc hscan = (HeapScanDesc) scan;
1045 : : Page targpage;
1046 : : OffsetNumber maxoffset;
1047 : : BufferHeapTupleTableSlot *hslot;
1048 : :
1049 [ - + ]: 5028971 : Assert(TTS_IS_BUFFERTUPLE(slot));
1050 : :
1051 : 5028971 : hslot = (BufferHeapTupleTableSlot *) slot;
1052 : 5028971 : targpage = BufferGetPage(hscan->rs_cbuf);
1053 : 5028971 : maxoffset = PageGetMaxOffsetNumber(targpage);
1054 : :
1055 : : /* Inner loop over all tuples on the selected page */
1056 [ + + ]: 5255047 : for (; hscan->rs_cindex <= maxoffset; hscan->rs_cindex++)
1057 : : {
1058 : : ItemId itemid;
1059 : 5193655 : HeapTuple targtuple = &hslot->base.tupdata;
1060 : 5193655 : bool sample_it = false;
1061 : :
1062 : 5193655 : itemid = PageGetItemId(targpage, hscan->rs_cindex);
1063 : :
1064 : : /*
1065 : : * We ignore unused and redirect line pointers. DEAD line pointers
1066 : : * should be counted as dead, because we need vacuum to run to get rid
1067 : : * of them. Note that this rule agrees with the way that
1068 : : * heap_page_prune_and_freeze() counts things.
1069 : : */
1070 [ + + ]: 5193655 : if (!ItemIdIsNormal(itemid))
1071 : : {
1072 [ + + ]: 120720 : if (ItemIdIsDead(itemid))
1073 : 13873 : *deadrows += 1;
1074 : 120720 : continue;
1075 : : }
1076 : :
1077 : 5072935 : ItemPointerSet(&targtuple->t_self, hscan->rs_cblock, hscan->rs_cindex);
1078 : :
1079 : 5072935 : targtuple->t_tableOid = RelationGetRelid(scan->rs_rd);
1080 : 5072935 : targtuple->t_data = (HeapTupleHeader) PageGetItem(targpage, itemid);
1081 : 5072935 : targtuple->t_len = ItemIdGetLength(itemid);
1082 : :
1083 [ + + + + : 5072935 : switch (HeapTupleSatisfiesVacuum(targtuple, OldestXmin,
- ]
1084 : : hscan->rs_cbuf))
1085 : : {
1086 : 4828484 : case HEAPTUPLE_LIVE:
1087 : 4828484 : sample_it = true;
1088 : 4828484 : *liverows += 1;
1089 : 4828484 : break;
1090 : :
1091 : 104481 : case HEAPTUPLE_DEAD:
1092 : : case HEAPTUPLE_RECENTLY_DEAD:
1093 : : /* Count dead and recently-dead rows */
1094 : 104481 : *deadrows += 1;
1095 : 104481 : break;
1096 : :
1097 : 139105 : case HEAPTUPLE_INSERT_IN_PROGRESS:
1098 : :
1099 : : /*
1100 : : * Insert-in-progress rows are not counted. We assume that
1101 : : * when the inserting transaction commits or aborts, it will
1102 : : * send a stats message to increment the proper count. This
1103 : : * works right only if that transaction ends after we finish
1104 : : * analyzing the table; if things happen in the other order,
1105 : : * its stats update will be overwritten by ours. However, the
1106 : : * error will be large only if the other transaction runs long
1107 : : * enough to insert many tuples, so assuming it will finish
1108 : : * after us is the safer option.
1109 : : *
1110 : : * A special case is that the inserting transaction might be
1111 : : * our own. In this case we should count and sample the row,
1112 : : * to accommodate users who load a table and analyze it in one
1113 : : * transaction. (pgstat_report_analyze has to adjust the
1114 : : * numbers we report to the cumulative stats system to make
1115 : : * this come out right.)
1116 : : */
1117 [ + - + + ]: 139105 : if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(targtuple->t_data)))
1118 : : {
1119 : 139088 : sample_it = true;
1120 : 139088 : *liverows += 1;
1121 : : }
1122 : 139105 : break;
1123 : :
1124 : 865 : case HEAPTUPLE_DELETE_IN_PROGRESS:
1125 : :
1126 : : /*
1127 : : * We count and sample delete-in-progress rows the same as
1128 : : * live ones, so that the stats counters come out right if the
1129 : : * deleting transaction commits after us, per the same
1130 : : * reasoning given above.
1131 : : *
1132 : : * If the delete was done by our own transaction, however, we
1133 : : * must count the row as dead to make pgstat_report_analyze's
1134 : : * stats adjustments come out right. (Note: this works out
1135 : : * properly when the row was both inserted and deleted in our
1136 : : * xact.)
1137 : : *
1138 : : * The net effect of these choices is that we act as though an
1139 : : * IN_PROGRESS transaction hasn't happened yet, except if it
1140 : : * is our own transaction, which we assume has happened.
1141 : : *
1142 : : * This approach ensures that we behave sanely if we see both
1143 : : * the pre-image and post-image rows for a row being updated
1144 : : * by a concurrent transaction: we will sample the pre-image
1145 : : * but not the post-image. We also get sane results if the
1146 : : * concurrent transaction never commits.
1147 : : */
1148 [ + - - + : 865 : if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetUpdateXid(targtuple->t_data)))
- - + + ]
1762 1149 : 858 : *deadrows += 1;
1150 : : else
1151 : : {
1842 1152 : 7 : sample_it = true;
1762 1153 : 7 : *liverows += 1;
1154 : : }
1842 1155 : 865 : break;
1156 : :
1842 andres@anarazel.de 1157 :UBC 0 : default:
1158 [ # # ]: 0 : elog(ERROR, "unexpected HeapTupleSatisfiesVacuum result");
1159 : : break;
1160 : : }
1161 : :
1842 andres@anarazel.de 1162 [ + + ]:CBC 5072935 : if (sample_it)
1163 : : {
1164 : 4967579 : ExecStoreBufferHeapTuple(targtuple, slot, hscan->rs_cbuf);
1165 : 4967579 : hscan->rs_cindex++;
1166 : :
1167 : : /* note that we leave the buffer locked here! */
1168 : 4967579 : return true;
1169 : : }
1170 : : }
1171 : :
1172 : : /* Now release the lock and pin on the page */
1173 : 61392 : UnlockReleaseBuffer(hscan->rs_cbuf);
1174 : 61392 : hscan->rs_cbuf = InvalidBuffer;
1175 : :
1176 : : /* also prevent old slot contents from having pin on page */
1177 : 61392 : ExecClearTuple(slot);
1178 : :
1179 : 61392 : return false;
1180 : : }
1181 : :
1182 : : static double
1845 1183 : 25434 : heapam_index_build_range_scan(Relation heapRelation,
1184 : : Relation indexRelation,
1185 : : IndexInfo *indexInfo,
1186 : : bool allow_sync,
1187 : : bool anyvisible,
1188 : : bool progress,
1189 : : BlockNumber start_blockno,
1190 : : BlockNumber numblocks,
1191 : : IndexBuildCallback callback,
1192 : : void *callback_state,
1193 : : TableScanDesc scan)
1194 : : {
1195 : : HeapScanDesc hscan;
1196 : : bool is_system_catalog;
1197 : : bool checking_uniqueness;
1198 : : HeapTuple heapTuple;
1199 : : Datum values[INDEX_MAX_KEYS];
1200 : : bool isnull[INDEX_MAX_KEYS];
1201 : : double reltuples;
1202 : : ExprState *predicate;
1203 : : TupleTableSlot *slot;
1204 : : EState *estate;
1205 : : ExprContext *econtext;
1206 : : Snapshot snapshot;
1207 : 25434 : bool need_unregister_snapshot = false;
1208 : : TransactionId OldestXmin;
1789 tgl@sss.pgh.pa.us 1209 : 25434 : BlockNumber previous_blkno = InvalidBlockNumber;
1845 andres@anarazel.de 1210 : 25434 : BlockNumber root_blkno = InvalidBlockNumber;
1211 : : OffsetNumber root_offsets[MaxHeapTuplesPerPage];
1212 : :
1213 : : /*
1214 : : * sanity checks
1215 : : */
1216 [ - + ]: 25434 : Assert(OidIsValid(indexRelation->rd_rel->relam));
1217 : :
1218 : : /* Remember if it's a system catalog */
1219 : 25434 : is_system_catalog = IsSystemRelation(heapRelation);
1220 : :
1221 : : /* See whether we're verifying uniqueness/exclusion properties */
1222 [ + + ]: 32312 : checking_uniqueness = (indexInfo->ii_Unique ||
1223 [ + + ]: 6878 : indexInfo->ii_ExclusionOps != NULL);
1224 : :
1225 : : /*
1226 : : * "Any visible" mode is not compatible with uniqueness checks; make sure
1227 : : * only one of those is requested.
1228 : : */
1229 [ + + - + ]: 25434 : Assert(!(anyvisible && checking_uniqueness));
1230 : :
1231 : : /*
1232 : : * Need an EState for evaluation of index expressions and partial-index
1233 : : * predicates. Also a slot to hold the current tuple.
1234 : : */
1235 : 25434 : estate = CreateExecutorState();
1236 [ - + ]: 25434 : econtext = GetPerTupleExprContext(estate);
1237 : 25434 : slot = table_slot_create(heapRelation, NULL);
1238 : :
1239 : : /* Arrange for econtext's scan tuple to be the tuple under test */
1240 : 25434 : econtext->ecxt_scantuple = slot;
1241 : :
1242 : : /* Set up execution state for predicate, if any. */
1243 : 25434 : predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
1244 : :
1245 : : /*
1246 : : * Prepare for scan of the base relation. In a normal index build, we use
1247 : : * SnapshotAny because we must retrieve all tuples and do our own time
1248 : : * qual checks (because we have to index RECENTLY_DEAD tuples). In a
1249 : : * concurrent build, or during bootstrap, we take a regular MVCC snapshot
1250 : : * and index whatever's live according to that.
1251 : : */
1252 : 25434 : OldestXmin = InvalidTransactionId;
1253 : :
1254 : : /* okay to ignore lazy VACUUMs here */
1255 [ + + + + ]: 25434 : if (!IsBootstrapProcessingMode() && !indexInfo->ii_Concurrent)
1341 1256 : 18832 : OldestXmin = GetOldestNonRemovableTransactionId(heapRelation);
1257 : :
1845 1258 [ + + ]: 25434 : if (!scan)
1259 : : {
1260 : : /*
1261 : : * Serial index build.
1262 : : *
1263 : : * Must begin our own heap scan in this case. We may also need to
1264 : : * register a snapshot whose lifetime is under our direct control.
1265 : : */
1266 [ + + ]: 25214 : if (!TransactionIdIsValid(OldestXmin))
1267 : : {
1268 : 6551 : snapshot = RegisterSnapshot(GetTransactionSnapshot());
1269 : 6551 : need_unregister_snapshot = true;
1270 : : }
1271 : : else
1272 : 18663 : snapshot = SnapshotAny;
1273 : :
1274 : 25214 : scan = table_beginscan_strat(heapRelation, /* relation */
1275 : : snapshot, /* snapshot */
1276 : : 0, /* number of keys */
1277 : : NULL, /* scan key */
1278 : : true, /* buffer access strategy OK */
1279 : : allow_sync); /* syncscan OK? */
1280 : : }
1281 : : else
1282 : : {
1283 : : /*
1284 : : * Parallel index build.
1285 : : *
1286 : : * Parallel case never registers/unregisters own snapshot. Snapshot
1287 : : * is taken from parallel heap scan, and is SnapshotAny or an MVCC
1288 : : * snapshot, based on same criteria as serial case.
1289 : : */
1290 [ - + ]: 220 : Assert(!IsBootstrapProcessingMode());
1291 [ - + ]: 220 : Assert(allow_sync);
1292 : 220 : snapshot = scan->rs_snapshot;
1293 : : }
1294 : :
1295 : 25434 : hscan = (HeapScanDesc) scan;
1296 : :
1297 : : /*
1298 : : * Must have called GetOldestNonRemovableTransactionId() if using
1299 : : * SnapshotAny. Shouldn't have for an MVCC snapshot. (It's especially
1300 : : * worth checking this for parallel builds, since ambuild routines that
1301 : : * support parallel builds must work these details out for themselves.)
1302 : : */
1341 1303 [ + + - + : 25434 : Assert(snapshot == SnapshotAny || IsMVCCSnapshot(snapshot));
- - ]
1304 [ + + - + ]: 25434 : Assert(snapshot == SnapshotAny ? TransactionIdIsValid(OldestXmin) :
1305 : : !TransactionIdIsValid(OldestXmin));
1306 [ + + - + ]: 25434 : Assert(snapshot == SnapshotAny || !anyvisible);
1307 : :
1308 : : /* Publish number of blocks to scan */
1839 alvherre@alvh.no-ip. 1309 [ + + ]: 25434 : if (progress)
1310 : : {
1311 : : BlockNumber nblocks;
1312 : :
1313 [ + + ]: 23816 : if (hscan->rs_base.rs_parallel != NULL)
1314 : : {
1315 : : ParallelBlockTableScanDesc pbscan;
1316 : :
1317 : 75 : pbscan = (ParallelBlockTableScanDesc) hscan->rs_base.rs_parallel;
1318 : 75 : nblocks = pbscan->phs_nblocks;
1319 : : }
1320 : : else
1321 : 23741 : nblocks = hscan->rs_nblocks;
1322 : :
1323 : 23816 : pgstat_progress_update_param(PROGRESS_SCAN_BLOCKS_TOTAL,
1324 : : nblocks);
1325 : : }
1326 : :
1327 : : /* set our scan endpoints */
1845 andres@anarazel.de 1328 [ + + ]: 25434 : if (!allow_sync)
1329 : 1795 : heap_setscanlimits(scan, start_blockno, numblocks);
1330 : : else
1331 : : {
1332 : : /* syncscan can only be requested on whole relation */
1333 [ - + ]: 23639 : Assert(start_blockno == 0);
1334 [ - + ]: 23639 : Assert(numblocks == InvalidBlockNumber);
1335 : : }
1336 : :
1337 : 25434 : reltuples = 0;
1338 : :
1339 : : /*
1340 : : * Scan all tuples in the base relation.
1341 : : */
1342 [ + + ]: 8312160 : while ((heapTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
1343 : : {
1344 : : bool tupleIsAlive;
1345 : :
1346 [ + + ]: 8286732 : CHECK_FOR_INTERRUPTS();
1347 : :
1348 : : /* Report scan progress, if asked to. */
1839 alvherre@alvh.no-ip. 1349 [ + + ]: 8286732 : if (progress)
1350 : : {
1789 tgl@sss.pgh.pa.us 1351 : 6788845 : BlockNumber blocks_done = heapam_scan_get_blocks_done(hscan);
1352 : :
1839 alvherre@alvh.no-ip. 1353 [ + + ]: 6788845 : if (blocks_done != previous_blkno)
1354 : : {
1355 : 85537 : pgstat_progress_update_param(PROGRESS_SCAN_BLOCKS_DONE,
1356 : : blocks_done);
1357 : 85537 : previous_blkno = blocks_done;
1358 : : }
1359 : : }
1360 : :
1361 : : /*
1362 : : * When dealing with a HOT-chain of updated tuples, we want to index
1363 : : * the values of the live tuple (if any), but index it under the TID
1364 : : * of the chain's root tuple. This approach is necessary to preserve
1365 : : * the HOT-chain structure in the heap. So we need to be able to find
1366 : : * the root item offset for every tuple that's in a HOT-chain. When
1367 : : * first reaching a new page of the relation, call
1368 : : * heap_get_root_tuples() to build a map of root item offsets on the
1369 : : * page.
1370 : : *
1371 : : * It might look unsafe to use this information across buffer
1372 : : * lock/unlock. However, we hold ShareLock on the table so no
1373 : : * ordinary insert/update/delete should occur; and we hold pin on the
1374 : : * buffer continuously while visiting the page, so no pruning
1375 : : * operation can occur either.
1376 : : *
1377 : : * In cases with only ShareUpdateExclusiveLock on the table, it's
1378 : : * possible for some HOT tuples to appear that we didn't know about
1379 : : * when we first read the page. To handle that case, we re-obtain the
1380 : : * list of root offsets when a HOT tuple points to a root item that we
1381 : : * don't know about.
1382 : : *
1383 : : * Also, although our opinions about tuple liveness could change while
1384 : : * we scan the page (due to concurrent transaction commits/aborts),
1385 : : * the chain root locations won't, so this info doesn't need to be
1386 : : * rebuilt after waiting for another transaction.
1387 : : *
1388 : : * Note the implied assumption that there is no more than one live
1389 : : * tuple per HOT-chain --- else we could create more than one index
1390 : : * entry pointing to the same root tuple.
1391 : : */
1845 andres@anarazel.de 1392 [ + + ]: 8286732 : if (hscan->rs_cblock != root_blkno)
1393 : : {
1394 : 100525 : Page page = BufferGetPage(hscan->rs_cbuf);
1395 : :
1396 : 100525 : LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_SHARE);
1397 : 100525 : heap_get_root_tuples(page, root_offsets);
1398 : 100525 : LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_UNLOCK);
1399 : :
1400 : 100525 : root_blkno = hscan->rs_cblock;
1401 : : }
1402 : :
1403 [ + + ]: 8286732 : if (snapshot == SnapshotAny)
1404 : : {
1405 : : /* do our own time qual check */
1406 : : bool indexIt;
1407 : : TransactionId xwait;
1408 : :
1845 andres@anarazel.de 1409 :UBC 0 : recheck:
1410 : :
1411 : : /*
1412 : : * We could possibly get away with not locking the buffer here,
1413 : : * since caller should hold ShareLock on the relation, but let's
1414 : : * be conservative about it. (This remark is still correct even
1415 : : * with HOT-pruning: our pin on the buffer prevents pruning.)
1416 : : */
1845 andres@anarazel.de 1417 :CBC 7249575 : LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_SHARE);
1418 : :
1419 : : /*
1420 : : * The criteria for counting a tuple as live in this block need to
1421 : : * match what analyze.c's heapam_scan_analyze_next_tuple() does,
1422 : : * otherwise CREATE INDEX and ANALYZE may produce wildly different
1423 : : * reltuples values, e.g. when there are many recently-dead
1424 : : * tuples.
1425 : : */
1426 [ + + + + : 7249575 : switch (HeapTupleSatisfiesVacuum(heapTuple, OldestXmin,
+ - ]
1427 : : hscan->rs_cbuf))
1428 : : {
1429 : 786 : case HEAPTUPLE_DEAD:
1430 : : /* Definitely dead, we can ignore it */
1431 : 786 : indexIt = false;
1432 : 786 : tupleIsAlive = false;
1433 : 786 : break;
1434 : 5365450 : case HEAPTUPLE_LIVE:
1435 : : /* Normal case, index and unique-check it */
1436 : 5365450 : indexIt = true;
1437 : 5365450 : tupleIsAlive = true;
1438 : : /* Count it as live, too */
1439 : 5365450 : reltuples += 1;
1440 : 5365450 : break;
1441 : 116026 : case HEAPTUPLE_RECENTLY_DEAD:
1442 : :
1443 : : /*
1444 : : * If tuple is recently deleted then we must index it
1445 : : * anyway to preserve MVCC semantics. (Pre-existing
1446 : : * transactions could try to use the index after we finish
1447 : : * building it, and may need to see such tuples.)
1448 : : *
1449 : : * However, if it was HOT-updated then we must only index
1450 : : * the live tuple at the end of the HOT-chain. Since this
1451 : : * breaks semantics for pre-existing snapshots, mark the
1452 : : * index as unusable for them.
1453 : : *
1454 : : * We don't count recently-dead tuples in reltuples, even
1455 : : * if we index them; see heapam_scan_analyze_next_tuple().
1456 : : */
1457 [ + + + - : 116026 : if (HeapTupleIsHotUpdated(heapTuple))
+ - ]
1458 : : {
1459 : 109 : indexIt = false;
1460 : : /* mark the index as unsafe for old snapshots */
1461 : 109 : indexInfo->ii_BrokenHotChain = true;
1462 : : }
1463 : : else
1464 : 115917 : indexIt = true;
1465 : : /* In any case, exclude the tuple from unique-checking */
1466 : 116026 : tupleIsAlive = false;
1467 : 116026 : break;
1468 : 1767181 : case HEAPTUPLE_INSERT_IN_PROGRESS:
1469 : :
1470 : : /*
1471 : : * In "anyvisible" mode, this tuple is visible and we
1472 : : * don't need any further checks.
1473 : : */
1474 [ + + ]: 1767181 : if (anyvisible)
1475 : : {
1476 : 30736 : indexIt = true;
1477 : 30736 : tupleIsAlive = true;
1478 : 30736 : reltuples += 1;
1479 : 30736 : break;
1480 : : }
1481 : :
1482 : : /*
1483 : : * Since caller should hold ShareLock or better, normally
1484 : : * the only way to see this is if it was inserted earlier
1485 : : * in our own transaction. However, it can happen in
1486 : : * system catalogs, since we tend to release write lock
1487 : : * before commit there. Give a warning if neither case
1488 : : * applies.
1489 : : */
1490 [ + - ]: 1736445 : xwait = HeapTupleHeaderGetXmin(heapTuple->t_data);
1491 [ + + ]: 1736445 : if (!TransactionIdIsCurrentTransactionId(xwait))
1492 : : {
1493 [ - + ]: 15 : if (!is_system_catalog)
1845 andres@anarazel.de 1494 [ # # ]:UBC 0 : elog(WARNING, "concurrent insert in progress within table \"%s\"",
1495 : : RelationGetRelationName(heapRelation));
1496 : :
1497 : : /*
1498 : : * If we are performing uniqueness checks, indexing
1499 : : * such a tuple could lead to a bogus uniqueness
1500 : : * failure. In that case we wait for the inserting
1501 : : * transaction to finish and check again.
1502 : : */
1845 andres@anarazel.de 1503 [ - + ]:CBC 15 : if (checking_uniqueness)
1504 : : {
1505 : : /*
1506 : : * Must drop the lock on the buffer before we wait
1507 : : */
1845 andres@anarazel.de 1508 :UBC 0 : LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_UNLOCK);
1509 : 0 : XactLockTableWait(xwait, heapRelation,
1510 : : &heapTuple->t_self,
1511 : : XLTW_InsertIndexUnique);
1512 [ # # ]: 0 : CHECK_FOR_INTERRUPTS();
1513 : 0 : goto recheck;
1514 : : }
1515 : : }
1516 : : else
1517 : : {
1518 : : /*
1519 : : * For consistency with
1520 : : * heapam_scan_analyze_next_tuple(), count
1521 : : * HEAPTUPLE_INSERT_IN_PROGRESS tuples as live only
1522 : : * when inserted by our own transaction.
1523 : : */
1845 andres@anarazel.de 1524 :CBC 1736430 : reltuples += 1;
1525 : : }
1526 : :
1527 : : /*
1528 : : * We must index such tuples, since if the index build
1529 : : * commits then they're good.
1530 : : */
1531 : 1736445 : indexIt = true;
1532 : 1736445 : tupleIsAlive = true;
1533 : 1736445 : break;
1534 : 132 : case HEAPTUPLE_DELETE_IN_PROGRESS:
1535 : :
1536 : : /*
1537 : : * As with INSERT_IN_PROGRESS case, this is unexpected
1538 : : * unless it's our own deletion or a system catalog; but
1539 : : * in anyvisible mode, this tuple is visible.
1540 : : */
1541 [ - + ]: 132 : if (anyvisible)
1542 : : {
1845 andres@anarazel.de 1543 :UBC 0 : indexIt = true;
1544 : 0 : tupleIsAlive = false;
1545 : 0 : reltuples += 1;
1546 : 0 : break;
1547 : : }
1548 : :
1845 andres@anarazel.de 1549 [ + - - + :CBC 132 : xwait = HeapTupleHeaderGetUpdateXid(heapTuple->t_data);
- - ]
1550 [ + + ]: 132 : if (!TransactionIdIsCurrentTransactionId(xwait))
1551 : : {
1552 [ - + ]: 93 : if (!is_system_catalog)
1845 andres@anarazel.de 1553 [ # # ]:UBC 0 : elog(WARNING, "concurrent delete in progress within table \"%s\"",
1554 : : RelationGetRelationName(heapRelation));
1555 : :
1556 : : /*
1557 : : * If we are performing uniqueness checks, assuming
1558 : : * the tuple is dead could lead to missing a
1559 : : * uniqueness violation. In that case we wait for the
1560 : : * deleting transaction to finish and check again.
1561 : : *
1562 : : * Also, if it's a HOT-updated tuple, we should not
1563 : : * index it but rather the live tuple at the end of
1564 : : * the HOT-chain. However, the deleting transaction
1565 : : * could abort, possibly leaving this tuple as live
1566 : : * after all, in which case it has to be indexed. The
1567 : : * only way to know what to do is to wait for the
1568 : : * deleting transaction to finish and check again.
1569 : : */
1845 andres@anarazel.de 1570 [ + - ]:CBC 93 : if (checking_uniqueness ||
1571 [ - + - - : 93 : HeapTupleIsHotUpdated(heapTuple))
- - ]
1572 : : {
1573 : : /*
1574 : : * Must drop the lock on the buffer before we wait
1575 : : */
1845 andres@anarazel.de 1576 :UBC 0 : LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_UNLOCK);
1577 : 0 : XactLockTableWait(xwait, heapRelation,
1578 : : &heapTuple->t_self,
1579 : : XLTW_InsertIndexUnique);
1580 [ # # ]: 0 : CHECK_FOR_INTERRUPTS();
1581 : 0 : goto recheck;
1582 : : }
1583 : :
1584 : : /*
1585 : : * Otherwise index it but don't check for uniqueness,
1586 : : * the same as a RECENTLY_DEAD tuple.
1587 : : */
1845 andres@anarazel.de 1588 :CBC 93 : indexIt = true;
1589 : :
1590 : : /*
1591 : : * Count HEAPTUPLE_DELETE_IN_PROGRESS tuples as live,
1592 : : * if they were not deleted by the current
1593 : : * transaction. That's what
1594 : : * heapam_scan_analyze_next_tuple() does, and we want
1595 : : * the behavior to be consistent.
1596 : : */
1597 : 93 : reltuples += 1;
1598 : : }
1599 [ - + - - : 39 : else if (HeapTupleIsHotUpdated(heapTuple))
- - ]
1600 : : {
1601 : : /*
1602 : : * It's a HOT-updated tuple deleted by our own xact.
1603 : : * We can assume the deletion will commit (else the
1604 : : * index contents don't matter), so treat the same as
1605 : : * RECENTLY_DEAD HOT-updated tuples.
1606 : : */
1845 andres@anarazel.de 1607 :UBC 0 : indexIt = false;
1608 : : /* mark the index as unsafe for old snapshots */
1609 : 0 : indexInfo->ii_BrokenHotChain = true;
1610 : : }
1611 : : else
1612 : : {
1613 : : /*
1614 : : * It's a regular tuple deleted by our own xact. Index
1615 : : * it, but don't check for uniqueness nor count in
1616 : : * reltuples, the same as a RECENTLY_DEAD tuple.
1617 : : */
1845 andres@anarazel.de 1618 :CBC 39 : indexIt = true;
1619 : : }
1620 : : /* In any case, exclude the tuple from unique-checking */
1621 : 132 : tupleIsAlive = false;
1622 : 132 : break;
1845 andres@anarazel.de 1623 :UBC 0 : default:
1624 [ # # ]: 0 : elog(ERROR, "unexpected HeapTupleSatisfiesVacuum result");
1625 : : indexIt = tupleIsAlive = false; /* keep compiler quiet */
1626 : : break;
1627 : : }
1628 : :
1845 andres@anarazel.de 1629 :CBC 7249575 : LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_UNLOCK);
1630 : :
1631 [ + + ]: 7249575 : if (!indexIt)
1632 : 895 : continue;
1633 : : }
1634 : : else
1635 : : {
1636 : : /* heap_getnext did the time qual check */
1637 : 1037157 : tupleIsAlive = true;
1638 : 1037157 : reltuples += 1;
1639 : : }
1640 : :
1641 : 8285837 : MemoryContextReset(econtext->ecxt_per_tuple_memory);
1642 : :
1643 : : /* Set up for predicate or expression evaluation */
1644 : 8285837 : ExecStoreBufferHeapTuple(heapTuple, slot, hscan->rs_cbuf);
1645 : :
1646 : : /*
1647 : : * In a partial index, discard tuples that don't satisfy the
1648 : : * predicate.
1649 : : */
1650 [ + + ]: 8285837 : if (predicate != NULL)
1651 : : {
1652 [ + + ]: 42283 : if (!ExecQual(predicate, econtext))
1653 : 12787 : continue;
1654 : : }
1655 : :
1656 : : /*
1657 : : * For the current heap tuple, extract all the attributes we use in
1658 : : * this index, and note which are null. This also performs evaluation
1659 : : * of any expressions needed.
1660 : : */
1661 : 8273050 : FormIndexDatum(indexInfo,
1662 : : slot,
1663 : : estate,
1664 : : values,
1665 : : isnull);
1666 : :
1667 : : /*
1668 : : * You'd think we should go ahead and build the index tuple here, but
1669 : : * some index AMs want to do further processing on the data first. So
1670 : : * pass the values[] and isnull[] arrays, instead.
1671 : : */
1672 : :
1673 [ + + ]: 8273044 : if (HeapTupleIsHeapOnly(heapTuple))
1674 : : {
1675 : : /*
1676 : : * For a heap-only tuple, pretend its TID is that of the root. See
1677 : : * src/backend/access/heap/README.HOT for discussion.
1678 : : */
1679 : : ItemPointerData tid;
1680 : : OffsetNumber offnum;
1681 : :
1682 : 4194 : offnum = ItemPointerGetOffsetNumber(&heapTuple->t_self);
1683 : :
1684 : : /*
1685 : : * If a HOT tuple points to a root that we don't know about,
1686 : : * obtain root items afresh. If that still fails, report it as
1687 : : * corruption.
1688 : : */
1340 alvherre@alvh.no-ip. 1689 [ - + ]: 4194 : if (root_offsets[offnum - 1] == InvalidOffsetNumber)
1690 : : {
1068 tgl@sss.pgh.pa.us 1691 :UBC 0 : Page page = BufferGetPage(hscan->rs_cbuf);
1692 : :
1340 alvherre@alvh.no-ip. 1693 : 0 : LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_SHARE);
1694 : 0 : heap_get_root_tuples(page, root_offsets);
1695 : 0 : LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_UNLOCK);
1696 : : }
1697 : :
1845 andres@anarazel.de 1698 [ + - + - :CBC 4194 : if (!OffsetNumberIsValid(root_offsets[offnum - 1]))
- + ]
1845 andres@anarazel.de 1699 [ # # ]:UBC 0 : ereport(ERROR,
1700 : : (errcode(ERRCODE_DATA_CORRUPTED),
1701 : : errmsg_internal("failed to find parent tuple for heap-only tuple at (%u,%u) in table \"%s\"",
1702 : : ItemPointerGetBlockNumber(&heapTuple->t_self),
1703 : : offnum,
1704 : : RelationGetRelationName(heapRelation))));
1705 : :
1619 andres@anarazel.de 1706 :CBC 4194 : ItemPointerSet(&tid, ItemPointerGetBlockNumber(&heapTuple->t_self),
1707 : 4194 : root_offsets[offnum - 1]);
1708 : :
1709 : : /* Call the AM's callback routine to process the tuple */
1710 : 4194 : callback(indexRelation, &tid, values, isnull, tupleIsAlive,
1711 : : callback_state);
1712 : : }
1713 : : else
1714 : : {
1715 : : /* Call the AM's callback routine to process the tuple */
1716 : 8268850 : callback(indexRelation, &heapTuple->t_self, values, isnull,
1717 : : tupleIsAlive, callback_state);
1718 : : }
1719 : : }
1720 : :
1721 : : /* Report scan progress one last time. */
1839 alvherre@alvh.no-ip. 1722 [ + + ]: 25428 : if (progress)
1723 : : {
1724 : : BlockNumber blks_done;
1725 : :
1726 [ + + ]: 23810 : if (hscan->rs_base.rs_parallel != NULL)
1727 : : {
1728 : : ParallelBlockTableScanDesc pbscan;
1729 : :
1730 : 75 : pbscan = (ParallelBlockTableScanDesc) hscan->rs_base.rs_parallel;
1731 : 75 : blks_done = pbscan->phs_nblocks;
1732 : : }
1733 : : else
1734 : 23735 : blks_done = hscan->rs_nblocks;
1735 : :
1736 : 23810 : pgstat_progress_update_param(PROGRESS_SCAN_BLOCKS_DONE,
1737 : : blks_done);
1738 : : }
1739 : :
1845 andres@anarazel.de 1740 : 25428 : table_endscan(scan);
1741 : :
1742 : : /* we can now forget our snapshot, if set and registered by us */
1743 [ + + ]: 25428 : if (need_unregister_snapshot)
1744 : 6548 : UnregisterSnapshot(snapshot);
1745 : :
1746 : 25428 : ExecDropSingleTupleTableSlot(slot);
1747 : :
1748 : 25428 : FreeExecutorState(estate);
1749 : :
1750 : : /* These may have been pointing to the now-gone estate */
1751 : 25428 : indexInfo->ii_ExpressionsState = NIL;
1752 : 25428 : indexInfo->ii_PredicateState = NULL;
1753 : :
1754 : 25428 : return reltuples;
1755 : : }
1756 : :
1757 : : static void
1758 : 299 : heapam_index_validate_scan(Relation heapRelation,
1759 : : Relation indexRelation,
1760 : : IndexInfo *indexInfo,
1761 : : Snapshot snapshot,
1762 : : ValidateIndexState *state)
1763 : : {
1764 : : TableScanDesc scan;
1765 : : HeapScanDesc hscan;
1766 : : HeapTuple heapTuple;
1767 : : Datum values[INDEX_MAX_KEYS];
1768 : : bool isnull[INDEX_MAX_KEYS];
1769 : : ExprState *predicate;
1770 : : TupleTableSlot *slot;
1771 : : EState *estate;
1772 : : ExprContext *econtext;
1773 : 299 : BlockNumber root_blkno = InvalidBlockNumber;
1774 : : OffsetNumber root_offsets[MaxHeapTuplesPerPage];
1775 : : bool in_index[MaxHeapTuplesPerPage];
1789 tgl@sss.pgh.pa.us 1776 : 299 : BlockNumber previous_blkno = InvalidBlockNumber;
1777 : :
1778 : : /* state variables for the merge */
1845 andres@anarazel.de 1779 : 299 : ItemPointer indexcursor = NULL;
1780 : : ItemPointerData decoded;
1781 : 299 : bool tuplesort_empty = false;
1782 : :
1783 : : /*
1784 : : * sanity checks
1785 : : */
1786 [ - + ]: 299 : Assert(OidIsValid(indexRelation->rd_rel->relam));
1787 : :
1788 : : /*
1789 : : * Need an EState for evaluation of index expressions and partial-index
1790 : : * predicates. Also a slot to hold the current tuple.
1791 : : */
1792 : 299 : estate = CreateExecutorState();
1793 [ - + ]: 299 : econtext = GetPerTupleExprContext(estate);
1794 : 299 : slot = MakeSingleTupleTableSlot(RelationGetDescr(heapRelation),
1795 : : &TTSOpsHeapTuple);
1796 : :
1797 : : /* Arrange for econtext's scan tuple to be the tuple under test */
1798 : 299 : econtext->ecxt_scantuple = slot;
1799 : :
1800 : : /* Set up execution state for predicate, if any. */
1801 : 299 : predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
1802 : :
1803 : : /*
1804 : : * Prepare for scan of the base relation. We need just those tuples
1805 : : * satisfying the passed-in reference snapshot. We must disable syncscan
1806 : : * here, because it's critical that we read from block zero forward to
1807 : : * match the sorted TIDs.
1808 : : */
1809 : 299 : scan = table_beginscan_strat(heapRelation, /* relation */
1810 : : snapshot, /* snapshot */
1811 : : 0, /* number of keys */
1812 : : NULL, /* scan key */
1813 : : true, /* buffer access strategy OK */
1814 : : false); /* syncscan not OK */
1815 : 299 : hscan = (HeapScanDesc) scan;
1816 : :
1839 alvherre@alvh.no-ip. 1817 : 299 : pgstat_progress_update_param(PROGRESS_SCAN_BLOCKS_TOTAL,
1818 : 299 : hscan->rs_nblocks);
1819 : :
1820 : : /*
1821 : : * Scan all tuples matching the snapshot.
1822 : : */
1845 andres@anarazel.de 1823 [ + + ]: 17628 : while ((heapTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
1824 : : {
1825 : 17329 : ItemPointer heapcursor = &heapTuple->t_self;
1826 : : ItemPointerData rootTuple;
1827 : : OffsetNumber root_offnum;
1828 : :
1829 [ - + ]: 17329 : CHECK_FOR_INTERRUPTS();
1830 : :
1831 : 17329 : state->htups += 1;
1832 : :
1839 alvherre@alvh.no-ip. 1833 [ + + ]: 17329 : if ((previous_blkno == InvalidBlockNumber) ||
1834 [ + + ]: 17154 : (hscan->rs_cblock != previous_blkno))
1835 : : {
1836 : 525 : pgstat_progress_update_param(PROGRESS_SCAN_BLOCKS_DONE,
1837 : 525 : hscan->rs_cblock);
1838 : 525 : previous_blkno = hscan->rs_cblock;
1839 : : }
1840 : :
1841 : : /*
1842 : : * As commented in table_index_build_scan, we should index heap-only
1843 : : * tuples under the TIDs of their root tuples; so when we advance onto
1844 : : * a new heap page, build a map of root item offsets on the page.
1845 : : *
1846 : : * This complicates merging against the tuplesort output: we will
1847 : : * visit the live tuples in order by their offsets, but the root
1848 : : * offsets that we need to compare against the index contents might be
1849 : : * ordered differently. So we might have to "look back" within the
1850 : : * tuplesort output, but only within the current page. We handle that
1851 : : * by keeping a bool array in_index[] showing all the
1852 : : * already-passed-over tuplesort output TIDs of the current page. We
1853 : : * clear that array here, when advancing onto a new heap page.
1854 : : */
1845 andres@anarazel.de 1855 [ + + ]: 17329 : if (hscan->rs_cblock != root_blkno)
1856 : : {
1857 : 525 : Page page = BufferGetPage(hscan->rs_cbuf);
1858 : :
1859 : 525 : LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_SHARE);
1860 : 525 : heap_get_root_tuples(page, root_offsets);
1861 : 525 : LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_UNLOCK);
1862 : :
1863 : 525 : memset(in_index, 0, sizeof(in_index));
1864 : :
1865 : 525 : root_blkno = hscan->rs_cblock;
1866 : : }
1867 : :
1868 : : /* Convert actual tuple TID to root TID */
1869 : 17329 : rootTuple = *heapcursor;
1870 : 17329 : root_offnum = ItemPointerGetOffsetNumber(heapcursor);
1871 : :
1872 [ + + ]: 17329 : if (HeapTupleIsHeapOnly(heapTuple))
1873 : : {
1874 : 4 : root_offnum = root_offsets[root_offnum - 1];
1875 [ + - + - : 4 : if (!OffsetNumberIsValid(root_offnum))
- + ]
1845 andres@anarazel.de 1876 [ # # ]:UBC 0 : ereport(ERROR,
1877 : : (errcode(ERRCODE_DATA_CORRUPTED),
1878 : : errmsg_internal("failed to find parent tuple for heap-only tuple at (%u,%u) in table \"%s\"",
1879 : : ItemPointerGetBlockNumber(heapcursor),
1880 : : ItemPointerGetOffsetNumber(heapcursor),
1881 : : RelationGetRelationName(heapRelation))));
1845 andres@anarazel.de 1882 :CBC 4 : ItemPointerSetOffsetNumber(&rootTuple, root_offnum);
1883 : : }
1884 : :
1885 : : /*
1886 : : * "merge" by skipping through the index tuples until we find or pass
1887 : : * the current root tuple.
1888 : : */
1889 [ + + + + ]: 34626 : while (!tuplesort_empty &&
1890 [ + + ]: 34424 : (!indexcursor ||
1891 : 34424 : ItemPointerCompare(indexcursor, &rootTuple) < 0))
1892 : : {
1893 : : Datum ts_val;
1894 : : bool ts_isnull;
1895 : :
1896 [ + + ]: 17297 : if (indexcursor)
1897 : : {
1898 : : /*
1899 : : * Remember index items seen earlier on the current heap page
1900 : : */
1901 [ + + ]: 17122 : if (ItemPointerGetBlockNumber(indexcursor) == root_blkno)
1902 : 16774 : in_index[ItemPointerGetOffsetNumber(indexcursor) - 1] = true;
1903 : : }
1904 : :
1905 : 17297 : tuplesort_empty = !tuplesort_getdatum(state->tuplesort, true,
1906 : : false, &ts_val, &ts_isnull,
534 drowley@postgresql.o 1907 : 17297 : NULL);
1845 andres@anarazel.de 1908 [ + + - + ]: 17297 : Assert(tuplesort_empty || !ts_isnull);
1909 [ + + ]: 17297 : if (!tuplesort_empty)
1910 : : {
1911 : 17281 : itemptr_decode(&decoded, DatumGetInt64(ts_val));
1912 : 17281 : indexcursor = &decoded;
1913 : : }
1914 : : else
1915 : : {
1916 : : /* Be tidy */
1917 : 16 : indexcursor = NULL;
1918 : : }
1919 : : }
1920 : :
1921 : : /*
1922 : : * If the tuplesort has overshot *and* we didn't see a match earlier,
1923 : : * then this tuple is missing from the index, so insert it.
1924 : : */
1925 [ + + + + ]: 34631 : if ((tuplesort_empty ||
1926 : 17302 : ItemPointerCompare(indexcursor, &rootTuple) > 0) &&
1927 [ + + ]: 56 : !in_index[root_offnum - 1])
1928 : : {
1929 : 52 : MemoryContextReset(econtext->ecxt_per_tuple_memory);
1930 : :
1931 : : /* Set up for predicate or expression evaluation */
1932 : 52 : ExecStoreHeapTuple(heapTuple, slot, false);
1933 : :
1934 : : /*
1935 : : * In a partial index, discard tuples that don't satisfy the
1936 : : * predicate.
1937 : : */
1938 [ + + ]: 52 : if (predicate != NULL)
1939 : : {
1940 [ + - ]: 24 : if (!ExecQual(predicate, econtext))
1941 : 24 : continue;
1942 : : }
1943 : :
1944 : : /*
1945 : : * For the current heap tuple, extract all the attributes we use
1946 : : * in this index, and note which are null. This also performs
1947 : : * evaluation of any expressions needed.
1948 : : */
1949 : 28 : FormIndexDatum(indexInfo,
1950 : : slot,
1951 : : estate,
1952 : : values,
1953 : : isnull);
1954 : :
1955 : : /*
1956 : : * You'd think we should go ahead and build the index tuple here,
1957 : : * but some index AMs want to do further processing on the data
1958 : : * first. So pass the values[] and isnull[] arrays, instead.
1959 : : */
1960 : :
1961 : : /*
1962 : : * If the tuple is already committed dead, you might think we
1963 : : * could suppress uniqueness checking, but this is no longer true
1964 : : * in the presence of HOT, because the insert is actually a proxy
1965 : : * for a uniqueness check on the whole HOT-chain. That is, the
1966 : : * tuple we have here could be dead because it was already
1967 : : * HOT-updated, and if so the updating transaction will not have
1968 : : * thought it should insert index entries. The index AM will
1969 : : * check the whole HOT-chain and correctly detect a conflict if
1970 : : * there is one.
1971 : : */
1972 : :
1973 : 28 : index_insert(indexRelation,
1974 : : values,
1975 : : isnull,
1976 : : &rootTuple,
1977 : : heapRelation,
1978 : 28 : indexInfo->ii_Unique ?
1979 : : UNIQUE_CHECK_YES : UNIQUE_CHECK_NO,
1980 : : false,
1981 : : indexInfo);
1982 : :
1983 : 28 : state->tups_inserted += 1;
1984 : : }
1985 : : }
1986 : :
1987 : 299 : table_endscan(scan);
1988 : :
1989 : 299 : ExecDropSingleTupleTableSlot(slot);
1990 : :
1991 : 299 : FreeExecutorState(estate);
1992 : :
1993 : : /* These may have been pointing to the now-gone estate */
1994 : 299 : indexInfo->ii_ExpressionsState = NIL;
1995 : 299 : indexInfo->ii_PredicateState = NULL;
1996 : 299 : }
1997 : :
1998 : : /*
1999 : : * Return the number of blocks that have been read by this scan since
2000 : : * starting. This is meant for progress reporting rather than be fully
2001 : : * accurate: in a parallel scan, workers can be concurrently reading blocks
2002 : : * further ahead than what we report.
2003 : : */
2004 : : static BlockNumber
1839 alvherre@alvh.no-ip. 2005 : 6788845 : heapam_scan_get_blocks_done(HeapScanDesc hscan)
2006 : : {
2007 : 6788845 : ParallelBlockTableScanDesc bpscan = NULL;
2008 : : BlockNumber startblock;
2009 : : BlockNumber blocks_done;
2010 : :
2011 [ + + ]: 6788845 : if (hscan->rs_base.rs_parallel != NULL)
2012 : : {
2013 : 922445 : bpscan = (ParallelBlockTableScanDesc) hscan->rs_base.rs_parallel;
2014 : 922445 : startblock = bpscan->phs_startblock;
2015 : : }
2016 : : else
2017 : 5866400 : startblock = hscan->rs_startblock;
2018 : :
2019 : : /*
2020 : : * Might have wrapped around the end of the relation, if startblock was
2021 : : * not zero.
2022 : : */
2023 [ + + ]: 6788845 : if (hscan->rs_cblock > startblock)
2024 : 6547896 : blocks_done = hscan->rs_cblock - startblock;
2025 : : else
2026 : : {
2027 : : BlockNumber nblocks;
2028 : :
2029 [ + + ]: 240949 : nblocks = bpscan != NULL ? bpscan->phs_nblocks : hscan->rs_nblocks;
2030 : 240949 : blocks_done = nblocks - startblock +
2031 : 240949 : hscan->rs_cblock;
2032 : : }
2033 : :
2034 : 6788845 : return blocks_done;
2035 : : }
2036 : :
2037 : :
2038 : : /* ------------------------------------------------------------------------
2039 : : * Miscellaneous callbacks for the heap AM
2040 : : * ------------------------------------------------------------------------
2041 : : */
2042 : :
2043 : : /*
2044 : : * Check to see whether the table needs a TOAST table. It does only if
2045 : : * (1) there are any toastable attributes, and (2) the maximum length
2046 : : * of a tuple could exceed TOAST_TUPLE_THRESHOLD. (We don't want to
2047 : : * create a toast table for something like "f1 varchar(20)".)
2048 : : */
2049 : : static bool
1790 rhaas@postgresql.org 2050 : 21755 : heapam_relation_needs_toast_table(Relation rel)
2051 : : {
2052 : 21755 : int32 data_length = 0;
2053 : 21755 : bool maxlength_unknown = false;
2054 : 21755 : bool has_toastable_attrs = false;
2055 : 21755 : TupleDesc tupdesc = rel->rd_att;
2056 : : int32 tuple_length;
2057 : : int i;
2058 : :
2059 [ + + ]: 84153 : for (i = 0; i < tupdesc->natts; i++)
2060 : : {
2061 : 62398 : Form_pg_attribute att = TupleDescAttr(tupdesc, i);
2062 : :
2063 [ + + ]: 62398 : if (att->attisdropped)
2064 : 507 : continue;
2065 [ + + + + : 61891 : data_length = att_align_nominal(data_length, att->attalign);
+ + - + ]
2066 [ + + ]: 61891 : if (att->attlen > 0)
2067 : : {
2068 : : /* Fixed-length types are never toastable */
2069 : 46656 : data_length += att->attlen;
2070 : : }
2071 : : else
2072 : : {
2073 : 15235 : int32 maxlen = type_maximum_size(att->atttypid,
2074 : : att->atttypmod);
2075 : :
2076 [ + + ]: 15235 : if (maxlen < 0)
2077 : 13087 : maxlength_unknown = true;
2078 : : else
2079 : 2148 : data_length += maxlen;
1502 tgl@sss.pgh.pa.us 2080 [ + + ]: 15235 : if (att->attstorage != TYPSTORAGE_PLAIN)
1790 rhaas@postgresql.org 2081 : 14916 : has_toastable_attrs = true;
2082 : : }
2083 : : }
2084 [ + + ]: 21755 : if (!has_toastable_attrs)
2085 : 12443 : return false; /* nothing to toast? */
2086 [ + + ]: 9312 : if (maxlength_unknown)
2087 : 7652 : return true; /* any unlimited-length attrs? */
2088 : 1660 : tuple_length = MAXALIGN(SizeofHeapTupleHeader +
2089 : 1660 : BITMAPLEN(tupdesc->natts)) +
2090 : 1660 : MAXALIGN(data_length);
2091 : 1660 : return (tuple_length > TOAST_TUPLE_THRESHOLD);
2092 : : }
2093 : :
2094 : : /*
2095 : : * TOAST tables for heap relations are just heap relations.
2096 : : */
2097 : : static Oid
1559 2098 : 7919 : heapam_relation_toast_am(Relation rel)
2099 : : {
2100 : 7919 : return rel->rd_rel->relam;
2101 : : }
2102 : :
2103 : :
2104 : : /* ------------------------------------------------------------------------
2105 : : * Planner related callbacks for the heap AM
2106 : : * ------------------------------------------------------------------------
2107 : : */
2108 : :
2109 : : #define HEAP_OVERHEAD_BYTES_PER_TUPLE \
2110 : : (MAXALIGN(SizeofHeapTupleHeader) + sizeof(ItemIdData))
2111 : : #define HEAP_USABLE_BYTES_PER_PAGE \
2112 : : (BLCKSZ - SizeOfPageHeaderData)
2113 : :
2114 : : static void
1842 andres@anarazel.de 2115 : 193700 : heapam_estimate_rel_size(Relation rel, int32 *attr_widths,
2116 : : BlockNumber *pages, double *tuples,
2117 : : double *allvisfrac)
2118 : : {
1742 rhaas@postgresql.org 2119 : 193700 : table_block_relation_estimate_size(rel, attr_widths, pages,
2120 : : tuples, allvisfrac,
2121 : : HEAP_OVERHEAD_BYTES_PER_TUPLE,
2122 : : HEAP_USABLE_BYTES_PER_PAGE);
1842 andres@anarazel.de 2123 : 193700 : }
2124 : :
2125 : :
2126 : : /* ------------------------------------------------------------------------
2127 : : * Executor related callbacks for the heap AM
2128 : : * ------------------------------------------------------------------------
2129 : : */
2130 : :
2131 : : static bool
1841 2132 : 194649 : heapam_scan_bitmap_next_block(TableScanDesc scan,
2133 : : TBMIterateResult *tbmres)
2134 : : {
2135 : 194649 : HeapScanDesc hscan = (HeapScanDesc) scan;
515 peter@eisentraut.org 2136 : 194649 : BlockNumber block = tbmres->blockno;
2137 : : Buffer buffer;
2138 : : Snapshot snapshot;
2139 : : int ntup;
2140 : :
1841 andres@anarazel.de 2141 : 194649 : hscan->rs_cindex = 0;
2142 : 194649 : hscan->rs_ntuples = 0;
2143 : :
2144 : : /*
2145 : : * We can skip fetching the heap page if we don't need any fields from the
2146 : : * heap, the bitmap entries don't need rechecking, and all tuples on the
2147 : : * page are visible to our transaction.
2148 : : */
7 tomas.vondra@postgre 2149 [ + + ]:GNC 194649 : if (!(scan->rs_flags & SO_NEED_TUPLES) &&
2150 [ + + ]: 48533 : !tbmres->recheck &&
2151 [ + + ]: 28876 : VM_ALL_VISIBLE(scan->rs_rd, tbmres->blockno, &hscan->rs_vmbuffer))
2152 : : {
2153 : : /* can't be lossy in the skip_fetch case */
2154 [ - + ]: 10383 : Assert(tbmres->ntuples >= 0);
2155 [ - + ]: 10383 : Assert(hscan->rs_empty_tuples_pending >= 0);
2156 : :
2157 : 10383 : hscan->rs_empty_tuples_pending += tbmres->ntuples;
2158 : :
2159 : 10383 : return true;
2160 : : }
2161 : :
2162 : : /*
2163 : : * Ignore any claimed entries past what we think is the end of the
2164 : : * relation. It may have been extended after the start of our scan (we
2165 : : * only hold an AccessShareLock, and it could be inserts from this
2166 : : * backend). We don't take this optimization in SERIALIZABLE isolation
2167 : : * though, as we need to examine all invisible tuples reachable by the
2168 : : * index.
2169 : : */
286 tmunro@postgresql.or 2170 [ + + - + ]:CBC 184266 : if (!IsolationIsSerializable() && block >= hscan->rs_nblocks)
1841 andres@anarazel.de 2171 :LBC (6) : return false;
2172 : :
2173 : : /*
2174 : : * Acquire pin on the target heap page, trading in any pin we held before.
2175 : : */
1841 andres@anarazel.de 2176 :CBC 184266 : hscan->rs_cbuf = ReleaseAndReadBuffer(hscan->rs_cbuf,
2177 : : scan->rs_rd,
2178 : : block);
515 peter@eisentraut.org 2179 : 184266 : hscan->rs_cblock = block;
1841 andres@anarazel.de 2180 : 184266 : buffer = hscan->rs_cbuf;
2181 : 184266 : snapshot = scan->rs_snapshot;
2182 : :
2183 : 184266 : ntup = 0;
2184 : :
2185 : : /*
2186 : : * Prune and repair fragmentation for the whole page, if possible.
2187 : : */
2188 : 184266 : heap_page_prune_opt(scan->rs_rd, buffer);
2189 : :
2190 : : /*
2191 : : * We must hold share lock on the buffer content while examining tuple
2192 : : * visibility. Afterwards, however, the tuples we have found to be
2193 : : * visible are guaranteed good as long as we hold the buffer pin.
2194 : : */
2195 : 184266 : LockBuffer(buffer, BUFFER_LOCK_SHARE);
2196 : :
2197 : : /*
2198 : : * We need two separate strategies for lossy and non-lossy cases.
2199 : : */
2200 [ + + ]: 184266 : if (tbmres->ntuples >= 0)
2201 : : {
2202 : : /*
2203 : : * Bitmap is non-lossy, so we just look through the offsets listed in
2204 : : * tbmres; but we have to follow any HOT chain starting at each such
2205 : : * offset.
2206 : : */
2207 : : int curslot;
2208 : :
2209 [ + + ]: 2595512 : for (curslot = 0; curslot < tbmres->ntuples; curslot++)
2210 : : {
2211 : 2489890 : OffsetNumber offnum = tbmres->offsets[curslot];
2212 : : ItemPointerData tid;
2213 : : HeapTupleData heapTuple;
2214 : :
515 peter@eisentraut.org 2215 : 2489890 : ItemPointerSet(&tid, block, offnum);
1841 andres@anarazel.de 2216 [ + + ]: 2489890 : if (heap_hot_search_buffer(&tid, scan->rs_rd, buffer, snapshot,
2217 : : &heapTuple, NULL, true))
2218 : 2381600 : hscan->rs_vistuples[ntup++] = ItemPointerGetOffsetNumber(&tid);
2219 : : }
2220 : : }
2221 : : else
2222 : : {
2223 : : /*
2224 : : * Bitmap is lossy, so we must examine each line pointer on the page.
2225 : : * But we can ignore HOT chains, since we'll check each tuple anyway.
2226 : : */
515 peter@eisentraut.org 2227 : 78641 : Page page = BufferGetPage(buffer);
2228 : 78641 : OffsetNumber maxoff = PageGetMaxOffsetNumber(page);
2229 : : OffsetNumber offnum;
2230 : :
1841 andres@anarazel.de 2231 [ + + ]: 605306 : for (offnum = FirstOffsetNumber; offnum <= maxoff; offnum = OffsetNumberNext(offnum))
2232 : : {
2233 : : ItemId lp;
2234 : : HeapTupleData loctup;
2235 : : bool valid;
2236 : :
515 peter@eisentraut.org 2237 : 526665 : lp = PageGetItemId(page, offnum);
1841 andres@anarazel.de 2238 [ - + ]: 526665 : if (!ItemIdIsNormal(lp))
1841 andres@anarazel.de 2239 :UBC 0 : continue;
515 peter@eisentraut.org 2240 :CBC 526665 : loctup.t_data = (HeapTupleHeader) PageGetItem(page, lp);
1841 andres@anarazel.de 2241 : 526665 : loctup.t_len = ItemIdGetLength(lp);
2242 : 526665 : loctup.t_tableOid = scan->rs_rd->rd_id;
515 peter@eisentraut.org 2243 : 526665 : ItemPointerSet(&loctup.t_self, block, offnum);
1841 andres@anarazel.de 2244 : 526665 : valid = HeapTupleSatisfiesVisibility(&loctup, snapshot, buffer);
2245 [ + + ]: 526665 : if (valid)
2246 : : {
2247 : 526602 : hscan->rs_vistuples[ntup++] = offnum;
1538 tmunro@postgresql.or 2248 :UBC 0 : PredicateLockTID(scan->rs_rd, &loctup.t_self, snapshot,
1538 tmunro@postgresql.or 2249 [ + - ]:CBC 526602 : HeapTupleHeaderGetXmin(loctup.t_data));
2250 : : }
2251 : 526665 : HeapCheckForSerializableConflictOut(valid, scan->rs_rd, &loctup,
2252 : : buffer, snapshot);
2253 : : }
2254 : : }
2255 : :
1841 andres@anarazel.de 2256 : 184263 : LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
2257 : :
2258 [ - + ]: 184263 : Assert(ntup <= MaxHeapTuplesPerPage);
2259 : 184263 : hscan->rs_ntuples = ntup;
2260 : :
2261 : 184263 : return ntup > 0;
2262 : : }
2263 : :
2264 : : static bool
2265 : 3395050 : heapam_scan_bitmap_next_tuple(TableScanDesc scan,
2266 : : TBMIterateResult *tbmres,
2267 : : TupleTableSlot *slot)
2268 : : {
2269 : 3395050 : HeapScanDesc hscan = (HeapScanDesc) scan;
2270 : : OffsetNumber targoffset;
2271 : : Page page;
2272 : : ItemId lp;
2273 : :
7 tomas.vondra@postgre 2274 [ + + ]:GNC 3395050 : if (hscan->rs_empty_tuples_pending > 0)
2275 : : {
2276 : : /*
2277 : : * If we don't have to fetch the tuple, just return nulls.
2278 : : */
2279 : 294048 : ExecStoreAllNullTuple(slot);
2280 : 294048 : hscan->rs_empty_tuples_pending--;
2281 : 294048 : return true;
2282 : : }
2283 : :
2284 : : /*
2285 : : * Out of range? If so, nothing more to look at on this page
2286 : : */
1841 andres@anarazel.de 2287 [ + - + + ]:CBC 3101002 : if (hscan->rs_cindex < 0 || hscan->rs_cindex >= hscan->rs_ntuples)
2288 : 194390 : return false;
2289 : :
2290 : 2906612 : targoffset = hscan->rs_vistuples[hscan->rs_cindex];
515 peter@eisentraut.org 2291 : 2906612 : page = BufferGetPage(hscan->rs_cbuf);
2292 : 2906612 : lp = PageGetItemId(page, targoffset);
1841 andres@anarazel.de 2293 [ - + ]: 2906612 : Assert(ItemIdIsNormal(lp));
2294 : :
515 peter@eisentraut.org 2295 : 2906612 : hscan->rs_ctup.t_data = (HeapTupleHeader) PageGetItem(page, lp);
1841 andres@anarazel.de 2296 : 2906612 : hscan->rs_ctup.t_len = ItemIdGetLength(lp);
2297 : 2906612 : hscan->rs_ctup.t_tableOid = scan->rs_rd->rd_id;
2298 : 2906612 : ItemPointerSet(&hscan->rs_ctup.t_self, hscan->rs_cblock, targoffset);
2299 : :
2300 [ - + - - : 2906612 : pgstat_count_heap_fetch(scan->rs_rd);
+ - ]
2301 : :
2302 : : /*
2303 : : * Set up the result slot to point to this tuple. Note that the slot
2304 : : * acquires a pin on the buffer.
2305 : : */
2306 : 2906612 : ExecStoreBufferHeapTuple(&hscan->rs_ctup,
2307 : : slot,
2308 : : hscan->rs_cbuf);
2309 : :
2310 : 2906612 : hscan->rs_cindex++;
2311 : :
2312 : 2906612 : return true;
2313 : : }
2314 : :
2315 : : static bool
1842 2316 : 6454 : heapam_scan_sample_next_block(TableScanDesc scan, SampleScanState *scanstate)
2317 : : {
2318 : 6454 : HeapScanDesc hscan = (HeapScanDesc) scan;
2319 : 6454 : TsmRoutine *tsm = scanstate->tsmroutine;
2320 : : BlockNumber blockno;
2321 : :
2322 : : /* return false immediately if relation is empty */
2323 [ - + ]: 6454 : if (hscan->rs_nblocks == 0)
1842 andres@anarazel.de 2324 :UBC 0 : return false;
2325 : :
2326 : : /* release previous scan buffer, if any */
10 drowley@postgresql.o 2327 [ + + ]:GNC 6454 : if (BufferIsValid(hscan->rs_cbuf))
2328 : : {
2329 : 6366 : ReleaseBuffer(hscan->rs_cbuf);
2330 : 6366 : hscan->rs_cbuf = InvalidBuffer;
2331 : : }
2332 : :
2333 [ + + ]: 6454 : if (tsm->NextSampleBlock)
2334 : 2221 : blockno = tsm->NextSampleBlock(scanstate, hscan->rs_nblocks);
2335 : : else
2336 : : {
2337 : : /* scanning table sequentially */
2338 : :
1842 andres@anarazel.de 2339 [ + + ]:CBC 4233 : if (hscan->rs_cblock == InvalidBlockNumber)
2340 : : {
2341 [ - + ]: 39 : Assert(!hscan->rs_inited);
2342 : 39 : blockno = hscan->rs_startblock;
2343 : : }
2344 : : else
2345 : : {
2346 [ - + ]: 4194 : Assert(hscan->rs_inited);
2347 : :
2348 : 4194 : blockno = hscan->rs_cblock + 1;
2349 : :
2350 [ + + ]: 4194 : if (blockno >= hscan->rs_nblocks)
2351 : : {
2352 : : /* wrap to beginning of rel, might not have started at 0 */
2353 : 39 : blockno = 0;
2354 : : }
2355 : :
2356 : : /*
2357 : : * Report our new scan position for synchronization purposes.
2358 : : *
2359 : : * Note: we do this before checking for end of scan so that the
2360 : : * final state of the position hint is back at the start of the
2361 : : * rel. That's not strictly necessary, but otherwise when you run
2362 : : * the same query multiple times the starting position would shift
2363 : : * a little bit backwards on every invocation, which is confusing.
2364 : : * We don't guarantee any specific ordering in general, though.
2365 : : */
1792 2366 [ - + ]: 4194 : if (scan->rs_flags & SO_ALLOW_SYNC)
1842 andres@anarazel.de 2367 :UBC 0 : ss_report_location(scan->rs_rd, blockno);
2368 : :
1842 andres@anarazel.de 2369 [ + + ]:CBC 4194 : if (blockno == hscan->rs_startblock)
2370 : : {
2371 : 39 : blockno = InvalidBlockNumber;
2372 : : }
2373 : : }
2374 : : }
2375 : :
10 drowley@postgresql.o 2376 :GNC 6454 : hscan->rs_cblock = blockno;
2377 : :
1842 andres@anarazel.de 2378 [ + + ]:CBC 6454 : if (!BlockNumberIsValid(blockno))
2379 : : {
2380 : 85 : hscan->rs_inited = false;
2381 : 85 : return false;
2382 : : }
2383 : :
10 drowley@postgresql.o 2384 [ - + ]:GNC 6369 : Assert(hscan->rs_cblock < hscan->rs_nblocks);
2385 : :
2386 : : /*
2387 : : * Be sure to check for interrupts at least once per page. Checks at
2388 : : * higher code levels won't be able to stop a sample scan that encounters
2389 : : * many pages' worth of consecutive dead tuples.
2390 : : */
2391 [ - + ]: 6369 : CHECK_FOR_INTERRUPTS();
2392 : :
2393 : : /* Read page using selected strategy */
2394 : 6369 : hscan->rs_cbuf = ReadBufferExtended(hscan->rs_base.rs_rd, MAIN_FORKNUM,
2395 : : blockno, RBM_NORMAL, hscan->rs_strategy);
2396 : :
2397 : : /* in pagemode, prune the page and determine visible tuple offsets */
2398 [ + + ]: 6369 : if (hscan->rs_base.rs_flags & SO_ALLOW_PAGEMODE)
2399 : 4275 : heap_prepare_pagescan(scan);
2400 : :
2401 : 6369 : hscan->rs_inited = true;
1842 andres@anarazel.de 2402 :CBC 6369 : return true;
2403 : : }
2404 : :
2405 : : static bool
2406 : 126946 : heapam_scan_sample_next_tuple(TableScanDesc scan, SampleScanState *scanstate,
2407 : : TupleTableSlot *slot)
2408 : : {
2409 : 126946 : HeapScanDesc hscan = (HeapScanDesc) scan;
2410 : 126946 : TsmRoutine *tsm = scanstate->tsmroutine;
2411 : 126946 : BlockNumber blockno = hscan->rs_cblock;
1792 2412 : 126946 : bool pagemode = (scan->rs_flags & SO_ALLOW_PAGEMODE) != 0;
2413 : :
2414 : : Page page;
2415 : : bool all_visible;
2416 : : OffsetNumber maxoffset;
2417 : :
2418 : : /*
2419 : : * When not using pagemode, we must lock the buffer during tuple
2420 : : * visibility checks.
2421 : : */
1842 2422 [ + + ]: 126946 : if (!pagemode)
2423 : 2097 : LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_SHARE);
2424 : :
2425 : 126946 : page = (Page) BufferGetPage(hscan->rs_cbuf);
2426 [ + + ]: 253342 : all_visible = PageIsAllVisible(page) &&
2427 [ + - ]: 126396 : !scan->rs_snapshot->takenDuringRecovery;
2428 : 126946 : maxoffset = PageGetMaxOffsetNumber(page);
2429 : :
2430 : : for (;;)
1842 andres@anarazel.de 2431 :UBC 0 : {
2432 : : OffsetNumber tupoffset;
2433 : :
1842 andres@anarazel.de 2434 [ - + ]:CBC 126946 : CHECK_FOR_INTERRUPTS();
2435 : :
2436 : : /* Ask the tablesample method which tuples to check on this page. */
2437 : 126946 : tupoffset = tsm->NextSampleTuple(scanstate,
2438 : : blockno,
2439 : : maxoffset);
2440 : :
2441 [ + + + - : 126946 : if (OffsetNumberIsValid(tupoffset))
+ + ]
2442 : : {
2443 : : ItemId itemid;
2444 : : bool visible;
2445 : 120580 : HeapTuple tuple = &(hscan->rs_ctup);
2446 : :
2447 : : /* Skip invalid tuple pointers. */
2448 : 120580 : itemid = PageGetItemId(page, tupoffset);
2449 [ - + ]: 120580 : if (!ItemIdIsNormal(itemid))
1842 andres@anarazel.de 2450 :UBC 0 : continue;
2451 : :
1842 andres@anarazel.de 2452 :CBC 120580 : tuple->t_data = (HeapTupleHeader) PageGetItem(page, itemid);
2453 : 120580 : tuple->t_len = ItemIdGetLength(itemid);
2454 : 120580 : ItemPointerSet(&(tuple->t_self), blockno, tupoffset);
2455 : :
2456 : :
2457 [ + + ]: 120580 : if (all_visible)
2458 : 120174 : visible = true;
2459 : : else
2460 : 406 : visible = SampleHeapTupleVisible(scan, hscan->rs_cbuf,
2461 : : tuple, tupoffset);
2462 : :
2463 : : /* in pagemode, heap_prepare_pagescan did this for us */
2464 [ + + ]: 120580 : if (!pagemode)
1538 tmunro@postgresql.or 2465 : 3 : HeapCheckForSerializableConflictOut(visible, scan->rs_rd, tuple,
2466 : : hscan->rs_cbuf, scan->rs_snapshot);
2467 : :
2468 : : /* Try next tuple from same page. */
1842 andres@anarazel.de 2469 [ - + ]: 120580 : if (!visible)
1842 andres@anarazel.de 2470 :UBC 0 : continue;
2471 : :
2472 : : /* Found visible tuple, return it. */
1842 andres@anarazel.de 2473 [ + + ]:CBC 120580 : if (!pagemode)
2474 : 3 : LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_UNLOCK);
2475 : :
2476 : 120580 : ExecStoreBufferHeapTuple(tuple, slot, hscan->rs_cbuf);
2477 : :
2478 : : /* Count successfully-fetched tuples as heap fetches */
2479 [ - + - - : 120580 : pgstat_count_heap_getnext(scan->rs_rd);
+ - ]
2480 : :
2481 : 120580 : return true;
2482 : : }
2483 : : else
2484 : : {
2485 : : /*
2486 : : * If we get here, it means we've exhausted the items on this page
2487 : : * and it's time to move to the next.
2488 : : */
2489 [ + + ]: 6366 : if (!pagemode)
2490 : 2094 : LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_UNLOCK);
2491 : :
2492 : 6366 : ExecClearTuple(slot);
2493 : 6366 : return false;
2494 : : }
2495 : : }
2496 : :
2497 : : Assert(0);
2498 : : }
2499 : :
2500 : :
2501 : : /* ----------------------------------------------------------------------------
2502 : : * Helper functions for the above.
2503 : : * ----------------------------------------------------------------------------
2504 : : */
2505 : :
2506 : : /*
2507 : : * Reconstruct and rewrite the given tuple
2508 : : *
2509 : : * We cannot simply copy the tuple as-is, for several reasons:
2510 : : *
2511 : : * 1. We'd like to squeeze out the values of any dropped columns, both
2512 : : * to save space and to ensure we have no corner-case failures. (It's
2513 : : * possible for example that the new table hasn't got a TOAST table
2514 : : * and so is unable to store any large values of dropped cols.)
2515 : : *
2516 : : * 2. The tuple might not even be legal for the new table; this is
2517 : : * currently only known to happen as an after-effect of ALTER TABLE
2518 : : * SET WITHOUT OIDS.
2519 : : *
2520 : : * So, we must reconstruct the tuple from component Datums.
2521 : : */
2522 : : static void
2523 : 375258 : reform_and_rewrite_tuple(HeapTuple tuple,
2524 : : Relation OldHeap, Relation NewHeap,
2525 : : Datum *values, bool *isnull, RewriteState rwstate)
2526 : : {
2527 : 375258 : TupleDesc oldTupDesc = RelationGetDescr(OldHeap);
2528 : 375258 : TupleDesc newTupDesc = RelationGetDescr(NewHeap);
2529 : : HeapTuple copiedTuple;
2530 : : int i;
2531 : :
2532 : 375258 : heap_deform_tuple(tuple, oldTupDesc, values, isnull);
2533 : :
2534 : : /* Be sure to null out any dropped columns */
2535 [ + + ]: 3052584 : for (i = 0; i < newTupDesc->natts; i++)
2536 : : {
2537 [ - + ]: 2677326 : if (TupleDescAttr(newTupDesc, i)->attisdropped)
1842 andres@anarazel.de 2538 :UBC 0 : isnull[i] = true;
2539 : : }
2540 : :
1842 andres@anarazel.de 2541 :CBC 375258 : copiedTuple = heap_form_tuple(newTupDesc, values, isnull);
2542 : :
2543 : : /* The heap rewrite module does the rest */
2544 : 375258 : rewrite_heap_tuple(rwstate, tuple, copiedTuple);
2545 : :
2546 : 375258 : heap_freetuple(copiedTuple);
2547 : 375258 : }
2548 : :
2549 : : /*
2550 : : * Check visibility of the tuple.
2551 : : */
2552 : : static bool
2553 : 406 : SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer,
2554 : : HeapTuple tuple,
2555 : : OffsetNumber tupoffset)
2556 : : {
2557 : 406 : HeapScanDesc hscan = (HeapScanDesc) scan;
2558 : :
1792 2559 [ + + ]: 406 : if (scan->rs_flags & SO_ALLOW_PAGEMODE)
2560 : : {
2561 : : /*
2562 : : * In pageatatime mode, heap_prepare_pagescan() already did visibility
2563 : : * checks, so just look at the info it left in rs_vistuples[].
2564 : : *
2565 : : * We use a binary search over the known-sorted array. Note: we could
2566 : : * save some effort if we insisted that NextSampleTuple select tuples
2567 : : * in increasing order, but it's not clear that there would be enough
2568 : : * gain to justify the restriction.
2569 : : */
1842 2570 : 403 : int start = 0,
2571 : 403 : end = hscan->rs_ntuples - 1;
2572 : :
2573 [ + - ]: 780 : while (start <= end)
2574 : : {
2575 : 780 : int mid = (start + end) / 2;
2576 : 780 : OffsetNumber curoffset = hscan->rs_vistuples[mid];
2577 : :
2578 [ + + ]: 780 : if (tupoffset == curoffset)
2579 : 403 : return true;
2580 [ + + ]: 377 : else if (tupoffset < curoffset)
2581 : 150 : end = mid - 1;
2582 : : else
2583 : 227 : start = mid + 1;
2584 : : }
2585 : :
1842 andres@anarazel.de 2586 :UBC 0 : return false;
2587 : : }
2588 : : else
2589 : : {
2590 : : /* Otherwise, we have to check the tuple individually. */
1842 andres@anarazel.de 2591 :CBC 3 : return HeapTupleSatisfiesVisibility(tuple, scan->rs_snapshot,
2592 : : buffer);
2593 : : }
2594 : : }
2595 : :
2596 : : /*
2597 : : * heapap_analyze -- implementation of relation_analyze() for heap
2598 : : * table access method
2599 : : */
2600 : : static void
6 akorotkov@postgresql 2601 :GNC 6900 : heapam_analyze(Relation relation, AcquireSampleRowsFunc *func,
2602 : : BlockNumber *totalpages, BufferAccessStrategy bstrategy)
2603 : : {
2604 : 6900 : block_level_table_analyze(relation, func, totalpages, bstrategy,
2605 : : heapam_scan_analyze_next_block,
2606 : : heapam_scan_analyze_next_tuple);
2607 : 6900 : }
2608 : :
2609 : : /* ------------------------------------------------------------------------
2610 : : * Definition of the heap table access method.
2611 : : * ------------------------------------------------------------------------
2612 : : */
2613 : :
2614 : : static const TableAmRoutine heapam_methods = {
2615 : : .type = T_TableAmRoutine,
2616 : :
2617 : : .slot_callbacks = heapam_slot_callbacks,
2618 : :
2619 : : .scan_begin = heap_beginscan,
2620 : : .scan_end = heap_endscan,
2621 : : .scan_rescan = heap_rescan,
2622 : : .scan_getnextslot = heap_getnextslot,
2623 : :
2624 : : .scan_set_tidrange = heap_set_tidrange,
2625 : : .scan_getnextslot_tidrange = heap_getnextslot_tidrange,
2626 : :
2627 : : .parallelscan_estimate = table_block_parallelscan_estimate,
2628 : : .parallelscan_initialize = table_block_parallelscan_initialize,
2629 : : .parallelscan_reinitialize = table_block_parallelscan_reinitialize,
2630 : :
2631 : : .index_fetch_begin = heapam_index_fetch_begin,
2632 : : .index_fetch_reset = heapam_index_fetch_reset,
2633 : : .index_fetch_end = heapam_index_fetch_end,
2634 : : .index_fetch_tuple = heapam_index_fetch_tuple,
2635 : :
2636 : : .tuple_insert = heapam_tuple_insert,
2637 : : .tuple_insert_speculative = heapam_tuple_insert_speculative,
2638 : : .tuple_complete_speculative = heapam_tuple_complete_speculative,
2639 : : .multi_insert = heap_multi_insert,
2640 : : .tuple_delete = heapam_tuple_delete,
2641 : : .tuple_update = heapam_tuple_update,
2642 : : .tuple_lock = heapam_tuple_lock,
2643 : :
2644 : : .tuple_fetch_row_version = heapam_fetch_row_version,
2645 : : .tuple_get_latest_tid = heap_get_latest_tid,
2646 : : .tuple_tid_valid = heapam_tuple_tid_valid,
2647 : : .tuple_satisfies_snapshot = heapam_tuple_satisfies_snapshot,
2648 : : .index_delete_tuples = heap_index_delete_tuples,
2649 : :
2650 : : .relation_set_new_filelocator = heapam_relation_set_new_filelocator,
2651 : : .relation_nontransactional_truncate = heapam_relation_nontransactional_truncate,
2652 : : .relation_copy_data = heapam_relation_copy_data,
2653 : : .relation_copy_for_cluster = heapam_relation_copy_for_cluster,
2654 : : .relation_vacuum = heap_vacuum_rel,
2655 : : .index_build_range_scan = heapam_index_build_range_scan,
2656 : : .index_validate_scan = heapam_index_validate_scan,
2657 : : .relation_analyze = heapam_analyze,
2658 : :
2659 : : .relation_size = table_block_relation_size,
2660 : : .relation_needs_toast_table = heapam_relation_needs_toast_table,
2661 : : .relation_toast_am = heapam_relation_toast_am,
2662 : : .relation_fetch_toast_slice = heap_fetch_toast_slice,
2663 : :
2664 : : .relation_estimate_size = heapam_estimate_rel_size,
2665 : :
2666 : : .scan_bitmap_next_block = heapam_scan_bitmap_next_block,
2667 : : .scan_bitmap_next_tuple = heapam_scan_bitmap_next_tuple,
2668 : : .scan_sample_next_block = heapam_scan_sample_next_block,
2669 : : .scan_sample_next_tuple = heapam_scan_sample_next_tuple
2670 : : };
2671 : :
2672 : :
2673 : : const TableAmRoutine *
1866 andres@anarazel.de 2674 :CBC 8836093 : GetHeapamTableAmRoutine(void)
2675 : : {
2676 : 8836093 : return &heapam_methods;
2677 : : }
2678 : :
2679 : : Datum
2680 : 953400 : heap_tableam_handler(PG_FUNCTION_ARGS)
2681 : : {
2682 : 953400 : PG_RETURN_POINTER(&heapam_methods);
2683 : : }
|