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
56 andres@anarazel.de 142 :GNC 17816682 : 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 [ + + + + ]: 17816682 : if (state && *state == SHB_DISABLED)
150 : 22 : return;
151 : :
6852 tgl@sss.pgh.pa.us 152 [ + + ]:CBC 17816660 : if (TransactionIdIsValid(xid))
153 : : {
56 andres@anarazel.de 154 [ + + ]:GNC 17652991 : if (BufferIsPermanent(buffer))
155 : : {
156 : : /* NB: xid must be known committed here! */
157 : 16507269 : XLogRecPtr commitLSN = TransactionIdGetCommitLSN(xid);
158 : :
159 [ + + + + ]: 16753724 : if (XLogNeedsFlush(commitLSN) &&
160 : 246455 : BufferGetLSNAtomic(buffer) < commitLSN)
161 : : {
162 : : /* not flushed and no LSN interlock, so don't set hint */
163 : 217543 : 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 [ + + ]: 17599117 : if (!state)
175 : : {
176 : 15815333 : BufferSetHintBits16(&tuple->t_infomask,
177 : 15815333 : tuple->t_infomask | infomask, buffer);
178 : 15815333 : return;
179 : : }
180 : :
181 [ + + ]: 1783784 : if (*state == SHB_INITIAL)
182 : : {
183 [ + + ]: 98503 : if (!BufferBeginSetHintBits(buffer))
184 : : {
185 : 10 : *state = SHB_DISABLED;
3732 andres@anarazel.de 186 :CBC 10 : return;
187 : : }
188 : :
56 andres@anarazel.de 189 :GNC 98493 : *state = SHB_ENABLED;
190 : : }
6852 tgl@sss.pgh.pa.us 191 :CBC 1783774 : tuple->t_infomask |= infomask;
192 : : }
193 : :
194 : : /*
195 : : * Simple wrapper around SetHintBitsExt(), use when operating on a single
196 : : * tuple.
197 : : */
198 : : static inline void
56 andres@anarazel.de 199 :GNC 14643846 : SetHintBits(HeapTupleHeader tuple, Buffer buffer,
200 : : uint16 infomask, TransactionId xid)
201 : : {
202 : 14643846 : SetHintBitsExt(tuple, buffer, infomask, xid, NULL);
6852 tgl@sss.pgh.pa.us 203 :GIC 14643846 : }
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
6839 tgl@sss.pgh.pa.us 212 :CBC 278 : 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 : : */
113 andres@anarazel.de 220 [ - + ]:GNC 278 : Assert(BufferIsLockedByMeInMode(buffer, BUFFER_LOCK_EXCLUSIVE));
221 : :
6839 tgl@sss.pgh.pa.us 222 :CBC 278 : SetHintBits(tuple, buffer, infomask, xid);
223 : 278 : }
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
137 andres@anarazel.de 232 :GNC 39079825 : HeapTupleCleanMoved(HeapTupleHeader tuple, Buffer buffer)
233 : : {
234 : : TransactionId xvac;
235 : :
236 : : /* only used by pre-9.0 binary upgrades */
237 [ + - ]: 39079825 : if (likely(!(tuple->t_infomask & (HEAP_MOVED_OFF | HEAP_MOVED_IN))))
238 : 39079825 : return true;
239 : :
137 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
4670 rhaas@postgresql.org 297 :CBC 605139 : HeapTupleSatisfiesSelf(HeapTuple htup, Snapshot snapshot, Buffer buffer)
298 : : {
299 : 605139 : HeapTupleHeader tuple = htup->t_data;
300 : :
301 [ - + ]: 605139 : Assert(ItemPointerIsValid(&htup->t_self));
302 [ - + ]: 605139 : Assert(htup->t_tableOid != InvalidOid);
303 : :
4517 304 [ + + ]: 605139 : if (!HeapTupleHeaderXminCommitted(tuple))
305 : : {
306 [ - + ]: 605071 : if (HeapTupleHeaderXminInvalid(tuple))
10108 bruce@momjian.us 307 :UBC 0 : return false;
308 : :
137 andres@anarazel.de 309 [ - + ]:GNC 605071 : if (!HeapTupleCleanMoved(tuple, buffer))
137 andres@anarazel.de 310 :UNC 0 : return false;
4517 rhaas@postgresql.org 311 [ + - ]:CBC 605071 : else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
312 : : {
10411 vadim4o@yahoo.com 313 [ + + ]: 605071 : if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
10108 bruce@momjian.us 314 : 604990 : return true;
315 : :
4724 316 [ + + ]: 81 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
7677 tgl@sss.pgh.pa.us 317 : 13 : return true;
318 : :
4850 alvherre@alvh.no-ip. 319 [ - + ]: 68 : if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
320 : : {
321 : : TransactionId xmax;
322 : :
4850 alvherre@alvh.no-ip. 323 :UBC 0 : xmax = HeapTupleGetUpdateXid(tuple);
324 : :
325 : : /* not LOCKED_ONLY, so it has to have an xmax */
4540 326 [ # # ]: 0 : Assert(TransactionIdIsValid(xmax));
327 : :
328 : : /* updating subtransaction must have aborted */
4850 329 [ # # ]: 0 : if (!TransactionIdIsCurrentTransactionId(xmax))
330 : 0 : return true;
331 : : else
332 : 0 : return false;
333 : : }
334 : :
4850 alvherre@alvh.no-ip. 335 [ + + ]:CBC 68 : if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
336 : : {
337 : : /* deleting subtransaction must have aborted */
6839 tgl@sss.pgh.pa.us 338 : 12 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
339 : : InvalidTransactionId);
7978 340 : 12 : return true;
341 : : }
342 : :
10003 vadim4o@yahoo.com 343 : 56 : return false;
344 : : }
4517 rhaas@postgresql.org 345 [ # # ]:UBC 0 : else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
10108 bruce@momjian.us 346 : 0 : return false;
4517 rhaas@postgresql.org 347 [ # # ]: 0 : else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
6839 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);
7668 355 : 0 : return false;
356 : : }
357 : : }
358 : :
359 : : /* by here, the inserting transaction has committed */
360 : :
10411 vadim4o@yahoo.com 361 [ + - ]:CBC 68 : if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
10108 bruce@momjian.us 362 : 68 : return true;
363 : :
10411 vadim4o@yahoo.com 364 [ # # ]:UBC 0 : if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
365 : : {
4850 alvherre@alvh.no-ip. 366 [ # # ]: 0 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
10003 vadim4o@yahoo.com 367 : 0 : return true;
9842 bruce@momjian.us 368 : 0 : return false; /* updated by other */
369 : : }
370 : :
7677 tgl@sss.pgh.pa.us 371 [ # # ]: 0 : if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
372 : : {
373 : : TransactionId xmax;
374 : :
4850 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 */
4540 381 [ # # ]: 0 : Assert(TransactionIdIsValid(xmax));
382 : :
4850 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 */
7677 tgl@sss.pgh.pa.us 390 : 0 : return true;
391 : : }
392 : :
4850 alvherre@alvh.no-ip. 393 [ # # ]: 0 : if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
394 : : {
395 [ # # ]: 0 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
10003 vadim4o@yahoo.com 396 : 0 : return true;
10108 bruce@momjian.us 397 : 0 : return false;
398 : : }
399 : :
4850 alvherre@alvh.no-ip. 400 [ # # ]: 0 : if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
7668 tgl@sss.pgh.pa.us 401 : 0 : return true;
402 : :
4850 alvherre@alvh.no-ip. 403 [ # # ]: 0 : if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
404 : : {
405 : : /* it must have aborted or crashed */
6839 tgl@sss.pgh.pa.us 406 : 0 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
407 : : InvalidTransactionId);
10108 bruce@momjian.us 408 : 0 : return true;
409 : : }
410 : :
411 : : /* xmax transaction committed */
412 : :
4850 alvherre@alvh.no-ip. 413 [ # # ]: 0 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
414 : : {
6839 tgl@sss.pgh.pa.us 415 : 0 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
416 : : InvalidTransactionId);
10003 vadim4o@yahoo.com 417 : 0 : return true;
418 : : }
419 : :
6839 tgl@sss.pgh.pa.us 420 : 0 : SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
421 : : HeapTupleHeaderGetRawXmax(tuple));
10108 bruce@momjian.us 422 : 0 : return false;
423 : : }
424 : :
425 : : /*
426 : : * HeapTupleSatisfiesAny
427 : : * Dummy "satisfies" routine: any tuple satisfies SnapshotAny.
428 : : */
429 : : static bool
4670 rhaas@postgresql.org 430 :CBC 12158881 : HeapTupleSatisfiesAny(HeapTuple htup, Snapshot snapshot, Buffer buffer)
431 : : {
6981 tgl@sss.pgh.pa.us 432 : 12158881 : 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
4670 rhaas@postgresql.org 452 : 96292 : HeapTupleSatisfiesToast(HeapTuple htup, Snapshot snapshot,
453 : : Buffer buffer)
454 : : {
455 : 96292 : HeapTupleHeader tuple = htup->t_data;
456 : :
457 [ - + ]: 96292 : Assert(ItemPointerIsValid(&htup->t_self));
458 [ - + ]: 96292 : Assert(htup->t_tableOid != InvalidOid);
459 : :
4517 460 [ + + ]: 96292 : if (!HeapTupleHeaderXminCommitted(tuple))
461 : : {
462 [ - + ]: 34924 : if (HeapTupleHeaderXminInvalid(tuple))
8875 tgl@sss.pgh.pa.us 463 :UBC 0 : return false;
464 : :
137 andres@anarazel.de 465 [ - + ]:GNC 34924 : if (!HeapTupleCleanMoved(tuple, buffer))
137 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 : : */
4015 andres@anarazel.de 473 [ - + ]:CBC 34924 : else if (!TransactionIdIsValid(HeapTupleHeaderGetXmin(tuple)))
4015 andres@anarazel.de 474 :UBC 0 : return false;
475 : : }
476 : :
477 : : /* otherwise assume the tuple is valid for TOAST. */
8875 tgl@sss.pgh.pa.us 478 :CBC 96292 : 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
4670 rhaas@postgresql.org 511 : 4919187 : HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid,
512 : : Buffer buffer)
513 : : {
514 : 4919187 : HeapTupleHeader tuple = htup->t_data;
515 : :
516 [ - + ]: 4919187 : Assert(ItemPointerIsValid(&htup->t_self));
517 [ - + ]: 4919187 : Assert(htup->t_tableOid != InvalidOid);
518 : :
4517 519 [ + + ]: 4919187 : if (!HeapTupleHeaderXminCommitted(tuple))
520 : : {
521 [ - + ]: 279441 : if (HeapTupleHeaderXminInvalid(tuple))
2600 andres@anarazel.de 522 :UBC 0 : return TM_Invisible;
523 : :
137 andres@anarazel.de 524 [ - + ]:GNC 279441 : else if (!HeapTupleCleanMoved(tuple, buffer))
137 andres@anarazel.de 525 :UNC 0 : return TM_Invisible;
4517 rhaas@postgresql.org 526 [ + + ]:CBC 279441 : else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
527 : : {
8725 bruce@momjian.us 528 [ + + ]: 276413 : if (HeapTupleHeaderGetCmin(tuple) >= curcid)
2600 andres@anarazel.de 529 : 28 : return TM_Invisible; /* inserted after scan started */
530 : :
8644 bruce@momjian.us 531 [ + + ]: 276385 : if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
2600 andres@anarazel.de 532 : 236158 : return TM_Ok;
533 : :
4521 alvherre@alvh.no-ip. 534 [ + + ]: 40227 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
535 : : {
536 : : TransactionId xmax;
537 : :
538 : 40215 : 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 [ + + ]: 40215 : if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
548 : : {
4043 549 [ + - ]: 31 : if (MultiXactIdIsRunning(xmax, true))
2600 andres@anarazel.de 550 : 31 : return TM_BeingModified;
551 : : else
2600 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 : : */
4521 alvherre@alvh.no-ip. 560 [ - + ]:CBC 40184 : if (!TransactionIdIsInProgress(xmax))
2600 andres@anarazel.de 561 :UBC 0 : return TM_Ok;
2600 andres@anarazel.de 562 :CBC 40184 : return TM_BeingModified;
563 : : }
564 : :
4850 alvherre@alvh.no-ip. 565 [ + + ]: 12 : if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
566 : : {
567 : : TransactionId xmax;
568 : :
569 : 8 : xmax = HeapTupleGetUpdateXid(tuple);
570 : :
571 : : /* not LOCKED_ONLY, so it has to have an xmax */
4540 572 [ - + ]: 8 : Assert(TransactionIdIsValid(xmax));
573 : :
574 : : /* deleting subtransaction must have aborted */
4850 575 [ + - ]: 8 : if (!TransactionIdIsCurrentTransactionId(xmax))
576 : : {
4043 577 [ + - ]: 8 : if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple),
578 : : false))
2600 andres@anarazel.de 579 : 8 : return TM_BeingModified;
2600 andres@anarazel.de 580 :UBC 0 : return TM_Ok;
581 : : }
582 : : else
583 : : {
4850 alvherre@alvh.no-ip. 584 [ # # ]: 0 : if (HeapTupleHeaderGetCmax(tuple) >= curcid)
2600 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 : :
4850 alvherre@alvh.no-ip. 591 [ - + ]:CBC 4 : if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
592 : : {
593 : : /* deleting subtransaction must have aborted */
6839 tgl@sss.pgh.pa.us 594 :UBC 0 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
595 : : InvalidTransactionId);
2600 andres@anarazel.de 596 : 0 : return TM_Ok;
597 : : }
598 : :
8725 bruce@momjian.us 599 [ + - ]:CBC 4 : if (HeapTupleHeaderGetCmax(tuple) >= curcid)
2600 andres@anarazel.de 600 : 4 : return TM_SelfModified; /* updated after scan started */
601 : : else
2600 andres@anarazel.de 602 :UBC 0 : return TM_Invisible; /* updated before scan started */
603 : : }
4517 rhaas@postgresql.org 604 [ - + ]:CBC 3028 : else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
2600 andres@anarazel.de 605 :UBC 0 : return TM_Invisible;
4517 rhaas@postgresql.org 606 [ + - ]:CBC 3028 : else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
6839 tgl@sss.pgh.pa.us 607 : 3028 : SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
608 : : HeapTupleHeaderGetRawXmin(tuple));
609 : : else
610 : : {
611 : : /* it must have aborted or crashed */
6839 tgl@sss.pgh.pa.us 612 :UBC 0 : SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
613 : : InvalidTransactionId);
2600 andres@anarazel.de 614 : 0 : return TM_Invisible;
615 : : }
616 : : }
617 : :
618 : : /* by here, the inserting transaction has committed */
619 : :
8644 bruce@momjian.us 620 [ + + ]:CBC 4642774 : if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
2600 andres@anarazel.de 621 : 4112304 : return TM_Ok;
622 : :
8903 tgl@sss.pgh.pa.us 623 [ + + ]: 530470 : if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
624 : : {
4850 alvherre@alvh.no-ip. 625 [ - + ]: 165 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
2600 andres@anarazel.de 626 :UBC 0 : return TM_Ok;
1898 alvherre@alvh.no-ip. 627 [ + - ]:CBC 165 : if (!ItemPointerEquals(&htup->t_self, &tuple->t_ctid))
2600 andres@anarazel.de 628 : 165 : return TM_Updated; /* updated by other */
629 : : else
2600 andres@anarazel.de 630 :UBC 0 : return TM_Deleted; /* deleted by other */
631 : : }
632 : :
7677 tgl@sss.pgh.pa.us 633 [ + + ]:CBC 530305 : if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
634 : : {
635 : : TransactionId xmax;
636 : :
3602 alvherre@alvh.no-ip. 637 [ - + ]: 74011 : if (HEAP_LOCKED_UPGRADED(tuple->t_infomask))
2600 andres@anarazel.de 638 :UBC 0 : return TM_Ok;
639 : :
4850 alvherre@alvh.no-ip. 640 [ + + ]:CBC 74011 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
641 : : {
3602 642 [ + + ]: 71856 : if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), true))
2600 andres@anarazel.de 643 : 71313 : return TM_BeingModified;
644 : :
4850 alvherre@alvh.no-ip. 645 : 543 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
2600 andres@anarazel.de 646 : 543 : return TM_Ok;
647 : : }
648 : :
4850 alvherre@alvh.no-ip. 649 : 2155 : xmax = HeapTupleGetUpdateXid(tuple);
4298 650 [ - + ]: 2155 : if (!TransactionIdIsValid(xmax))
651 : : {
4298 alvherre@alvh.no-ip. 652 [ # # ]:UBC 0 : if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
2600 andres@anarazel.de 653 : 0 : return TM_BeingModified;
654 : : }
655 : :
656 : : /* not LOCKED_ONLY, so it has to have an xmax */
4540 alvherre@alvh.no-ip. 657 [ - + ]:CBC 2155 : Assert(TransactionIdIsValid(xmax));
658 : :
4850 659 [ - + ]: 2155 : if (TransactionIdIsCurrentTransactionId(xmax))
660 : : {
4850 alvherre@alvh.no-ip. 661 [ # # ]:UBC 0 : if (HeapTupleHeaderGetCmax(tuple) >= curcid)
2600 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 : :
4298 alvherre@alvh.no-ip. 667 [ + + ]:CBC 2155 : if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
2600 andres@anarazel.de 668 : 2150 : return TM_BeingModified;
669 : :
4850 alvherre@alvh.no-ip. 670 [ + + ]: 5 : if (TransactionIdDidCommit(xmax))
671 : : {
1898 alvherre@alvh.no-ip. 672 [ - + ]:GBC 1 : if (!ItemPointerEquals(&htup->t_self, &tuple->t_ctid))
2600 andres@anarazel.de 673 :UBC 0 : return TM_Updated;
674 : : else
2600 andres@anarazel.de 675 :GBC 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 : :
4298 alvherre@alvh.no-ip. 683 [ + - ]:CBC 4 : if (!MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
684 : : {
685 : : /*
686 : : * There's no member, even just a locker, alive anymore, so we can
687 : : * mark the Xmax as invalid.
688 : : */
4540 689 : 4 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
690 : : InvalidTransactionId);
2600 andres@anarazel.de 691 : 4 : return TM_Ok;
692 : : }
693 : : else
694 : : {
695 : : /* There are lockers running */
2600 andres@anarazel.de 696 :UBC 0 : return TM_BeingModified;
697 : : }
698 : : }
699 : :
4850 alvherre@alvh.no-ip. 700 [ + + ]:CBC 456294 : if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
701 : : {
702 [ + + ]: 452283 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
2600 andres@anarazel.de 703 : 452170 : return TM_BeingModified;
8725 bruce@momjian.us 704 [ + - ]: 113 : if (HeapTupleHeaderGetCmax(tuple) >= curcid)
2600 andres@anarazel.de 705 : 113 : return TM_SelfModified; /* updated after scan started */
706 : : else
2600 andres@anarazel.de 707 :UBC 0 : return TM_Invisible; /* updated before scan started */
708 : : }
709 : :
4850 alvherre@alvh.no-ip. 710 [ + + ]:CBC 4011 : if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
2600 andres@anarazel.de 711 : 1512 : return TM_BeingModified;
712 : :
4850 alvherre@alvh.no-ip. 713 [ + + ]: 2499 : if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
714 : : {
715 : : /* it must have aborted or crashed */
6839 tgl@sss.pgh.pa.us 716 : 278 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
717 : : InvalidTransactionId);
2600 andres@anarazel.de 718 : 278 : return TM_Ok;
719 : : }
720 : :
721 : : /* xmax transaction committed */
722 : :
4850 alvherre@alvh.no-ip. 723 [ + + ]: 2221 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
724 : : {
6839 tgl@sss.pgh.pa.us 725 : 2140 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
726 : : InvalidTransactionId);
2600 andres@anarazel.de 727 : 2140 : return TM_Ok;
728 : : }
729 : :
6839 tgl@sss.pgh.pa.us 730 : 81 : SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
731 : : HeapTupleHeaderGetRawXmax(tuple));
1898 alvherre@alvh.no-ip. 732 [ + + ]: 81 : if (!ItemPointerEquals(&htup->t_self, &tuple->t_ctid))
2600 andres@anarazel.de 733 : 74 : return TM_Updated; /* updated by other */
734 : : else
735 : 7 : return TM_Deleted; /* deleted by other */
736 : : }
737 : :
738 : : /*
739 : : * HeapTupleSatisfiesDirty
740 : : * True iff heap tuple is valid including effects of open transactions.
741 : : *
742 : : * See SNAPSHOT_DIRTY's definition for the intended behaviour.
743 : : *
744 : : * This is essentially like HeapTupleSatisfiesSelf as far as effects of
745 : : * the current transaction and committed/aborted xacts are concerned.
746 : : * However, we also include the effects of other xacts still in progress.
747 : : *
748 : : * A special hack is that the passed-in snapshot struct is used as an
749 : : * output argument to return the xids of concurrent xacts that affected the
750 : : * tuple. snapshot->xmin is set to the tuple's xmin if that is another
751 : : * transaction that's still in progress; or to InvalidTransactionId if the
752 : : * tuple's xmin is committed good, committed dead, or my own xact.
753 : : * Similarly for snapshot->xmax and the tuple's xmax. If the tuple was
754 : : * inserted speculatively, meaning that the inserter might still back down
755 : : * on the insertion without aborting the whole transaction, the associated
756 : : * token is also returned in snapshot->speculativeToken.
757 : : */
758 : : static bool
4670 rhaas@postgresql.org 759 : 7812990 : HeapTupleSatisfiesDirty(HeapTuple htup, Snapshot snapshot,
760 : : Buffer buffer)
761 : : {
762 : 7812990 : HeapTupleHeader tuple = htup->t_data;
763 : :
764 [ - + ]: 7812990 : Assert(ItemPointerIsValid(&htup->t_self));
765 [ - + ]: 7812990 : Assert(htup->t_tableOid != InvalidOid);
766 : :
6981 tgl@sss.pgh.pa.us 767 : 7812990 : snapshot->xmin = snapshot->xmax = InvalidTransactionId;
4015 andres@anarazel.de 768 : 7812990 : snapshot->speculativeToken = 0;
769 : :
4517 rhaas@postgresql.org 770 [ + + ]: 7812990 : if (!HeapTupleHeaderXminCommitted(tuple))
771 : : {
772 [ + + ]: 7405895 : if (HeapTupleHeaderXminInvalid(tuple))
10003 vadim4o@yahoo.com 773 : 861 : return false;
774 : :
137 andres@anarazel.de 775 [ - + ]:GNC 7405034 : if (!HeapTupleCleanMoved(tuple, buffer))
137 andres@anarazel.de 776 :UNC 0 : return false;
4517 rhaas@postgresql.org 777 [ + + ]:CBC 7405034 : else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
778 : : {
10003 vadim4o@yahoo.com 779 [ + + ]: 7383976 : if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
780 : 32903 : return true;
781 : :
4724 bruce@momjian.us 782 [ + + ]: 7351073 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
7677 tgl@sss.pgh.pa.us 783 : 5085 : return true;
784 : :
4850 alvherre@alvh.no-ip. 785 [ + + ]: 7345988 : 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 */
4540 792 [ - + ]: 16 : Assert(TransactionIdIsValid(xmax));
793 : :
794 : : /* updating subtransaction must have aborted */
4850 795 [ - + ]: 16 : if (!TransactionIdIsCurrentTransactionId(xmax))
4850 alvherre@alvh.no-ip. 796 :UBC 0 : return true;
797 : : else
4850 alvherre@alvh.no-ip. 798 :CBC 16 : return false;
799 : : }
800 : :
801 [ - + ]: 7345972 : if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
802 : : {
803 : : /* deleting subtransaction must have aborted */
6839 tgl@sss.pgh.pa.us 804 :UBC 0 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
805 : : InvalidTransactionId);
7978 806 : 0 : return true;
807 : : }
808 : :
10003 vadim4o@yahoo.com 809 :CBC 7345972 : return false;
810 : : }
4517 rhaas@postgresql.org 811 [ + + ]: 21058 : 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 : : */
4015 andres@anarazel.de 819 [ + + ]: 48 : if (HeapTupleHeaderIsSpeculative(tuple))
820 : : {
821 : 2 : snapshot->speculativeToken =
822 : 2 : HeapTupleHeaderGetSpeculativeToken(tuple);
823 : :
824 [ - + ]: 2 : Assert(snapshot->speculativeToken != 0);
825 : : }
826 : :
4517 rhaas@postgresql.org 827 : 48 : snapshot->xmin = HeapTupleHeaderGetRawXmin(tuple);
828 : : /* XXX shouldn't we fall through to look at xmax? */
9842 bruce@momjian.us 829 : 48 : return true; /* in insertion by other */
830 : : }
4517 rhaas@postgresql.org 831 [ + + ]: 21010 : else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
6839 tgl@sss.pgh.pa.us 832 : 20433 : SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
833 : : HeapTupleHeaderGetRawXmin(tuple));
834 : : else
835 : : {
836 : : /* it must have aborted or crashed */
837 : 577 : SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
838 : : InvalidTransactionId);
7668 839 : 577 : return false;
840 : : }
841 : : }
842 : :
843 : : /* by here, the inserting transaction has committed */
844 : :
10003 vadim4o@yahoo.com 845 [ + + ]: 427528 : if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
846 : 137501 : return true;
847 : :
848 [ + + ]: 290027 : if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
849 : : {
4850 alvherre@alvh.no-ip. 850 [ - + ]: 102959 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
10003 vadim4o@yahoo.com 851 :UBC 0 : return true;
9842 bruce@momjian.us 852 :CBC 102959 : return false; /* updated by other */
853 : : }
854 : :
7677 tgl@sss.pgh.pa.us 855 [ + + ]: 187068 : if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
856 : : {
857 : : TransactionId xmax;
858 : :
4850 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 */
4540 865 [ - + ]: 17 : Assert(TransactionIdIsValid(xmax));
866 : :
4850 867 [ + + ]: 17 : if (TransactionIdIsCurrentTransactionId(xmax))
868 : 1 : return false;
869 [ - + ]: 16 : if (TransactionIdIsInProgress(xmax))
870 : : {
4850 alvherre@alvh.no-ip. 871 :UBC 0 : snapshot->xmax = xmax;
872 : 0 : return true;
873 : : }
4850 alvherre@alvh.no-ip. 874 [ + - ]:CBC 16 : if (TransactionIdDidCommit(xmax))
875 : 16 : return false;
876 : : /* it must have aborted or crashed */
7677 tgl@sss.pgh.pa.us 877 :UBC 0 : return true;
878 : : }
879 : :
4850 alvherre@alvh.no-ip. 880 [ + + ]:CBC 187038 : if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
881 : : {
882 [ + + ]: 150567 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
9437 inoue@tpf.co.jp 883 : 61 : return true;
10003 vadim4o@yahoo.com 884 : 150506 : return false;
885 : : }
886 : :
4850 alvherre@alvh.no-ip. 887 [ + + ]: 36471 : if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
888 : : {
4659 889 [ + + ]: 22 : if (!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
890 : 12 : snapshot->xmax = HeapTupleHeaderGetRawXmax(tuple);
7668 tgl@sss.pgh.pa.us 891 : 22 : return true;
892 : : }
893 : :
4850 alvherre@alvh.no-ip. 894 [ + + ]: 36449 : if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
895 : : {
896 : : /* it must have aborted or crashed */
6839 tgl@sss.pgh.pa.us 897 : 43 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
898 : : InvalidTransactionId);
7668 899 : 43 : return true;
900 : : }
901 : :
902 : : /* xmax transaction committed */
903 : :
4850 alvherre@alvh.no-ip. 904 [ + + ]: 36406 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
905 : : {
6839 tgl@sss.pgh.pa.us 906 : 10325 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
907 : : InvalidTransactionId);
10003 vadim4o@yahoo.com 908 : 10325 : return true;
909 : : }
910 : :
6839 tgl@sss.pgh.pa.us 911 : 26081 : SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
912 : : HeapTupleHeaderGetRawXmax(tuple));
9842 bruce@momjian.us 913 : 26081 : 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
4670 rhaas@postgresql.org 939 : 101417620 : HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot,
940 : : Buffer buffer, SetHintBitsState *state)
941 : : {
942 : 101417620 : 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 : : */
420 heikki.linnakangas@i 951 [ + + - + ]: 101417620 : Assert(snapshot->regd_count > 0 || snapshot->active_count > 0);
952 : :
4670 rhaas@postgresql.org 953 [ - + ]: 101417620 : Assert(ItemPointerIsValid(&htup->t_self));
954 [ - + ]: 101417620 : Assert(htup->t_tableOid != InvalidOid);
955 : :
4517 956 [ + + ]: 101417620 : if (!HeapTupleHeaderXminCommitted(tuple))
957 : : {
958 [ + + ]: 17697403 : if (HeapTupleHeaderXminInvalid(tuple))
10002 vadim4o@yahoo.com 959 : 225072 : return false;
960 : :
137 andres@anarazel.de 961 [ - + ]:GNC 17472331 : if (!HeapTupleCleanMoved(tuple, buffer))
137 andres@anarazel.de 962 :UNC 0 : return false;
4517 rhaas@postgresql.org 963 [ + + ]:CBC 17472331 : else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
964 : : {
8725 bruce@momjian.us 965 [ + + ]: 14656125 : if (HeapTupleHeaderGetCmin(tuple) >= snapshot->curcid)
10002 vadim4o@yahoo.com 966 : 9180 : return false; /* inserted after scan started */
967 : :
968 [ + + ]: 14646945 : if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
969 : 10760978 : return true;
970 : :
4724 bruce@momjian.us 971 [ + + ]: 3885967 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
7677 tgl@sss.pgh.pa.us 972 : 2203 : return true;
973 : :
4850 alvherre@alvh.no-ip. 974 [ + + ]: 3883764 : if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
975 : : {
976 : : TransactionId xmax;
977 : :
978 : 8 : xmax = HeapTupleGetUpdateXid(tuple);
979 : :
980 : : /* not LOCKED_ONLY, so it has to have an xmax */
4540 981 [ - + ]: 8 : Assert(TransactionIdIsValid(xmax));
982 : :
983 : : /* updating subtransaction must have aborted */
4850 984 [ + - ]: 8 : if (!TransactionIdIsCurrentTransactionId(xmax))
985 : 8 : return true;
4850 alvherre@alvh.no-ip. 986 [ # # ]:UBC 0 : else if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
987 : 0 : return true; /* updated after scan started */
988 : : else
3240 tgl@sss.pgh.pa.us 989 : 0 : return false; /* updated before scan started */
990 : : }
991 : :
4850 alvherre@alvh.no-ip. 992 [ + + ]:CBC 3883756 : if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
993 : : {
994 : : /* deleting subtransaction must have aborted */
56 andres@anarazel.de 995 :GNC 46 : SetHintBitsExt(tuple, buffer, HEAP_XMAX_INVALID,
996 : : InvalidTransactionId, state);
7978 tgl@sss.pgh.pa.us 997 :CBC 46 : return true;
998 : : }
999 : :
8725 bruce@momjian.us 1000 [ + + ]: 3883710 : if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
10002 vadim4o@yahoo.com 1001 : 885 : return true; /* deleted after scan started */
1002 : : else
1003 : 3882825 : return false; /* deleted before scan started */
1004 : : }
3905 tgl@sss.pgh.pa.us 1005 [ + + ]: 2816206 : else if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
10002 vadim4o@yahoo.com 1006 : 42332 : return false;
4517 rhaas@postgresql.org 1007 [ + + ]: 2773874 : else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
56 andres@anarazel.de 1008 :GNC 2715539 : SetHintBitsExt(tuple, buffer, HEAP_XMIN_COMMITTED,
1009 : : HeapTupleHeaderGetRawXmin(tuple), state);
1010 : : else
1011 : : {
1012 : : /* it must have aborted or crashed */
1013 : 58335 : SetHintBitsExt(tuple, buffer, HEAP_XMIN_INVALID,
1014 : : InvalidTransactionId, state);
7668 tgl@sss.pgh.pa.us 1015 :CBC 58335 : return false;
1016 : : }
1017 : : }
1018 : : else
1019 : : {
1020 : : /* xmin is committed, but maybe not according to our snapshot */
3905 1021 [ + + + + ]: 161356623 : if (!HeapTupleHeaderXminFrozen(tuple) &&
1022 : 77636406 : XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
1023 : 4844 : return false; /* treat as still in progress */
1024 : : }
1025 : :
1026 : : /* by here, the inserting transaction has committed */
1027 : :
10002 vadim4o@yahoo.com 1028 [ + + ]: 86430912 : if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
1029 : 78848761 : return true;
1030 : :
4850 alvherre@alvh.no-ip. 1031 [ + + ]: 7582151 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
7677 tgl@sss.pgh.pa.us 1032 : 508487 : return true;
1033 : :
1034 [ + + ]: 7073664 : if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1035 : : {
1036 : : TransactionId xmax;
1037 : :
1038 : : /* already checked above */
4850 alvherre@alvh.no-ip. 1039 [ - + ]: 78682 : Assert(!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask));
1040 : :
1041 : 78682 : xmax = HeapTupleGetUpdateXid(tuple);
1042 : :
1043 : : /* not LOCKED_ONLY, so it has to have an xmax */
4540 1044 [ - + ]: 78682 : Assert(TransactionIdIsValid(xmax));
1045 : :
4850 1046 [ + + ]: 78682 : if (TransactionIdIsCurrentTransactionId(xmax))
1047 : : {
1048 [ - + ]: 23 : if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
4850 alvherre@alvh.no-ip. 1049 :UBC 0 : return true; /* deleted after scan started */
1050 : : else
4850 alvherre@alvh.no-ip. 1051 :CBC 23 : return false; /* deleted before scan started */
1052 : : }
3905 tgl@sss.pgh.pa.us 1053 [ + + ]: 78659 : if (XidInMVCCSnapshot(xmax, snapshot))
4850 alvherre@alvh.no-ip. 1054 : 2126 : return true;
1055 [ + + ]: 76533 : if (TransactionIdDidCommit(xmax))
3905 tgl@sss.pgh.pa.us 1056 : 76517 : return false; /* updating transaction committed */
1057 : : /* it must have aborted or crashed */
10002 vadim4o@yahoo.com 1058 : 16 : return true;
1059 : : }
1060 : :
1061 [ + + ]: 6994982 : if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
1062 : : {
4850 alvherre@alvh.no-ip. 1063 [ + + ]: 600625 : if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
1064 : : {
8725 bruce@momjian.us 1065 [ + + ]: 183072 : if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
9842 1066 : 1836 : return true; /* deleted after scan started */
1067 : : else
1068 : 181236 : return false; /* deleted before scan started */
1069 : : }
1070 : :
3905 tgl@sss.pgh.pa.us 1071 [ + + ]: 417553 : if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
7668 1072 : 18637 : return true;
1073 : :
4850 alvherre@alvh.no-ip. 1074 [ + + ]: 398916 : if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
1075 : : {
1076 : : /* it must have aborted or crashed */
56 andres@anarazel.de 1077 :GNC 9021 : SetHintBitsExt(tuple, buffer, HEAP_XMAX_INVALID,
1078 : : InvalidTransactionId, state);
10002 vadim4o@yahoo.com 1079 :CBC 9021 : return true;
1080 : : }
1081 : :
1082 : : /* xmax transaction committed */
56 andres@anarazel.de 1083 :GNC 389895 : 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 */
3905 tgl@sss.pgh.pa.us 1089 [ + + ]:CBC 6394357 : if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
1090 : 1410 : return true; /* treat as still in progress */
1091 : : }
1092 : :
1093 : : /* xmax transaction committed */
1094 : :
10002 vadim4o@yahoo.com 1095 : 6782842 : 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
4670 rhaas@postgresql.org 1113 : 9336570 : HeapTupleSatisfiesVacuum(HeapTuple htup, TransactionId OldestXmin,
1114 : : Buffer buffer)
1115 : : {
2092 andres@anarazel.de 1116 : 9336570 : TransactionId dead_after = InvalidTransactionId;
1117 : : HTSV_Result res;
1118 : :
1119 : 9336570 : res = HeapTupleSatisfiesVacuumHorizon(htup, buffer, &dead_after);
1120 : :
1121 [ + + ]: 9336570 : if (res == HEAPTUPLE_RECENTLY_DEAD)
1122 : : {
1123 [ - + ]: 194524 : Assert(TransactionIdIsValid(dead_after));
1124 : :
1125 [ + + ]: 194524 : if (TransactionIdPrecedes(dead_after, OldestXmin))
1126 : 22442 : res = HEAPTUPLE_DEAD;
1127 : : }
1128 : : else
1129 [ - + ]: 9142046 : Assert(!TransactionIdIsValid(dead_after));
1130 : :
1131 : 9336570 : 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 : 42862910 : HeapTupleSatisfiesVacuumHorizon(HeapTuple htup, Buffer buffer, TransactionId *dead_after)
1148 : : {
4670 rhaas@postgresql.org 1149 : 42862910 : HeapTupleHeader tuple = htup->t_data;
1150 : :
1151 [ - + ]: 42862910 : Assert(ItemPointerIsValid(&htup->t_self));
1152 [ - + ]: 42862910 : Assert(htup->t_tableOid != InvalidOid);
2092 andres@anarazel.de 1153 [ - + ]: 42862910 : Assert(dead_after != NULL);
1154 : :
1155 : 42862910 : *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 : : */
4517 rhaas@postgresql.org 1163 [ + + ]: 42862910 : if (!HeapTupleHeaderXminCommitted(tuple))
1164 : : {
1165 [ + + ]: 13303087 : if (HeapTupleHeaderXminInvalid(tuple))
9063 tgl@sss.pgh.pa.us 1166 : 20063 : return HEAPTUPLE_DEAD;
137 andres@anarazel.de 1167 [ - + ]:GNC 13283024 : else if (!HeapTupleCleanMoved(tuple, buffer))
137 andres@anarazel.de 1168 :UNC 0 : return HEAPTUPLE_DEAD;
4353 andres@anarazel.de 1169 [ + + ]:CBC 13283024 : else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
1170 : : {
8261 tgl@sss.pgh.pa.us 1171 [ + + ]: 2347786 : if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
1172 : 2305936 : return HEAPTUPLE_INSERT_IN_PROGRESS;
1173 : : /* only locked? run infomask-only check first, for performance */
4673 alvherre@alvh.no-ip. 1174 [ + + - + ]: 75428 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask) ||
1175 : 33578 : HeapTupleHeaderIsOnlyLocked(tuple))
8261 tgl@sss.pgh.pa.us 1176 : 8272 : return HEAPTUPLE_INSERT_IN_PROGRESS;
1177 : : /* inserted and then deleted by same xact */
4353 andres@anarazel.de 1178 [ + + ]: 33578 : if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetUpdateXid(tuple)))
1179 : 33564 : return HEAPTUPLE_DELETE_IN_PROGRESS;
1180 : : /* deleting subtransaction must have aborted */
4353 andres@anarazel.de 1181 :GBC 14 : return HEAPTUPLE_INSERT_IN_PROGRESS;
1182 : : }
4353 andres@anarazel.de 1183 [ + + ]:CBC 10935238 : 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 : 5820 : return HEAPTUPLE_INSERT_IN_PROGRESS;
1194 : : }
4517 rhaas@postgresql.org 1195 [ + + ]: 10929418 : else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
6839 tgl@sss.pgh.pa.us 1196 : 10867844 : 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 : 61574 : SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
1204 : : InvalidTransactionId);
9063 1205 : 61574 : 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 [ + + ]: 40427667 : if (tuple->t_infomask & HEAP_XMAX_INVALID)
1220 : 35706446 : return HEAPTUPLE_LIVE;
1221 : :
4850 alvherre@alvh.no-ip. 1222 [ + + ]: 4721221 : 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 : : */
8880 tgl@sss.pgh.pa.us 1230 [ + - ]: 18396 : if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
1231 : : {
7677 1232 [ + + ]: 18396 : 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 : : */
3602 alvherre@alvh.no-ip. 1238 [ + - + + ]: 472 : if (!HEAP_LOCKED_UPGRADED(tuple->t_infomask) &&
4298 1239 : 236 : MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple),
1240 : : true))
7677 tgl@sss.pgh.pa.us 1241 : 21 : return HEAPTUPLE_LIVE;
4850 alvherre@alvh.no-ip. 1242 : 215 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
1243 : : }
1244 : : else
1245 : : {
1246 [ + + ]: 18160 : if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
7677 tgl@sss.pgh.pa.us 1247 :GBC 4 : return HEAPTUPLE_LIVE;
4850 alvherre@alvh.no-ip. 1248 :CBC 18156 : 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 : :
8903 tgl@sss.pgh.pa.us 1259 : 18371 : return HEAPTUPLE_LIVE;
1260 : : }
1261 : :
7677 1262 [ + + ]: 4702825 : if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1263 : : {
3105 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 */
4540 alvherre@alvh.no-ip. 1270 [ - + ]: 102 : Assert(TransactionIdIsValid(xmax));
1271 : :
3105 andres@anarazel.de 1272 [ + + ]: 102 : if (TransactionIdIsInProgress(xmax))
3105 andres@anarazel.de 1273 :GBC 1 : return HEAPTUPLE_DELETE_IN_PROGRESS;
3105 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 : : */
2092 1284 : 101 : *dead_after = xmax;
1285 : 101 : return HEAPTUPLE_RECENTLY_DEAD;
1286 : : }
3105 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 : :
4540 alvherre@alvh.no-ip. 1296 : 0 : return HEAPTUPLE_LIVE;
1297 : : }
1298 : :
9063 tgl@sss.pgh.pa.us 1299 [ + + ]:CBC 4702723 : if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
1300 : : {
4850 alvherre@alvh.no-ip. 1301 [ + + ]: 3707924 : if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
9059 tgl@sss.pgh.pa.us 1302 : 75690 : return HEAPTUPLE_DELETE_IN_PROGRESS;
4850 alvherre@alvh.no-ip. 1303 [ + + ]: 3632234 : else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
6839 tgl@sss.pgh.pa.us 1304 : 3629911 : 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 : 2323 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
1312 : : InvalidTransactionId);
9063 1313 : 2323 : 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 : : */
2092 andres@anarazel.de 1327 : 4624710 : *dead_after = HeapTupleHeaderGetRawXmax(tuple);
1328 : 4624710 : 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
3162 tgl@sss.pgh.pa.us 1345 : 479396 : HeapTupleSatisfiesNonVacuumable(HeapTuple htup, Snapshot snapshot,
1346 : : Buffer buffer)
1347 : : {
2092 andres@anarazel.de 1348 : 479396 : TransactionId dead_after = InvalidTransactionId;
1349 : : HTSV_Result res;
1350 : :
1351 : 479396 : res = HeapTupleSatisfiesVacuumHorizon(htup, buffer, &dead_after);
1352 : :
1353 [ + + ]: 479396 : if (res == HEAPTUPLE_RECENTLY_DEAD)
1354 : : {
1355 [ - + ]: 101079 : Assert(TransactionIdIsValid(dead_after));
1356 : :
42 melanieplageman@gmai 1357 [ + + ]:GNC 101079 : if (GlobalVisTestIsRemovableXid(snapshot->vistest, dead_after, true))
2092 andres@anarazel.de 1358 :CBC 81424 : res = HEAPTUPLE_DEAD;
1359 : : }
1360 : : else
1361 [ - + ]: 378317 : Assert(!TransactionIdIsValid(dead_after));
1362 : :
1363 : 479396 : 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 : 8562330 : HeapTupleIsSurelyDead(HeapTuple htup, GlobalVisState *vistest)
1382 : : {
4670 rhaas@postgresql.org 1383 : 8562330 : HeapTupleHeader tuple = htup->t_data;
1384 : :
1385 [ - + ]: 8562330 : Assert(ItemPointerIsValid(&htup->t_self));
1386 [ - + ]: 8562330 : 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 : : */
4517 1394 [ + + ]: 8562330 : if (!HeapTupleHeaderXminCommitted(tuple))
1700 michael@paquier.xyz 1395 : 7511099 : return HeapTupleHeaderXminInvalid(tuple);
1396 : :
1397 : : /*
1398 : : * If the inserting transaction committed, but any deleting transaction
1399 : : * aborted, the tuple is still alive.
1400 : : */
4850 alvherre@alvh.no-ip. 1401 [ + + ]: 1051231 : if (tuple->t_infomask & HEAP_XMAX_INVALID)
1402 : 47 : return false;
1403 : :
1404 : : /*
1405 : : * If the XMAX is just a lock, the tuple is still alive.
1406 : : */
1407 [ + + ]: 1051184 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
4850 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 : : */
4850 alvherre@alvh.no-ip. 1414 [ + + ]:CBC 1051182 : if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
5116 rhaas@postgresql.org 1415 : 146 : return false;
1416 : :
1417 : : /* If deleter isn't known to have committed, assume it's still running. */
1418 [ + + ]: 1051036 : if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
1419 : 285131 : return false;
1420 : :
1421 : : /* Deleter committed, so tuple is dead if the XID is old enough. */
2092 andres@anarazel.de 1422 : 765905 : return GlobalVisTestIsRemovableXid(vistest,
1423 : : HeapTupleHeaderGetRawXmax(tuple),
1424 : : true);
1425 : : }
1426 : :
1427 : : /*
1428 : : * Is the tuple really only locked? That is, is it not updated?
1429 : : *
1430 : : * It's easy to check just infomask bits if the locker is not a multi; but
1431 : : * otherwise we need to verify that the updating transaction has not aborted.
1432 : : *
1433 : : * This function is here because it follows the same visibility rules laid out
1434 : : * at the top of this file.
1435 : : */
1436 : : bool
4850 alvherre@alvh.no-ip. 1437 : 125145 : HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple)
1438 : : {
1439 : : TransactionId xmax;
1440 : :
1441 : : /* if there's no valid Xmax, then there's obviously no update either */
1442 [ - + ]: 125145 : if (tuple->t_infomask & HEAP_XMAX_INVALID)
4850 alvherre@alvh.no-ip. 1443 :UBC 0 : return true;
1444 : :
4850 alvherre@alvh.no-ip. 1445 [ + + ]:CBC 125145 : if (tuple->t_infomask & HEAP_XMAX_LOCK_ONLY)
1446 : 71725 : return true;
1447 : :
1448 : : /* invalid xmax means no update */
1449 [ - + ]: 53420 : if (!TransactionIdIsValid(HeapTupleHeaderGetRawXmax(tuple)))
4850 alvherre@alvh.no-ip. 1450 :UBC 0 : return true;
1451 : :
1452 : : /*
1453 : : * if HEAP_XMAX_LOCK_ONLY is not set and not a multi, then this must
1454 : : * necessarily have been updated
1455 : : */
4850 alvherre@alvh.no-ip. 1456 [ + + ]:CBC 53420 : if (!(tuple->t_infomask & HEAP_XMAX_IS_MULTI))
1457 : 51273 : return false;
1458 : :
1459 : : /* ... but if it's a multi, then perhaps the updating Xid aborted. */
1460 : 2147 : xmax = HeapTupleGetUpdateXid(tuple);
1461 : :
1462 : : /* not LOCKED_ONLY, so it has to have an xmax */
4540 1463 [ - + ]: 2147 : Assert(TransactionIdIsValid(xmax));
1464 : :
4850 1465 [ - + ]: 2147 : if (TransactionIdIsCurrentTransactionId(xmax))
4850 alvherre@alvh.no-ip. 1466 :UBC 0 : return false;
4850 alvherre@alvh.no-ip. 1467 [ + + ]:CBC 2147 : if (TransactionIdIsInProgress(xmax))
1468 : 2112 : return false;
1469 [ + + ]: 35 : if (TransactionIdDidCommit(xmax))
1470 : 11 : return false;
1471 : :
1472 : : /*
1473 : : * not current, not in progress, not committed -- must have aborted or
1474 : : * crashed
1475 : : */
1476 : 24 : return true;
1477 : : }
1478 : :
1479 : : /*
1480 : : * check whether the transaction id 'xid' is in the pre-sorted array 'xip'.
1481 : : */
1482 : : static bool
4446 rhaas@postgresql.org 1483 : 55073 : TransactionIdInArray(TransactionId xid, TransactionId *xip, Size num)
1484 : : {
1524 tgl@sss.pgh.pa.us 1485 [ + + + + ]: 76447 : return num > 0 &&
1486 : 21374 : bsearch(&xid, xip, num, sizeof(TransactionId), xidComparator) != NULL;
1487 : : }
1488 : :
1489 : : /*
1490 : : * See the comments for HeapTupleSatisfiesMVCC for the semantics this function
1491 : : * obeys.
1492 : : *
1493 : : * Only usable on tuples from catalog tables!
1494 : : *
1495 : : * We don't need to support HEAP_MOVED_(IN|OFF) for now because we only support
1496 : : * reading catalog pages which couldn't have been created in an older version.
1497 : : *
1498 : : * We don't set any hint bits in here as it seems unlikely to be beneficial as
1499 : : * those should already be set by normal access and it seems to be too
1500 : : * dangerous to do so as the semantics of doing so during timetravel are more
1501 : : * complicated than when dealing "only" with the present.
1502 : : */
1503 : : static bool
4446 rhaas@postgresql.org 1504 : 45961 : HeapTupleSatisfiesHistoricMVCC(HeapTuple htup, Snapshot snapshot,
1505 : : Buffer buffer)
1506 : : {
1507 : 45961 : HeapTupleHeader tuple = htup->t_data;
1508 : 45961 : TransactionId xmin = HeapTupleHeaderGetXmin(tuple);
1509 : 45961 : TransactionId xmax = HeapTupleHeaderGetRawXmax(tuple);
1510 : :
1511 [ - + ]: 45961 : Assert(ItemPointerIsValid(&htup->t_self));
1512 [ - + ]: 45961 : Assert(htup->t_tableOid != InvalidOid);
1513 : :
1514 : : /* inserting transaction aborted */
1515 [ + + ]: 45961 : if (HeapTupleHeaderXminInvalid(tuple))
1516 : : {
1517 [ - + ]: 79 : Assert(!TransactionIdDidCommit(xmin));
1518 : 79 : return false;
1519 : : }
1520 : : /* check if it's one of our txids, toplevel is also in there */
1521 [ + + ]: 45882 : else if (TransactionIdInArray(xmin, snapshot->subxip, snapshot->subxcnt))
1522 : : {
1523 : : bool resolved;
1524 : 522 : CommandId cmin = HeapTupleHeaderGetRawCommandId(tuple);
1525 : 522 : CommandId cmax = InvalidCommandId;
1526 : :
1527 : : /*
1528 : : * another transaction might have (tried to) delete this tuple or
1529 : : * cmin/cmax was stored in a combo CID. So we need to lookup the
1530 : : * actual values externally.
1531 : : */
1532 : 522 : resolved = ResolveCminCmaxDuringDecoding(HistoricSnapshotGetTupleCids(), snapshot,
1533 : : htup, buffer,
1534 : : &cmin, &cmax);
1535 : :
1536 : : /*
1537 : : * If we haven't resolved the combo CID to cmin/cmax, that means we
1538 : : * have not decoded the combo CID yet. That means the cmin is
1539 : : * definitely in the future, and we're not supposed to see the tuple
1540 : : * yet.
1541 : : *
1542 : : * XXX This only applies to decoding of in-progress transactions. In
1543 : : * regular logical decoding we only execute this code at commit time,
1544 : : * at which point we should have seen all relevant combo CIDs. So
1545 : : * ideally, we should error out in this case but in practice, this
1546 : : * won't happen. If we are too worried about this then we can add an
1547 : : * elog inside ResolveCminCmaxDuringDecoding.
1548 : : *
1549 : : * XXX For the streaming case, we can track the largest combo CID
1550 : : * assigned, and error out based on this (when unable to resolve combo
1551 : : * CID below that observed maximum value).
1552 : : */
1553 [ + + ]: 522 : if (!resolved)
2096 akapila@postgresql.o 1554 : 75 : return false;
1555 : :
4446 rhaas@postgresql.org 1556 [ - + ]: 517 : Assert(cmin != InvalidCommandId);
1557 : :
1558 [ + + ]: 517 : if (cmin >= snapshot->curcid)
4382 bruce@momjian.us 1559 : 70 : return false; /* inserted after scan started */
1560 : : /* fall through */
1561 : : }
1562 : : /* committed before our xmin horizon. Do a normal visibility check. */
4446 rhaas@postgresql.org 1563 [ + + ]: 45360 : else if (TransactionIdPrecedes(xmin, snapshot->xmin))
1564 : : {
1565 [ + + - + ]: 42091 : Assert(!(HeapTupleHeaderXminCommitted(tuple) &&
1566 : : !TransactionIdDidCommit(xmin)));
1567 : :
1568 : : /* check for hint bit first, consult clog afterwards */
1569 [ + + ]: 42091 : if (!HeapTupleHeaderXminCommitted(tuple) &&
1570 [ + + ]: 60 : !TransactionIdDidCommit(xmin))
1571 : 4 : return false;
1572 : : /* fall through */
1573 : : }
1574 : : /* beyond our xmax horizon, i.e. invisible */
1575 [ + + ]: 3269 : else if (TransactionIdFollowsOrEquals(xmin, snapshot->xmax))
1576 : : {
1577 : 113 : return false;
1578 : : }
1579 : : /* check if it's a committed transaction in [xmin, xmax) */
4382 bruce@momjian.us 1580 [ - + ]: 3156 : else if (TransactionIdInArray(xmin, snapshot->xip, snapshot->xcnt))
1581 : : {
1582 : : /* fall through */
1583 : : }
1584 : :
1585 : : /*
1586 : : * none of the above, i.e. between [xmin, xmax) but hasn't committed. I.e.
1587 : : * invisible.
1588 : : */
1589 : : else
1590 : : {
4446 rhaas@postgresql.org 1591 :UBC 0 : return false;
1592 : : }
1593 : :
1594 : : /* at this point we know xmin is visible, go on to check xmax */
1595 : :
1596 : : /* xid invalid or aborted */
4446 rhaas@postgresql.org 1597 [ + + ]:CBC 45690 : if (tuple->t_infomask & HEAP_XMAX_INVALID)
1598 : 36971 : return true;
1599 : : /* locked tuples are always visible */
1600 [ + + ]: 8719 : else if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
1601 : 4172 : return true;
1602 : :
1603 : : /*
1604 : : * We can see multis here if we're looking at user tables or if somebody
1605 : : * SELECT ... FOR SHARE/UPDATE a system table.
1606 : : */
1607 [ + + ]: 4547 : else if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1608 : : {
1609 : 50 : xmax = HeapTupleGetUpdateXid(tuple);
1610 : : }
1611 : :
1612 : : /* check if it's one of our txids, toplevel is also in there */
1613 [ + + ]: 4547 : if (TransactionIdInArray(xmax, snapshot->subxip, snapshot->subxcnt))
1614 : : {
1615 : : bool resolved;
1616 : : CommandId cmin;
4382 bruce@momjian.us 1617 : 297 : CommandId cmax = HeapTupleHeaderGetRawCommandId(tuple);
1618 : :
1619 : : /* Lookup actual cmin/cmax values */
4446 rhaas@postgresql.org 1620 : 297 : resolved = ResolveCminCmaxDuringDecoding(HistoricSnapshotGetTupleCids(), snapshot,
1621 : : htup, buffer,
1622 : : &cmin, &cmax);
1623 : :
1624 : : /*
1625 : : * If we haven't resolved the combo CID to cmin/cmax, that means we
1626 : : * have not decoded the combo CID yet. That means the cmax is
1627 : : * definitely in the future, and we're still supposed to see the
1628 : : * tuple.
1629 : : *
1630 : : * XXX This only applies to decoding of in-progress transactions. In
1631 : : * regular logical decoding we only execute this code at commit time,
1632 : : * at which point we should have seen all relevant combo CIDs. So
1633 : : * ideally, we should error out in this case but in practice, this
1634 : : * won't happen. If we are too worried about this then we can add an
1635 : : * elog inside ResolveCminCmaxDuringDecoding.
1636 : : *
1637 : : * XXX For the streaming case, we can track the largest combo CID
1638 : : * assigned, and error out based on this (when unable to resolve combo
1639 : : * CID below that observed maximum value).
1640 : : */
2096 akapila@postgresql.o 1641 [ + + - + ]: 297 : if (!resolved || cmax == InvalidCommandId)
1642 : 11 : return true;
1643 : :
4446 rhaas@postgresql.org 1644 [ + + ]: 286 : if (cmax >= snapshot->curcid)
4382 bruce@momjian.us 1645 : 84 : return true; /* deleted after scan started */
1646 : : else
1647 : 202 : return false; /* deleted before scan started */
1648 : : }
1649 : : /* below xmin horizon, normal transaction state is valid */
4446 rhaas@postgresql.org 1650 [ + + ]: 4250 : else if (TransactionIdPrecedes(xmax, snapshot->xmin))
1651 : : {
1652 [ + + - + ]: 2455 : Assert(!(tuple->t_infomask & HEAP_XMAX_COMMITTED &&
1653 : : !TransactionIdDidCommit(xmax)));
1654 : :
1655 : : /* check hint bit first */
1656 [ + + ]: 2455 : if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
1657 : 2380 : return false;
1658 : :
1659 : : /* check clog */
1660 : 75 : return !TransactionIdDidCommit(xmax);
1661 : : }
1662 : : /* above xmax horizon, we cannot possibly see the deleting transaction */
1663 [ + + ]: 1795 : else if (TransactionIdFollowsOrEquals(xmax, snapshot->xmax))
1664 : 307 : return true;
1665 : : /* xmax is between [xmin, xmax), check known committed array */
1666 [ + - ]: 1488 : else if (TransactionIdInArray(xmax, snapshot->xip, snapshot->xcnt))
1667 : 1488 : return false;
1668 : : /* xmax is between [xmin, xmax), but known not to have committed yet */
1669 : : else
4446 rhaas@postgresql.org 1670 :UBC 0 : return true;
1671 : : }
1672 : :
1673 : : /*
1674 : : * Perform HeapTupleSatisfiesMVCC() on each passed in tuple. This is more
1675 : : * efficient than doing HeapTupleSatisfiesMVCC() one-by-one.
1676 : : *
1677 : : * To be checked tuples are passed via BatchMVCCState->tuples. Each tuple's
1678 : : * visibility is stored in batchmvcc->visible[]. In addition,
1679 : : * ->vistuples_dense is set to contain the offsets of visible tuples.
1680 : : *
1681 : : * The reason this is more efficient than HeapTupleSatisfiesMVCC() is that it
1682 : : * avoids a cross-translation-unit function call for each tuple, allows the
1683 : : * compiler to optimize across calls to HeapTupleSatisfiesMVCC and allows
1684 : : * setting hint bits more efficiently (see the one BufferFinishSetHintBits()
1685 : : * call below).
1686 : : *
1687 : : * Returns the number of visible tuples.
1688 : : */
1689 : : int
113 andres@anarazel.de 1690 :GNC 2073900 : HeapTupleSatisfiesMVCCBatch(Snapshot snapshot, Buffer buffer,
1691 : : int ntups,
1692 : : BatchMVCCState *batchmvcc,
1693 : : OffsetNumber *vistuples_dense)
1694 : : {
1695 : 2073900 : int nvis = 0;
56 1696 : 2073900 : SetHintBitsState state = SHB_INITIAL;
1697 : :
113 1698 [ - + ]: 2073900 : Assert(IsMVCCSnapshot(snapshot));
1699 : :
1700 [ + + ]: 82388247 : for (int i = 0; i < ntups; i++)
1701 : : {
1702 : : bool valid;
1703 : 80314347 : HeapTuple tup = &batchmvcc->tuples[i];
1704 : :
56 1705 : 80314347 : valid = HeapTupleSatisfiesMVCC(tup, snapshot, buffer, &state);
113 1706 : 80314347 : batchmvcc->visible[i] = valid;
1707 : :
1708 [ + + ]: 80314347 : if (likely(valid))
1709 : : {
1710 : 70832289 : vistuples_dense[nvis] = tup->t_self.ip_posid;
1711 : 70832289 : nvis++;
1712 : : }
1713 : : }
1714 : :
56 1715 [ + + ]: 2073900 : if (state == SHB_ENABLED)
1716 : 98493 : BufferFinishSetHintBits(buffer, true, true);
1717 : :
113 1718 : 2073900 : return nvis;
1719 : : }
1720 : :
1721 : : /*
1722 : : * HeapTupleSatisfiesVisibility
1723 : : * True iff heap tuple satisfies a time qual.
1724 : : *
1725 : : * Notes:
1726 : : * Assumes heap tuple is valid, and buffer at least share locked.
1727 : : *
1728 : : * Hint bits in the HeapTuple's t_infomask may be updated as a side effect;
1729 : : * if so, the indicated buffer is marked dirty.
1730 : : */
1731 : : bool
1324 pg@bowt.ie 1732 :CBC 42301932 : HeapTupleSatisfiesVisibility(HeapTuple htup, Snapshot snapshot, Buffer buffer)
1733 : : {
2661 andres@anarazel.de 1734 [ + + + + : 42301932 : switch (snapshot->snapshot_type)
+ + + - ]
1735 : : {
1736 : 21103273 : case SNAPSHOT_MVCC:
56 andres@anarazel.de 1737 :GNC 21103273 : return HeapTupleSatisfiesMVCC(htup, snapshot, buffer, NULL);
2661 andres@anarazel.de 1738 :CBC 605139 : case SNAPSHOT_SELF:
1324 pg@bowt.ie 1739 : 605139 : return HeapTupleSatisfiesSelf(htup, snapshot, buffer);
2661 andres@anarazel.de 1740 : 12158881 : case SNAPSHOT_ANY:
1324 pg@bowt.ie 1741 : 12158881 : return HeapTupleSatisfiesAny(htup, snapshot, buffer);
2661 andres@anarazel.de 1742 : 96292 : case SNAPSHOT_TOAST:
1324 pg@bowt.ie 1743 : 96292 : return HeapTupleSatisfiesToast(htup, snapshot, buffer);
2661 andres@anarazel.de 1744 : 7812990 : case SNAPSHOT_DIRTY:
1324 pg@bowt.ie 1745 : 7812990 : return HeapTupleSatisfiesDirty(htup, snapshot, buffer);
2661 andres@anarazel.de 1746 : 45961 : case SNAPSHOT_HISTORIC_MVCC:
1324 pg@bowt.ie 1747 : 45961 : return HeapTupleSatisfiesHistoricMVCC(htup, snapshot, buffer);
2661 andres@anarazel.de 1748 : 479396 : case SNAPSHOT_NON_VACUUMABLE:
1324 pg@bowt.ie 1749 : 479396 : return HeapTupleSatisfiesNonVacuumable(htup, snapshot, buffer);
1750 : : }
1751 : :
2661 andres@anarazel.de 1752 :UBC 0 : return false; /* keep compiler quiet */
1753 : : }
|