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