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