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