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