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-2024, 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 18674006 : SetHintBits(HeapTupleHeader tuple, Buffer buffer,
115 : uint16 infomask, TransactionId xid)
116 : {
117 18674006 : if (TransactionIdIsValid(xid))
118 : {
119 : /* NB: xid must be known committed here! */
120 18422016 : XLogRecPtr commitLSN = TransactionIdGetCommitLSN(xid);
121 :
122 18914480 : if (BufferIsPermanent(buffer) && XLogNeedsFlush(commitLSN) &&
123 492464 : BufferGetLSNAtomic(buffer) < commitLSN)
124 : {
125 : /* not flushed and no LSN interlock, so don't set hint */
126 411508 : return;
127 : }
128 : }
129 :
130 18262498 : tuple->t_infomask |= infomask;
131 18262498 : 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 352 : HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
142 : uint16 infomask, TransactionId xid)
143 : {
144 352 : SetHintBits(tuple, buffer, infomask, xid);
145 352 : }
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 5104 : HeapTupleSatisfiesSelf(HeapTuple htup, Snapshot snapshot, Buffer buffer)
171 : {
172 5104 : HeapTupleHeader tuple = htup->t_data;
173 :
174 : Assert(ItemPointerIsValid(&htup->t_self));
175 : Assert(htup->t_tableOid != InvalidOid);
176 :
177 5104 : if (!HeapTupleHeaderXminCommitted(tuple))
178 : {
179 5002 : if (HeapTupleHeaderXminInvalid(tuple))
180 0 : return false;
181 :
182 : /* Used by pre-9.0 binary upgrades */
183 5002 : 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 5002 : 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 5002 : else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
222 : {
223 5002 : if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
224 4880 : 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 15645366 : HeapTupleSatisfiesAny(HeapTuple htup, Snapshot snapshot, Buffer buffer)
341 : {
342 15645366 : 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 151618 : HeapTupleSatisfiesToast(HeapTuple htup, Snapshot snapshot,
363 : Buffer buffer)
364 : {
365 151618 : HeapTupleHeader tuple = htup->t_data;
366 :
367 : Assert(ItemPointerIsValid(&htup->t_self));
368 : Assert(htup->t_tableOid != InvalidOid);
369 :
370 151618 : if (!HeapTupleHeaderXminCommitted(tuple))
371 : {
372 119432 : if (HeapTupleHeaderXminInvalid(tuple))
373 0 : return false;
374 :
375 : /* Used by pre-9.0 binary upgrades */
376 119432 : 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 119432 : 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 119432 : else if (!TransactionIdIsValid(HeapTupleHeaderGetXmin(tuple)))
421 0 : return false;
422 : }
423 :
424 : /* otherwise assume the tuple is valid for TOAST. */
425 151618 : 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 3878220 : HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid,
459 : Buffer buffer)
460 : {
461 3878220 : HeapTupleHeader tuple = htup->t_data;
462 :
463 : Assert(ItemPointerIsValid(&htup->t_self));
464 : Assert(htup->t_tableOid != InvalidOid);
465 :
466 3878220 : if (!HeapTupleHeaderXminCommitted(tuple))
467 : {
468 472904 : if (HeapTupleHeaderXminInvalid(tuple))
469 0 : return TM_Invisible;
470 :
471 : /* Used by pre-9.0 binary upgrades */
472 472904 : 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 472904 : 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 472904 : else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
511 : {
512 464100 : if (HeapTupleHeaderGetCmin(tuple) >= curcid)
513 24 : return TM_Invisible; /* inserted after scan started */
514 :
515 464076 : if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
516 383660 : return TM_Ok;
517 :
518 80416 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
519 : {
520 : TransactionId xmax;
521 :
522 80396 : 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 80396 : 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 80334 : if (!TransactionIdIsInProgress(xmax))
545 0 : return TM_Ok;
546 80334 : 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 8804 : else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
589 0 : return TM_Invisible;
590 8804 : else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
591 8804 : 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 3414120 : if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
605 3317420 : return TM_Ok;
606 :
607 96700 : if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
608 : {
609 292 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
610 0 : return TM_Ok;
611 292 : if (!ItemPointerEquals(&htup->t_self, &tuple->t_ctid))
612 292 : return TM_Updated; /* updated by other */
613 : else
614 0 : return TM_Deleted; /* deleted by other */
615 : }
616 :
617 96408 : if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
618 : {
619 : TransactionId xmax;
620 :
621 1214 : if (HEAP_LOCKED_UPGRADED(tuple->t_infomask))
622 0 : return TM_Ok;
623 :
624 1214 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
625 : {
626 1140 : if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), true))
627 212 : return TM_BeingModified;
628 :
629 928 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
630 928 : 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 95194 : if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
685 : {
686 85782 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
687 85630 : 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 9412 : if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
695 2480 : return TM_BeingModified;
696 :
697 6932 : if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
698 : {
699 : /* it must have aborted or crashed */
700 396 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
701 : InvalidTransactionId);
702 396 : return TM_Ok;
703 : }
704 :
705 : /* xmax transaction committed */
706 :
707 6536 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
708 : {
709 6404 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
710 : InvalidTransactionId);
711 6404 : return TM_Ok;
712 : }
713 :
714 132 : SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
715 : HeapTupleHeaderGetRawXmax(tuple));
716 132 : if (!ItemPointerEquals(&htup->t_self, &tuple->t_ctid))
717 132 : 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 11788280 : HeapTupleSatisfiesDirty(HeapTuple htup, Snapshot snapshot,
744 : Buffer buffer)
745 : {
746 11788280 : HeapTupleHeader tuple = htup->t_data;
747 :
748 : Assert(ItemPointerIsValid(&htup->t_self));
749 : Assert(htup->t_tableOid != InvalidOid);
750 :
751 11788280 : snapshot->xmin = snapshot->xmax = InvalidTransactionId;
752 11788280 : snapshot->speculativeToken = 0;
753 :
754 11788280 : if (!HeapTupleHeaderXminCommitted(tuple))
755 : {
756 11161262 : if (HeapTupleHeaderXminInvalid(tuple))
757 594 : return false;
758 :
759 : /* Used by pre-9.0 binary upgrades */
760 11160668 : 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 11160668 : 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 11160668 : else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
799 : {
800 11109958 : if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
801 61986 : return true;
802 :
803 11047972 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
804 10080 : return true;
805 :
806 11037892 : 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 11037860 : 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 11037860 : return false;
831 : }
832 50710 : 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 96 : if (HeapTupleHeaderIsSpeculative(tuple))
841 : {
842 4 : snapshot->speculativeToken =
843 4 : HeapTupleHeaderGetSpeculativeToken(tuple);
844 :
845 : Assert(snapshot->speculativeToken != 0);
846 : }
847 :
848 96 : snapshot->xmin = HeapTupleHeaderGetRawXmin(tuple);
849 : /* XXX shouldn't we fall through to look at xmax? */
850 96 : return true; /* in insertion by other */
851 : }
852 50614 : else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
853 49736 : SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
854 : HeapTupleHeaderGetRawXmin(tuple));
855 : else
856 : {
857 : /* it must have aborted or crashed */
858 878 : SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
859 : InvalidTransactionId);
860 878 : return false;
861 : }
862 : }
863 :
864 : /* by here, the inserting transaction has committed */
865 :
866 676754 : if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
867 264806 : return true;
868 :
869 411948 : if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
870 : {
871 132608 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
872 0 : return true;
873 132608 : return false; /* updated by other */
874 : }
875 :
876 279340 : 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 279278 : if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
902 : {
903 204740 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
904 44 : return true;
905 204696 : return false;
906 : }
907 :
908 74538 : 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 74510 : 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 74486 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
926 : {
927 26978 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
928 : InvalidTransactionId);
929 26978 : return true;
930 : }
931 :
932 47508 : SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
933 : HeapTupleHeaderGetRawXmax(tuple));
934 47508 : 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 143383408 : HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot,
961 : Buffer buffer)
962 : {
963 143383408 : HeapTupleHeader tuple = htup->t_data;
964 :
965 : Assert(ItemPointerIsValid(&htup->t_self));
966 : Assert(htup->t_tableOid != InvalidOid);
967 :
968 143383408 : if (!HeapTupleHeaderXminCommitted(tuple))
969 : {
970 28310754 : if (HeapTupleHeaderXminInvalid(tuple))
971 386230 : return false;
972 :
973 : /* Used by pre-9.0 binary upgrades */
974 27924524 : if (tuple->t_infomask & HEAP_MOVED_OFF)
975 : {
976 0 : TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
977 :
978 0 : if (TransactionIdIsCurrentTransactionId(xvac))
979 0 : return false;
980 0 : if (!XidInMVCCSnapshot(xvac, snapshot))
981 : {
982 0 : if (TransactionIdDidCommit(xvac))
983 : {
984 0 : SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
985 : InvalidTransactionId);
986 0 : return false;
987 : }
988 0 : SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
989 : InvalidTransactionId);
990 : }
991 : }
992 : /* Used by pre-9.0 binary upgrades */
993 27924524 : else if (tuple->t_infomask & HEAP_MOVED_IN)
994 : {
995 0 : TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
996 :
997 0 : if (!TransactionIdIsCurrentTransactionId(xvac))
998 : {
999 0 : if (XidInMVCCSnapshot(xvac, snapshot))
1000 0 : return false;
1001 0 : if (TransactionIdDidCommit(xvac))
1002 0 : SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
1003 : InvalidTransactionId);
1004 : else
1005 : {
1006 0 : SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
1007 : InvalidTransactionId);
1008 0 : return false;
1009 : }
1010 : }
1011 : }
1012 27924524 : else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
1013 : {
1014 20274214 : if (HeapTupleHeaderGetCmin(tuple) >= snapshot->curcid)
1015 12674 : return false; /* inserted after scan started */
1016 :
1017 20261540 : if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
1018 14411972 : return true;
1019 :
1020 5849568 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
1021 4300 : return true;
1022 :
1023 5845268 : if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1024 : {
1025 : TransactionId xmax;
1026 :
1027 14 : xmax = HeapTupleGetUpdateXid(tuple);
1028 :
1029 : /* not LOCKED_ONLY, so it has to have an xmax */
1030 : Assert(TransactionIdIsValid(xmax));
1031 :
1032 : /* updating subtransaction must have aborted */
1033 14 : if (!TransactionIdIsCurrentTransactionId(xmax))
1034 14 : return true;
1035 0 : else if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
1036 0 : return true; /* updated after scan started */
1037 : else
1038 0 : return false; /* updated before scan started */
1039 : }
1040 :
1041 5845254 : if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
1042 : {
1043 : /* deleting subtransaction must have aborted */
1044 22 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
1045 : InvalidTransactionId);
1046 22 : return true;
1047 : }
1048 :
1049 5845232 : if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
1050 1390 : return true; /* deleted after scan started */
1051 : else
1052 5843842 : return false; /* deleted before scan started */
1053 : }
1054 7650310 : else if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
1055 18138 : return false;
1056 7632172 : else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
1057 7524322 : SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
1058 : HeapTupleHeaderGetRawXmin(tuple));
1059 : else
1060 : {
1061 : /* it must have aborted or crashed */
1062 107850 : SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
1063 : InvalidTransactionId);
1064 107850 : return false;
1065 : }
1066 : }
1067 : else
1068 : {
1069 : /* xmin is committed, but maybe not according to our snapshot */
1070 224971206 : if (!HeapTupleHeaderXminFrozen(tuple) &&
1071 109898552 : XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
1072 1666 : return false; /* treat as still in progress */
1073 : }
1074 :
1075 : /* by here, the inserting transaction has committed */
1076 :
1077 122595310 : if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
1078 113600954 : return true;
1079 :
1080 8994356 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
1081 85886 : return true;
1082 :
1083 8908470 : if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1084 : {
1085 : TransactionId xmax;
1086 :
1087 : /* already checked above */
1088 : Assert(!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask));
1089 :
1090 352 : xmax = HeapTupleGetUpdateXid(tuple);
1091 :
1092 : /* not LOCKED_ONLY, so it has to have an xmax */
1093 : Assert(TransactionIdIsValid(xmax));
1094 :
1095 352 : if (TransactionIdIsCurrentTransactionId(xmax))
1096 : {
1097 46 : if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
1098 0 : return true; /* deleted after scan started */
1099 : else
1100 46 : return false; /* deleted before scan started */
1101 : }
1102 306 : if (XidInMVCCSnapshot(xmax, snapshot))
1103 36 : return true;
1104 270 : if (TransactionIdDidCommit(xmax))
1105 260 : return false; /* updating transaction committed */
1106 : /* it must have aborted or crashed */
1107 10 : return true;
1108 : }
1109 :
1110 8908118 : if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
1111 : {
1112 915086 : if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
1113 : {
1114 220660 : if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
1115 2014 : return true; /* deleted after scan started */
1116 : else
1117 218646 : return false; /* deleted before scan started */
1118 : }
1119 :
1120 694426 : if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
1121 12286 : return true;
1122 :
1123 682140 : if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
1124 : {
1125 : /* it must have aborted or crashed */
1126 15106 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
1127 : InvalidTransactionId);
1128 15106 : return true;
1129 : }
1130 :
1131 : /* xmax transaction committed */
1132 667034 : SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
1133 : HeapTupleHeaderGetRawXmax(tuple));
1134 : }
1135 : else
1136 : {
1137 : /* xmax is committed, but maybe not according to our snapshot */
1138 7993032 : if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
1139 394 : return true; /* treat as still in progress */
1140 : }
1141 :
1142 : /* xmax transaction committed */
1143 :
1144 8659672 : return false;
1145 : }
1146 :
1147 :
1148 : /*
1149 : * HeapTupleSatisfiesVacuum
1150 : *
1151 : * Determine the status of tuples for VACUUM purposes. Here, what
1152 : * we mainly want to know is if a tuple is potentially visible to *any*
1153 : * running transaction. If so, it can't be removed yet by VACUUM.
1154 : *
1155 : * OldestXmin is a cutoff XID (obtained from
1156 : * GetOldestNonRemovableTransactionId()). Tuples deleted by XIDs >=
1157 : * OldestXmin are deemed "recently dead"; they might still be visible to some
1158 : * open transaction, so we can't remove them, even if we see that the deleting
1159 : * transaction has committed.
1160 : */
1161 : HTSV_Result
1162 25268346 : HeapTupleSatisfiesVacuum(HeapTuple htup, TransactionId OldestXmin,
1163 : Buffer buffer)
1164 : {
1165 25268346 : TransactionId dead_after = InvalidTransactionId;
1166 : HTSV_Result res;
1167 :
1168 25268346 : res = HeapTupleSatisfiesVacuumHorizon(htup, buffer, &dead_after);
1169 :
1170 25268346 : if (res == HEAPTUPLE_RECENTLY_DEAD)
1171 : {
1172 : Assert(TransactionIdIsValid(dead_after));
1173 :
1174 479246 : if (TransactionIdPrecedes(dead_after, OldestXmin))
1175 45058 : res = HEAPTUPLE_DEAD;
1176 : }
1177 : else
1178 : Assert(!TransactionIdIsValid(dead_after));
1179 :
1180 25268346 : return res;
1181 : }
1182 :
1183 : /*
1184 : * Work horse for HeapTupleSatisfiesVacuum and similar routines.
1185 : *
1186 : * In contrast to HeapTupleSatisfiesVacuum this routine, when encountering a
1187 : * tuple that could still be visible to some backend, stores the xid that
1188 : * needs to be compared with the horizon in *dead_after, and returns
1189 : * HEAPTUPLE_RECENTLY_DEAD. The caller then can perform the comparison with
1190 : * the horizon. This is e.g. useful when comparing with different horizons.
1191 : *
1192 : * Note: HEAPTUPLE_DEAD can still be returned here, e.g. if the inserting
1193 : * transaction aborted.
1194 : */
1195 : HTSV_Result
1196 50564852 : HeapTupleSatisfiesVacuumHorizon(HeapTuple htup, Buffer buffer, TransactionId *dead_after)
1197 : {
1198 50564852 : HeapTupleHeader tuple = htup->t_data;
1199 :
1200 : Assert(ItemPointerIsValid(&htup->t_self));
1201 : Assert(htup->t_tableOid != InvalidOid);
1202 : Assert(dead_after != NULL);
1203 :
1204 50564852 : *dead_after = InvalidTransactionId;
1205 :
1206 : /*
1207 : * Has inserting transaction committed?
1208 : *
1209 : * If the inserting transaction aborted, then the tuple was never visible
1210 : * to any other transaction, so we can delete it immediately.
1211 : */
1212 50564852 : if (!HeapTupleHeaderXminCommitted(tuple))
1213 : {
1214 11790784 : if (HeapTupleHeaderXminInvalid(tuple))
1215 22824 : return HEAPTUPLE_DEAD;
1216 : /* Used by pre-9.0 binary upgrades */
1217 11767960 : else if (tuple->t_infomask & HEAP_MOVED_OFF)
1218 : {
1219 0 : TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
1220 :
1221 0 : if (TransactionIdIsCurrentTransactionId(xvac))
1222 0 : return HEAPTUPLE_DELETE_IN_PROGRESS;
1223 0 : if (TransactionIdIsInProgress(xvac))
1224 0 : return HEAPTUPLE_DELETE_IN_PROGRESS;
1225 0 : if (TransactionIdDidCommit(xvac))
1226 : {
1227 0 : SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
1228 : InvalidTransactionId);
1229 0 : return HEAPTUPLE_DEAD;
1230 : }
1231 0 : SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
1232 : InvalidTransactionId);
1233 : }
1234 : /* Used by pre-9.0 binary upgrades */
1235 11767960 : else if (tuple->t_infomask & HEAP_MOVED_IN)
1236 : {
1237 0 : TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
1238 :
1239 0 : if (TransactionIdIsCurrentTransactionId(xvac))
1240 0 : return HEAPTUPLE_INSERT_IN_PROGRESS;
1241 0 : if (TransactionIdIsInProgress(xvac))
1242 0 : return HEAPTUPLE_INSERT_IN_PROGRESS;
1243 0 : if (TransactionIdDidCommit(xvac))
1244 0 : SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
1245 : InvalidTransactionId);
1246 : else
1247 : {
1248 0 : SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
1249 : InvalidTransactionId);
1250 0 : return HEAPTUPLE_DEAD;
1251 : }
1252 : }
1253 11767960 : else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
1254 : {
1255 4093692 : if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
1256 4022096 : return HEAPTUPLE_INSERT_IN_PROGRESS;
1257 : /* only locked? run infomask-only check first, for performance */
1258 126824 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask) ||
1259 55228 : HeapTupleHeaderIsOnlyLocked(tuple))
1260 16368 : return HEAPTUPLE_INSERT_IN_PROGRESS;
1261 : /* inserted and then deleted by same xact */
1262 55228 : if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetUpdateXid(tuple)))
1263 55222 : return HEAPTUPLE_DELETE_IN_PROGRESS;
1264 : /* deleting subtransaction must have aborted */
1265 6 : return HEAPTUPLE_INSERT_IN_PROGRESS;
1266 : }
1267 7674268 : else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
1268 : {
1269 : /*
1270 : * It'd be possible to discern between INSERT/DELETE in progress
1271 : * here by looking at xmax - but that doesn't seem beneficial for
1272 : * the majority of callers and even detrimental for some. We'd
1273 : * rather have callers look at/wait for xmin than xmax. It's
1274 : * always correct to return INSERT_IN_PROGRESS because that's
1275 : * what's happening from the view of other backends.
1276 : */
1277 5242 : return HEAPTUPLE_INSERT_IN_PROGRESS;
1278 : }
1279 7669026 : else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
1280 7604990 : SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
1281 : HeapTupleHeaderGetRawXmin(tuple));
1282 : else
1283 : {
1284 : /*
1285 : * Not in Progress, Not Committed, so either Aborted or crashed
1286 : */
1287 64036 : SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
1288 : InvalidTransactionId);
1289 64036 : return HEAPTUPLE_DEAD;
1290 : }
1291 :
1292 : /*
1293 : * At this point the xmin is known committed, but we might not have
1294 : * been able to set the hint bit yet; so we can no longer Assert that
1295 : * it's set.
1296 : */
1297 : }
1298 :
1299 : /*
1300 : * Okay, the inserter committed, so it was good at some point. Now what
1301 : * about the deleting transaction?
1302 : */
1303 46379058 : if (tuple->t_infomask & HEAP_XMAX_INVALID)
1304 37737978 : return HEAPTUPLE_LIVE;
1305 :
1306 8641080 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
1307 : {
1308 : /*
1309 : * "Deleting" xact really only locked it, so the tuple is live in any
1310 : * case. However, we should make sure that either XMAX_COMMITTED or
1311 : * XMAX_INVALID gets set once the xact is gone, to reduce the costs of
1312 : * examining the tuple for future xacts.
1313 : */
1314 26908 : if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
1315 : {
1316 26908 : if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1317 : {
1318 : /*
1319 : * If it's a pre-pg_upgrade tuple, the multixact cannot
1320 : * possibly be running; otherwise have to check.
1321 : */
1322 868 : if (!HEAP_LOCKED_UPGRADED(tuple->t_infomask) &&
1323 434 : MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple),
1324 : true))
1325 4 : return HEAPTUPLE_LIVE;
1326 430 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
1327 : }
1328 : else
1329 : {
1330 26474 : if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
1331 0 : return HEAPTUPLE_LIVE;
1332 26474 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
1333 : InvalidTransactionId);
1334 : }
1335 : }
1336 :
1337 : /*
1338 : * We don't really care whether xmax did commit, abort or crash. We
1339 : * know that xmax did lock the tuple, but it did not and will never
1340 : * actually update it.
1341 : */
1342 :
1343 26904 : return HEAPTUPLE_LIVE;
1344 : }
1345 :
1346 8614172 : if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1347 : {
1348 10 : TransactionId xmax = HeapTupleGetUpdateXid(tuple);
1349 :
1350 : /* already checked above */
1351 : Assert(!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask));
1352 :
1353 : /* not LOCKED_ONLY, so it has to have an xmax */
1354 : Assert(TransactionIdIsValid(xmax));
1355 :
1356 10 : if (TransactionIdIsInProgress(xmax))
1357 0 : return HEAPTUPLE_DELETE_IN_PROGRESS;
1358 10 : else if (TransactionIdDidCommit(xmax))
1359 : {
1360 : /*
1361 : * The multixact might still be running due to lockers. Need to
1362 : * allow for pruning if below the xid horizon regardless --
1363 : * otherwise we could end up with a tuple where the updater has to
1364 : * be removed due to the horizon, but is not pruned away. It's
1365 : * not a problem to prune that tuple, because any remaining
1366 : * lockers will also be present in newer tuple versions.
1367 : */
1368 10 : *dead_after = xmax;
1369 10 : return HEAPTUPLE_RECENTLY_DEAD;
1370 : }
1371 0 : else if (!MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
1372 : {
1373 : /*
1374 : * Not in Progress, Not Committed, so either Aborted or crashed.
1375 : * Mark the Xmax as invalid.
1376 : */
1377 0 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
1378 : }
1379 :
1380 0 : return HEAPTUPLE_LIVE;
1381 : }
1382 :
1383 8614162 : if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
1384 : {
1385 7256396 : if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
1386 4734822 : return HEAPTUPLE_DELETE_IN_PROGRESS;
1387 2521574 : else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
1388 2519256 : SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
1389 : HeapTupleHeaderGetRawXmax(tuple));
1390 : else
1391 : {
1392 : /*
1393 : * Not in Progress, Not Committed, so either Aborted or crashed
1394 : */
1395 2318 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
1396 : InvalidTransactionId);
1397 2318 : return HEAPTUPLE_LIVE;
1398 : }
1399 :
1400 : /*
1401 : * At this point the xmax is known committed, but we might not have
1402 : * been able to set the hint bit yet; so we can no longer Assert that
1403 : * it's set.
1404 : */
1405 : }
1406 :
1407 : /*
1408 : * Deleter committed, allow caller to check if it was recent enough that
1409 : * some open transactions could still see the tuple.
1410 : */
1411 3877022 : *dead_after = HeapTupleHeaderGetRawXmax(tuple);
1412 3877022 : return HEAPTUPLE_RECENTLY_DEAD;
1413 : }
1414 :
1415 :
1416 : /*
1417 : * HeapTupleSatisfiesNonVacuumable
1418 : *
1419 : * True if tuple might be visible to some transaction; false if it's
1420 : * surely dead to everyone, ie, vacuumable.
1421 : *
1422 : * See SNAPSHOT_NON_VACUUMABLE's definition for the intended behaviour.
1423 : *
1424 : * This is an interface to HeapTupleSatisfiesVacuum that's callable via
1425 : * HeapTupleSatisfiesSnapshot, so it can be used through a Snapshot.
1426 : * snapshot->vistest must have been set up with the horizon to use.
1427 : */
1428 : static bool
1429 650934 : HeapTupleSatisfiesNonVacuumable(HeapTuple htup, Snapshot snapshot,
1430 : Buffer buffer)
1431 : {
1432 650934 : TransactionId dead_after = InvalidTransactionId;
1433 : HTSV_Result res;
1434 :
1435 650934 : res = HeapTupleSatisfiesVacuumHorizon(htup, buffer, &dead_after);
1436 :
1437 650934 : if (res == HEAPTUPLE_RECENTLY_DEAD)
1438 : {
1439 : Assert(TransactionIdIsValid(dead_after));
1440 :
1441 144570 : if (GlobalVisTestIsRemovableXid(snapshot->vistest, dead_after))
1442 120666 : res = HEAPTUPLE_DEAD;
1443 : }
1444 : else
1445 : Assert(!TransactionIdIsValid(dead_after));
1446 :
1447 650934 : return res != HEAPTUPLE_DEAD;
1448 : }
1449 :
1450 :
1451 : /*
1452 : * HeapTupleIsSurelyDead
1453 : *
1454 : * Cheaply determine whether a tuple is surely dead to all onlookers.
1455 : * We sometimes use this in lieu of HeapTupleSatisfiesVacuum when the
1456 : * tuple has just been tested by another visibility routine (usually
1457 : * HeapTupleSatisfiesMVCC) and, therefore, any hint bits that can be set
1458 : * should already be set. We assume that if no hint bits are set, the xmin
1459 : * or xmax transaction is still running. This is therefore faster than
1460 : * HeapTupleSatisfiesVacuum, because we consult neither procarray nor CLOG.
1461 : * It's okay to return false when in doubt, but we must return true only
1462 : * if the tuple is removable.
1463 : */
1464 : bool
1465 12672052 : HeapTupleIsSurelyDead(HeapTuple htup, GlobalVisState *vistest)
1466 : {
1467 12672052 : HeapTupleHeader tuple = htup->t_data;
1468 :
1469 : Assert(ItemPointerIsValid(&htup->t_self));
1470 : Assert(htup->t_tableOid != InvalidOid);
1471 :
1472 : /*
1473 : * If the inserting transaction is marked invalid, then it aborted, and
1474 : * the tuple is definitely dead. If it's marked neither committed nor
1475 : * invalid, then we assume it's still alive (since the presumption is that
1476 : * all relevant hint bits were just set moments ago).
1477 : */
1478 12672052 : if (!HeapTupleHeaderXminCommitted(tuple))
1479 11257030 : return HeapTupleHeaderXminInvalid(tuple);
1480 :
1481 : /*
1482 : * If the inserting transaction committed, but any deleting transaction
1483 : * aborted, the tuple is still alive.
1484 : */
1485 1415022 : if (tuple->t_infomask & HEAP_XMAX_INVALID)
1486 16 : return false;
1487 :
1488 : /*
1489 : * If the XMAX is just a lock, the tuple is still alive.
1490 : */
1491 1415006 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
1492 0 : return false;
1493 :
1494 : /*
1495 : * If the Xmax is a MultiXact, it might be dead or alive, but we cannot
1496 : * know without checking pg_multixact.
1497 : */
1498 1415006 : if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1499 104 : return false;
1500 :
1501 : /* If deleter isn't known to have committed, assume it's still running. */
1502 1414902 : if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
1503 357104 : return false;
1504 :
1505 : /* Deleter committed, so tuple is dead if the XID is old enough. */
1506 1057798 : return GlobalVisTestIsRemovableXid(vistest,
1507 : HeapTupleHeaderGetRawXmax(tuple));
1508 : }
1509 :
1510 : /*
1511 : * Is the tuple really only locked? That is, is it not updated?
1512 : *
1513 : * It's easy to check just infomask bits if the locker is not a multi; but
1514 : * otherwise we need to verify that the updating transaction has not aborted.
1515 : *
1516 : * This function is here because it follows the same visibility rules laid out
1517 : * at the top of this file.
1518 : */
1519 : bool
1520 95452 : HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple)
1521 : {
1522 : TransactionId xmax;
1523 :
1524 : /* if there's no valid Xmax, then there's obviously no update either */
1525 95452 : if (tuple->t_infomask & HEAP_XMAX_INVALID)
1526 0 : return true;
1527 :
1528 95452 : if (tuple->t_infomask & HEAP_XMAX_LOCK_ONLY)
1529 1058 : return true;
1530 :
1531 : /* invalid xmax means no update */
1532 94394 : if (!TransactionIdIsValid(HeapTupleHeaderGetRawXmax(tuple)))
1533 0 : return true;
1534 :
1535 : /*
1536 : * if HEAP_XMAX_LOCK_ONLY is not set and not a multi, then this must
1537 : * necessarily have been updated
1538 : */
1539 94394 : if (!(tuple->t_infomask & HEAP_XMAX_IS_MULTI))
1540 94342 : return false;
1541 :
1542 : /* ... but if it's a multi, then perhaps the updating Xid aborted. */
1543 52 : xmax = HeapTupleGetUpdateXid(tuple);
1544 :
1545 : /* not LOCKED_ONLY, so it has to have an xmax */
1546 : Assert(TransactionIdIsValid(xmax));
1547 :
1548 52 : if (TransactionIdIsCurrentTransactionId(xmax))
1549 0 : return false;
1550 52 : if (TransactionIdIsInProgress(xmax))
1551 8 : return false;
1552 44 : if (TransactionIdDidCommit(xmax))
1553 22 : return false;
1554 :
1555 : /*
1556 : * not current, not in progress, not committed -- must have aborted or
1557 : * crashed
1558 : */
1559 22 : return true;
1560 : }
1561 :
1562 : /*
1563 : * check whether the transaction id 'xid' is in the pre-sorted array 'xip'.
1564 : */
1565 : static bool
1566 76088 : TransactionIdInArray(TransactionId xid, TransactionId *xip, Size num)
1567 : {
1568 108574 : return num > 0 &&
1569 32486 : bsearch(&xid, xip, num, sizeof(TransactionId), xidComparator) != NULL;
1570 : }
1571 :
1572 : /*
1573 : * See the comments for HeapTupleSatisfiesMVCC for the semantics this function
1574 : * obeys.
1575 : *
1576 : * Only usable on tuples from catalog tables!
1577 : *
1578 : * We don't need to support HEAP_MOVED_(IN|OFF) for now because we only support
1579 : * reading catalog pages which couldn't have been created in an older version.
1580 : *
1581 : * We don't set any hint bits in here as it seems unlikely to be beneficial as
1582 : * those should already be set by normal access and it seems to be too
1583 : * dangerous to do so as the semantics of doing so during timetravel are more
1584 : * complicated than when dealing "only" with the present.
1585 : */
1586 : static bool
1587 60830 : HeapTupleSatisfiesHistoricMVCC(HeapTuple htup, Snapshot snapshot,
1588 : Buffer buffer)
1589 : {
1590 60830 : HeapTupleHeader tuple = htup->t_data;
1591 60830 : TransactionId xmin = HeapTupleHeaderGetXmin(tuple);
1592 60830 : TransactionId xmax = HeapTupleHeaderGetRawXmax(tuple);
1593 :
1594 : Assert(ItemPointerIsValid(&htup->t_self));
1595 : Assert(htup->t_tableOid != InvalidOid);
1596 :
1597 : /* inserting transaction aborted */
1598 60830 : if (HeapTupleHeaderXminInvalid(tuple))
1599 : {
1600 : Assert(!TransactionIdDidCommit(xmin));
1601 150 : return false;
1602 : }
1603 : /* check if it's one of our txids, toplevel is also in there */
1604 60680 : else if (TransactionIdInArray(xmin, snapshot->subxip, snapshot->subxcnt))
1605 : {
1606 : bool resolved;
1607 894 : CommandId cmin = HeapTupleHeaderGetRawCommandId(tuple);
1608 894 : CommandId cmax = InvalidCommandId;
1609 :
1610 : /*
1611 : * another transaction might have (tried to) delete this tuple or
1612 : * cmin/cmax was stored in a combo CID. So we need to lookup the
1613 : * actual values externally.
1614 : */
1615 894 : resolved = ResolveCminCmaxDuringDecoding(HistoricSnapshotGetTupleCids(), snapshot,
1616 : htup, buffer,
1617 : &cmin, &cmax);
1618 :
1619 : /*
1620 : * If we haven't resolved the combo CID to cmin/cmax, that means we
1621 : * have not decoded the combo CID yet. That means the cmin is
1622 : * definitely in the future, and we're not supposed to see the tuple
1623 : * yet.
1624 : *
1625 : * XXX This only applies to decoding of in-progress transactions. In
1626 : * regular logical decoding we only execute this code at commit time,
1627 : * at which point we should have seen all relevant combo CIDs. So
1628 : * ideally, we should error out in this case but in practice, this
1629 : * won't happen. If we are too worried about this then we can add an
1630 : * elog inside ResolveCminCmaxDuringDecoding.
1631 : *
1632 : * XXX For the streaming case, we can track the largest combo CID
1633 : * assigned, and error out based on this (when unable to resolve combo
1634 : * CID below that observed maximum value).
1635 : */
1636 894 : if (!resolved)
1637 98 : return false;
1638 :
1639 : Assert(cmin != InvalidCommandId);
1640 :
1641 894 : if (cmin >= snapshot->curcid)
1642 98 : return false; /* inserted after scan started */
1643 : /* fall through */
1644 : }
1645 : /* committed before our xmin horizon. Do a normal visibility check. */
1646 59786 : else if (TransactionIdPrecedes(xmin, snapshot->xmin))
1647 : {
1648 : Assert(!(HeapTupleHeaderXminCommitted(tuple) &&
1649 : !TransactionIdDidCommit(xmin)));
1650 :
1651 : /* check for hint bit first, consult clog afterwards */
1652 54396 : if (!HeapTupleHeaderXminCommitted(tuple) &&
1653 96 : !TransactionIdDidCommit(xmin))
1654 0 : return false;
1655 : /* fall through */
1656 : }
1657 : /* beyond our xmax horizon, i.e. invisible */
1658 5390 : else if (TransactionIdFollowsOrEquals(xmin, snapshot->xmax))
1659 : {
1660 220 : return false;
1661 : }
1662 : /* check if it's a committed transaction in [xmin, xmax) */
1663 5170 : else if (TransactionIdInArray(xmin, snapshot->xip, snapshot->xcnt))
1664 : {
1665 : /* fall through */
1666 : }
1667 :
1668 : /*
1669 : * none of the above, i.e. between [xmin, xmax) but hasn't committed. I.e.
1670 : * invisible.
1671 : */
1672 : else
1673 : {
1674 0 : return false;
1675 : }
1676 :
1677 : /* at this point we know xmin is visible, go on to check xmax */
1678 :
1679 : /* xid invalid or aborted */
1680 60362 : if (tuple->t_infomask & HEAP_XMAX_INVALID)
1681 50924 : return true;
1682 : /* locked tuples are always visible */
1683 9438 : else if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
1684 1724 : return true;
1685 :
1686 : /*
1687 : * We can see multis here if we're looking at user tables or if somebody
1688 : * SELECT ... FOR SHARE/UPDATE a system table.
1689 : */
1690 7714 : else if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1691 : {
1692 106 : xmax = HeapTupleGetUpdateXid(tuple);
1693 : }
1694 :
1695 : /* check if it's one of our txids, toplevel is also in there */
1696 7714 : if (TransactionIdInArray(xmax, snapshot->subxip, snapshot->subxcnt))
1697 : {
1698 : bool resolved;
1699 : CommandId cmin;
1700 548 : CommandId cmax = HeapTupleHeaderGetRawCommandId(tuple);
1701 :
1702 : /* Lookup actual cmin/cmax values */
1703 548 : resolved = ResolveCminCmaxDuringDecoding(HistoricSnapshotGetTupleCids(), snapshot,
1704 : htup, buffer,
1705 : &cmin, &cmax);
1706 :
1707 : /*
1708 : * If we haven't resolved the combo CID to cmin/cmax, that means we
1709 : * have not decoded the combo CID yet. That means the cmax is
1710 : * definitely in the future, and we're still supposed to see the
1711 : * tuple.
1712 : *
1713 : * XXX This only applies to decoding of in-progress transactions. In
1714 : * regular logical decoding we only execute this code at commit time,
1715 : * at which point we should have seen all relevant combo CIDs. So
1716 : * ideally, we should error out in this case but in practice, this
1717 : * won't happen. If we are too worried about this then we can add an
1718 : * elog inside ResolveCminCmaxDuringDecoding.
1719 : *
1720 : * XXX For the streaming case, we can track the largest combo CID
1721 : * assigned, and error out based on this (when unable to resolve combo
1722 : * CID below that observed maximum value).
1723 : */
1724 548 : if (!resolved || cmax == InvalidCommandId)
1725 22 : return true;
1726 :
1727 526 : if (cmax >= snapshot->curcid)
1728 160 : return true; /* deleted after scan started */
1729 : else
1730 366 : return false; /* deleted before scan started */
1731 : }
1732 : /* below xmin horizon, normal transaction state is valid */
1733 7166 : else if (TransactionIdPrecedes(xmax, snapshot->xmin))
1734 : {
1735 : Assert(!(tuple->t_infomask & HEAP_XMAX_COMMITTED &&
1736 : !TransactionIdDidCommit(xmax)));
1737 :
1738 : /* check hint bit first */
1739 4190 : if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
1740 4040 : return false;
1741 :
1742 : /* check clog */
1743 150 : return !TransactionIdDidCommit(xmax);
1744 : }
1745 : /* above xmax horizon, we cannot possibly see the deleting transaction */
1746 2976 : else if (TransactionIdFollowsOrEquals(xmax, snapshot->xmax))
1747 452 : return true;
1748 : /* xmax is between [xmin, xmax), check known committed array */
1749 2524 : else if (TransactionIdInArray(xmax, snapshot->xip, snapshot->xcnt))
1750 2524 : return false;
1751 : /* xmax is between [xmin, xmax), but known not to have committed yet */
1752 : else
1753 0 : return true;
1754 : }
1755 :
1756 : /*
1757 : * HeapTupleSatisfiesVisibility
1758 : * True iff heap tuple satisfies a time qual.
1759 : *
1760 : * Notes:
1761 : * Assumes heap tuple is valid, and buffer at least share locked.
1762 : *
1763 : * Hint bits in the HeapTuple's t_infomask may be updated as a side effect;
1764 : * if so, the indicated buffer is marked dirty.
1765 : */
1766 : bool
1767 171685540 : HeapTupleSatisfiesVisibility(HeapTuple htup, Snapshot snapshot, Buffer buffer)
1768 : {
1769 171685540 : switch (snapshot->snapshot_type)
1770 : {
1771 143383408 : case SNAPSHOT_MVCC:
1772 143383408 : return HeapTupleSatisfiesMVCC(htup, snapshot, buffer);
1773 5104 : case SNAPSHOT_SELF:
1774 5104 : return HeapTupleSatisfiesSelf(htup, snapshot, buffer);
1775 15645366 : case SNAPSHOT_ANY:
1776 15645366 : return HeapTupleSatisfiesAny(htup, snapshot, buffer);
1777 151618 : case SNAPSHOT_TOAST:
1778 151618 : return HeapTupleSatisfiesToast(htup, snapshot, buffer);
1779 11788280 : case SNAPSHOT_DIRTY:
1780 11788280 : return HeapTupleSatisfiesDirty(htup, snapshot, buffer);
1781 60830 : case SNAPSHOT_HISTORIC_MVCC:
1782 60830 : return HeapTupleSatisfiesHistoricMVCC(htup, snapshot, buffer);
1783 650934 : case SNAPSHOT_NON_VACUUMABLE:
1784 650934 : return HeapTupleSatisfiesNonVacuumable(htup, snapshot, buffer);
1785 : }
1786 :
1787 0 : return false; /* keep compiler quiet */
1788 : }
|