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