Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * heapam_visibility.c
4 : * Tuple visibility rules for tuples stored in heap.
5 : *
6 : * NOTE: all the HeapTupleSatisfies routines will update the tuple's
7 : * "hint" status bits if we see that the inserting or deleting transaction
8 : * has now committed or aborted (and it is safe to set the hint bits).
9 : * If the hint bits are changed, MarkBufferDirtyHint is called on
10 : * the passed-in buffer. The caller must hold not only a pin, but at least
11 : * shared buffer content lock on the buffer containing the tuple.
12 : *
13 : * NOTE: When using a non-MVCC snapshot, we must check
14 : * TransactionIdIsInProgress (which looks in the PGPROC array) before
15 : * TransactionIdDidCommit (which look in pg_xact). Otherwise we have a race
16 : * condition: we might decide that a just-committed transaction crashed,
17 : * because none of the tests succeed. xact.c is careful to record
18 : * commit/abort in pg_xact before it unsets MyProc->xid in the PGPROC array.
19 : * That fixes that problem, but it also means there is a window where
20 : * TransactionIdIsInProgress and TransactionIdDidCommit will both return true.
21 : * If we check only TransactionIdDidCommit, we could consider a tuple
22 : * committed when a later GetSnapshotData call will still think the
23 : * originating transaction is in progress, which leads to application-level
24 : * inconsistency. The upshot is that we gotta check TransactionIdIsInProgress
25 : * first in all code paths, except for a few cases where we are looking at
26 : * subtransactions of our own main transaction and so there can't be any race
27 : * condition.
28 : *
29 : * We can't use TransactionIdDidAbort here because it won't treat transactions
30 : * that were in progress during a crash as aborted. We determine that
31 : * transactions aborted/crashed through process of elimination instead.
32 : *
33 : * When using an MVCC snapshot, we rely on XidInMVCCSnapshot rather than
34 : * TransactionIdIsInProgress, but the logic is otherwise the same: do not
35 : * check pg_xact until after deciding that the xact is no longer in progress.
36 : *
37 : *
38 : * Summary of visibility functions:
39 : *
40 : * HeapTupleSatisfiesMVCC()
41 : * visible to supplied snapshot, excludes current command
42 : * HeapTupleSatisfiesUpdate()
43 : * visible to instant snapshot, with user-supplied command
44 : * counter and more complex result
45 : * HeapTupleSatisfiesSelf()
46 : * visible to instant snapshot and current command
47 : * HeapTupleSatisfiesDirty()
48 : * like HeapTupleSatisfiesSelf(), but includes open transactions
49 : * HeapTupleSatisfiesVacuum()
50 : * visible to any running transaction, used by VACUUM
51 : * HeapTupleSatisfiesNonVacuumable()
52 : * Snapshot-style API for HeapTupleSatisfiesVacuum
53 : * HeapTupleSatisfiesToast()
54 : * visible unless part of interrupted vacuum, used for TOAST
55 : * HeapTupleSatisfiesAny()
56 : * all tuples are visible
57 : *
58 : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
59 : * Portions Copyright (c) 1994, Regents of the University of California
60 : *
61 : * IDENTIFICATION
62 : * src/backend/access/heap/heapam_visibility.c
63 : *
64 : *-------------------------------------------------------------------------
65 : */
66 :
67 : #include "postgres.h"
68 :
69 : #include "access/heapam.h"
70 : #include "access/htup_details.h"
71 : #include "access/multixact.h"
72 : #include "access/tableam.h"
73 : #include "access/transam.h"
74 : #include "access/xact.h"
75 : #include "access/xlog.h"
76 : #include "storage/bufmgr.h"
77 : #include "storage/procarray.h"
78 : #include "utils/builtins.h"
79 : #include "utils/snapmgr.h"
80 :
81 :
82 : /*
83 : * To be allowed to set hint bits, SetHintBits() needs to call
84 : * BufferBeginSetHintBits(). However, that's not free, and some callsites call
85 : * SetHintBits() on many tuples in a row. For those it makes sense to amortize
86 : * the cost of BufferBeginSetHintBits(). Additionally it's desirable to defer
87 : * the cost of BufferBeginSetHintBits() until a hint bit needs to actually be
88 : * set. This enum serves as the necessary state space passed to
89 : * SetHintBitsExt().
90 : */
91 : typedef enum SetHintBitsState
92 : {
93 : /* not yet checked if hint bits may be set */
94 : SHB_INITIAL,
95 : /* failed to get permission to set hint bits, don't check again */
96 : SHB_DISABLED,
97 : /* allowed to set hint bits */
98 : SHB_ENABLED,
99 : } SetHintBitsState;
100 :
101 : /*
102 : * SetHintBitsExt()
103 : *
104 : * Set commit/abort hint bits on a tuple, if appropriate at this time.
105 : *
106 : * To be allowed to set a hint bit on a tuple, the page must not be undergoing
107 : * IO at this time (otherwise we e.g. could corrupt PG's page checksum or even
108 : * the filesystem's, as is known to happen with btrfs).
109 : *
110 : * The right to set a hint bit can be acquired on a page level with
111 : * BufferBeginSetHintBits(). Only a single backend gets the right to set hint
112 : * bits at a time. Alternatively, if called with a NULL SetHintBitsState*,
113 : * hint bits are set with BufferSetHintBits16().
114 : *
115 : * It is only safe to set a transaction-committed hint bit if we know the
116 : * transaction's commit record is guaranteed to be flushed to disk before the
117 : * buffer, or if the table is temporary or unlogged and will be obliterated by
118 : * a crash anyway. We cannot change the LSN of the page here, because we may
119 : * hold only a share lock on the buffer, so we can only use the LSN to
120 : * interlock this if the buffer's LSN already is newer than the commit LSN;
121 : * otherwise we have to just refrain from setting the hint bit until some
122 : * future re-examination of the tuple.
123 : *
124 : * We can always set hint bits when marking a transaction aborted. (Some
125 : * code in heapam.c relies on that!)
126 : *
127 : * Also, if we are cleaning up HEAP_MOVED_IN or HEAP_MOVED_OFF entries, then
128 : * we can always set the hint bits, since pre-9.0 VACUUM FULL always used
129 : * synchronous commits and didn't move tuples that weren't previously
130 : * hinted. (This is not known by this subroutine, but is applied by its
131 : * callers.) Note: old-style VACUUM FULL is gone, but we have to keep this
132 : * module's support for MOVED_OFF/MOVED_IN flag bits for as long as we
133 : * support in-place update from pre-9.0 databases.
134 : *
135 : * Normal commits may be asynchronous, so for those we need to get the LSN
136 : * of the transaction and then check whether this is flushed.
137 : *
138 : * The caller should pass xid as the XID of the transaction to check, or
139 : * InvalidTransactionId if no check is needed.
140 : */
141 : static inline void
142 17292471 : SetHintBitsExt(HeapTupleHeader tuple, Buffer buffer,
143 : uint16 infomask, TransactionId xid, SetHintBitsState *state)
144 : {
145 : /*
146 : * In batched mode, if we previously did not get permission to set hint
147 : * bits, don't try again - in all likelihood IO is still going on.
148 : */
149 17292471 : if (state && *state == SHB_DISABLED)
150 287 : return;
151 :
152 17292184 : if (TransactionIdIsValid(xid))
153 : {
154 17127742 : if (BufferIsPermanent(buffer))
155 : {
156 : /* NB: xid must be known committed here! */
157 16091713 : XLogRecPtr commitLSN = TransactionIdGetCommitLSN(xid);
158 :
159 16278118 : if (XLogNeedsFlush(commitLSN) &&
160 186405 : BufferGetLSNAtomic(buffer) < commitLSN)
161 : {
162 : /* not flushed and no LSN interlock, so don't set hint */
163 169008 : return;
164 : }
165 : }
166 : }
167 :
168 : /*
169 : * If we're not operating in batch mode, use BufferSetHintBits16() to mark
170 : * the page dirty, that's cheaper than
171 : * BufferBeginSetHintBits()/BufferFinishSetHintBits(). That's important
172 : * for cases where we set a lot of hint bits on a page individually.
173 : */
174 17123176 : if (!state)
175 : {
176 15393734 : BufferSetHintBits16(&tuple->t_infomask,
177 15393734 : tuple->t_infomask | infomask, buffer);
178 15393734 : return;
179 : }
180 :
181 1729442 : if (*state == SHB_INITIAL)
182 : {
183 97678 : if (!BufferBeginSetHintBits(buffer))
184 : {
185 10 : *state = SHB_DISABLED;
186 10 : return;
187 : }
188 :
189 97668 : *state = SHB_ENABLED;
190 : }
191 1729432 : tuple->t_infomask |= infomask;
192 : }
193 :
194 : /*
195 : * Simple wrapper around SetHintBitExt(), use when operating on a single
196 : * tuple.
197 : */
198 : static inline void
199 14188731 : SetHintBits(HeapTupleHeader tuple, Buffer buffer,
200 : uint16 infomask, TransactionId xid)
201 : {
202 14188731 : SetHintBitsExt(tuple, buffer, infomask, xid, NULL);
203 14188731 : }
204 :
205 : /*
206 : * HeapTupleSetHintBits --- exported version of SetHintBits()
207 : *
208 : * This must be separate because of C99's brain-dead notions about how to
209 : * implement inline functions.
210 : */
211 : void
212 279 : HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
213 : uint16 infomask, TransactionId xid)
214 : {
215 : /*
216 : * The uses from heapam.c rely on being able to perform the hint bit
217 : * updates, which can only be guaranteed if we are holding an exclusive
218 : * lock on the buffer - which all callers are doing.
219 : */
220 : Assert(BufferIsLockedByMeInMode(buffer, BUFFER_LOCK_EXCLUSIVE));
221 :
222 279 : SetHintBits(tuple, buffer, infomask, xid);
223 279 : }
224 :
225 : /*
226 : * If HEAP_MOVED_OFF or HEAP_MOVED_IN are set on the tuple, remove them and
227 : * adjust hint bits. See the comment for SetHintBits() for more background.
228 : *
229 : * This helper returns false if the row ought to be invisible, true otherwise.
230 : */
231 : static inline bool
232 38487919 : HeapTupleCleanMoved(HeapTupleHeader tuple, Buffer buffer)
233 : {
234 : TransactionId xvac;
235 :
236 : /* only used by pre-9.0 binary upgrades */
237 38487919 : if (likely(!(tuple->t_infomask & (HEAP_MOVED_OFF | HEAP_MOVED_IN))))
238 38487919 : return true;
239 :
240 0 : xvac = HeapTupleHeaderGetXvac(tuple);
241 :
242 0 : if (TransactionIdIsCurrentTransactionId(xvac))
243 0 : elog(ERROR, "encountered tuple with HEAP_MOVED considered current");
244 :
245 0 : if (TransactionIdIsInProgress(xvac))
246 0 : elog(ERROR, "encountered tuple with HEAP_MOVED considered in-progress");
247 :
248 0 : if (tuple->t_infomask & HEAP_MOVED_OFF)
249 : {
250 0 : if (TransactionIdDidCommit(xvac))
251 : {
252 0 : SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
253 : InvalidTransactionId);
254 0 : return false;
255 : }
256 0 : SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
257 : InvalidTransactionId);
258 : }
259 0 : else if (tuple->t_infomask & HEAP_MOVED_IN)
260 : {
261 0 : if (TransactionIdDidCommit(xvac))
262 0 : SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
263 : InvalidTransactionId);
264 : else
265 : {
266 0 : SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
267 : InvalidTransactionId);
268 0 : return false;
269 : }
270 : }
271 :
272 0 : return true;
273 : }
274 :
275 : /*
276 : * HeapTupleSatisfiesSelf
277 : * True iff heap tuple is valid "for itself".
278 : *
279 : * See SNAPSHOT_MVCC's definition for the intended behaviour.
280 : *
281 : * Note:
282 : * Assumes heap tuple is valid.
283 : *
284 : * The satisfaction of "itself" requires the following:
285 : *
286 : * ((Xmin == my-transaction && the row was updated by the current transaction, and
287 : * (Xmax is null it was not deleted
288 : * [|| Xmax != my-transaction)]) [or it was deleted by another transaction]
289 : * ||
290 : *
291 : * (Xmin is committed && the row was modified by a committed transaction, and
292 : * (Xmax is null || the row has not been deleted, or
293 : * (Xmax != my-transaction && the row was deleted by another transaction
294 : * Xmax is not committed))) that has not been committed
295 : */
296 : static bool
297 605126 : HeapTupleSatisfiesSelf(HeapTuple htup, Snapshot snapshot, Buffer buffer)
298 : {
299 605126 : HeapTupleHeader tuple = htup->t_data;
300 :
301 : Assert(ItemPointerIsValid(&htup->t_self));
302 : Assert(htup->t_tableOid != InvalidOid);
303 :
304 605126 : if (!HeapTupleHeaderXminCommitted(tuple))
305 : {
306 605058 : if (HeapTupleHeaderXminInvalid(tuple))
307 0 : return false;
308 :
309 605058 : if (!HeapTupleCleanMoved(tuple, buffer))
310 0 : return false;
311 605058 : else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
312 : {
313 605058 : if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
314 604977 : return true;
315 :
316 81 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
317 13 : return true;
318 :
319 68 : if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
320 : {
321 : TransactionId xmax;
322 :
323 0 : xmax = HeapTupleGetUpdateXid(tuple);
324 :
325 : /* not LOCKED_ONLY, so it has to have an xmax */
326 : Assert(TransactionIdIsValid(xmax));
327 :
328 : /* updating subtransaction must have aborted */
329 0 : if (!TransactionIdIsCurrentTransactionId(xmax))
330 0 : return true;
331 : else
332 0 : return false;
333 : }
334 :
335 68 : if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
336 : {
337 : /* deleting subtransaction must have aborted */
338 12 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
339 : InvalidTransactionId);
340 12 : return true;
341 : }
342 :
343 56 : return false;
344 : }
345 0 : else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
346 0 : return false;
347 0 : else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
348 0 : SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
349 : HeapTupleHeaderGetRawXmin(tuple));
350 : else
351 : {
352 : /* it must have aborted or crashed */
353 0 : SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
354 : InvalidTransactionId);
355 0 : return false;
356 : }
357 : }
358 :
359 : /* by here, the inserting transaction has committed */
360 :
361 68 : if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
362 68 : return true;
363 :
364 0 : if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
365 : {
366 0 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
367 0 : return true;
368 0 : return false; /* updated by other */
369 : }
370 :
371 0 : if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
372 : {
373 : TransactionId xmax;
374 :
375 0 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
376 0 : return true;
377 :
378 0 : xmax = HeapTupleGetUpdateXid(tuple);
379 :
380 : /* not LOCKED_ONLY, so it has to have an xmax */
381 : Assert(TransactionIdIsValid(xmax));
382 :
383 0 : if (TransactionIdIsCurrentTransactionId(xmax))
384 0 : return false;
385 0 : if (TransactionIdIsInProgress(xmax))
386 0 : return true;
387 0 : if (TransactionIdDidCommit(xmax))
388 0 : return false;
389 : /* it must have aborted or crashed */
390 0 : return true;
391 : }
392 :
393 0 : if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
394 : {
395 0 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
396 0 : return true;
397 0 : return false;
398 : }
399 :
400 0 : if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
401 0 : return true;
402 :
403 0 : if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
404 : {
405 : /* it must have aborted or crashed */
406 0 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
407 : InvalidTransactionId);
408 0 : return true;
409 : }
410 :
411 : /* xmax transaction committed */
412 :
413 0 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
414 : {
415 0 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
416 : InvalidTransactionId);
417 0 : return true;
418 : }
419 :
420 0 : SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
421 : HeapTupleHeaderGetRawXmax(tuple));
422 0 : return false;
423 : }
424 :
425 : /*
426 : * HeapTupleSatisfiesAny
427 : * Dummy "satisfies" routine: any tuple satisfies SnapshotAny.
428 : */
429 : static bool
430 12001599 : HeapTupleSatisfiesAny(HeapTuple htup, Snapshot snapshot, Buffer buffer)
431 : {
432 12001599 : return true;
433 : }
434 :
435 : /*
436 : * HeapTupleSatisfiesToast
437 : * True iff heap tuple is valid as a TOAST row.
438 : *
439 : * See SNAPSHOT_TOAST's definition for the intended behaviour.
440 : *
441 : * This is a simplified version that only checks for VACUUM moving conditions.
442 : * It's appropriate for TOAST usage because TOAST really doesn't want to do
443 : * its own time qual checks; if you can see the main table row that contains
444 : * a TOAST reference, you should be able to see the TOASTed value. However,
445 : * vacuuming a TOAST table is independent of the main table, and in case such
446 : * a vacuum fails partway through, we'd better do this much checking.
447 : *
448 : * Among other things, this means you can't do UPDATEs of rows in a TOAST
449 : * table.
450 : */
451 : static bool
452 95147 : HeapTupleSatisfiesToast(HeapTuple htup, Snapshot snapshot,
453 : Buffer buffer)
454 : {
455 95147 : HeapTupleHeader tuple = htup->t_data;
456 :
457 : Assert(ItemPointerIsValid(&htup->t_self));
458 : Assert(htup->t_tableOid != InvalidOid);
459 :
460 95147 : if (!HeapTupleHeaderXminCommitted(tuple))
461 : {
462 35019 : if (HeapTupleHeaderXminInvalid(tuple))
463 0 : return false;
464 :
465 35019 : if (!HeapTupleCleanMoved(tuple, buffer))
466 0 : return false;
467 :
468 : /*
469 : * An invalid Xmin can be left behind by a speculative insertion that
470 : * is canceled by super-deleting the tuple. This also applies to
471 : * TOAST tuples created during speculative insertion.
472 : */
473 35019 : else if (!TransactionIdIsValid(HeapTupleHeaderGetXmin(tuple)))
474 0 : return false;
475 : }
476 :
477 : /* otherwise assume the tuple is valid for TOAST. */
478 95147 : return true;
479 : }
480 :
481 : /*
482 : * HeapTupleSatisfiesUpdate
483 : *
484 : * This function returns a more detailed result code than most of the
485 : * functions in this file, since UPDATE needs to know more than "is it
486 : * visible?". It also allows for user-supplied CommandId rather than
487 : * relying on CurrentCommandId.
488 : *
489 : * The possible return codes are:
490 : *
491 : * TM_Invisible: the tuple didn't exist at all when the scan started, e.g. it
492 : * was created by a later CommandId.
493 : *
494 : * TM_Ok: The tuple is valid and visible, so it may be updated.
495 : *
496 : * TM_SelfModified: The tuple was updated by the current transaction, after
497 : * the current scan started.
498 : *
499 : * TM_Updated: The tuple was updated by a committed transaction (including
500 : * the case where the tuple was moved into a different partition).
501 : *
502 : * TM_Deleted: The tuple was deleted by a committed transaction.
503 : *
504 : * TM_BeingModified: The tuple is being updated by an in-progress transaction
505 : * other than the current transaction. (Note: this includes the case where
506 : * the tuple is share-locked by a MultiXact, even if the MultiXact includes
507 : * the current transaction. Callers that want to distinguish that case must
508 : * test for it themselves.)
509 : */
510 : TM_Result
511 5050894 : HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid,
512 : Buffer buffer)
513 : {
514 5050894 : HeapTupleHeader tuple = htup->t_data;
515 :
516 : Assert(ItemPointerIsValid(&htup->t_self));
517 : Assert(htup->t_tableOid != InvalidOid);
518 :
519 5050894 : if (!HeapTupleHeaderXminCommitted(tuple))
520 : {
521 279340 : if (HeapTupleHeaderXminInvalid(tuple))
522 0 : return TM_Invisible;
523 :
524 279340 : else if (!HeapTupleCleanMoved(tuple, buffer))
525 0 : return TM_Invisible;
526 279340 : else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
527 : {
528 276270 : if (HeapTupleHeaderGetCmin(tuple) >= curcid)
529 28 : return TM_Invisible; /* inserted after scan started */
530 :
531 276242 : if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
532 236017 : return TM_Ok;
533 :
534 40225 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
535 : {
536 : TransactionId xmax;
537 :
538 40213 : xmax = HeapTupleHeaderGetRawXmax(tuple);
539 :
540 : /*
541 : * Careful here: even though this tuple was created by our own
542 : * transaction, it might be locked by other transactions, if
543 : * the original version was key-share locked when we updated
544 : * it.
545 : */
546 :
547 40213 : if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
548 : {
549 31 : if (MultiXactIdIsRunning(xmax, true))
550 31 : return TM_BeingModified;
551 : else
552 0 : return TM_Ok;
553 : }
554 :
555 : /*
556 : * If the locker is gone, then there is nothing of interest
557 : * left in this Xmax; otherwise, report the tuple as
558 : * locked/updated.
559 : */
560 40182 : if (!TransactionIdIsInProgress(xmax))
561 0 : return TM_Ok;
562 40182 : return TM_BeingModified;
563 : }
564 :
565 12 : if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
566 : {
567 : TransactionId xmax;
568 :
569 8 : xmax = HeapTupleGetUpdateXid(tuple);
570 :
571 : /* not LOCKED_ONLY, so it has to have an xmax */
572 : Assert(TransactionIdIsValid(xmax));
573 :
574 : /* deleting subtransaction must have aborted */
575 8 : if (!TransactionIdIsCurrentTransactionId(xmax))
576 : {
577 8 : if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple),
578 : false))
579 8 : return TM_BeingModified;
580 0 : return TM_Ok;
581 : }
582 : else
583 : {
584 0 : if (HeapTupleHeaderGetCmax(tuple) >= curcid)
585 0 : return TM_SelfModified; /* updated after scan started */
586 : else
587 0 : return TM_Invisible; /* updated before scan started */
588 : }
589 : }
590 :
591 4 : if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
592 : {
593 : /* deleting subtransaction must have aborted */
594 0 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
595 : InvalidTransactionId);
596 0 : return TM_Ok;
597 : }
598 :
599 4 : if (HeapTupleHeaderGetCmax(tuple) >= curcid)
600 4 : return TM_SelfModified; /* updated after scan started */
601 : else
602 0 : return TM_Invisible; /* updated before scan started */
603 : }
604 3070 : else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
605 0 : return TM_Invisible;
606 3070 : else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
607 3070 : SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
608 : HeapTupleHeaderGetRawXmin(tuple));
609 : else
610 : {
611 : /* it must have aborted or crashed */
612 0 : SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
613 : InvalidTransactionId);
614 0 : return TM_Invisible;
615 : }
616 : }
617 :
618 : /* by here, the inserting transaction has committed */
619 :
620 4774624 : if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
621 4244216 : return TM_Ok;
622 :
623 530408 : if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
624 : {
625 168 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
626 0 : return TM_Ok;
627 168 : if (!ItemPointerEquals(&htup->t_self, &tuple->t_ctid))
628 168 : return TM_Updated; /* updated by other */
629 : else
630 0 : return TM_Deleted; /* deleted by other */
631 : }
632 :
633 530240 : if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
634 : {
635 : TransactionId xmax;
636 :
637 74002 : if (HEAP_LOCKED_UPGRADED(tuple->t_infomask))
638 0 : return TM_Ok;
639 :
640 74002 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
641 : {
642 71847 : if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), true))
643 71314 : return TM_BeingModified;
644 :
645 533 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
646 533 : return TM_Ok;
647 : }
648 :
649 2155 : xmax = HeapTupleGetUpdateXid(tuple);
650 2155 : if (!TransactionIdIsValid(xmax))
651 : {
652 0 : if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
653 0 : return TM_BeingModified;
654 : }
655 :
656 : /* not LOCKED_ONLY, so it has to have an xmax */
657 : Assert(TransactionIdIsValid(xmax));
658 :
659 2155 : if (TransactionIdIsCurrentTransactionId(xmax))
660 : {
661 0 : if (HeapTupleHeaderGetCmax(tuple) >= curcid)
662 0 : return TM_SelfModified; /* updated after scan started */
663 : else
664 0 : return TM_Invisible; /* updated before scan started */
665 : }
666 :
667 2155 : if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
668 2151 : return TM_BeingModified;
669 :
670 4 : if (TransactionIdDidCommit(xmax))
671 : {
672 1 : if (!ItemPointerEquals(&htup->t_self, &tuple->t_ctid))
673 0 : return TM_Updated;
674 : else
675 1 : return TM_Deleted;
676 : }
677 :
678 : /*
679 : * By here, the update in the Xmax is either aborted or crashed, but
680 : * what about the other members?
681 : */
682 :
683 3 : if (!MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
684 : {
685 : /*
686 : * There's no member, even just a locker, alive anymore, so we can
687 : * mark the Xmax as invalid.
688 : */
689 3 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
690 : InvalidTransactionId);
691 3 : return TM_Ok;
692 : }
693 : else
694 : {
695 : /* There are lockers running */
696 0 : return TM_BeingModified;
697 : }
698 : }
699 :
700 456238 : if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
701 : {
702 452266 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
703 452161 : return TM_BeingModified;
704 105 : if (HeapTupleHeaderGetCmax(tuple) >= curcid)
705 105 : return TM_SelfModified; /* updated after scan started */
706 : else
707 0 : return TM_Invisible; /* updated before scan started */
708 : }
709 :
710 3972 : if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
711 1478 : return TM_BeingModified;
712 :
713 2494 : if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
714 : {
715 : /* it must have aborted or crashed */
716 277 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
717 : InvalidTransactionId);
718 277 : return TM_Ok;
719 : }
720 :
721 : /* xmax transaction committed */
722 :
723 2217 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
724 : {
725 2136 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
726 : InvalidTransactionId);
727 2136 : return TM_Ok;
728 : }
729 :
730 81 : SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
731 : HeapTupleHeaderGetRawXmax(tuple));
732 81 : if (!ItemPointerEquals(&htup->t_self, &tuple->t_ctid))
733 74 : return TM_Updated; /* updated by other */
734 : else
735 7 : return TM_Deleted; /* deleted by other */
736 : }
737 :
738 : /*
739 : * HeapTupleSatisfiesDirty
740 : * True iff heap tuple is valid including effects of open transactions.
741 : *
742 : * See SNAPSHOT_DIRTY's definition for the intended behaviour.
743 : *
744 : * This is essentially like HeapTupleSatisfiesSelf as far as effects of
745 : * the current transaction and committed/aborted xacts are concerned.
746 : * However, we also include the effects of other xacts still in progress.
747 : *
748 : * A special hack is that the passed-in snapshot struct is used as an
749 : * output argument to return the xids of concurrent xacts that affected the
750 : * tuple. snapshot->xmin is set to the tuple's xmin if that is another
751 : * transaction that's still in progress; or to InvalidTransactionId if the
752 : * tuple's xmin is committed good, committed dead, or my own xact.
753 : * Similarly for snapshot->xmax and the tuple's xmax. If the tuple was
754 : * inserted speculatively, meaning that the inserter might still back down
755 : * on the insertion without aborting the whole transaction, the associated
756 : * token is also returned in snapshot->speculativeToken.
757 : */
758 : static bool
759 7789792 : HeapTupleSatisfiesDirty(HeapTuple htup, Snapshot snapshot,
760 : Buffer buffer)
761 : {
762 7789792 : HeapTupleHeader tuple = htup->t_data;
763 :
764 : Assert(ItemPointerIsValid(&htup->t_self));
765 : Assert(htup->t_tableOid != InvalidOid);
766 :
767 7789792 : snapshot->xmin = snapshot->xmax = InvalidTransactionId;
768 7789792 : snapshot->speculativeToken = 0;
769 :
770 7789792 : if (!HeapTupleHeaderXminCommitted(tuple))
771 : {
772 7398346 : if (HeapTupleHeaderXminInvalid(tuple))
773 753 : return false;
774 :
775 7397593 : if (!HeapTupleCleanMoved(tuple, buffer))
776 0 : return false;
777 7397593 : else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
778 : {
779 7383531 : if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
780 32867 : return true;
781 :
782 7350664 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
783 5085 : return true;
784 :
785 7345579 : if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
786 : {
787 : TransactionId xmax;
788 :
789 16 : xmax = HeapTupleGetUpdateXid(tuple);
790 :
791 : /* not LOCKED_ONLY, so it has to have an xmax */
792 : Assert(TransactionIdIsValid(xmax));
793 :
794 : /* updating subtransaction must have aborted */
795 16 : if (!TransactionIdIsCurrentTransactionId(xmax))
796 0 : return true;
797 : else
798 16 : return false;
799 : }
800 :
801 7345563 : if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
802 : {
803 : /* deleting subtransaction must have aborted */
804 0 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
805 : InvalidTransactionId);
806 0 : return true;
807 : }
808 :
809 7345563 : return false;
810 : }
811 14062 : else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
812 : {
813 : /*
814 : * Return the speculative token to caller. Caller can worry about
815 : * xmax, since it requires a conclusively locked row version, and
816 : * a concurrent update to this tuple is a conflict of its
817 : * purposes.
818 : */
819 45 : if (HeapTupleHeaderIsSpeculative(tuple))
820 : {
821 2 : snapshot->speculativeToken =
822 2 : HeapTupleHeaderGetSpeculativeToken(tuple);
823 :
824 : Assert(snapshot->speculativeToken != 0);
825 : }
826 :
827 45 : snapshot->xmin = HeapTupleHeaderGetRawXmin(tuple);
828 : /* XXX shouldn't we fall through to look at xmax? */
829 45 : return true; /* in insertion by other */
830 : }
831 14017 : else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
832 13467 : SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
833 : HeapTupleHeaderGetRawXmin(tuple));
834 : else
835 : {
836 : /* it must have aborted or crashed */
837 550 : SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
838 : InvalidTransactionId);
839 550 : return false;
840 : }
841 : }
842 :
843 : /* by here, the inserting transaction has committed */
844 :
845 404913 : if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
846 137534 : return true;
847 :
848 267379 : if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
849 : {
850 87352 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
851 0 : return true;
852 87352 : return false; /* updated by other */
853 : }
854 :
855 180027 : if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
856 : {
857 : TransactionId xmax;
858 :
859 30 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
860 13 : return true;
861 :
862 17 : xmax = HeapTupleGetUpdateXid(tuple);
863 :
864 : /* not LOCKED_ONLY, so it has to have an xmax */
865 : Assert(TransactionIdIsValid(xmax));
866 :
867 17 : if (TransactionIdIsCurrentTransactionId(xmax))
868 1 : return false;
869 16 : if (TransactionIdIsInProgress(xmax))
870 : {
871 0 : snapshot->xmax = xmax;
872 0 : return true;
873 : }
874 16 : if (TransactionIdDidCommit(xmax))
875 16 : return false;
876 : /* it must have aborted or crashed */
877 0 : return true;
878 : }
879 :
880 179997 : if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
881 : {
882 148729 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
883 71 : return true;
884 148658 : return false;
885 : }
886 :
887 31268 : if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
888 : {
889 22 : if (!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
890 12 : snapshot->xmax = HeapTupleHeaderGetRawXmax(tuple);
891 22 : return true;
892 : }
893 :
894 31246 : if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
895 : {
896 : /* it must have aborted or crashed */
897 56 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
898 : InvalidTransactionId);
899 56 : return true;
900 : }
901 :
902 : /* xmax transaction committed */
903 :
904 31190 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
905 : {
906 10293 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
907 : InvalidTransactionId);
908 10293 : return true;
909 : }
910 :
911 20897 : SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
912 : HeapTupleHeaderGetRawXmax(tuple));
913 20897 : return false; /* updated by other */
914 : }
915 :
916 : /*
917 : * HeapTupleSatisfiesMVCC
918 : * True iff heap tuple is valid for the given MVCC snapshot.
919 : *
920 : * See SNAPSHOT_MVCC's definition for the intended behaviour.
921 : *
922 : * Notice that here, we will not update the tuple status hint bits if the
923 : * inserting/deleting transaction is still running according to our snapshot,
924 : * even if in reality it's committed or aborted by now. This is intentional.
925 : * Checking the true transaction state would require access to high-traffic
926 : * shared data structures, creating contention we'd rather do without, and it
927 : * would not change the result of our visibility check anyway. The hint bits
928 : * will be updated by the first visitor that has a snapshot new enough to see
929 : * the inserting/deleting transaction as done. In the meantime, the cost of
930 : * leaving the hint bits unset is basically that each HeapTupleSatisfiesMVCC
931 : * call will need to run TransactionIdIsCurrentTransactionId in addition to
932 : * XidInMVCCSnapshot (but it would have to do the latter anyway). In the old
933 : * coding where we tried to set the hint bits as soon as possible, we instead
934 : * did TransactionIdIsInProgress in each call --- to no avail, as long as the
935 : * inserting/deleting transaction was still running --- which was more cycles
936 : * and more contention on ProcArrayLock.
937 : */
938 : static inline bool
939 105945787 : HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot,
940 : Buffer buffer, SetHintBitsState *state)
941 : {
942 105945787 : HeapTupleHeader tuple = htup->t_data;
943 :
944 : /*
945 : * Assert that the caller has registered the snapshot. This function
946 : * doesn't care about the registration as such, but in general you
947 : * shouldn't try to use a snapshot without registration because it might
948 : * get invalidated while it's still in use, and this is a convenient place
949 : * to check for that.
950 : */
951 : Assert(snapshot->regd_count > 0 || snapshot->active_count > 0);
952 :
953 : Assert(ItemPointerIsValid(&htup->t_self));
954 : Assert(htup->t_tableOid != InvalidOid);
955 :
956 105945787 : if (!HeapTupleHeaderXminCommitted(tuple))
957 : {
958 17696606 : if (HeapTupleHeaderXminInvalid(tuple))
959 345511 : return false;
960 :
961 17351095 : if (!HeapTupleCleanMoved(tuple, buffer))
962 0 : return false;
963 17351095 : else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
964 : {
965 14621133 : if (HeapTupleHeaderGetCmin(tuple) >= snapshot->curcid)
966 9155 : return false; /* inserted after scan started */
967 :
968 14611978 : if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
969 10724277 : return true;
970 :
971 3887701 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
972 2193 : return true;
973 :
974 3885508 : if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
975 : {
976 : TransactionId xmax;
977 :
978 8 : xmax = HeapTupleGetUpdateXid(tuple);
979 :
980 : /* not LOCKED_ONLY, so it has to have an xmax */
981 : Assert(TransactionIdIsValid(xmax));
982 :
983 : /* updating subtransaction must have aborted */
984 8 : if (!TransactionIdIsCurrentTransactionId(xmax))
985 8 : return true;
986 0 : else if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
987 0 : return true; /* updated after scan started */
988 : else
989 0 : return false; /* updated before scan started */
990 : }
991 :
992 3885500 : if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
993 : {
994 : /* deleting subtransaction must have aborted */
995 46 : SetHintBitsExt(tuple, buffer, HEAP_XMAX_INVALID,
996 : InvalidTransactionId, state);
997 46 : return true;
998 : }
999 :
1000 3885454 : if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
1001 885 : return true; /* deleted after scan started */
1002 : else
1003 3884569 : return false; /* deleted before scan started */
1004 : }
1005 2729962 : else if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
1006 35385 : return false;
1007 2694577 : else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
1008 2636467 : SetHintBitsExt(tuple, buffer, HEAP_XMIN_COMMITTED,
1009 : HeapTupleHeaderGetRawXmin(tuple), state);
1010 : else
1011 : {
1012 : /* it must have aborted or crashed */
1013 58110 : SetHintBitsExt(tuple, buffer, HEAP_XMIN_INVALID,
1014 : InvalidTransactionId, state);
1015 58110 : return false;
1016 : }
1017 : }
1018 : else
1019 : {
1020 : /* xmin is committed, but maybe not according to our snapshot */
1021 170750177 : if (!HeapTupleHeaderXminFrozen(tuple) &&
1022 82500996 : XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
1023 2814 : return false; /* treat as still in progress */
1024 : }
1025 :
1026 : /* by here, the inserting transaction has committed */
1027 :
1028 90882834 : if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
1029 80912119 : return true;
1030 :
1031 9970715 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
1032 508041 : return true;
1033 :
1034 9462674 : if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1035 : {
1036 : TransactionId xmax;
1037 :
1038 : /* already checked above */
1039 : Assert(!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask));
1040 :
1041 78672 : xmax = HeapTupleGetUpdateXid(tuple);
1042 :
1043 : /* not LOCKED_ONLY, so it has to have an xmax */
1044 : Assert(TransactionIdIsValid(xmax));
1045 :
1046 78672 : if (TransactionIdIsCurrentTransactionId(xmax))
1047 : {
1048 23 : if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
1049 0 : return true; /* deleted after scan started */
1050 : else
1051 23 : return false; /* deleted before scan started */
1052 : }
1053 78649 : if (XidInMVCCSnapshot(xmax, snapshot))
1054 2126 : return true;
1055 76523 : if (TransactionIdDidCommit(xmax))
1056 76507 : return false; /* updating transaction committed */
1057 : /* it must have aborted or crashed */
1058 16 : return true;
1059 : }
1060 :
1061 9384002 : if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
1062 : {
1063 603602 : if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
1064 : {
1065 181538 : if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
1066 1826 : return true; /* deleted after scan started */
1067 : else
1068 179712 : return false; /* deleted before scan started */
1069 : }
1070 :
1071 422064 : if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
1072 12947 : return true;
1073 :
1074 409117 : if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
1075 : {
1076 : /* it must have aborted or crashed */
1077 8963 : SetHintBitsExt(tuple, buffer, HEAP_XMAX_INVALID,
1078 : InvalidTransactionId, state);
1079 8963 : return true;
1080 : }
1081 :
1082 : /* xmax transaction committed */
1083 400154 : SetHintBitsExt(tuple, buffer, HEAP_XMAX_COMMITTED,
1084 : HeapTupleHeaderGetRawXmax(tuple), state);
1085 : }
1086 : else
1087 : {
1088 : /* xmax is committed, but maybe not according to our snapshot */
1089 8780400 : if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
1090 713 : return true; /* treat as still in progress */
1091 : }
1092 :
1093 : /* xmax transaction committed */
1094 :
1095 9179841 : return false;
1096 : }
1097 :
1098 :
1099 : /*
1100 : * HeapTupleSatisfiesVacuum
1101 : *
1102 : * Determine the status of tuples for VACUUM purposes. Here, what
1103 : * we mainly want to know is if a tuple is potentially visible to *any*
1104 : * running transaction. If so, it can't be removed yet by VACUUM.
1105 : *
1106 : * OldestXmin is a cutoff XID (obtained from
1107 : * GetOldestNonRemovableTransactionId()). Tuples deleted by XIDs >=
1108 : * OldestXmin are deemed "recently dead"; they might still be visible to some
1109 : * open transaction, so we can't remove them, even if we see that the deleting
1110 : * transaction has committed.
1111 : */
1112 : HTSV_Result
1113 9180850 : HeapTupleSatisfiesVacuum(HeapTuple htup, TransactionId OldestXmin,
1114 : Buffer buffer)
1115 : {
1116 9180850 : TransactionId dead_after = InvalidTransactionId;
1117 : HTSV_Result res;
1118 :
1119 9180850 : res = HeapTupleSatisfiesVacuumHorizon(htup, buffer, &dead_after);
1120 :
1121 9180850 : if (res == HEAPTUPLE_RECENTLY_DEAD)
1122 : {
1123 : Assert(TransactionIdIsValid(dead_after));
1124 :
1125 196492 : if (TransactionIdPrecedes(dead_after, OldestXmin))
1126 22117 : res = HEAPTUPLE_DEAD;
1127 : }
1128 : else
1129 : Assert(!TransactionIdIsValid(dead_after));
1130 :
1131 9180850 : return res;
1132 : }
1133 :
1134 : /*
1135 : * Work horse for HeapTupleSatisfiesVacuum and similar routines.
1136 : *
1137 : * In contrast to HeapTupleSatisfiesVacuum this routine, when encountering a
1138 : * tuple that could still be visible to some backend, stores the xid that
1139 : * needs to be compared with the horizon in *dead_after, and returns
1140 : * HEAPTUPLE_RECENTLY_DEAD. The caller then can perform the comparison with
1141 : * the horizon. This is e.g. useful when comparing with different horizons.
1142 : *
1143 : * Note: HEAPTUPLE_DEAD can still be returned here, e.g. if the inserting
1144 : * transaction aborted.
1145 : */
1146 : HTSV_Result
1147 40795947 : HeapTupleSatisfiesVacuumHorizon(HeapTuple htup, Buffer buffer, TransactionId *dead_after)
1148 : {
1149 40795947 : HeapTupleHeader tuple = htup->t_data;
1150 :
1151 : Assert(ItemPointerIsValid(&htup->t_self));
1152 : Assert(htup->t_tableOid != InvalidOid);
1153 : Assert(dead_after != NULL);
1154 :
1155 40795947 : *dead_after = InvalidTransactionId;
1156 :
1157 : /*
1158 : * Has inserting transaction committed?
1159 : *
1160 : * If the inserting transaction aborted, then the tuple was never visible
1161 : * to any other transaction, so we can delete it immediately.
1162 : */
1163 40795947 : if (!HeapTupleHeaderXminCommitted(tuple))
1164 : {
1165 12837991 : if (HeapTupleHeaderXminInvalid(tuple))
1166 18177 : return HEAPTUPLE_DEAD;
1167 12819814 : else if (!HeapTupleCleanMoved(tuple, buffer))
1168 0 : return HEAPTUPLE_DEAD;
1169 12819814 : else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
1170 : {
1171 2347575 : if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
1172 2305287 : return HEAPTUPLE_INSERT_IN_PROGRESS;
1173 : /* only locked? run infomask-only check first, for performance */
1174 76464 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask) ||
1175 34176 : HeapTupleHeaderIsOnlyLocked(tuple))
1176 8112 : return HEAPTUPLE_INSERT_IN_PROGRESS;
1177 : /* inserted and then deleted by same xact */
1178 34176 : if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetUpdateXid(tuple)))
1179 34176 : return HEAPTUPLE_DELETE_IN_PROGRESS;
1180 : /* deleting subtransaction must have aborted */
1181 0 : return HEAPTUPLE_INSERT_IN_PROGRESS;
1182 : }
1183 10472239 : else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
1184 : {
1185 : /*
1186 : * It'd be possible to discern between INSERT/DELETE in progress
1187 : * here by looking at xmax - but that doesn't seem beneficial for
1188 : * the majority of callers and even detrimental for some. We'd
1189 : * rather have callers look at/wait for xmin than xmax. It's
1190 : * always correct to return INSERT_IN_PROGRESS because that's
1191 : * what's happening from the view of other backends.
1192 : */
1193 4953 : return HEAPTUPLE_INSERT_IN_PROGRESS;
1194 : }
1195 10467286 : else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
1196 10404697 : SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
1197 : HeapTupleHeaderGetRawXmin(tuple));
1198 : else
1199 : {
1200 : /*
1201 : * Not in Progress, Not Committed, so either Aborted or crashed
1202 : */
1203 62589 : SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
1204 : InvalidTransactionId);
1205 62589 : return HEAPTUPLE_DEAD;
1206 : }
1207 :
1208 : /*
1209 : * At this point the xmin is known committed, but we might not have
1210 : * been able to set the hint bit yet; so we can no longer Assert that
1211 : * it's set.
1212 : */
1213 : }
1214 :
1215 : /*
1216 : * Okay, the inserter committed, so it was good at some point. Now what
1217 : * about the deleting transaction?
1218 : */
1219 38362653 : if (tuple->t_infomask & HEAP_XMAX_INVALID)
1220 29705764 : return HEAPTUPLE_LIVE;
1221 :
1222 8656889 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
1223 : {
1224 : /*
1225 : * "Deleting" xact really only locked it, so the tuple is live in any
1226 : * case. However, we should make sure that either XMAX_COMMITTED or
1227 : * XMAX_INVALID gets set once the xact is gone, to reduce the costs of
1228 : * examining the tuple for future xacts.
1229 : */
1230 18415 : if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
1231 : {
1232 18415 : if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1233 : {
1234 : /*
1235 : * If it's a pre-pg_upgrade tuple, the multixact cannot
1236 : * possibly be running; otherwise have to check.
1237 : */
1238 466 : if (!HEAP_LOCKED_UPGRADED(tuple->t_infomask) &&
1239 233 : MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple),
1240 : true))
1241 19 : return HEAPTUPLE_LIVE;
1242 214 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
1243 : }
1244 : else
1245 : {
1246 18182 : if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
1247 4 : return HEAPTUPLE_LIVE;
1248 18178 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
1249 : InvalidTransactionId);
1250 : }
1251 : }
1252 :
1253 : /*
1254 : * We don't really care whether xmax did commit, abort or crash. We
1255 : * know that xmax did lock the tuple, but it did not and will never
1256 : * actually update it.
1257 : */
1258 :
1259 18392 : return HEAPTUPLE_LIVE;
1260 : }
1261 :
1262 8638474 : if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1263 : {
1264 102 : TransactionId xmax = HeapTupleGetUpdateXid(tuple);
1265 :
1266 : /* already checked above */
1267 : Assert(!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask));
1268 :
1269 : /* not LOCKED_ONLY, so it has to have an xmax */
1270 : Assert(TransactionIdIsValid(xmax));
1271 :
1272 102 : if (TransactionIdIsInProgress(xmax))
1273 1 : return HEAPTUPLE_DELETE_IN_PROGRESS;
1274 101 : else if (TransactionIdDidCommit(xmax))
1275 : {
1276 : /*
1277 : * The multixact might still be running due to lockers. Need to
1278 : * allow for pruning if below the xid horizon regardless --
1279 : * otherwise we could end up with a tuple where the updater has to
1280 : * be removed due to the horizon, but is not pruned away. It's
1281 : * not a problem to prune that tuple, because any remaining
1282 : * lockers will also be present in newer tuple versions.
1283 : */
1284 101 : *dead_after = xmax;
1285 101 : return HEAPTUPLE_RECENTLY_DEAD;
1286 : }
1287 0 : else if (!MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
1288 : {
1289 : /*
1290 : * Not in Progress, Not Committed, so either Aborted or crashed.
1291 : * Mark the Xmax as invalid.
1292 : */
1293 0 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
1294 : }
1295 :
1296 0 : return HEAPTUPLE_LIVE;
1297 : }
1298 :
1299 8638372 : if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
1300 : {
1301 7739231 : if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
1302 4087832 : return HEAPTUPLE_DELETE_IN_PROGRESS;
1303 3651399 : else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
1304 3648994 : SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
1305 : HeapTupleHeaderGetRawXmax(tuple));
1306 : else
1307 : {
1308 : /*
1309 : * Not in Progress, Not Committed, so either Aborted or crashed
1310 : */
1311 2405 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
1312 : InvalidTransactionId);
1313 2405 : return HEAPTUPLE_LIVE;
1314 : }
1315 :
1316 : /*
1317 : * At this point the xmax is known committed, but we might not have
1318 : * been able to set the hint bit yet; so we can no longer Assert that
1319 : * it's set.
1320 : */
1321 : }
1322 :
1323 : /*
1324 : * Deleter committed, allow caller to check if it was recent enough that
1325 : * some open transactions could still see the tuple.
1326 : */
1327 4548135 : *dead_after = HeapTupleHeaderGetRawXmax(tuple);
1328 4548135 : return HEAPTUPLE_RECENTLY_DEAD;
1329 : }
1330 :
1331 :
1332 : /*
1333 : * HeapTupleSatisfiesNonVacuumable
1334 : *
1335 : * True if tuple might be visible to some transaction; false if it's
1336 : * surely dead to everyone, ie, vacuumable.
1337 : *
1338 : * See SNAPSHOT_NON_VACUUMABLE's definition for the intended behaviour.
1339 : *
1340 : * This is an interface to HeapTupleSatisfiesVacuum that's callable via
1341 : * HeapTupleSatisfiesSnapshot, so it can be used through a Snapshot.
1342 : * snapshot->vistest must have been set up with the horizon to use.
1343 : */
1344 : static bool
1345 491924 : HeapTupleSatisfiesNonVacuumable(HeapTuple htup, Snapshot snapshot,
1346 : Buffer buffer)
1347 : {
1348 491924 : TransactionId dead_after = InvalidTransactionId;
1349 : HTSV_Result res;
1350 :
1351 491924 : res = HeapTupleSatisfiesVacuumHorizon(htup, buffer, &dead_after);
1352 :
1353 491924 : if (res == HEAPTUPLE_RECENTLY_DEAD)
1354 : {
1355 : Assert(TransactionIdIsValid(dead_after));
1356 :
1357 104843 : if (GlobalVisTestIsRemovableXid(snapshot->vistest, dead_after, true))
1358 86216 : res = HEAPTUPLE_DEAD;
1359 : }
1360 : else
1361 : Assert(!TransactionIdIsValid(dead_after));
1362 :
1363 491924 : return res != HEAPTUPLE_DEAD;
1364 : }
1365 :
1366 :
1367 : /*
1368 : * HeapTupleIsSurelyDead
1369 : *
1370 : * Cheaply determine whether a tuple is surely dead to all onlookers.
1371 : * We sometimes use this in lieu of HeapTupleSatisfiesVacuum when the
1372 : * tuple has just been tested by another visibility routine (usually
1373 : * HeapTupleSatisfiesMVCC) and, therefore, any hint bits that can be set
1374 : * should already be set. We assume that if no hint bits are set, the xmin
1375 : * or xmax transaction is still running. This is therefore faster than
1376 : * HeapTupleSatisfiesVacuum, because we consult neither procarray nor CLOG.
1377 : * It's okay to return false when in doubt, but we must return true only
1378 : * if the tuple is removable.
1379 : */
1380 : bool
1381 8537002 : HeapTupleIsSurelyDead(HeapTuple htup, GlobalVisState *vistest)
1382 : {
1383 8537002 : HeapTupleHeader tuple = htup->t_data;
1384 :
1385 : Assert(ItemPointerIsValid(&htup->t_self));
1386 : Assert(htup->t_tableOid != InvalidOid);
1387 :
1388 : /*
1389 : * If the inserting transaction is marked invalid, then it aborted, and
1390 : * the tuple is definitely dead. If it's marked neither committed nor
1391 : * invalid, then we assume it's still alive (since the presumption is that
1392 : * all relevant hint bits were just set moments ago).
1393 : */
1394 8537002 : if (!HeapTupleHeaderXminCommitted(tuple))
1395 7508640 : return HeapTupleHeaderXminInvalid(tuple);
1396 :
1397 : /*
1398 : * If the inserting transaction committed, but any deleting transaction
1399 : * aborted, the tuple is still alive.
1400 : */
1401 1028362 : if (tuple->t_infomask & HEAP_XMAX_INVALID)
1402 18 : return false;
1403 :
1404 : /*
1405 : * If the XMAX is just a lock, the tuple is still alive.
1406 : */
1407 1028344 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
1408 2 : return false;
1409 :
1410 : /*
1411 : * If the Xmax is a MultiXact, it might be dead or alive, but we cannot
1412 : * know without checking pg_multixact.
1413 : */
1414 1028342 : if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1415 146 : return false;
1416 :
1417 : /* If deleter isn't known to have committed, assume it's still running. */
1418 1028196 : if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
1419 285542 : return false;
1420 :
1421 : /* Deleter committed, so tuple is dead if the XID is old enough. */
1422 742654 : return GlobalVisTestIsRemovableXid(vistest,
1423 : HeapTupleHeaderGetRawXmax(tuple),
1424 : true);
1425 : }
1426 :
1427 : /*
1428 : * Is the tuple really only locked? That is, is it not updated?
1429 : *
1430 : * It's easy to check just infomask bits if the locker is not a multi; but
1431 : * otherwise we need to verify that the updating transaction has not aborted.
1432 : *
1433 : * This function is here because it follows the same visibility rules laid out
1434 : * at the top of this file.
1435 : */
1436 : bool
1437 127855 : HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple)
1438 : {
1439 : TransactionId xmax;
1440 :
1441 : /* if there's no valid Xmax, then there's obviously no update either */
1442 127855 : if (tuple->t_infomask & HEAP_XMAX_INVALID)
1443 0 : return true;
1444 :
1445 127855 : if (tuple->t_infomask & HEAP_XMAX_LOCK_ONLY)
1446 71708 : return true;
1447 :
1448 : /* invalid xmax means no update */
1449 56147 : if (!TransactionIdIsValid(HeapTupleHeaderGetRawXmax(tuple)))
1450 0 : return true;
1451 :
1452 : /*
1453 : * if HEAP_XMAX_LOCK_ONLY is not set and not a multi, then this must
1454 : * necessarily have been updated
1455 : */
1456 56147 : if (!(tuple->t_infomask & HEAP_XMAX_IS_MULTI))
1457 53999 : return false;
1458 :
1459 : /* ... but if it's a multi, then perhaps the updating Xid aborted. */
1460 2148 : xmax = HeapTupleGetUpdateXid(tuple);
1461 :
1462 : /* not LOCKED_ONLY, so it has to have an xmax */
1463 : Assert(TransactionIdIsValid(xmax));
1464 :
1465 2148 : if (TransactionIdIsCurrentTransactionId(xmax))
1466 0 : return false;
1467 2148 : if (TransactionIdIsInProgress(xmax))
1468 2112 : return false;
1469 36 : if (TransactionIdDidCommit(xmax))
1470 11 : return false;
1471 :
1472 : /*
1473 : * not current, not in progress, not committed -- must have aborted or
1474 : * crashed
1475 : */
1476 25 : return true;
1477 : }
1478 :
1479 : /*
1480 : * check whether the transaction id 'xid' is in the pre-sorted array 'xip'.
1481 : */
1482 : static bool
1483 56098 : TransactionIdInArray(TransactionId xid, TransactionId *xip, Size num)
1484 : {
1485 77593 : return num > 0 &&
1486 21495 : bsearch(&xid, xip, num, sizeof(TransactionId), xidComparator) != NULL;
1487 : }
1488 :
1489 : /*
1490 : * See the comments for HeapTupleSatisfiesMVCC for the semantics this function
1491 : * obeys.
1492 : *
1493 : * Only usable on tuples from catalog tables!
1494 : *
1495 : * We don't need to support HEAP_MOVED_(IN|OFF) for now because we only support
1496 : * reading catalog pages which couldn't have been created in an older version.
1497 : *
1498 : * We don't set any hint bits in here as it seems unlikely to be beneficial as
1499 : * those should already be set by normal access and it seems to be too
1500 : * dangerous to do so as the semantics of doing so during timetravel are more
1501 : * complicated than when dealing "only" with the present.
1502 : */
1503 : static bool
1504 46986 : HeapTupleSatisfiesHistoricMVCC(HeapTuple htup, Snapshot snapshot,
1505 : Buffer buffer)
1506 : {
1507 46986 : HeapTupleHeader tuple = htup->t_data;
1508 46986 : TransactionId xmin = HeapTupleHeaderGetXmin(tuple);
1509 46986 : TransactionId xmax = HeapTupleHeaderGetRawXmax(tuple);
1510 :
1511 : Assert(ItemPointerIsValid(&htup->t_self));
1512 : Assert(htup->t_tableOid != InvalidOid);
1513 :
1514 : /* inserting transaction aborted */
1515 46986 : if (HeapTupleHeaderXminInvalid(tuple))
1516 : {
1517 : Assert(!TransactionIdDidCommit(xmin));
1518 107 : return false;
1519 : }
1520 : /* check if it's one of our txids, toplevel is also in there */
1521 46879 : else if (TransactionIdInArray(xmin, snapshot->subxip, snapshot->subxcnt))
1522 : {
1523 : bool resolved;
1524 522 : CommandId cmin = HeapTupleHeaderGetRawCommandId(tuple);
1525 522 : CommandId cmax = InvalidCommandId;
1526 :
1527 : /*
1528 : * another transaction might have (tried to) delete this tuple or
1529 : * cmin/cmax was stored in a combo CID. So we need to lookup the
1530 : * actual values externally.
1531 : */
1532 522 : resolved = ResolveCminCmaxDuringDecoding(HistoricSnapshotGetTupleCids(), snapshot,
1533 : htup, buffer,
1534 : &cmin, &cmax);
1535 :
1536 : /*
1537 : * If we haven't resolved the combo CID to cmin/cmax, that means we
1538 : * have not decoded the combo CID yet. That means the cmin is
1539 : * definitely in the future, and we're not supposed to see the tuple
1540 : * yet.
1541 : *
1542 : * XXX This only applies to decoding of in-progress transactions. In
1543 : * regular logical decoding we only execute this code at commit time,
1544 : * at which point we should have seen all relevant combo CIDs. So
1545 : * ideally, we should error out in this case but in practice, this
1546 : * won't happen. If we are too worried about this then we can add an
1547 : * elog inside ResolveCminCmaxDuringDecoding.
1548 : *
1549 : * XXX For the streaming case, we can track the largest combo CID
1550 : * assigned, and error out based on this (when unable to resolve combo
1551 : * CID below that observed maximum value).
1552 : */
1553 522 : if (!resolved)
1554 73 : return false;
1555 :
1556 : Assert(cmin != InvalidCommandId);
1557 :
1558 517 : if (cmin >= snapshot->curcid)
1559 68 : return false; /* inserted after scan started */
1560 : /* fall through */
1561 : }
1562 : /* committed before our xmin horizon. Do a normal visibility check. */
1563 46357 : else if (TransactionIdPrecedes(xmin, snapshot->xmin))
1564 : {
1565 : Assert(!(HeapTupleHeaderXminCommitted(tuple) &&
1566 : !TransactionIdDidCommit(xmin)));
1567 :
1568 : /* check for hint bit first, consult clog afterwards */
1569 43054 : if (!HeapTupleHeaderXminCommitted(tuple) &&
1570 66 : !TransactionIdDidCommit(xmin))
1571 4 : return false;
1572 : /* fall through */
1573 : }
1574 : /* beyond our xmax horizon, i.e. invisible */
1575 3303 : else if (TransactionIdFollowsOrEquals(xmin, snapshot->xmax))
1576 : {
1577 114 : return false;
1578 : }
1579 : /* check if it's a committed transaction in [xmin, xmax) */
1580 3189 : else if (TransactionIdInArray(xmin, snapshot->xip, snapshot->xcnt))
1581 : {
1582 : /* fall through */
1583 : }
1584 :
1585 : /*
1586 : * none of the above, i.e. between [xmin, xmax) but hasn't committed. I.e.
1587 : * invisible.
1588 : */
1589 : else
1590 : {
1591 0 : return false;
1592 : }
1593 :
1594 : /* at this point we know xmin is visible, go on to check xmax */
1595 :
1596 : /* xid invalid or aborted */
1597 46688 : if (tuple->t_infomask & HEAP_XMAX_INVALID)
1598 38137 : return true;
1599 : /* locked tuples are always visible */
1600 8551 : else if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
1601 4020 : return true;
1602 :
1603 : /*
1604 : * We can see multis here if we're looking at user tables or if somebody
1605 : * SELECT ... FOR SHARE/UPDATE a system table.
1606 : */
1607 4531 : else if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1608 : {
1609 43 : xmax = HeapTupleGetUpdateXid(tuple);
1610 : }
1611 :
1612 : /* check if it's one of our txids, toplevel is also in there */
1613 4531 : if (TransactionIdInArray(xmax, snapshot->subxip, snapshot->subxcnt))
1614 : {
1615 : bool resolved;
1616 : CommandId cmin;
1617 296 : CommandId cmax = HeapTupleHeaderGetRawCommandId(tuple);
1618 :
1619 : /* Lookup actual cmin/cmax values */
1620 296 : resolved = ResolveCminCmaxDuringDecoding(HistoricSnapshotGetTupleCids(), snapshot,
1621 : htup, buffer,
1622 : &cmin, &cmax);
1623 :
1624 : /*
1625 : * If we haven't resolved the combo CID to cmin/cmax, that means we
1626 : * have not decoded the combo CID yet. That means the cmax is
1627 : * definitely in the future, and we're still supposed to see the
1628 : * tuple.
1629 : *
1630 : * XXX This only applies to decoding of in-progress transactions. In
1631 : * regular logical decoding we only execute this code at commit time,
1632 : * at which point we should have seen all relevant combo CIDs. So
1633 : * ideally, we should error out in this case but in practice, this
1634 : * won't happen. If we are too worried about this then we can add an
1635 : * elog inside ResolveCminCmaxDuringDecoding.
1636 : *
1637 : * XXX For the streaming case, we can track the largest combo CID
1638 : * assigned, and error out based on this (when unable to resolve combo
1639 : * CID below that observed maximum value).
1640 : */
1641 296 : if (!resolved || cmax == InvalidCommandId)
1642 11 : return true;
1643 :
1644 285 : if (cmax >= snapshot->curcid)
1645 84 : return true; /* deleted after scan started */
1646 : else
1647 201 : return false; /* deleted before scan started */
1648 : }
1649 : /* below xmin horizon, normal transaction state is valid */
1650 4235 : else if (TransactionIdPrecedes(xmax, snapshot->xmin))
1651 : {
1652 : Assert(!(tuple->t_infomask & HEAP_XMAX_COMMITTED &&
1653 : !TransactionIdDidCommit(xmax)));
1654 :
1655 : /* check hint bit first */
1656 2405 : if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
1657 2333 : return false;
1658 :
1659 : /* check clog */
1660 72 : return !TransactionIdDidCommit(xmax);
1661 : }
1662 : /* above xmax horizon, we cannot possibly see the deleting transaction */
1663 1830 : else if (TransactionIdFollowsOrEquals(xmax, snapshot->xmax))
1664 331 : return true;
1665 : /* xmax is between [xmin, xmax), check known committed array */
1666 1499 : else if (TransactionIdInArray(xmax, snapshot->xip, snapshot->xcnt))
1667 1499 : return false;
1668 : /* xmax is between [xmin, xmax), but known not to have committed yet */
1669 : else
1670 0 : return true;
1671 : }
1672 :
1673 : /*
1674 : * Perform HeaptupleSatisfiesMVCC() on each passed in tuple. This is more
1675 : * efficient than doing HeapTupleSatisfiesMVCC() one-by-one.
1676 : *
1677 : * To be checked tuples are passed via BatchMVCCState->tuples. Each tuple's
1678 : * visibility is stored in batchmvcc->visible[]. In addition,
1679 : * ->vistuples_dense is set to contain the offsets of visible tuples.
1680 : *
1681 : * The reason this is more efficient than HeapTupleSatisfiesMVCC() is that it
1682 : * avoids a cross-translation-unit function call for each tuple, allows the
1683 : * compiler to optimize across calls to HeapTupleSatisfiesMVCC and allows
1684 : * setting hint bits more efficiently (see the one BufferFinishSetHintBits()
1685 : * call below).
1686 : *
1687 : * Returns the number of visible tuples.
1688 : */
1689 : int
1690 2463768 : HeapTupleSatisfiesMVCCBatch(Snapshot snapshot, Buffer buffer,
1691 : int ntups,
1692 : BatchMVCCState *batchmvcc,
1693 : OffsetNumber *vistuples_dense)
1694 : {
1695 2463768 : int nvis = 0;
1696 2463768 : SetHintBitsState state = SHB_INITIAL;
1697 :
1698 : Assert(IsMVCCSnapshot(snapshot));
1699 :
1700 86464369 : for (int i = 0; i < ntups; i++)
1701 : {
1702 : bool valid;
1703 84000601 : HeapTuple tup = &batchmvcc->tuples[i];
1704 :
1705 84000601 : valid = HeapTupleSatisfiesMVCC(tup, snapshot, buffer, &state);
1706 84000601 : batchmvcc->visible[i] = valid;
1707 :
1708 84000601 : if (likely(valid))
1709 : {
1710 71818935 : vistuples_dense[nvis] = tup->t_self.ip_posid;
1711 71818935 : nvis++;
1712 : }
1713 : }
1714 :
1715 2463768 : if (state == SHB_ENABLED)
1716 97668 : BufferFinishSetHintBits(buffer, true, true);
1717 :
1718 2463768 : return nvis;
1719 : }
1720 :
1721 : /*
1722 : * HeapTupleSatisfiesVisibility
1723 : * True iff heap tuple satisfies a time qual.
1724 : *
1725 : * Notes:
1726 : * Assumes heap tuple is valid, and buffer at least share locked.
1727 : *
1728 : * Hint bits in the HeapTuple's t_infomask may be updated as a side effect;
1729 : * if so, the indicated buffer is marked dirty.
1730 : */
1731 : bool
1732 42975760 : HeapTupleSatisfiesVisibility(HeapTuple htup, Snapshot snapshot, Buffer buffer)
1733 : {
1734 42975760 : switch (snapshot->snapshot_type)
1735 : {
1736 21945186 : case SNAPSHOT_MVCC:
1737 21945186 : return HeapTupleSatisfiesMVCC(htup, snapshot, buffer, NULL);
1738 605126 : case SNAPSHOT_SELF:
1739 605126 : return HeapTupleSatisfiesSelf(htup, snapshot, buffer);
1740 12001599 : case SNAPSHOT_ANY:
1741 12001599 : return HeapTupleSatisfiesAny(htup, snapshot, buffer);
1742 95147 : case SNAPSHOT_TOAST:
1743 95147 : return HeapTupleSatisfiesToast(htup, snapshot, buffer);
1744 7789792 : case SNAPSHOT_DIRTY:
1745 7789792 : return HeapTupleSatisfiesDirty(htup, snapshot, buffer);
1746 46986 : case SNAPSHOT_HISTORIC_MVCC:
1747 46986 : return HeapTupleSatisfiesHistoricMVCC(htup, snapshot, buffer);
1748 491924 : case SNAPSHOT_NON_VACUUMABLE:
1749 491924 : return HeapTupleSatisfiesNonVacuumable(htup, snapshot, buffer);
1750 : }
1751 :
1752 0 : return false; /* keep compiler quiet */
1753 : }
|