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-2025, 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 : * SetHintBits()
84 : *
85 : * Set commit/abort hint bits on a tuple, if appropriate at this time.
86 : *
87 : * It is only safe to set a transaction-committed hint bit if we know the
88 : * transaction's commit record is guaranteed to be flushed to disk before the
89 : * buffer, or if the table is temporary or unlogged and will be obliterated by
90 : * a crash anyway. We cannot change the LSN of the page here, because we may
91 : * hold only a share lock on the buffer, so we can only use the LSN to
92 : * interlock this if the buffer's LSN already is newer than the commit LSN;
93 : * otherwise we have to just refrain from setting the hint bit until some
94 : * future re-examination of the tuple.
95 : *
96 : * We can always set hint bits when marking a transaction aborted. (Some
97 : * code in heapam.c relies on that!)
98 : *
99 : * Also, if we are cleaning up HEAP_MOVED_IN or HEAP_MOVED_OFF entries, then
100 : * we can always set the hint bits, since pre-9.0 VACUUM FULL always used
101 : * synchronous commits and didn't move tuples that weren't previously
102 : * hinted. (This is not known by this subroutine, but is applied by its
103 : * callers.) Note: old-style VACUUM FULL is gone, but we have to keep this
104 : * module's support for MOVED_OFF/MOVED_IN flag bits for as long as we
105 : * support in-place update from pre-9.0 databases.
106 : *
107 : * Normal commits may be asynchronous, so for those we need to get the LSN
108 : * of the transaction and then check whether this is flushed.
109 : *
110 : * The caller should pass xid as the XID of the transaction to check, or
111 : * InvalidTransactionId if no check is needed.
112 : */
113 : static inline void
114 19196900 : SetHintBits(HeapTupleHeader tuple, Buffer buffer,
115 : uint16 infomask, TransactionId xid)
116 : {
117 19196900 : if (TransactionIdIsValid(xid))
118 : {
119 : /* NB: xid must be known committed here! */
120 18937280 : XLogRecPtr commitLSN = TransactionIdGetCommitLSN(xid);
121 :
122 19429988 : if (BufferIsPermanent(buffer) && XLogNeedsFlush(commitLSN) &&
123 492708 : BufferGetLSNAtomic(buffer) < commitLSN)
124 : {
125 : /* not flushed and no LSN interlock, so don't set hint */
126 417266 : return;
127 : }
128 : }
129 :
130 18779634 : tuple->t_infomask |= infomask;
131 18779634 : MarkBufferDirtyHint(buffer, true);
132 : }
133 :
134 : /*
135 : * HeapTupleSetHintBits --- exported version of SetHintBits()
136 : *
137 : * This must be separate because of C99's brain-dead notions about how to
138 : * implement inline functions.
139 : */
140 : void
141 350 : HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
142 : uint16 infomask, TransactionId xid)
143 : {
144 350 : SetHintBits(tuple, buffer, infomask, xid);
145 350 : }
146 :
147 :
148 : /*
149 : * HeapTupleSatisfiesSelf
150 : * True iff heap tuple is valid "for itself".
151 : *
152 : * See SNAPSHOT_MVCC's definition for the intended behaviour.
153 : *
154 : * Note:
155 : * Assumes heap tuple is valid.
156 : *
157 : * The satisfaction of "itself" requires the following:
158 : *
159 : * ((Xmin == my-transaction && the row was updated by the current transaction, and
160 : * (Xmax is null it was not deleted
161 : * [|| Xmax != my-transaction)]) [or it was deleted by another transaction]
162 : * ||
163 : *
164 : * (Xmin is committed && the row was modified by a committed transaction, and
165 : * (Xmax is null || the row has not been deleted, or
166 : * (Xmax != my-transaction && the row was deleted by another transaction
167 : * Xmax is not committed))) that has not been committed
168 : */
169 : static bool
170 5106 : HeapTupleSatisfiesSelf(HeapTuple htup, Snapshot snapshot, Buffer buffer)
171 : {
172 5106 : HeapTupleHeader tuple = htup->t_data;
173 :
174 : Assert(ItemPointerIsValid(&htup->t_self));
175 : Assert(htup->t_tableOid != InvalidOid);
176 :
177 5106 : if (!HeapTupleHeaderXminCommitted(tuple))
178 : {
179 5004 : if (HeapTupleHeaderXminInvalid(tuple))
180 0 : return false;
181 :
182 : /* Used by pre-9.0 binary upgrades */
183 5004 : if (tuple->t_infomask & HEAP_MOVED_OFF)
184 : {
185 0 : TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
186 :
187 0 : if (TransactionIdIsCurrentTransactionId(xvac))
188 0 : return false;
189 0 : if (!TransactionIdIsInProgress(xvac))
190 : {
191 0 : if (TransactionIdDidCommit(xvac))
192 : {
193 0 : SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
194 : InvalidTransactionId);
195 0 : return false;
196 : }
197 0 : SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
198 : InvalidTransactionId);
199 : }
200 : }
201 : /* Used by pre-9.0 binary upgrades */
202 5004 : else if (tuple->t_infomask & HEAP_MOVED_IN)
203 : {
204 0 : TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
205 :
206 0 : if (!TransactionIdIsCurrentTransactionId(xvac))
207 : {
208 0 : if (TransactionIdIsInProgress(xvac))
209 0 : return false;
210 0 : if (TransactionIdDidCommit(xvac))
211 0 : SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
212 : InvalidTransactionId);
213 : else
214 : {
215 0 : SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
216 : InvalidTransactionId);
217 0 : return false;
218 : }
219 : }
220 : }
221 5004 : else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
222 : {
223 5004 : if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
224 4882 : return true;
225 :
226 122 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
227 20 : return true;
228 :
229 102 : if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
230 : {
231 : TransactionId xmax;
232 :
233 0 : xmax = HeapTupleGetUpdateXid(tuple);
234 :
235 : /* not LOCKED_ONLY, so it has to have an xmax */
236 : Assert(TransactionIdIsValid(xmax));
237 :
238 : /* updating subtransaction must have aborted */
239 0 : if (!TransactionIdIsCurrentTransactionId(xmax))
240 0 : return true;
241 : else
242 0 : return false;
243 : }
244 :
245 102 : if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
246 : {
247 : /* deleting subtransaction must have aborted */
248 18 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
249 : InvalidTransactionId);
250 18 : return true;
251 : }
252 :
253 84 : return false;
254 : }
255 0 : else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
256 0 : return false;
257 0 : else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
258 0 : SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
259 : HeapTupleHeaderGetRawXmin(tuple));
260 : else
261 : {
262 : /* it must have aborted or crashed */
263 0 : SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
264 : InvalidTransactionId);
265 0 : return false;
266 : }
267 : }
268 :
269 : /* by here, the inserting transaction has committed */
270 :
271 102 : if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
272 102 : return true;
273 :
274 0 : if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
275 : {
276 0 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
277 0 : return true;
278 0 : return false; /* updated by other */
279 : }
280 :
281 0 : if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
282 : {
283 : TransactionId xmax;
284 :
285 0 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
286 0 : return true;
287 :
288 0 : xmax = HeapTupleGetUpdateXid(tuple);
289 :
290 : /* not LOCKED_ONLY, so it has to have an xmax */
291 : Assert(TransactionIdIsValid(xmax));
292 :
293 0 : if (TransactionIdIsCurrentTransactionId(xmax))
294 0 : return false;
295 0 : if (TransactionIdIsInProgress(xmax))
296 0 : return true;
297 0 : if (TransactionIdDidCommit(xmax))
298 0 : return false;
299 : /* it must have aborted or crashed */
300 0 : return true;
301 : }
302 :
303 0 : if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
304 : {
305 0 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
306 0 : return true;
307 0 : return false;
308 : }
309 :
310 0 : if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
311 0 : return true;
312 :
313 0 : if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
314 : {
315 : /* it must have aborted or crashed */
316 0 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
317 : InvalidTransactionId);
318 0 : return true;
319 : }
320 :
321 : /* xmax transaction committed */
322 :
323 0 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
324 : {
325 0 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
326 : InvalidTransactionId);
327 0 : return true;
328 : }
329 :
330 0 : SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
331 : HeapTupleHeaderGetRawXmax(tuple));
332 0 : return false;
333 : }
334 :
335 : /*
336 : * HeapTupleSatisfiesAny
337 : * Dummy "satisfies" routine: any tuple satisfies SnapshotAny.
338 : */
339 : static bool
340 15797462 : HeapTupleSatisfiesAny(HeapTuple htup, Snapshot snapshot, Buffer buffer)
341 : {
342 15797462 : return true;
343 : }
344 :
345 : /*
346 : * HeapTupleSatisfiesToast
347 : * True iff heap tuple is valid as a TOAST row.
348 : *
349 : * See SNAPSHOT_TOAST's definition for the intended behaviour.
350 : *
351 : * This is a simplified version that only checks for VACUUM moving conditions.
352 : * It's appropriate for TOAST usage because TOAST really doesn't want to do
353 : * its own time qual checks; if you can see the main table row that contains
354 : * a TOAST reference, you should be able to see the TOASTed value. However,
355 : * vacuuming a TOAST table is independent of the main table, and in case such
356 : * a vacuum fails partway through, we'd better do this much checking.
357 : *
358 : * Among other things, this means you can't do UPDATEs of rows in a TOAST
359 : * table.
360 : */
361 : static bool
362 155928 : HeapTupleSatisfiesToast(HeapTuple htup, Snapshot snapshot,
363 : Buffer buffer)
364 : {
365 155928 : HeapTupleHeader tuple = htup->t_data;
366 :
367 : Assert(ItemPointerIsValid(&htup->t_self));
368 : Assert(htup->t_tableOid != InvalidOid);
369 :
370 155928 : if (!HeapTupleHeaderXminCommitted(tuple))
371 : {
372 121006 : if (HeapTupleHeaderXminInvalid(tuple))
373 0 : return false;
374 :
375 : /* Used by pre-9.0 binary upgrades */
376 121006 : if (tuple->t_infomask & HEAP_MOVED_OFF)
377 : {
378 0 : TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
379 :
380 0 : if (TransactionIdIsCurrentTransactionId(xvac))
381 0 : return false;
382 0 : if (!TransactionIdIsInProgress(xvac))
383 : {
384 0 : if (TransactionIdDidCommit(xvac))
385 : {
386 0 : SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
387 : InvalidTransactionId);
388 0 : return false;
389 : }
390 0 : SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
391 : InvalidTransactionId);
392 : }
393 : }
394 : /* Used by pre-9.0 binary upgrades */
395 121006 : else if (tuple->t_infomask & HEAP_MOVED_IN)
396 : {
397 0 : TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
398 :
399 0 : if (!TransactionIdIsCurrentTransactionId(xvac))
400 : {
401 0 : if (TransactionIdIsInProgress(xvac))
402 0 : return false;
403 0 : if (TransactionIdDidCommit(xvac))
404 0 : SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
405 : InvalidTransactionId);
406 : else
407 : {
408 0 : SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
409 : InvalidTransactionId);
410 0 : return false;
411 : }
412 : }
413 : }
414 :
415 : /*
416 : * An invalid Xmin can be left behind by a speculative insertion that
417 : * is canceled by super-deleting the tuple. This also applies to
418 : * TOAST tuples created during speculative insertion.
419 : */
420 121006 : else if (!TransactionIdIsValid(HeapTupleHeaderGetXmin(tuple)))
421 0 : return false;
422 : }
423 :
424 : /* otherwise assume the tuple is valid for TOAST. */
425 155928 : return true;
426 : }
427 :
428 : /*
429 : * HeapTupleSatisfiesUpdate
430 : *
431 : * This function returns a more detailed result code than most of the
432 : * functions in this file, since UPDATE needs to know more than "is it
433 : * visible?". It also allows for user-supplied CommandId rather than
434 : * relying on CurrentCommandId.
435 : *
436 : * The possible return codes are:
437 : *
438 : * TM_Invisible: the tuple didn't exist at all when the scan started, e.g. it
439 : * was created by a later CommandId.
440 : *
441 : * TM_Ok: The tuple is valid and visible, so it may be updated.
442 : *
443 : * TM_SelfModified: The tuple was updated by the current transaction, after
444 : * the current scan started.
445 : *
446 : * TM_Updated: The tuple was updated by a committed transaction (including
447 : * the case where the tuple was moved into a different partition).
448 : *
449 : * TM_Deleted: The tuple was deleted by a committed transaction.
450 : *
451 : * TM_BeingModified: The tuple is being updated by an in-progress transaction
452 : * other than the current transaction. (Note: this includes the case where
453 : * the tuple is share-locked by a MultiXact, even if the MultiXact includes
454 : * the current transaction. Callers that want to distinguish that case must
455 : * test for it themselves.)
456 : */
457 : TM_Result
458 3978842 : HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid,
459 : Buffer buffer)
460 : {
461 3978842 : HeapTupleHeader tuple = htup->t_data;
462 :
463 : Assert(ItemPointerIsValid(&htup->t_self));
464 : Assert(htup->t_tableOid != InvalidOid);
465 :
466 3978842 : if (!HeapTupleHeaderXminCommitted(tuple))
467 : {
468 481520 : if (HeapTupleHeaderXminInvalid(tuple))
469 0 : return TM_Invisible;
470 :
471 : /* Used by pre-9.0 binary upgrades */
472 481520 : if (tuple->t_infomask & HEAP_MOVED_OFF)
473 : {
474 0 : TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
475 :
476 0 : if (TransactionIdIsCurrentTransactionId(xvac))
477 0 : return TM_Invisible;
478 0 : if (!TransactionIdIsInProgress(xvac))
479 : {
480 0 : if (TransactionIdDidCommit(xvac))
481 : {
482 0 : SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
483 : InvalidTransactionId);
484 0 : return TM_Invisible;
485 : }
486 0 : SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
487 : InvalidTransactionId);
488 : }
489 : }
490 : /* Used by pre-9.0 binary upgrades */
491 481520 : else if (tuple->t_infomask & HEAP_MOVED_IN)
492 : {
493 0 : TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
494 :
495 0 : if (!TransactionIdIsCurrentTransactionId(xvac))
496 : {
497 0 : if (TransactionIdIsInProgress(xvac))
498 0 : return TM_Invisible;
499 0 : if (TransactionIdDidCommit(xvac))
500 0 : SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
501 : InvalidTransactionId);
502 : else
503 : {
504 0 : SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
505 : InvalidTransactionId);
506 0 : return TM_Invisible;
507 : }
508 : }
509 : }
510 481520 : else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
511 : {
512 472746 : if (HeapTupleHeaderGetCmin(tuple) >= curcid)
513 24 : return TM_Invisible; /* inserted after scan started */
514 :
515 472722 : if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
516 392308 : return TM_Ok;
517 :
518 80414 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
519 : {
520 : TransactionId xmax;
521 :
522 80394 : xmax = HeapTupleHeaderGetRawXmax(tuple);
523 :
524 : /*
525 : * Careful here: even though this tuple was created by our own
526 : * transaction, it might be locked by other transactions, if
527 : * the original version was key-share locked when we updated
528 : * it.
529 : */
530 :
531 80394 : if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
532 : {
533 62 : if (MultiXactIdIsRunning(xmax, true))
534 62 : return TM_BeingModified;
535 : else
536 0 : return TM_Ok;
537 : }
538 :
539 : /*
540 : * If the locker is gone, then there is nothing of interest
541 : * left in this Xmax; otherwise, report the tuple as
542 : * locked/updated.
543 : */
544 80332 : if (!TransactionIdIsInProgress(xmax))
545 0 : return TM_Ok;
546 80332 : return TM_BeingModified;
547 : }
548 :
549 20 : if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
550 : {
551 : TransactionId xmax;
552 :
553 14 : xmax = HeapTupleGetUpdateXid(tuple);
554 :
555 : /* not LOCKED_ONLY, so it has to have an xmax */
556 : Assert(TransactionIdIsValid(xmax));
557 :
558 : /* deleting subtransaction must have aborted */
559 14 : if (!TransactionIdIsCurrentTransactionId(xmax))
560 : {
561 14 : if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple),
562 : false))
563 14 : return TM_BeingModified;
564 0 : return TM_Ok;
565 : }
566 : else
567 : {
568 0 : if (HeapTupleHeaderGetCmax(tuple) >= curcid)
569 0 : return TM_SelfModified; /* updated after scan started */
570 : else
571 0 : return TM_Invisible; /* updated before scan started */
572 : }
573 : }
574 :
575 6 : if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
576 : {
577 : /* deleting subtransaction must have aborted */
578 0 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
579 : InvalidTransactionId);
580 0 : return TM_Ok;
581 : }
582 :
583 6 : if (HeapTupleHeaderGetCmax(tuple) >= curcid)
584 6 : return TM_SelfModified; /* updated after scan started */
585 : else
586 0 : return TM_Invisible; /* updated before scan started */
587 : }
588 8774 : else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
589 0 : return TM_Invisible;
590 8774 : else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
591 8774 : SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
592 : HeapTupleHeaderGetRawXmin(tuple));
593 : else
594 : {
595 : /* it must have aborted or crashed */
596 0 : SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
597 : InvalidTransactionId);
598 0 : return TM_Invisible;
599 : }
600 : }
601 :
602 : /* by here, the inserting transaction has committed */
603 :
604 3506096 : if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
605 3409600 : return TM_Ok;
606 :
607 96496 : if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
608 : {
609 286 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
610 0 : return TM_Ok;
611 286 : if (!ItemPointerEquals(&htup->t_self, &tuple->t_ctid))
612 286 : return TM_Updated; /* updated by other */
613 : else
614 0 : return TM_Deleted; /* deleted by other */
615 : }
616 :
617 96210 : if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
618 : {
619 : TransactionId xmax;
620 :
621 1220 : if (HEAP_LOCKED_UPGRADED(tuple->t_infomask))
622 0 : return TM_Ok;
623 :
624 1220 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
625 : {
626 1146 : if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), true))
627 214 : return TM_BeingModified;
628 :
629 932 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
630 932 : return TM_Ok;
631 : }
632 :
633 74 : xmax = HeapTupleGetUpdateXid(tuple);
634 74 : if (!TransactionIdIsValid(xmax))
635 : {
636 0 : if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
637 0 : return TM_BeingModified;
638 : }
639 :
640 : /* not LOCKED_ONLY, so it has to have an xmax */
641 : Assert(TransactionIdIsValid(xmax));
642 :
643 74 : if (TransactionIdIsCurrentTransactionId(xmax))
644 : {
645 0 : if (HeapTupleHeaderGetCmax(tuple) >= curcid)
646 0 : return TM_SelfModified; /* updated after scan started */
647 : else
648 0 : return TM_Invisible; /* updated before scan started */
649 : }
650 :
651 74 : if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
652 62 : return TM_BeingModified;
653 :
654 12 : if (TransactionIdDidCommit(xmax))
655 : {
656 2 : if (!ItemPointerEquals(&htup->t_self, &tuple->t_ctid))
657 0 : return TM_Updated;
658 : else
659 2 : return TM_Deleted;
660 : }
661 :
662 : /*
663 : * By here, the update in the Xmax is either aborted or crashed, but
664 : * what about the other members?
665 : */
666 :
667 10 : if (!MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
668 : {
669 : /*
670 : * There's no member, even just a locker, alive anymore, so we can
671 : * mark the Xmax as invalid.
672 : */
673 10 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
674 : InvalidTransactionId);
675 10 : return TM_Ok;
676 : }
677 : else
678 : {
679 : /* There are lockers running */
680 0 : return TM_BeingModified;
681 : }
682 : }
683 :
684 94990 : if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
685 : {
686 85464 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
687 85312 : return TM_BeingModified;
688 152 : if (HeapTupleHeaderGetCmax(tuple) >= curcid)
689 152 : return TM_SelfModified; /* updated after scan started */
690 : else
691 0 : return TM_Invisible; /* updated before scan started */
692 : }
693 :
694 9526 : if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
695 2472 : return TM_BeingModified;
696 :
697 7054 : if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
698 : {
699 : /* it must have aborted or crashed */
700 388 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
701 : InvalidTransactionId);
702 388 : return TM_Ok;
703 : }
704 :
705 : /* xmax transaction committed */
706 :
707 6666 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
708 : {
709 6536 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
710 : InvalidTransactionId);
711 6536 : return TM_Ok;
712 : }
713 :
714 130 : SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
715 : HeapTupleHeaderGetRawXmax(tuple));
716 130 : if (!ItemPointerEquals(&htup->t_self, &tuple->t_ctid))
717 130 : return TM_Updated; /* updated by other */
718 : else
719 0 : return TM_Deleted; /* deleted by other */
720 : }
721 :
722 : /*
723 : * HeapTupleSatisfiesDirty
724 : * True iff heap tuple is valid including effects of open transactions.
725 : *
726 : * See SNAPSHOT_DIRTY's definition for the intended behaviour.
727 : *
728 : * This is essentially like HeapTupleSatisfiesSelf as far as effects of
729 : * the current transaction and committed/aborted xacts are concerned.
730 : * However, we also include the effects of other xacts still in progress.
731 : *
732 : * A special hack is that the passed-in snapshot struct is used as an
733 : * output argument to return the xids of concurrent xacts that affected the
734 : * tuple. snapshot->xmin is set to the tuple's xmin if that is another
735 : * transaction that's still in progress; or to InvalidTransactionId if the
736 : * tuple's xmin is committed good, committed dead, or my own xact.
737 : * Similarly for snapshot->xmax and the tuple's xmax. If the tuple was
738 : * inserted speculatively, meaning that the inserter might still back down
739 : * on the insertion without aborting the whole transaction, the associated
740 : * token is also returned in snapshot->speculativeToken.
741 : */
742 : static bool
743 11831656 : HeapTupleSatisfiesDirty(HeapTuple htup, Snapshot snapshot,
744 : Buffer buffer)
745 : {
746 11831656 : HeapTupleHeader tuple = htup->t_data;
747 :
748 : Assert(ItemPointerIsValid(&htup->t_self));
749 : Assert(htup->t_tableOid != InvalidOid);
750 :
751 11831656 : snapshot->xmin = snapshot->xmax = InvalidTransactionId;
752 11831656 : snapshot->speculativeToken = 0;
753 :
754 11831656 : if (!HeapTupleHeaderXminCommitted(tuple))
755 : {
756 11164258 : if (HeapTupleHeaderXminInvalid(tuple))
757 818 : return false;
758 :
759 : /* Used by pre-9.0 binary upgrades */
760 11163440 : if (tuple->t_infomask & HEAP_MOVED_OFF)
761 : {
762 0 : TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
763 :
764 0 : if (TransactionIdIsCurrentTransactionId(xvac))
765 0 : return false;
766 0 : if (!TransactionIdIsInProgress(xvac))
767 : {
768 0 : if (TransactionIdDidCommit(xvac))
769 : {
770 0 : SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
771 : InvalidTransactionId);
772 0 : return false;
773 : }
774 0 : SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
775 : InvalidTransactionId);
776 : }
777 : }
778 : /* Used by pre-9.0 binary upgrades */
779 11163440 : else if (tuple->t_infomask & HEAP_MOVED_IN)
780 : {
781 0 : TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
782 :
783 0 : if (!TransactionIdIsCurrentTransactionId(xvac))
784 : {
785 0 : if (TransactionIdIsInProgress(xvac))
786 0 : return false;
787 0 : if (TransactionIdDidCommit(xvac))
788 0 : SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
789 : InvalidTransactionId);
790 : else
791 : {
792 0 : SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
793 : InvalidTransactionId);
794 0 : return false;
795 : }
796 : }
797 : }
798 11163440 : else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
799 : {
800 11112102 : if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
801 61928 : return true;
802 :
803 11050174 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
804 10080 : return true;
805 :
806 11040094 : if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
807 : {
808 : TransactionId xmax;
809 :
810 32 : xmax = HeapTupleGetUpdateXid(tuple);
811 :
812 : /* not LOCKED_ONLY, so it has to have an xmax */
813 : Assert(TransactionIdIsValid(xmax));
814 :
815 : /* updating subtransaction must have aborted */
816 32 : if (!TransactionIdIsCurrentTransactionId(xmax))
817 0 : return true;
818 : else
819 32 : return false;
820 : }
821 :
822 11040062 : if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
823 : {
824 : /* deleting subtransaction must have aborted */
825 0 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
826 : InvalidTransactionId);
827 0 : return true;
828 : }
829 :
830 11040062 : return false;
831 : }
832 51338 : else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
833 : {
834 : /*
835 : * Return the speculative token to caller. Caller can worry about
836 : * xmax, since it requires a conclusively locked row version, and
837 : * a concurrent update to this tuple is a conflict of its
838 : * purposes.
839 : */
840 98 : if (HeapTupleHeaderIsSpeculative(tuple))
841 : {
842 4 : snapshot->speculativeToken =
843 4 : HeapTupleHeaderGetSpeculativeToken(tuple);
844 :
845 : Assert(snapshot->speculativeToken != 0);
846 : }
847 :
848 98 : snapshot->xmin = HeapTupleHeaderGetRawXmin(tuple);
849 : /* XXX shouldn't we fall through to look at xmax? */
850 98 : return true; /* in insertion by other */
851 : }
852 51240 : else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
853 50338 : SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
854 : HeapTupleHeaderGetRawXmin(tuple));
855 : else
856 : {
857 : /* it must have aborted or crashed */
858 902 : SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
859 : InvalidTransactionId);
860 902 : return false;
861 : }
862 : }
863 :
864 : /* by here, the inserting transaction has committed */
865 :
866 717736 : if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
867 265102 : return true;
868 :
869 452634 : if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
870 : {
871 156090 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
872 0 : return true;
873 156090 : return false; /* updated by other */
874 : }
875 :
876 296544 : if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
877 : {
878 : TransactionId xmax;
879 :
880 62 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
881 26 : return true;
882 :
883 36 : xmax = HeapTupleGetUpdateXid(tuple);
884 :
885 : /* not LOCKED_ONLY, so it has to have an xmax */
886 : Assert(TransactionIdIsValid(xmax));
887 :
888 36 : if (TransactionIdIsCurrentTransactionId(xmax))
889 2 : return false;
890 34 : if (TransactionIdIsInProgress(xmax))
891 : {
892 0 : snapshot->xmax = xmax;
893 0 : return true;
894 : }
895 34 : if (TransactionIdDidCommit(xmax))
896 34 : return false;
897 : /* it must have aborted or crashed */
898 0 : return true;
899 : }
900 :
901 296482 : if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
902 : {
903 221876 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
904 44 : return true;
905 221832 : return false;
906 : }
907 :
908 74606 : if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
909 : {
910 28 : if (!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
911 24 : snapshot->xmax = HeapTupleHeaderGetRawXmax(tuple);
912 28 : return true;
913 : }
914 :
915 74578 : if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
916 : {
917 : /* it must have aborted or crashed */
918 24 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
919 : InvalidTransactionId);
920 24 : return true;
921 : }
922 :
923 : /* xmax transaction committed */
924 :
925 74554 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
926 : {
927 26790 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
928 : InvalidTransactionId);
929 26790 : return true;
930 : }
931 :
932 47764 : SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
933 : HeapTupleHeaderGetRawXmax(tuple));
934 47764 : return false; /* updated by other */
935 : }
936 :
937 : /*
938 : * HeapTupleSatisfiesMVCC
939 : * True iff heap tuple is valid for the given MVCC snapshot.
940 : *
941 : * See SNAPSHOT_MVCC's definition for the intended behaviour.
942 : *
943 : * Notice that here, we will not update the tuple status hint bits if the
944 : * inserting/deleting transaction is still running according to our snapshot,
945 : * even if in reality it's committed or aborted by now. This is intentional.
946 : * Checking the true transaction state would require access to high-traffic
947 : * shared data structures, creating contention we'd rather do without, and it
948 : * would not change the result of our visibility check anyway. The hint bits
949 : * will be updated by the first visitor that has a snapshot new enough to see
950 : * the inserting/deleting transaction as done. In the meantime, the cost of
951 : * leaving the hint bits unset is basically that each HeapTupleSatisfiesMVCC
952 : * call will need to run TransactionIdIsCurrentTransactionId in addition to
953 : * XidInMVCCSnapshot (but it would have to do the latter anyway). In the old
954 : * coding where we tried to set the hint bits as soon as possible, we instead
955 : * did TransactionIdIsInProgress in each call --- to no avail, as long as the
956 : * inserting/deleting transaction was still running --- which was more cycles
957 : * and more contention on ProcArrayLock.
958 : */
959 : static bool
960 155582036 : HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot,
961 : Buffer buffer)
962 : {
963 155582036 : HeapTupleHeader tuple = htup->t_data;
964 :
965 : /*
966 : * Assert that the caller has registered the snapshot. This function
967 : * doesn't care about the registration as such, but in general you
968 : * shouldn't try to use a snapshot without registration because it might
969 : * get invalidated while it's still in use, and this is a convenient place
970 : * to check for that.
971 : */
972 : Assert(snapshot->regd_count > 0 || snapshot->active_count > 0);
973 :
974 : Assert(ItemPointerIsValid(&htup->t_self));
975 : Assert(htup->t_tableOid != InvalidOid);
976 :
977 155582036 : if (!HeapTupleHeaderXminCommitted(tuple))
978 : {
979 28791390 : if (HeapTupleHeaderXminInvalid(tuple))
980 535652 : return false;
981 :
982 : /* Used by pre-9.0 binary upgrades */
983 28255738 : if (tuple->t_infomask & HEAP_MOVED_OFF)
984 : {
985 0 : TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
986 :
987 0 : if (TransactionIdIsCurrentTransactionId(xvac))
988 0 : return false;
989 0 : if (!XidInMVCCSnapshot(xvac, snapshot))
990 : {
991 0 : if (TransactionIdDidCommit(xvac))
992 : {
993 0 : SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
994 : InvalidTransactionId);
995 0 : return false;
996 : }
997 0 : SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
998 : InvalidTransactionId);
999 : }
1000 : }
1001 : /* Used by pre-9.0 binary upgrades */
1002 28255738 : else if (tuple->t_infomask & HEAP_MOVED_IN)
1003 : {
1004 0 : TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
1005 :
1006 0 : if (!TransactionIdIsCurrentTransactionId(xvac))
1007 : {
1008 0 : if (XidInMVCCSnapshot(xvac, snapshot))
1009 0 : return false;
1010 0 : if (TransactionIdDidCommit(xvac))
1011 0 : SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
1012 : InvalidTransactionId);
1013 : else
1014 : {
1015 0 : SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
1016 : InvalidTransactionId);
1017 0 : return false;
1018 : }
1019 : }
1020 : }
1021 28255738 : else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
1022 : {
1023 20404788 : if (HeapTupleHeaderGetCmin(tuple) >= snapshot->curcid)
1024 13098 : return false; /* inserted after scan started */
1025 :
1026 20391690 : if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
1027 14528852 : return true;
1028 :
1029 5862838 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
1030 4298 : return true;
1031 :
1032 5858540 : if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1033 : {
1034 : TransactionId xmax;
1035 :
1036 14 : xmax = HeapTupleGetUpdateXid(tuple);
1037 :
1038 : /* not LOCKED_ONLY, so it has to have an xmax */
1039 : Assert(TransactionIdIsValid(xmax));
1040 :
1041 : /* updating subtransaction must have aborted */
1042 14 : if (!TransactionIdIsCurrentTransactionId(xmax))
1043 14 : return true;
1044 0 : else if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
1045 0 : return true; /* updated after scan started */
1046 : else
1047 0 : return false; /* updated before scan started */
1048 : }
1049 :
1050 5858526 : if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
1051 : {
1052 : /* deleting subtransaction must have aborted */
1053 22 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
1054 : InvalidTransactionId);
1055 22 : return true;
1056 : }
1057 :
1058 5858504 : if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
1059 1476 : return true; /* deleted after scan started */
1060 : else
1061 5857028 : return false; /* deleted before scan started */
1062 : }
1063 7850950 : else if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
1064 31394 : return false;
1065 7819556 : else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
1066 7706378 : SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
1067 : HeapTupleHeaderGetRawXmin(tuple));
1068 : else
1069 : {
1070 : /* it must have aborted or crashed */
1071 113178 : SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
1072 : InvalidTransactionId);
1073 113178 : return false;
1074 : }
1075 : }
1076 : else
1077 : {
1078 : /* xmin is committed, but maybe not according to our snapshot */
1079 246605518 : if (!HeapTupleHeaderXminFrozen(tuple) &&
1080 119814872 : XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
1081 5106 : return false; /* treat as still in progress */
1082 : }
1083 :
1084 : /* by here, the inserting transaction has committed */
1085 :
1086 134491918 : if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
1087 122111298 : return true;
1088 :
1089 12380620 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
1090 85198 : return true;
1091 :
1092 12295422 : if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1093 : {
1094 : TransactionId xmax;
1095 :
1096 : /* already checked above */
1097 : Assert(!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask));
1098 :
1099 352 : xmax = HeapTupleGetUpdateXid(tuple);
1100 :
1101 : /* not LOCKED_ONLY, so it has to have an xmax */
1102 : Assert(TransactionIdIsValid(xmax));
1103 :
1104 352 : if (TransactionIdIsCurrentTransactionId(xmax))
1105 : {
1106 46 : if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
1107 0 : return true; /* deleted after scan started */
1108 : else
1109 46 : return false; /* deleted before scan started */
1110 : }
1111 306 : if (XidInMVCCSnapshot(xmax, snapshot))
1112 36 : return true;
1113 270 : if (TransactionIdDidCommit(xmax))
1114 260 : return false; /* updating transaction committed */
1115 : /* it must have aborted or crashed */
1116 10 : return true;
1117 : }
1118 :
1119 12295070 : if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
1120 : {
1121 974348 : if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
1122 : {
1123 235066 : if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
1124 2020 : return true; /* deleted after scan started */
1125 : else
1126 233046 : return false; /* deleted before scan started */
1127 : }
1128 :
1129 739282 : if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
1130 17256 : return true;
1131 :
1132 722026 : if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
1133 : {
1134 : /* it must have aborted or crashed */
1135 14382 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
1136 : InvalidTransactionId);
1137 14382 : return true;
1138 : }
1139 :
1140 : /* xmax transaction committed */
1141 707644 : SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
1142 : HeapTupleHeaderGetRawXmax(tuple));
1143 : }
1144 : else
1145 : {
1146 : /* xmax is committed, but maybe not according to our snapshot */
1147 11320722 : if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
1148 1196 : return true; /* treat as still in progress */
1149 : }
1150 :
1151 : /* xmax transaction committed */
1152 :
1153 12027170 : return false;
1154 : }
1155 :
1156 :
1157 : /*
1158 : * HeapTupleSatisfiesVacuum
1159 : *
1160 : * Determine the status of tuples for VACUUM purposes. Here, what
1161 : * we mainly want to know is if a tuple is potentially visible to *any*
1162 : * running transaction. If so, it can't be removed yet by VACUUM.
1163 : *
1164 : * OldestXmin is a cutoff XID (obtained from
1165 : * GetOldestNonRemovableTransactionId()). Tuples deleted by XIDs >=
1166 : * OldestXmin are deemed "recently dead"; they might still be visible to some
1167 : * open transaction, so we can't remove them, even if we see that the deleting
1168 : * transaction has committed.
1169 : */
1170 : HTSV_Result
1171 26695648 : HeapTupleSatisfiesVacuum(HeapTuple htup, TransactionId OldestXmin,
1172 : Buffer buffer)
1173 : {
1174 26695648 : TransactionId dead_after = InvalidTransactionId;
1175 : HTSV_Result res;
1176 :
1177 26695648 : res = HeapTupleSatisfiesVacuumHorizon(htup, buffer, &dead_after);
1178 :
1179 26695648 : if (res == HEAPTUPLE_RECENTLY_DEAD)
1180 : {
1181 : Assert(TransactionIdIsValid(dead_after));
1182 :
1183 497834 : if (TransactionIdPrecedes(dead_after, OldestXmin))
1184 49766 : res = HEAPTUPLE_DEAD;
1185 : }
1186 : else
1187 : Assert(!TransactionIdIsValid(dead_after));
1188 :
1189 26695648 : return res;
1190 : }
1191 :
1192 : /*
1193 : * Work horse for HeapTupleSatisfiesVacuum and similar routines.
1194 : *
1195 : * In contrast to HeapTupleSatisfiesVacuum this routine, when encountering a
1196 : * tuple that could still be visible to some backend, stores the xid that
1197 : * needs to be compared with the horizon in *dead_after, and returns
1198 : * HEAPTUPLE_RECENTLY_DEAD. The caller then can perform the comparison with
1199 : * the horizon. This is e.g. useful when comparing with different horizons.
1200 : *
1201 : * Note: HEAPTUPLE_DEAD can still be returned here, e.g. if the inserting
1202 : * transaction aborted.
1203 : */
1204 : HTSV_Result
1205 64565802 : HeapTupleSatisfiesVacuumHorizon(HeapTuple htup, Buffer buffer, TransactionId *dead_after)
1206 : {
1207 64565802 : HeapTupleHeader tuple = htup->t_data;
1208 :
1209 : Assert(ItemPointerIsValid(&htup->t_self));
1210 : Assert(htup->t_tableOid != InvalidOid);
1211 : Assert(dead_after != NULL);
1212 :
1213 64565802 : *dead_after = InvalidTransactionId;
1214 :
1215 : /*
1216 : * Has inserting transaction committed?
1217 : *
1218 : * If the inserting transaction aborted, then the tuple was never visible
1219 : * to any other transaction, so we can delete it immediately.
1220 : */
1221 64565802 : if (!HeapTupleHeaderXminCommitted(tuple))
1222 : {
1223 12068378 : if (HeapTupleHeaderXminInvalid(tuple))
1224 25176 : return HEAPTUPLE_DEAD;
1225 : /* Used by pre-9.0 binary upgrades */
1226 12043202 : else if (tuple->t_infomask & HEAP_MOVED_OFF)
1227 : {
1228 0 : TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
1229 :
1230 0 : if (TransactionIdIsCurrentTransactionId(xvac))
1231 0 : return HEAPTUPLE_DELETE_IN_PROGRESS;
1232 0 : if (TransactionIdIsInProgress(xvac))
1233 0 : return HEAPTUPLE_DELETE_IN_PROGRESS;
1234 0 : if (TransactionIdDidCommit(xvac))
1235 : {
1236 0 : SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
1237 : InvalidTransactionId);
1238 0 : return HEAPTUPLE_DEAD;
1239 : }
1240 0 : SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
1241 : InvalidTransactionId);
1242 : }
1243 : /* Used by pre-9.0 binary upgrades */
1244 12043202 : else if (tuple->t_infomask & HEAP_MOVED_IN)
1245 : {
1246 0 : TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
1247 :
1248 0 : if (TransactionIdIsCurrentTransactionId(xvac))
1249 0 : return HEAPTUPLE_INSERT_IN_PROGRESS;
1250 0 : if (TransactionIdIsInProgress(xvac))
1251 0 : return HEAPTUPLE_INSERT_IN_PROGRESS;
1252 0 : if (TransactionIdDidCommit(xvac))
1253 0 : SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
1254 : InvalidTransactionId);
1255 : else
1256 : {
1257 0 : SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
1258 : InvalidTransactionId);
1259 0 : return HEAPTUPLE_DEAD;
1260 : }
1261 : }
1262 12043202 : else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
1263 : {
1264 4100762 : if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
1265 4028846 : return HEAPTUPLE_INSERT_IN_PROGRESS;
1266 : /* only locked? run infomask-only check first, for performance */
1267 127468 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask) ||
1268 55552 : HeapTupleHeaderIsOnlyLocked(tuple))
1269 16364 : return HEAPTUPLE_INSERT_IN_PROGRESS;
1270 : /* inserted and then deleted by same xact */
1271 55552 : if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetUpdateXid(tuple)))
1272 55546 : return HEAPTUPLE_DELETE_IN_PROGRESS;
1273 : /* deleting subtransaction must have aborted */
1274 6 : return HEAPTUPLE_INSERT_IN_PROGRESS;
1275 : }
1276 7942440 : else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
1277 : {
1278 : /*
1279 : * It'd be possible to discern between INSERT/DELETE in progress
1280 : * here by looking at xmax - but that doesn't seem beneficial for
1281 : * the majority of callers and even detrimental for some. We'd
1282 : * rather have callers look at/wait for xmin than xmax. It's
1283 : * always correct to return INSERT_IN_PROGRESS because that's
1284 : * what's happening from the view of other backends.
1285 : */
1286 5626 : return HEAPTUPLE_INSERT_IN_PROGRESS;
1287 : }
1288 7936814 : else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
1289 7870190 : SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
1290 : HeapTupleHeaderGetRawXmin(tuple));
1291 : else
1292 : {
1293 : /*
1294 : * Not in Progress, Not Committed, so either Aborted or crashed
1295 : */
1296 66624 : SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
1297 : InvalidTransactionId);
1298 66624 : return HEAPTUPLE_DEAD;
1299 : }
1300 :
1301 : /*
1302 : * At this point the xmin is known committed, but we might not have
1303 : * been able to set the hint bit yet; so we can no longer Assert that
1304 : * it's set.
1305 : */
1306 : }
1307 :
1308 : /*
1309 : * Okay, the inserter committed, so it was good at some point. Now what
1310 : * about the deleting transaction?
1311 : */
1312 60367614 : if (tuple->t_infomask & HEAP_XMAX_INVALID)
1313 44800608 : return HEAPTUPLE_LIVE;
1314 :
1315 15567006 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
1316 : {
1317 : /*
1318 : * "Deleting" xact really only locked it, so the tuple is live in any
1319 : * case. However, we should make sure that either XMAX_COMMITTED or
1320 : * XMAX_INVALID gets set once the xact is gone, to reduce the costs of
1321 : * examining the tuple for future xacts.
1322 : */
1323 27156 : if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
1324 : {
1325 27156 : if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1326 : {
1327 : /*
1328 : * If it's a pre-pg_upgrade tuple, the multixact cannot
1329 : * possibly be running; otherwise have to check.
1330 : */
1331 860 : if (!HEAP_LOCKED_UPGRADED(tuple->t_infomask) &&
1332 430 : MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple),
1333 : true))
1334 4 : return HEAPTUPLE_LIVE;
1335 426 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
1336 : }
1337 : else
1338 : {
1339 26726 : if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
1340 0 : return HEAPTUPLE_LIVE;
1341 26726 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
1342 : InvalidTransactionId);
1343 : }
1344 : }
1345 :
1346 : /*
1347 : * We don't really care whether xmax did commit, abort or crash. We
1348 : * know that xmax did lock the tuple, but it did not and will never
1349 : * actually update it.
1350 : */
1351 :
1352 27152 : return HEAPTUPLE_LIVE;
1353 : }
1354 :
1355 15539850 : if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1356 : {
1357 10 : TransactionId xmax = HeapTupleGetUpdateXid(tuple);
1358 :
1359 : /* already checked above */
1360 : Assert(!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask));
1361 :
1362 : /* not LOCKED_ONLY, so it has to have an xmax */
1363 : Assert(TransactionIdIsValid(xmax));
1364 :
1365 10 : if (TransactionIdIsInProgress(xmax))
1366 0 : return HEAPTUPLE_DELETE_IN_PROGRESS;
1367 10 : else if (TransactionIdDidCommit(xmax))
1368 : {
1369 : /*
1370 : * The multixact might still be running due to lockers. Need to
1371 : * allow for pruning if below the xid horizon regardless --
1372 : * otherwise we could end up with a tuple where the updater has to
1373 : * be removed due to the horizon, but is not pruned away. It's
1374 : * not a problem to prune that tuple, because any remaining
1375 : * lockers will also be present in newer tuple versions.
1376 : */
1377 10 : *dead_after = xmax;
1378 10 : return HEAPTUPLE_RECENTLY_DEAD;
1379 : }
1380 0 : else if (!MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
1381 : {
1382 : /*
1383 : * Not in Progress, Not Committed, so either Aborted or crashed.
1384 : * Mark the Xmax as invalid.
1385 : */
1386 0 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
1387 : }
1388 :
1389 0 : return HEAPTUPLE_LIVE;
1390 : }
1391 :
1392 15539840 : if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
1393 : {
1394 14064980 : if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
1395 11516606 : return HEAPTUPLE_DELETE_IN_PROGRESS;
1396 2548374 : else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
1397 2545830 : SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
1398 : HeapTupleHeaderGetRawXmax(tuple));
1399 : else
1400 : {
1401 : /*
1402 : * Not in Progress, Not Committed, so either Aborted or crashed
1403 : */
1404 2544 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
1405 : InvalidTransactionId);
1406 2544 : return HEAPTUPLE_LIVE;
1407 : }
1408 :
1409 : /*
1410 : * At this point the xmax is known committed, but we might not have
1411 : * been able to set the hint bit yet; so we can no longer Assert that
1412 : * it's set.
1413 : */
1414 : }
1415 :
1416 : /*
1417 : * Deleter committed, allow caller to check if it was recent enough that
1418 : * some open transactions could still see the tuple.
1419 : */
1420 4020690 : *dead_after = HeapTupleHeaderGetRawXmax(tuple);
1421 4020690 : return HEAPTUPLE_RECENTLY_DEAD;
1422 : }
1423 :
1424 :
1425 : /*
1426 : * HeapTupleSatisfiesNonVacuumable
1427 : *
1428 : * True if tuple might be visible to some transaction; false if it's
1429 : * surely dead to everyone, ie, vacuumable.
1430 : *
1431 : * See SNAPSHOT_NON_VACUUMABLE's definition for the intended behaviour.
1432 : *
1433 : * This is an interface to HeapTupleSatisfiesVacuum that's callable via
1434 : * HeapTupleSatisfiesSnapshot, so it can be used through a Snapshot.
1435 : * snapshot->vistest must have been set up with the horizon to use.
1436 : */
1437 : static bool
1438 689866 : HeapTupleSatisfiesNonVacuumable(HeapTuple htup, Snapshot snapshot,
1439 : Buffer buffer)
1440 : {
1441 689866 : TransactionId dead_after = InvalidTransactionId;
1442 : HTSV_Result res;
1443 :
1444 689866 : res = HeapTupleSatisfiesVacuumHorizon(htup, buffer, &dead_after);
1445 :
1446 689866 : if (res == HEAPTUPLE_RECENTLY_DEAD)
1447 : {
1448 : Assert(TransactionIdIsValid(dead_after));
1449 :
1450 154372 : if (GlobalVisTestIsRemovableXid(snapshot->vistest, dead_after))
1451 124774 : res = HEAPTUPLE_DEAD;
1452 : }
1453 : else
1454 : Assert(!TransactionIdIsValid(dead_after));
1455 :
1456 689866 : return res != HEAPTUPLE_DEAD;
1457 : }
1458 :
1459 :
1460 : /*
1461 : * HeapTupleIsSurelyDead
1462 : *
1463 : * Cheaply determine whether a tuple is surely dead to all onlookers.
1464 : * We sometimes use this in lieu of HeapTupleSatisfiesVacuum when the
1465 : * tuple has just been tested by another visibility routine (usually
1466 : * HeapTupleSatisfiesMVCC) and, therefore, any hint bits that can be set
1467 : * should already be set. We assume that if no hint bits are set, the xmin
1468 : * or xmax transaction is still running. This is therefore faster than
1469 : * HeapTupleSatisfiesVacuum, because we consult neither procarray nor CLOG.
1470 : * It's okay to return false when in doubt, but we must return true only
1471 : * if the tuple is removable.
1472 : */
1473 : bool
1474 12779070 : HeapTupleIsSurelyDead(HeapTuple htup, GlobalVisState *vistest)
1475 : {
1476 12779070 : HeapTupleHeader tuple = htup->t_data;
1477 :
1478 : Assert(ItemPointerIsValid(&htup->t_self));
1479 : Assert(htup->t_tableOid != InvalidOid);
1480 :
1481 : /*
1482 : * If the inserting transaction is marked invalid, then it aborted, and
1483 : * the tuple is definitely dead. If it's marked neither committed nor
1484 : * invalid, then we assume it's still alive (since the presumption is that
1485 : * all relevant hint bits were just set moments ago).
1486 : */
1487 12779070 : if (!HeapTupleHeaderXminCommitted(tuple))
1488 11266492 : return HeapTupleHeaderXminInvalid(tuple);
1489 :
1490 : /*
1491 : * If the inserting transaction committed, but any deleting transaction
1492 : * aborted, the tuple is still alive.
1493 : */
1494 1512578 : if (tuple->t_infomask & HEAP_XMAX_INVALID)
1495 22 : return false;
1496 :
1497 : /*
1498 : * If the XMAX is just a lock, the tuple is still alive.
1499 : */
1500 1512556 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
1501 0 : return false;
1502 :
1503 : /*
1504 : * If the Xmax is a MultiXact, it might be dead or alive, but we cannot
1505 : * know without checking pg_multixact.
1506 : */
1507 1512556 : if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1508 104 : return false;
1509 :
1510 : /* If deleter isn't known to have committed, assume it's still running. */
1511 1512452 : if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
1512 383400 : return false;
1513 :
1514 : /* Deleter committed, so tuple is dead if the XID is old enough. */
1515 1129052 : return GlobalVisTestIsRemovableXid(vistest,
1516 : HeapTupleHeaderGetRawXmax(tuple));
1517 : }
1518 :
1519 : /*
1520 : * Is the tuple really only locked? That is, is it not updated?
1521 : *
1522 : * It's easy to check just infomask bits if the locker is not a multi; but
1523 : * otherwise we need to verify that the updating transaction has not aborted.
1524 : *
1525 : * This function is here because it follows the same visibility rules laid out
1526 : * at the top of this file.
1527 : */
1528 : bool
1529 112174 : HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple)
1530 : {
1531 : TransactionId xmax;
1532 :
1533 : /* if there's no valid Xmax, then there's obviously no update either */
1534 112174 : if (tuple->t_infomask & HEAP_XMAX_INVALID)
1535 0 : return true;
1536 :
1537 112174 : if (tuple->t_infomask & HEAP_XMAX_LOCK_ONLY)
1538 1062 : return true;
1539 :
1540 : /* invalid xmax means no update */
1541 111112 : if (!TransactionIdIsValid(HeapTupleHeaderGetRawXmax(tuple)))
1542 0 : return true;
1543 :
1544 : /*
1545 : * if HEAP_XMAX_LOCK_ONLY is not set and not a multi, then this must
1546 : * necessarily have been updated
1547 : */
1548 111112 : if (!(tuple->t_infomask & HEAP_XMAX_IS_MULTI))
1549 111060 : return false;
1550 :
1551 : /* ... but if it's a multi, then perhaps the updating Xid aborted. */
1552 52 : xmax = HeapTupleGetUpdateXid(tuple);
1553 :
1554 : /* not LOCKED_ONLY, so it has to have an xmax */
1555 : Assert(TransactionIdIsValid(xmax));
1556 :
1557 52 : if (TransactionIdIsCurrentTransactionId(xmax))
1558 0 : return false;
1559 52 : if (TransactionIdIsInProgress(xmax))
1560 8 : return false;
1561 44 : if (TransactionIdDidCommit(xmax))
1562 22 : return false;
1563 :
1564 : /*
1565 : * not current, not in progress, not committed -- must have aborted or
1566 : * crashed
1567 : */
1568 22 : return true;
1569 : }
1570 :
1571 : /*
1572 : * check whether the transaction id 'xid' is in the pre-sorted array 'xip'.
1573 : */
1574 : static bool
1575 77460 : TransactionIdInArray(TransactionId xid, TransactionId *xip, Size num)
1576 : {
1577 110408 : return num > 0 &&
1578 32948 : bsearch(&xid, xip, num, sizeof(TransactionId), xidComparator) != NULL;
1579 : }
1580 :
1581 : /*
1582 : * See the comments for HeapTupleSatisfiesMVCC for the semantics this function
1583 : * obeys.
1584 : *
1585 : * Only usable on tuples from catalog tables!
1586 : *
1587 : * We don't need to support HEAP_MOVED_(IN|OFF) for now because we only support
1588 : * reading catalog pages which couldn't have been created in an older version.
1589 : *
1590 : * We don't set any hint bits in here as it seems unlikely to be beneficial as
1591 : * those should already be set by normal access and it seems to be too
1592 : * dangerous to do so as the semantics of doing so during timetravel are more
1593 : * complicated than when dealing "only" with the present.
1594 : */
1595 : static bool
1596 61918 : HeapTupleSatisfiesHistoricMVCC(HeapTuple htup, Snapshot snapshot,
1597 : Buffer buffer)
1598 : {
1599 61918 : HeapTupleHeader tuple = htup->t_data;
1600 61918 : TransactionId xmin = HeapTupleHeaderGetXmin(tuple);
1601 61918 : TransactionId xmax = HeapTupleHeaderGetRawXmax(tuple);
1602 :
1603 : Assert(ItemPointerIsValid(&htup->t_self));
1604 : Assert(htup->t_tableOid != InvalidOid);
1605 :
1606 : /* inserting transaction aborted */
1607 61918 : if (HeapTupleHeaderXminInvalid(tuple))
1608 : {
1609 : Assert(!TransactionIdDidCommit(xmin));
1610 150 : return false;
1611 : }
1612 : /* check if it's one of our txids, toplevel is also in there */
1613 61768 : else if (TransactionIdInArray(xmin, snapshot->subxip, snapshot->subxcnt))
1614 : {
1615 : bool resolved;
1616 892 : CommandId cmin = HeapTupleHeaderGetRawCommandId(tuple);
1617 892 : CommandId cmax = InvalidCommandId;
1618 :
1619 : /*
1620 : * another transaction might have (tried to) delete this tuple or
1621 : * cmin/cmax was stored in a combo CID. So we need to lookup the
1622 : * actual values externally.
1623 : */
1624 892 : resolved = ResolveCminCmaxDuringDecoding(HistoricSnapshotGetTupleCids(), snapshot,
1625 : htup, buffer,
1626 : &cmin, &cmax);
1627 :
1628 : /*
1629 : * If we haven't resolved the combo CID to cmin/cmax, that means we
1630 : * have not decoded the combo CID yet. That means the cmin is
1631 : * definitely in the future, and we're not supposed to see the tuple
1632 : * yet.
1633 : *
1634 : * XXX This only applies to decoding of in-progress transactions. In
1635 : * regular logical decoding we only execute this code at commit time,
1636 : * at which point we should have seen all relevant combo CIDs. So
1637 : * ideally, we should error out in this case but in practice, this
1638 : * won't happen. If we are too worried about this then we can add an
1639 : * elog inside ResolveCminCmaxDuringDecoding.
1640 : *
1641 : * XXX For the streaming case, we can track the largest combo CID
1642 : * assigned, and error out based on this (when unable to resolve combo
1643 : * CID below that observed maximum value).
1644 : */
1645 892 : if (!resolved)
1646 98 : return false;
1647 :
1648 : Assert(cmin != InvalidCommandId);
1649 :
1650 892 : if (cmin >= snapshot->curcid)
1651 98 : return false; /* inserted after scan started */
1652 : /* fall through */
1653 : }
1654 : /* committed before our xmin horizon. Do a normal visibility check. */
1655 60876 : else if (TransactionIdPrecedes(xmin, snapshot->xmin))
1656 : {
1657 : Assert(!(HeapTupleHeaderXminCommitted(tuple) &&
1658 : !TransactionIdDidCommit(xmin)));
1659 :
1660 : /* check for hint bit first, consult clog afterwards */
1661 55478 : if (!HeapTupleHeaderXminCommitted(tuple) &&
1662 106 : !TransactionIdDidCommit(xmin))
1663 0 : return false;
1664 : /* fall through */
1665 : }
1666 : /* beyond our xmax horizon, i.e. invisible */
1667 5398 : else if (TransactionIdFollowsOrEquals(xmin, snapshot->xmax))
1668 : {
1669 220 : return false;
1670 : }
1671 : /* check if it's a committed transaction in [xmin, xmax) */
1672 5178 : else if (TransactionIdInArray(xmin, snapshot->xip, snapshot->xcnt))
1673 : {
1674 : /* fall through */
1675 : }
1676 :
1677 : /*
1678 : * none of the above, i.e. between [xmin, xmax) but hasn't committed. I.e.
1679 : * invisible.
1680 : */
1681 : else
1682 : {
1683 0 : return false;
1684 : }
1685 :
1686 : /* at this point we know xmin is visible, go on to check xmax */
1687 :
1688 : /* xid invalid or aborted */
1689 61450 : if (tuple->t_infomask & HEAP_XMAX_INVALID)
1690 51784 : return true;
1691 : /* locked tuples are always visible */
1692 9666 : else if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
1693 1724 : return true;
1694 :
1695 : /*
1696 : * We can see multis here if we're looking at user tables or if somebody
1697 : * SELECT ... FOR SHARE/UPDATE a system table.
1698 : */
1699 7942 : else if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1700 : {
1701 106 : xmax = HeapTupleGetUpdateXid(tuple);
1702 : }
1703 :
1704 : /* check if it's one of our txids, toplevel is also in there */
1705 7942 : if (TransactionIdInArray(xmax, snapshot->subxip, snapshot->subxcnt))
1706 : {
1707 : bool resolved;
1708 : CommandId cmin;
1709 552 : CommandId cmax = HeapTupleHeaderGetRawCommandId(tuple);
1710 :
1711 : /* Lookup actual cmin/cmax values */
1712 552 : resolved = ResolveCminCmaxDuringDecoding(HistoricSnapshotGetTupleCids(), snapshot,
1713 : htup, buffer,
1714 : &cmin, &cmax);
1715 :
1716 : /*
1717 : * If we haven't resolved the combo CID to cmin/cmax, that means we
1718 : * have not decoded the combo CID yet. That means the cmax is
1719 : * definitely in the future, and we're still supposed to see the
1720 : * tuple.
1721 : *
1722 : * XXX This only applies to decoding of in-progress transactions. In
1723 : * regular logical decoding we only execute this code at commit time,
1724 : * at which point we should have seen all relevant combo CIDs. So
1725 : * ideally, we should error out in this case but in practice, this
1726 : * won't happen. If we are too worried about this then we can add an
1727 : * elog inside ResolveCminCmaxDuringDecoding.
1728 : *
1729 : * XXX For the streaming case, we can track the largest combo CID
1730 : * assigned, and error out based on this (when unable to resolve combo
1731 : * CID below that observed maximum value).
1732 : */
1733 552 : if (!resolved || cmax == InvalidCommandId)
1734 22 : return true;
1735 :
1736 530 : if (cmax >= snapshot->curcid)
1737 170 : return true; /* deleted after scan started */
1738 : else
1739 360 : return false; /* deleted before scan started */
1740 : }
1741 : /* below xmin horizon, normal transaction state is valid */
1742 7390 : else if (TransactionIdPrecedes(xmax, snapshot->xmin))
1743 : {
1744 : Assert(!(tuple->t_infomask & HEAP_XMAX_COMMITTED &&
1745 : !TransactionIdDidCommit(xmax)));
1746 :
1747 : /* check hint bit first */
1748 4364 : if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
1749 4214 : return false;
1750 :
1751 : /* check clog */
1752 150 : return !TransactionIdDidCommit(xmax);
1753 : }
1754 : /* above xmax horizon, we cannot possibly see the deleting transaction */
1755 3026 : else if (TransactionIdFollowsOrEquals(xmax, snapshot->xmax))
1756 454 : return true;
1757 : /* xmax is between [xmin, xmax), check known committed array */
1758 2572 : else if (TransactionIdInArray(xmax, snapshot->xip, snapshot->xcnt))
1759 2572 : return false;
1760 : /* xmax is between [xmin, xmax), but known not to have committed yet */
1761 : else
1762 0 : return true;
1763 : }
1764 :
1765 : /*
1766 : * HeapTupleSatisfiesVisibility
1767 : * True iff heap tuple satisfies a time qual.
1768 : *
1769 : * Notes:
1770 : * Assumes heap tuple is valid, and buffer at least share locked.
1771 : *
1772 : * Hint bits in the HeapTuple's t_infomask may be updated as a side effect;
1773 : * if so, the indicated buffer is marked dirty.
1774 : */
1775 : bool
1776 184123972 : HeapTupleSatisfiesVisibility(HeapTuple htup, Snapshot snapshot, Buffer buffer)
1777 : {
1778 184123972 : switch (snapshot->snapshot_type)
1779 : {
1780 155582036 : case SNAPSHOT_MVCC:
1781 155582036 : return HeapTupleSatisfiesMVCC(htup, snapshot, buffer);
1782 5106 : case SNAPSHOT_SELF:
1783 5106 : return HeapTupleSatisfiesSelf(htup, snapshot, buffer);
1784 15797462 : case SNAPSHOT_ANY:
1785 15797462 : return HeapTupleSatisfiesAny(htup, snapshot, buffer);
1786 155928 : case SNAPSHOT_TOAST:
1787 155928 : return HeapTupleSatisfiesToast(htup, snapshot, buffer);
1788 11831656 : case SNAPSHOT_DIRTY:
1789 11831656 : return HeapTupleSatisfiesDirty(htup, snapshot, buffer);
1790 61918 : case SNAPSHOT_HISTORIC_MVCC:
1791 61918 : return HeapTupleSatisfiesHistoricMVCC(htup, snapshot, buffer);
1792 689866 : case SNAPSHOT_NON_VACUUMABLE:
1793 689866 : return HeapTupleSatisfiesNonVacuumable(htup, snapshot, buffer);
1794 : }
1795 :
1796 0 : return false; /* keep compiler quiet */
1797 : }
|