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