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