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
240 andres@anarazel.de 142 :CBC 17809155 : 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 [ + + + + ]: 17809155 : if (state && *state == SHB_DISABLED)
150 : 29 : return;
151 : :
7036 tgl@sss.pgh.pa.us 152 [ + + ]: 17809126 : if (TransactionIdIsValid(xid))
153 : : {
240 andres@anarazel.de 154 [ + + ]: 17638285 : if (BufferIsPermanent(buffer))
155 : : {
156 : : /* NB: xid must be known committed here! */
157 : 16502660 : XLogRecPtr commitLSN = TransactionIdGetCommitLSN(xid);
158 : :
159 [ + + + + ]: 16732376 : if (XLogNeedsFlush(commitLSN) &&
160 : 229716 : BufferGetLSNAtomic(buffer) < commitLSN)
161 : : {
162 : : /* not flushed and no LSN interlock, so don't set hint */
163 : 205637 : 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 [ + + ]: 17603489 : if (!state)
175 : : {
176 : 15858912 : BufferSetHintBits16(&tuple->t_infomask,
177 : 15858912 : tuple->t_infomask | infomask, buffer);
178 : 15858912 : return;
179 : : }
180 : :
181 [ + + ]: 1744577 : if (*state == SHB_INITIAL)
182 : : {
183 [ + + ]: 90865 : if (!BufferBeginSetHintBits(buffer))
184 : : {
185 : 9 : *state = SHB_DISABLED;
3916 186 : 9 : return;
187 : : }
188 : :
240 189 : 90856 : *state = SHB_ENABLED;
190 : : }
7036 tgl@sss.pgh.pa.us 191 : 1744568 : 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
240 andres@anarazel.de 199 : 14704660 : SetHintBits(HeapTupleHeader tuple, Buffer buffer,
200 : : uint16 infomask, TransactionId xid)
201 : : {
202 : 14704660 : SetHintBitsExt(tuple, buffer, infomask, xid, NULL);
7036 tgl@sss.pgh.pa.us 203 : 14704660 : }
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
7023 212 : 243 : 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 : : */
297 andres@anarazel.de 220 [ - + ]: 243 : Assert(BufferIsLockedByMeInMode(buffer, BUFFER_LOCK_EXCLUSIVE));
221 : :
7023 tgl@sss.pgh.pa.us 222 : 243 : SetHintBits(tuple, buffer, infomask, xid);
223 : 243 : }
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
321 andres@anarazel.de 232 : 39111652 : HeapTupleCleanMoved(HeapTupleHeader tuple, Buffer buffer)
233 : : {
234 : : TransactionId xvac;
235 : :
236 : : /* only used by pre-9.0 binary upgrades */
237 [ + - ]: 39111652 : if (likely(!(tuple->t_infomask & (HEAP_MOVED_OFF | HEAP_MOVED_IN))))
238 : 39111652 : return true;
239 : :
321 andres@anarazel.de 240 :UBC 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
4854 rhaas@postgresql.org 297 :CBC 656970 : HeapTupleSatisfiesSelf(HeapTuple htup, Snapshot snapshot, Buffer buffer)
298 : : {
299 : 656970 : HeapTupleHeader tuple = htup->t_data;
300 : :
301 [ - + ]: 656970 : Assert(ItemPointerIsValid(&htup->t_self));
302 [ - + ]: 656970 : Assert(htup->t_tableOid != InvalidOid);
303 : :
4701 304 [ + + ]: 656970 : if (!HeapTupleHeaderXminCommitted(tuple))
305 : : {
306 [ - + ]: 656079 : if (HeapTupleHeaderXminInvalid(tuple))
10292 bruce@momjian.us 307 :UBC 0 : return false;
308 : :
321 andres@anarazel.de 309 [ - + ]:CBC 656079 : if (!HeapTupleCleanMoved(tuple, buffer))
321 andres@anarazel.de 310 :UBC 0 : return false;
4701 rhaas@postgresql.org 311 [ + - ]:CBC 656079 : else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
312 : : {
10595 vadim4o@yahoo.com 313 [ + + ]: 656079 : if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
10292 bruce@momjian.us 314 : 655997 : return true;
315 : :
4908 316 [ + + ]: 82 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
7861 tgl@sss.pgh.pa.us 317 : 13 : return true;
318 : :
5034 alvherre@alvh.no-ip. 319 [ - + ]: 69 : if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
320 : : {
321 : : TransactionId xmax;
322 : :
5034 alvherre@alvh.no-ip. 323 :UBC 0 : xmax = HeapTupleGetUpdateXid(tuple);
324 : :
325 : : /* not LOCKED_ONLY, so it has to have an xmax */
4724 326 [ # # ]: 0 : Assert(TransactionIdIsValid(xmax));
327 : :
328 : : /* updating subtransaction must have aborted */
5034 329 [ # # ]: 0 : if (!TransactionIdIsCurrentTransactionId(xmax))
330 : 0 : return true;
331 : : else
332 : 0 : return false;
333 : : }
334 : :
5034 alvherre@alvh.no-ip. 335 [ + + ]:CBC 69 : if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
336 : : {
337 : : /* deleting subtransaction must have aborted */
7023 tgl@sss.pgh.pa.us 338 : 12 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
339 : : InvalidTransactionId);
8162 340 : 12 : return true;
341 : : }
342 : :
10187 vadim4o@yahoo.com 343 : 57 : return false;
344 : : }
4701 rhaas@postgresql.org 345 [ # # ]:UBC 0 : else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
10292 bruce@momjian.us 346 : 0 : return false;
4701 rhaas@postgresql.org 347 [ # # ]: 0 : else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
7023 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);
7852 355 : 0 : return false;
356 : : }
357 : : }
358 : :
359 : : /* by here, the inserting transaction has committed */
360 : :
10595 vadim4o@yahoo.com 361 [ + + ]:CBC 891 : if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
10292 bruce@momjian.us 362 : 789 : return true;
363 : :
10595 vadim4o@yahoo.com 364 [ + + ]: 102 : if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
365 : : {
5034 alvherre@alvh.no-ip. 366 [ - + ]: 73 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
10187 vadim4o@yahoo.com 367 :UBC 0 : return true;
10026 bruce@momjian.us 368 :CBC 73 : return false; /* updated by other */
369 : : }
370 : :
7861 tgl@sss.pgh.pa.us 371 [ - + ]: 29 : if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
372 : : {
373 : : TransactionId xmax;
374 : :
5034 alvherre@alvh.no-ip. 375 [ # # ]:UBC 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 */
4724 381 [ # # ]: 0 : Assert(TransactionIdIsValid(xmax));
382 : :
5034 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 */
7861 tgl@sss.pgh.pa.us 390 : 0 : return true;
391 : : }
392 : :
5034 alvherre@alvh.no-ip. 393 [ + + ]:CBC 29 : if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
394 : : {
395 [ - + ]: 28 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
10187 vadim4o@yahoo.com 396 :UBC 0 : return true;
10292 bruce@momjian.us 397 :CBC 28 : return false;
398 : : }
399 : :
5034 alvherre@alvh.no-ip. 400 [ - + ]:GBC 1 : if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
7852 tgl@sss.pgh.pa.us 401 :UBC 0 : return true;
402 : :
5034 alvherre@alvh.no-ip. 403 [ - + ]:GBC 1 : if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
404 : : {
405 : : /* it must have aborted or crashed */
7023 tgl@sss.pgh.pa.us 406 :UBC 0 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
407 : : InvalidTransactionId);
10292 bruce@momjian.us 408 : 0 : return true;
409 : : }
410 : :
411 : : /* xmax transaction committed */
412 : :
5034 alvherre@alvh.no-ip. 413 [ - + ]:GBC 1 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
414 : : {
7023 tgl@sss.pgh.pa.us 415 :UBC 0 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
416 : : InvalidTransactionId);
10187 vadim4o@yahoo.com 417 : 0 : return true;
418 : : }
419 : :
7023 tgl@sss.pgh.pa.us 420 :GBC 1 : SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
421 : : HeapTupleHeaderGetRawXmax(tuple));
10292 bruce@momjian.us 422 : 1 : return false;
423 : : }
424 : :
425 : : /*
426 : : * HeapTupleSatisfiesAny
427 : : * Dummy "satisfies" routine: any tuple satisfies SnapshotAny.
428 : : */
429 : : static bool
4854 rhaas@postgresql.org 430 :CBC 12223109 : HeapTupleSatisfiesAny(HeapTuple htup, Snapshot snapshot, Buffer buffer)
431 : : {
7165 tgl@sss.pgh.pa.us 432 : 12223109 : 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
4854 rhaas@postgresql.org 452 : 100166 : HeapTupleSatisfiesToast(HeapTuple htup, Snapshot snapshot,
453 : : Buffer buffer)
454 : : {
455 : 100166 : HeapTupleHeader tuple = htup->t_data;
456 : :
457 [ - + ]: 100166 : Assert(ItemPointerIsValid(&htup->t_self));
458 [ - + ]: 100166 : Assert(htup->t_tableOid != InvalidOid);
459 : :
4701 460 [ + + ]: 100166 : if (!HeapTupleHeaderXminCommitted(tuple))
461 : : {
462 [ - + ]: 39550 : if (HeapTupleHeaderXminInvalid(tuple))
9059 tgl@sss.pgh.pa.us 463 :UBC 0 : return false;
464 : :
321 andres@anarazel.de 465 [ - + ]:CBC 39550 : if (!HeapTupleCleanMoved(tuple, buffer))
321 andres@anarazel.de 466 :UBC 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 : : */
4199 andres@anarazel.de 473 [ - + ]:CBC 39550 : else if (!TransactionIdIsValid(HeapTupleHeaderGetXmin(tuple)))
4199 andres@anarazel.de 474 :UBC 0 : return false;
475 : : }
476 : :
477 : : /* otherwise assume the tuple is valid for TOAST. */
9059 tgl@sss.pgh.pa.us 478 :CBC 100166 : 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
4854 rhaas@postgresql.org 511 : 5128710 : HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid,
512 : : Buffer buffer)
513 : : {
514 : 5128710 : HeapTupleHeader tuple = htup->t_data;
515 : :
516 [ - + ]: 5128710 : Assert(ItemPointerIsValid(&htup->t_self));
517 [ - + ]: 5128710 : Assert(htup->t_tableOid != InvalidOid);
518 : :
4701 519 [ + + ]: 5128710 : if (!HeapTupleHeaderXminCommitted(tuple))
520 : : {
521 [ - + ]: 277221 : if (HeapTupleHeaderXminInvalid(tuple))
2784 andres@anarazel.de 522 :UBC 0 : return TM_Invisible;
523 : :
321 andres@anarazel.de 524 [ - + ]:CBC 277221 : else if (!HeapTupleCleanMoved(tuple, buffer))
321 andres@anarazel.de 525 :UBC 0 : return TM_Invisible;
4701 rhaas@postgresql.org 526 [ + + ]:CBC 277221 : else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
527 : : {
8909 bruce@momjian.us 528 [ + + ]: 273545 : if (HeapTupleHeaderGetCmin(tuple) >= curcid)
2784 andres@anarazel.de 529 : 28 : return TM_Invisible; /* inserted after scan started */
530 : :
8828 bruce@momjian.us 531 [ + + ]: 273517 : if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
2784 andres@anarazel.de 532 : 233236 : return TM_Ok;
533 : :
4705 alvherre@alvh.no-ip. 534 [ + + ]: 40281 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
535 : : {
536 : : TransactionId xmax;
537 : :
538 : 40265 : 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 [ + + ]: 40265 : if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
548 : : {
4227 549 [ + - ]: 31 : if (MultiXactIdIsRunning(xmax, true))
2784 andres@anarazel.de 550 : 31 : return TM_BeingModified;
551 : : else
2784 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 : : */
4705 alvherre@alvh.no-ip. 560 [ - + ]:CBC 40234 : if (!TransactionIdIsInProgress(xmax))
2784 andres@anarazel.de 561 :UBC 0 : return TM_Ok;
2784 andres@anarazel.de 562 :CBC 40234 : return TM_BeingModified;
563 : : }
564 : :
5034 alvherre@alvh.no-ip. 565 [ + + ]: 16 : 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 */
4724 572 [ - + ]: 8 : Assert(TransactionIdIsValid(xmax));
573 : :
574 : : /* deleting subtransaction must have aborted */
5034 575 [ + - ]: 8 : if (!TransactionIdIsCurrentTransactionId(xmax))
576 : : {
4227 577 [ + - ]: 8 : if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple),
578 : : false))
2784 andres@anarazel.de 579 : 8 : return TM_BeingModified;
2784 andres@anarazel.de 580 :UBC 0 : return TM_Ok;
581 : : }
582 : : else
583 : : {
5034 alvherre@alvh.no-ip. 584 [ # # ]: 0 : if (HeapTupleHeaderGetCmax(tuple) >= curcid)
2784 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 : :
5034 alvherre@alvh.no-ip. 591 [ - + ]:CBC 8 : if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
592 : : {
593 : : /* deleting subtransaction must have aborted */
7023 tgl@sss.pgh.pa.us 594 :UBC 0 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
595 : : InvalidTransactionId);
2784 andres@anarazel.de 596 : 0 : return TM_Ok;
597 : : }
598 : :
8909 bruce@momjian.us 599 [ + - ]:CBC 8 : if (HeapTupleHeaderGetCmax(tuple) >= curcid)
2784 andres@anarazel.de 600 : 8 : return TM_SelfModified; /* updated after scan started */
601 : : else
2784 andres@anarazel.de 602 :UBC 0 : return TM_Invisible; /* updated before scan started */
603 : : }
4701 rhaas@postgresql.org 604 [ - + ]:CBC 3676 : else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
2784 andres@anarazel.de 605 :UBC 0 : return TM_Invisible;
4701 rhaas@postgresql.org 606 [ + - ]:CBC 3676 : else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
7023 tgl@sss.pgh.pa.us 607 : 3676 : SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
608 : : HeapTupleHeaderGetRawXmin(tuple));
609 : : else
610 : : {
611 : : /* it must have aborted or crashed */
7023 tgl@sss.pgh.pa.us 612 :UBC 0 : SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
613 : : InvalidTransactionId);
2784 andres@anarazel.de 614 : 0 : return TM_Invisible;
615 : : }
616 : : }
617 : :
618 : : /* by here, the inserting transaction has committed */
619 : :
8828 bruce@momjian.us 620 [ + + ]:CBC 4855165 : if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
2784 andres@anarazel.de 621 : 4133954 : return TM_Ok;
622 : :
9087 tgl@sss.pgh.pa.us 623 [ + + ]: 721211 : if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
624 : : {
5034 alvherre@alvh.no-ip. 625 [ - + ]: 165 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
2784 andres@anarazel.de 626 :UBC 0 : return TM_Ok;
2082 alvherre@alvh.no-ip. 627 [ + + ]:CBC 165 : if (!ItemPointerEquals(&htup->t_self, &tuple->t_ctid))
2784 andres@anarazel.de 628 : 164 : return TM_Updated; /* updated by other */
629 : : else
630 : 1 : return TM_Deleted; /* deleted by other */
631 : : }
632 : :
7861 tgl@sss.pgh.pa.us 633 [ + + ]: 721046 : if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
634 : : {
635 : : TransactionId xmax;
636 : :
3786 alvherre@alvh.no-ip. 637 [ - + ]: 73987 : if (HEAP_LOCKED_UPGRADED(tuple->t_infomask))
2784 andres@anarazel.de 638 :UBC 0 : return TM_Ok;
639 : :
5034 alvherre@alvh.no-ip. 640 [ + + ]:CBC 73987 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
641 : : {
3786 642 [ + + ]: 71826 : if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), true))
2784 andres@anarazel.de 643 : 71316 : return TM_BeingModified;
644 : :
5034 alvherre@alvh.no-ip. 645 : 510 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
2784 andres@anarazel.de 646 : 510 : return TM_Ok;
647 : : }
648 : :
5034 alvherre@alvh.no-ip. 649 : 2161 : xmax = HeapTupleGetUpdateXid(tuple);
4482 650 [ - + ]: 2161 : if (!TransactionIdIsValid(xmax))
651 : : {
4482 alvherre@alvh.no-ip. 652 [ # # ]:UBC 0 : if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
2784 andres@anarazel.de 653 : 0 : return TM_BeingModified;
654 : : }
655 : :
656 : : /* not LOCKED_ONLY, so it has to have an xmax */
4724 alvherre@alvh.no-ip. 657 [ - + ]:CBC 2161 : Assert(TransactionIdIsValid(xmax));
658 : :
5034 659 [ - + ]: 2161 : if (TransactionIdIsCurrentTransactionId(xmax))
660 : : {
5034 alvherre@alvh.no-ip. 661 [ # # ]:UBC 0 : if (HeapTupleHeaderGetCmax(tuple) >= curcid)
2784 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 : :
4482 alvherre@alvh.no-ip. 667 [ + + ]:CBC 2161 : if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
2784 andres@anarazel.de 668 : 2158 : return TM_BeingModified;
669 : :
5034 alvherre@alvh.no-ip. 670 [ - + ]: 3 : if (TransactionIdDidCommit(xmax))
671 : : {
2082 alvherre@alvh.no-ip. 672 [ # # ]:LBC (1) : if (!ItemPointerEquals(&htup->t_self, &tuple->t_ctid))
2784 andres@anarazel.de 673 :UBC 0 : return TM_Updated;
674 : : else
2784 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 : :
4482 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 : : */
4724 689 : 3 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
690 : : InvalidTransactionId);
2784 andres@anarazel.de 691 : 3 : return TM_Ok;
692 : : }
693 : : else
694 : : {
695 : : /* There are lockers running */
2784 andres@anarazel.de 696 :UBC 0 : return TM_BeingModified;
697 : : }
698 : : }
699 : :
5034 alvherre@alvh.no-ip. 700 [ + + ]:CBC 647059 : if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
701 : : {
702 [ + + ]: 643066 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
2784 andres@anarazel.de 703 : 642965 : return TM_BeingModified;
8909 bruce@momjian.us 704 [ + - ]: 101 : if (HeapTupleHeaderGetCmax(tuple) >= curcid)
2784 andres@anarazel.de 705 : 101 : return TM_SelfModified; /* updated after scan started */
706 : : else
2784 andres@anarazel.de 707 :UBC 0 : return TM_Invisible; /* updated before scan started */
708 : : }
709 : :
5034 alvherre@alvh.no-ip. 710 [ + + ]:CBC 3993 : if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
2784 andres@anarazel.de 711 : 1429 : return TM_BeingModified;
712 : :
5034 alvherre@alvh.no-ip. 713 [ + + ]: 2564 : if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
714 : : {
715 : : /* it must have aborted or crashed */
7023 tgl@sss.pgh.pa.us 716 : 333 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
717 : : InvalidTransactionId);
2784 andres@anarazel.de 718 : 333 : return TM_Ok;
719 : : }
720 : :
721 : : /* xmax transaction committed */
722 : :
5034 alvherre@alvh.no-ip. 723 [ + + ]: 2231 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
724 : : {
7023 tgl@sss.pgh.pa.us 725 : 2163 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
726 : : InvalidTransactionId);
2784 andres@anarazel.de 727 : 2163 : return TM_Ok;
728 : : }
729 : :
7023 tgl@sss.pgh.pa.us 730 : 68 : SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
731 : : HeapTupleHeaderGetRawXmax(tuple));
2082 alvherre@alvh.no-ip. 732 [ + + ]: 68 : if (!ItemPointerEquals(&htup->t_self, &tuple->t_ctid))
2784 andres@anarazel.de 733 : 67 : 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
4854 rhaas@postgresql.org 759 : 7828478 : HeapTupleSatisfiesDirty(HeapTuple htup, Snapshot snapshot,
760 : : Buffer buffer)
761 : : {
762 : 7828478 : HeapTupleHeader tuple = htup->t_data;
763 : :
764 [ - + ]: 7828478 : Assert(ItemPointerIsValid(&htup->t_self));
765 [ - + ]: 7828478 : Assert(htup->t_tableOid != InvalidOid);
766 : :
7165 tgl@sss.pgh.pa.us 767 : 7828478 : snapshot->xmin = snapshot->xmax = InvalidTransactionId;
4199 andres@anarazel.de 768 : 7828478 : snapshot->speculativeToken = 0;
769 : :
4701 rhaas@postgresql.org 770 [ + + ]: 7828478 : if (!HeapTupleHeaderXminCommitted(tuple))
771 : : {
772 [ + + ]: 7410488 : if (HeapTupleHeaderXminInvalid(tuple))
10187 vadim4o@yahoo.com 773 : 699 : return false;
774 : :
321 andres@anarazel.de 775 [ - + ]: 7409789 : if (!HeapTupleCleanMoved(tuple, buffer))
321 andres@anarazel.de 776 :UBC 0 : return false;
4701 rhaas@postgresql.org 777 [ + + ]:CBC 7409789 : else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
778 : : {
10187 vadim4o@yahoo.com 779 [ + + ]: 7382762 : if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
780 : 31492 : return true;
781 : :
4908 bruce@momjian.us 782 [ + + ]: 7351270 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
7861 tgl@sss.pgh.pa.us 783 : 5041 : return true;
784 : :
5034 alvherre@alvh.no-ip. 785 [ + + ]: 7346229 : 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 */
4724 792 [ - + ]: 16 : Assert(TransactionIdIsValid(xmax));
793 : :
794 : : /* updating subtransaction must have aborted */
5034 795 [ - + ]: 16 : if (!TransactionIdIsCurrentTransactionId(xmax))
5034 alvherre@alvh.no-ip. 796 :UBC 0 : return true;
797 : : else
5034 alvherre@alvh.no-ip. 798 :CBC 16 : return false;
799 : : }
800 : :
801 [ - + ]: 7346213 : if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
802 : : {
803 : : /* deleting subtransaction must have aborted */
7023 tgl@sss.pgh.pa.us 804 :UBC 0 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
805 : : InvalidTransactionId);
8162 806 : 0 : return true;
807 : : }
808 : :
10187 vadim4o@yahoo.com 809 :CBC 7346213 : return false;
810 : : }
4701 rhaas@postgresql.org 811 [ + + ]: 27027 : 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 : : */
4199 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 : :
4701 rhaas@postgresql.org 827 : 48 : snapshot->xmin = HeapTupleHeaderGetRawXmin(tuple);
828 : : /* XXX shouldn't we fall through to look at xmax? */
10026 bruce@momjian.us 829 : 48 : return true; /* in insertion by other */
830 : : }
4701 rhaas@postgresql.org 831 [ + + ]: 26979 : else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
7023 tgl@sss.pgh.pa.us 832 : 26068 : SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
833 : : HeapTupleHeaderGetRawXmin(tuple));
834 : : else
835 : : {
836 : : /* it must have aborted or crashed */
837 : 911 : SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
838 : : InvalidTransactionId);
7852 839 : 911 : return false;
840 : : }
841 : : }
842 : :
843 : : /* by here, the inserting transaction has committed */
844 : :
10187 vadim4o@yahoo.com 845 [ + + ]: 444058 : if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
846 : 136489 : return true;
847 : :
848 [ + + ]: 307569 : if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
849 : : {
5034 alvherre@alvh.no-ip. 850 [ - + ]: 97842 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
10187 vadim4o@yahoo.com 851 :UBC 0 : return true;
10026 bruce@momjian.us 852 :CBC 97842 : return false; /* updated by other */
853 : : }
854 : :
7861 tgl@sss.pgh.pa.us 855 [ + + ]: 209727 : if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
856 : : {
857 : : TransactionId xmax;
858 : :
5034 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 */
4724 865 [ - + ]: 17 : Assert(TransactionIdIsValid(xmax));
866 : :
5034 867 [ + + ]: 17 : if (TransactionIdIsCurrentTransactionId(xmax))
868 : 1 : return false;
869 [ - + ]: 16 : if (TransactionIdIsInProgress(xmax))
870 : : {
5034 alvherre@alvh.no-ip. 871 :UBC 0 : snapshot->xmax = xmax;
872 : 0 : return true;
873 : : }
5034 alvherre@alvh.no-ip. 874 [ + - ]:CBC 16 : if (TransactionIdDidCommit(xmax))
875 : 16 : return false;
876 : : /* it must have aborted or crashed */
7861 tgl@sss.pgh.pa.us 877 :UBC 0 : return true;
878 : : }
879 : :
5034 alvherre@alvh.no-ip. 880 [ + + ]:CBC 209697 : if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
881 : : {
882 [ + + ]: 160118 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
9621 inoue@tpf.co.jp 883 : 71 : return true;
10187 vadim4o@yahoo.com 884 : 160047 : return false;
885 : : }
886 : :
5034 alvherre@alvh.no-ip. 887 [ + + ]: 49579 : if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
888 : : {
4843 889 [ + + ]: 24 : if (!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
890 : 14 : snapshot->xmax = HeapTupleHeaderGetRawXmax(tuple);
7852 tgl@sss.pgh.pa.us 891 : 24 : return true;
892 : : }
893 : :
5034 alvherre@alvh.no-ip. 894 [ + + ]: 49555 : if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
895 : : {
896 : : /* it must have aborted or crashed */
7023 tgl@sss.pgh.pa.us 897 : 57 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
898 : : InvalidTransactionId);
7852 899 : 57 : return true;
900 : : }
901 : :
902 : : /* xmax transaction committed */
903 : :
5034 alvherre@alvh.no-ip. 904 [ + + ]: 49498 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
905 : : {
7023 tgl@sss.pgh.pa.us 906 : 10322 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
907 : : InvalidTransactionId);
10187 vadim4o@yahoo.com 908 : 10322 : return true;
909 : : }
910 : :
7023 tgl@sss.pgh.pa.us 911 : 39176 : SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
912 : : HeapTupleHeaderGetRawXmax(tuple));
10026 bruce@momjian.us 913 : 39176 : 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
4854 rhaas@postgresql.org 939 : 88805367 : HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot,
940 : : Buffer buffer, SetHintBitsState *state)
941 : : {
942 : 88805367 : 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 : : */
604 heikki.linnakangas@i 951 [ + + - + ]: 88805367 : Assert(snapshot->regd_count > 0 || snapshot->active_count > 0);
952 : :
4854 rhaas@postgresql.org 953 [ - + ]: 88805367 : Assert(ItemPointerIsValid(&htup->t_self));
954 [ - + ]: 88805367 : Assert(htup->t_tableOid != InvalidOid);
955 : :
4701 956 [ + + ]: 88805367 : if (!HeapTupleHeaderXminCommitted(tuple))
957 : : {
958 [ + + ]: 17604963 : if (HeapTupleHeaderXminInvalid(tuple))
10186 vadim4o@yahoo.com 959 : 160022 : return false;
960 : :
321 andres@anarazel.de 961 [ - + ]: 17444941 : if (!HeapTupleCleanMoved(tuple, buffer))
321 andres@anarazel.de 962 :UBC 0 : return false;
4701 rhaas@postgresql.org 963 [ + + ]:CBC 17444941 : else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
964 : : {
8909 bruce@momjian.us 965 [ + + ]: 14685808 : if (HeapTupleHeaderGetCmin(tuple) >= snapshot->curcid)
10186 vadim4o@yahoo.com 966 : 71451 : return false; /* inserted after scan started */
967 : :
968 [ + + ]: 14614357 : if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
969 : 10737904 : return true;
970 : :
4908 bruce@momjian.us 971 [ + + ]: 3876453 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
7861 tgl@sss.pgh.pa.us 972 : 2275 : return true;
973 : :
5034 alvherre@alvh.no-ip. 974 [ + + ]: 3874178 : 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 */
4724 981 [ - + ]: 8 : Assert(TransactionIdIsValid(xmax));
982 : :
983 : : /* updating subtransaction must have aborted */
5034 984 [ + - ]: 8 : if (!TransactionIdIsCurrentTransactionId(xmax))
985 : 8 : return true;
5034 alvherre@alvh.no-ip. 986 [ # # ]:UBC 0 : else if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
987 : 0 : return true; /* updated after scan started */
988 : : else
3424 tgl@sss.pgh.pa.us 989 : 0 : return false; /* updated before scan started */
990 : : }
991 : :
5034 alvherre@alvh.no-ip. 992 [ + + ]:CBC 3874170 : if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
993 : : {
994 : : /* deleting subtransaction must have aborted */
240 andres@anarazel.de 995 : 22 : SetHintBitsExt(tuple, buffer, HEAP_XMAX_INVALID,
996 : : InvalidTransactionId, state);
8162 tgl@sss.pgh.pa.us 997 : 22 : return true;
998 : : }
999 : :
8909 bruce@momjian.us 1000 [ + + ]: 3874148 : if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
10186 vadim4o@yahoo.com 1001 : 767 : return true; /* deleted after scan started */
1002 : : else
1003 : 3873381 : return false; /* deleted before scan started */
1004 : : }
4089 tgl@sss.pgh.pa.us 1005 [ + + ]: 2759133 : else if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
10186 vadim4o@yahoo.com 1006 : 21727 : return false;
4701 rhaas@postgresql.org 1007 [ + + ]: 2737406 : else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
240 andres@anarazel.de 1008 : 2678046 : SetHintBitsExt(tuple, buffer, HEAP_XMIN_COMMITTED,
1009 : : HeapTupleHeaderGetRawXmin(tuple), state);
1010 : : else
1011 : : {
1012 : : /* it must have aborted or crashed */
1013 : 59360 : SetHintBitsExt(tuple, buffer, HEAP_XMIN_INVALID,
1014 : : InvalidTransactionId, state);
7852 tgl@sss.pgh.pa.us 1015 : 59360 : return false;
1016 : : }
1017 : : }
1018 : : else
1019 : : {
1020 : : /* xmin is committed, but maybe not according to our snapshot */
4089 1021 [ + + + + ]: 137995875 : if (!HeapTupleHeaderXminFrozen(tuple) &&
1022 : 66795471 : XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
1023 : 2608 : return false; /* treat as still in progress */
1024 : : }
1025 : :
1026 : : /* by here, the inserting transaction has committed */
1027 : :
10186 vadim4o@yahoo.com 1028 [ + + ]: 73875842 : if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
1029 : 68272949 : return true;
1030 : :
5034 alvherre@alvh.no-ip. 1031 [ + + ]: 5602893 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
7861 tgl@sss.pgh.pa.us 1032 : 699870 : return true;
1033 : :
1034 [ + + ]: 4903023 : if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1035 : : {
1036 : : TransactionId xmax;
1037 : :
1038 : : /* already checked above */
5034 alvherre@alvh.no-ip. 1039 [ - + ]: 78692 : Assert(!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask));
1040 : :
1041 : 78692 : xmax = HeapTupleGetUpdateXid(tuple);
1042 : :
1043 : : /* not LOCKED_ONLY, so it has to have an xmax */
4724 1044 [ - + ]: 78692 : Assert(TransactionIdIsValid(xmax));
1045 : :
5034 1046 [ + + ]: 78692 : if (TransactionIdIsCurrentTransactionId(xmax))
1047 : : {
1048 [ - + ]: 23 : if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
5034 alvherre@alvh.no-ip. 1049 :UBC 0 : return true; /* deleted after scan started */
1050 : : else
5034 alvherre@alvh.no-ip. 1051 :CBC 23 : return false; /* deleted before scan started */
1052 : : }
4089 tgl@sss.pgh.pa.us 1053 [ + + ]: 78669 : if (XidInMVCCSnapshot(xmax, snapshot))
5034 alvherre@alvh.no-ip. 1054 : 2131 : return true;
1055 [ + + ]: 76538 : if (TransactionIdDidCommit(xmax))
4089 tgl@sss.pgh.pa.us 1056 : 76519 : return false; /* updating transaction committed */
1057 : : /* it must have aborted or crashed */
10186 vadim4o@yahoo.com 1058 : 19 : return true;
1059 : : }
1060 : :
1061 [ + + ]: 4824331 : if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
1062 : : {
5034 alvherre@alvh.no-ip. 1063 [ + + ]: 550099 : if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
1064 : : {
8909 bruce@momjian.us 1065 [ + + ]: 171232 : if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
10026 1066 : 1501 : return true; /* deleted after scan started */
1067 : : else
1068 : 169731 : return false; /* deleted before scan started */
1069 : : }
1070 : :
4089 tgl@sss.pgh.pa.us 1071 [ + + ]: 378867 : if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
7852 1072 : 11800 : return true;
1073 : :
5034 alvherre@alvh.no-ip. 1074 [ + + ]: 367067 : if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
1075 : : {
1076 : : /* it must have aborted or crashed */
240 andres@anarazel.de 1077 : 8506 : SetHintBitsExt(tuple, buffer, HEAP_XMAX_INVALID,
1078 : : InvalidTransactionId, state);
10186 vadim4o@yahoo.com 1079 : 8506 : return true;
1080 : : }
1081 : :
1082 : : /* xmax transaction committed */
240 andres@anarazel.de 1083 : 358561 : 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 */
4089 tgl@sss.pgh.pa.us 1089 [ + + ]: 4274232 : if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
1090 : 444 : return true; /* treat as still in progress */
1091 : : }
1092 : :
1093 : : /* xmax transaction committed */
1094 : :
10186 vadim4o@yahoo.com 1095 : 4632349 : 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
4854 rhaas@postgresql.org 1113 : 9388535 : HeapTupleSatisfiesVacuum(HeapTuple htup, TransactionId OldestXmin,
1114 : : Buffer buffer)
1115 : : {
2276 andres@anarazel.de 1116 : 9388535 : TransactionId dead_after = InvalidTransactionId;
1117 : : HTSV_Result res;
1118 : :
1119 : 9388535 : res = HeapTupleSatisfiesVacuumHorizon(htup, buffer, &dead_after);
1120 : :
1121 [ + + ]: 9388535 : if (res == HEAPTUPLE_RECENTLY_DEAD)
1122 : : {
1123 [ - + ]: 199997 : Assert(TransactionIdIsValid(dead_after));
1124 : :
1125 [ + + ]: 199997 : if (TransactionIdPrecedes(dead_after, OldestXmin))
1126 : 19939 : res = HEAPTUPLE_DEAD;
1127 : : }
1128 : : else
1129 [ - + ]: 9188538 : Assert(!TransactionIdIsValid(dead_after));
1130 : :
1131 : 9388535 : 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 : 44247183 : HeapTupleSatisfiesVacuumHorizon(HeapTuple htup, Buffer buffer, TransactionId *dead_after)
1148 : : {
4854 rhaas@postgresql.org 1149 : 44247183 : HeapTupleHeader tuple = htup->t_data;
1150 : :
1151 [ - + ]: 44247183 : Assert(ItemPointerIsValid(&htup->t_self));
1152 [ - + ]: 44247183 : Assert(htup->t_tableOid != InvalidOid);
2276 andres@anarazel.de 1153 [ - + ]: 44247183 : Assert(dead_after != NULL);
1154 : :
1155 : 44247183 : *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 : : */
4701 rhaas@postgresql.org 1163 [ + + ]: 44247183 : if (!HeapTupleHeaderXminCommitted(tuple))
1164 : : {
1165 [ + + ]: 13334388 : if (HeapTupleHeaderXminInvalid(tuple))
9247 tgl@sss.pgh.pa.us 1166 : 50316 : return HEAPTUPLE_DEAD;
321 andres@anarazel.de 1167 [ - + ]: 13284072 : else if (!HeapTupleCleanMoved(tuple, buffer))
321 andres@anarazel.de 1168 :UBC 0 : return HEAPTUPLE_DEAD;
4537 andres@anarazel.de 1169 [ + + ]:CBC 13284072 : else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
1170 : : {
8445 tgl@sss.pgh.pa.us 1171 [ + + ]: 2358608 : if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
1172 : 2317288 : return HEAPTUPLE_INSERT_IN_PROGRESS;
1173 : : /* only locked? run infomask-only check first, for performance */
4857 alvherre@alvh.no-ip. 1174 [ + + - + ]: 74373 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask) ||
1175 : 33053 : HeapTupleHeaderIsOnlyLocked(tuple))
8445 tgl@sss.pgh.pa.us 1176 : 8267 : return HEAPTUPLE_INSERT_IN_PROGRESS;
1177 : : /* inserted and then deleted by same xact */
4537 andres@anarazel.de 1178 [ + + ]: 33053 : if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetUpdateXid(tuple)))
1179 : 33042 : return HEAPTUPLE_DELETE_IN_PROGRESS;
1180 : : /* deleting subtransaction must have aborted */
10 michael@paquier.xyz 1181 : 11 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
1182 : : InvalidTransactionId);
4537 andres@anarazel.de 1183 : 11 : return HEAPTUPLE_INSERT_IN_PROGRESS;
1184 : : }
1185 [ + + ]: 10925464 : else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
1186 : : {
1187 : : /*
1188 : : * It'd be possible to discern between INSERT/DELETE in progress
1189 : : * here by looking at xmax - but that doesn't seem beneficial for
1190 : : * the majority of callers and even detrimental for some. We'd
1191 : : * rather have callers look at/wait for xmin than xmax. It's
1192 : : * always correct to return INSERT_IN_PROGRESS because that's
1193 : : * what's happening from the view of other backends.
1194 : : */
1195 : 7761 : return HEAPTUPLE_INSERT_IN_PROGRESS;
1196 : : }
4701 rhaas@postgresql.org 1197 [ + + ]: 10917703 : else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
7023 tgl@sss.pgh.pa.us 1198 : 10850457 : SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
1199 : : HeapTupleHeaderGetRawXmin(tuple));
1200 : : else
1201 : : {
1202 : : /*
1203 : : * Not in Progress, Not Committed, so either Aborted or crashed
1204 : : */
1205 : 67246 : SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
1206 : : InvalidTransactionId);
9247 1207 : 67246 : return HEAPTUPLE_DEAD;
1208 : : }
1209 : :
1210 : : /*
1211 : : * At this point the xmin is known committed, but we might not have
1212 : : * been able to set the hint bit yet; so we can no longer Assert that
1213 : : * it's set.
1214 : : */
1215 : : }
1216 : :
1217 : : /*
1218 : : * Okay, the inserter committed, so it was good at some point. Now what
1219 : : * about the deleting transaction?
1220 : : */
1221 [ + + ]: 41763252 : if (tuple->t_infomask & HEAP_XMAX_INVALID)
1222 : 36807552 : return HEAPTUPLE_LIVE;
1223 : :
5034 alvherre@alvh.no-ip. 1224 [ + + ]: 4955700 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
1225 : : {
1226 : : /*
1227 : : * "Deleting" xact really only locked it, so the tuple is live in any
1228 : : * case. However, we should make sure that either XMAX_COMMITTED or
1229 : : * XMAX_INVALID gets set once the xact is gone, to reduce the costs of
1230 : : * examining the tuple for future xacts.
1231 : : */
9064 tgl@sss.pgh.pa.us 1232 [ + - ]: 18370 : if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
1233 : : {
7861 1234 [ + + ]: 18370 : if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1235 : : {
1236 : : /*
1237 : : * If it's a pre-pg_upgrade tuple, the multixact cannot
1238 : : * possibly be running; otherwise have to check.
1239 : : */
3786 alvherre@alvh.no-ip. 1240 [ + - + + ]: 504 : if (!HEAP_LOCKED_UPGRADED(tuple->t_infomask) &&
4482 1241 : 252 : MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple),
1242 : : true))
7861 tgl@sss.pgh.pa.us 1243 : 21 : return HEAPTUPLE_LIVE;
5034 alvherre@alvh.no-ip. 1244 : 231 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
1245 : : }
1246 : : else
1247 : : {
1248 [ + + ]: 18118 : if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
7861 tgl@sss.pgh.pa.us 1249 : 6 : return HEAPTUPLE_LIVE;
5034 alvherre@alvh.no-ip. 1250 : 18112 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
1251 : : InvalidTransactionId);
1252 : : }
1253 : : }
1254 : :
1255 : : /*
1256 : : * We don't really care whether xmax did commit, abort or crash. We
1257 : : * know that xmax did lock the tuple, but it did not and will never
1258 : : * actually update it.
1259 : : */
1260 : :
9087 tgl@sss.pgh.pa.us 1261 : 18343 : return HEAPTUPLE_LIVE;
1262 : : }
1263 : :
7861 1264 [ + + ]: 4937330 : if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1265 : : {
3289 andres@anarazel.de 1266 : 102 : TransactionId xmax = HeapTupleGetUpdateXid(tuple);
1267 : :
1268 : : /* already checked above */
1269 [ - + ]: 102 : Assert(!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask));
1270 : :
1271 : : /* not LOCKED_ONLY, so it has to have an xmax */
4724 alvherre@alvh.no-ip. 1272 [ - + ]: 102 : Assert(TransactionIdIsValid(xmax));
1273 : :
3289 andres@anarazel.de 1274 [ + + ]: 102 : if (TransactionIdIsInProgress(xmax))
1275 : 1 : return HEAPTUPLE_DELETE_IN_PROGRESS;
1276 [ + - ]: 101 : else if (TransactionIdDidCommit(xmax))
1277 : : {
1278 : : /*
1279 : : * The multixact might still be running due to lockers. Need to
1280 : : * allow for pruning if below the xid horizon regardless --
1281 : : * otherwise we could end up with a tuple where the updater has to
1282 : : * be removed due to the horizon, but is not pruned away. It's
1283 : : * not a problem to prune that tuple, because any remaining
1284 : : * lockers will also be present in newer tuple versions.
1285 : : */
2276 1286 : 101 : *dead_after = xmax;
1287 : 101 : return HEAPTUPLE_RECENTLY_DEAD;
1288 : : }
3289 andres@anarazel.de 1289 [ # # ]:UBC 0 : else if (!MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
1290 : : {
1291 : : /*
1292 : : * Not in Progress, Not Committed, so either Aborted or crashed.
1293 : : * Mark the Xmax as invalid.
1294 : : */
1295 : 0 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
1296 : : }
1297 : :
4724 alvherre@alvh.no-ip. 1298 : 0 : return HEAPTUPLE_LIVE;
1299 : : }
1300 : :
9247 tgl@sss.pgh.pa.us 1301 [ + + ]:CBC 4937228 : if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
1302 : : {
5034 alvherre@alvh.no-ip. 1303 [ + + ]: 3776733 : if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
9243 tgl@sss.pgh.pa.us 1304 : 91673 : return HEAPTUPLE_DELETE_IN_PROGRESS;
5034 alvherre@alvh.no-ip. 1305 [ + + ]: 3685060 : else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
7023 tgl@sss.pgh.pa.us 1306 : 3682096 : SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
1307 : : HeapTupleHeaderGetRawXmax(tuple));
1308 : : else
1309 : : {
1310 : : /*
1311 : : * Not in Progress, Not Committed, so either Aborted or crashed
1312 : : */
1313 : 2964 : SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
1314 : : InvalidTransactionId);
9247 1315 : 2964 : return HEAPTUPLE_LIVE;
1316 : : }
1317 : :
1318 : : /*
1319 : : * At this point the xmax is known committed, but we might not have
1320 : : * been able to set the hint bit yet; so we can no longer Assert that
1321 : : * it's set.
1322 : : */
1323 : : }
1324 : :
1325 : : /*
1326 : : * Deleter committed, allow caller to check if it was recent enough that
1327 : : * some open transactions could still see the tuple.
1328 : : */
2276 andres@anarazel.de 1329 : 4842591 : *dead_after = HeapTupleHeaderGetRawXmax(tuple);
1330 : 4842591 : return HEAPTUPLE_RECENTLY_DEAD;
1331 : : }
1332 : :
1333 : :
1334 : : /*
1335 : : * HeapTupleSatisfiesNonVacuumable
1336 : : *
1337 : : * True if tuple might be visible to some transaction; false if it's
1338 : : * surely dead to everyone, ie, vacuumable.
1339 : : *
1340 : : * See SNAPSHOT_NON_VACUUMABLE's definition for the intended behaviour.
1341 : : *
1342 : : * This is an interface to HeapTupleSatisfiesVacuum that's callable via
1343 : : * HeapTupleSatisfiesSnapshot, so it can be used through a Snapshot.
1344 : : * snapshot->vistest must have been set up with the horizon to use.
1345 : : */
1346 : : static bool
3346 tgl@sss.pgh.pa.us 1347 : 528827 : HeapTupleSatisfiesNonVacuumable(HeapTuple htup, Snapshot snapshot,
1348 : : Buffer buffer)
1349 : : {
2276 andres@anarazel.de 1350 : 528827 : TransactionId dead_after = InvalidTransactionId;
1351 : : HTSV_Result res;
1352 : :
1353 : 528827 : res = HeapTupleSatisfiesVacuumHorizon(htup, buffer, &dead_after);
1354 : :
1355 [ + + ]: 528827 : if (res == HEAPTUPLE_RECENTLY_DEAD)
1356 : : {
1357 [ - + ]: 104060 : Assert(TransactionIdIsValid(dead_after));
1358 : :
226 melanieplageman@gmai 1359 [ + + ]: 104060 : if (GlobalVisTestIsRemovableXid(snapshot->vistest, dead_after, true))
2276 andres@anarazel.de 1360 : 85645 : res = HEAPTUPLE_DEAD;
1361 : : }
1362 : : else
1363 [ - + ]: 424767 : Assert(!TransactionIdIsValid(dead_after));
1364 : :
1365 : 528827 : return res != HEAPTUPLE_DEAD;
1366 : : }
1367 : :
1368 : :
1369 : : /*
1370 : : * HeapTupleIsSurelyDead
1371 : : *
1372 : : * Cheaply determine whether a tuple is surely dead to all onlookers.
1373 : : * We sometimes use this in lieu of HeapTupleSatisfiesVacuum when the
1374 : : * tuple has just been tested by another visibility routine (usually
1375 : : * HeapTupleSatisfiesMVCC) and, therefore, any hint bits that can be set
1376 : : * should already be set. We assume that if no hint bits are set, the xmin
1377 : : * or xmax transaction is still running. This is therefore faster than
1378 : : * HeapTupleSatisfiesVacuum, because we consult neither procarray nor CLOG.
1379 : : * It's okay to return false when in doubt, but we must return true only
1380 : : * if the tuple is removable.
1381 : : */
1382 : : bool
1383 : 8619160 : HeapTupleIsSurelyDead(HeapTuple htup, GlobalVisState *vistest)
1384 : : {
4854 rhaas@postgresql.org 1385 : 8619160 : HeapTupleHeader tuple = htup->t_data;
1386 : :
1387 [ - + ]: 8619160 : Assert(ItemPointerIsValid(&htup->t_self));
1388 [ - + ]: 8619160 : Assert(htup->t_tableOid != InvalidOid);
1389 : :
1390 : : /*
1391 : : * If the inserting transaction is marked invalid, then it aborted, and
1392 : : * the tuple is definitely dead. If it's marked neither committed nor
1393 : : * invalid, then we assume it's still alive (since the presumption is that
1394 : : * all relevant hint bits were just set moments ago).
1395 : : */
4701 1396 [ + + ]: 8619160 : if (!HeapTupleHeaderXminCommitted(tuple))
1884 michael@paquier.xyz 1397 : 7588913 : return HeapTupleHeaderXminInvalid(tuple);
1398 : :
1399 : : /*
1400 : : * If the inserting transaction committed, but any deleting transaction
1401 : : * aborted, the tuple is still alive.
1402 : : */
5034 alvherre@alvh.no-ip. 1403 [ + + ]: 1030247 : if (tuple->t_infomask & HEAP_XMAX_INVALID)
1404 : 39 : return false;
1405 : :
1406 : : /*
1407 : : * If the XMAX is just a lock, the tuple is still alive.
1408 : : */
1409 [ + + ]: 1030208 : if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
1410 : 3 : return false;
1411 : :
1412 : : /*
1413 : : * If the Xmax is a MultiXact, it might be dead or alive, but we cannot
1414 : : * know without checking pg_multixact.
1415 : : */
1416 [ + + ]: 1030205 : if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
5300 rhaas@postgresql.org 1417 : 147 : return false;
1418 : :
1419 : : /* If deleter isn't known to have committed, assume it's still running. */
1420 [ + + ]: 1030058 : if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
1421 : 291117 : return false;
1422 : :
1423 : : /* Deleter committed, so tuple is dead if the XID is old enough. */
2276 andres@anarazel.de 1424 : 738941 : return GlobalVisTestIsRemovableXid(vistest,
1425 : : HeapTupleHeaderGetRawXmax(tuple),
1426 : : true);
1427 : : }
1428 : :
1429 : : /*
1430 : : * Is the tuple really only locked? That is, is it not updated?
1431 : : *
1432 : : * It's easy to check just infomask bits if the locker is not a multi; but
1433 : : * otherwise we need to verify that the updating transaction has not aborted.
1434 : : *
1435 : : * This function is here because it follows the same visibility rules laid out
1436 : : * at the top of this file.
1437 : : */
1438 : : bool
5034 alvherre@alvh.no-ip. 1439 : 132737 : HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple)
1440 : : {
1441 : : TransactionId xmax;
1442 : :
1443 : : /* if there's no valid Xmax, then there's obviously no update either */
1444 [ - + ]: 132737 : if (tuple->t_infomask & HEAP_XMAX_INVALID)
5034 alvherre@alvh.no-ip. 1445 :UBC 0 : return true;
1446 : :
5034 alvherre@alvh.no-ip. 1447 [ + + ]:CBC 132737 : if (tuple->t_infomask & HEAP_XMAX_LOCK_ONLY)
1448 : 71697 : return true;
1449 : :
1450 : : /* invalid xmax means no update */
1451 [ - + ]: 61040 : if (!TransactionIdIsValid(HeapTupleHeaderGetRawXmax(tuple)))
5034 alvherre@alvh.no-ip. 1452 :UBC 0 : return true;
1453 : :
1454 : : /*
1455 : : * if HEAP_XMAX_LOCK_ONLY is not set and not a multi, then this must
1456 : : * necessarily have been updated
1457 : : */
5034 alvherre@alvh.no-ip. 1458 [ + + ]:CBC 61040 : if (!(tuple->t_infomask & HEAP_XMAX_IS_MULTI))
1459 : 58890 : return false;
1460 : :
1461 : : /* ... but if it's a multi, then perhaps the updating Xid aborted. */
1462 : 2150 : xmax = HeapTupleGetUpdateXid(tuple);
1463 : :
1464 : : /* not LOCKED_ONLY, so it has to have an xmax */
4724 1465 [ - + ]: 2150 : Assert(TransactionIdIsValid(xmax));
1466 : :
5034 1467 [ - + ]: 2150 : if (TransactionIdIsCurrentTransactionId(xmax))
5034 alvherre@alvh.no-ip. 1468 :UBC 0 : return false;
5034 alvherre@alvh.no-ip. 1469 [ + + ]:CBC 2150 : if (TransactionIdIsInProgress(xmax))
1470 : 2112 : return false;
1471 [ + + ]: 38 : if (TransactionIdDidCommit(xmax))
1472 : 12 : return false;
1473 : :
1474 : : /*
1475 : : * not current, not in progress, not committed -- must have aborted or
1476 : : * crashed
1477 : : */
1478 : 26 : return true;
1479 : : }
1480 : :
1481 : : /*
1482 : : * check whether the transaction id 'xid' is in the pre-sorted array 'xip'.
1483 : : */
1484 : : static bool
4630 rhaas@postgresql.org 1485 : 57417 : TransactionIdInArray(TransactionId xid, TransactionId *xip, Size num)
1486 : : {
1708 tgl@sss.pgh.pa.us 1487 [ + + + + ]: 73647 : return num > 0 &&
1488 : 16230 : bsearch(&xid, xip, num, sizeof(TransactionId), xidComparator) != NULL;
1489 : : }
1490 : :
1491 : : /*
1492 : : * See the comments for HeapTupleSatisfiesMVCC for the semantics this function
1493 : : * obeys.
1494 : : *
1495 : : * Only usable on tuples from catalog tables!
1496 : : *
1497 : : * We don't need to support HEAP_MOVED_(IN|OFF) for now because we only support
1498 : : * reading catalog pages which couldn't have been created in an older version.
1499 : : *
1500 : : * We don't set any hint bits in here as it seems unlikely to be beneficial as
1501 : : * those should already be set by normal access and it seems to be too
1502 : : * dangerous to do so as the semantics of doing so during timetravel are more
1503 : : * complicated than when dealing "only" with the present.
1504 : : */
1505 : : static bool
4630 rhaas@postgresql.org 1506 : 47508 : HeapTupleSatisfiesHistoricMVCC(HeapTuple htup, Snapshot snapshot,
1507 : : Buffer buffer)
1508 : : {
1509 : 47508 : HeapTupleHeader tuple = htup->t_data;
1510 : 47508 : TransactionId xmin = HeapTupleHeaderGetXmin(tuple);
1511 : 47508 : TransactionId xmax = HeapTupleHeaderGetRawXmax(tuple);
1512 : :
1513 [ - + ]: 47508 : Assert(ItemPointerIsValid(&htup->t_self));
1514 [ - + ]: 47508 : Assert(htup->t_tableOid != InvalidOid);
1515 : :
1516 : : /* inserting transaction aborted */
1517 [ - + ]: 47508 : if (HeapTupleHeaderXminInvalid(tuple))
1518 : : {
4630 rhaas@postgresql.org 1519 [ # # ]:LBC (75) : Assert(!TransactionIdDidCommit(xmin));
1520 : (75) : return false;
1521 : : }
1522 : : /* check if it's one of our txids, toplevel is also in there */
4630 rhaas@postgresql.org 1523 [ + + ]:CBC 47508 : else if (TransactionIdInArray(xmin, snapshot->subxip, snapshot->subxcnt))
1524 : : {
1525 : : bool resolved;
1526 : 509 : CommandId cmin = HeapTupleHeaderGetRawCommandId(tuple);
1527 : 509 : CommandId cmax = InvalidCommandId;
1528 : :
1529 : : /*
1530 : : * another transaction might have (tried to) delete this tuple or
1531 : : * cmin/cmax was stored in a combo CID. So we need to lookup the
1532 : : * actual values externally.
1533 : : */
1534 : 509 : resolved = ResolveCminCmaxDuringDecoding(HistoricSnapshotGetTupleCids(), snapshot,
1535 : : htup, buffer,
1536 : : &cmin, &cmax);
1537 : :
1538 : : /*
1539 : : * If we haven't resolved the combo CID to cmin/cmax, that means we
1540 : : * have not decoded the combo CID yet. That means the cmin is
1541 : : * definitely in the future, and we're not supposed to see the tuple
1542 : : * yet.
1543 : : *
1544 : : * XXX This only applies to decoding of in-progress transactions. In
1545 : : * regular logical decoding we only execute this code at commit time,
1546 : : * at which point we should have seen all relevant combo CIDs. So
1547 : : * ideally, we should error out in this case but in practice, this
1548 : : * won't happen. If we are too worried about this then we can add an
1549 : : * elog inside ResolveCminCmaxDuringDecoding.
1550 : : *
1551 : : * XXX For the streaming case, we can track the largest combo CID
1552 : : * assigned, and error out based on this (when unable to resolve combo
1553 : : * CID below that observed maximum value).
1554 : : */
1555 [ - + ]: 509 : if (!resolved)
2280 akapila@postgresql.o 1556 : 40 : return false;
1557 : :
4630 rhaas@postgresql.org 1558 [ - + ]: 509 : Assert(cmin != InvalidCommandId);
1559 : :
1560 [ + + ]: 509 : if (cmin >= snapshot->curcid)
4566 bruce@momjian.us 1561 : 40 : return false; /* inserted after scan started */
1562 : : /* fall through */
1563 : : }
1564 : : /* committed before our xmin horizon. Do a normal visibility check. */
4630 rhaas@postgresql.org 1565 [ + + ]: 46999 : else if (TransactionIdPrecedes(xmin, snapshot->xmin))
1566 : : {
1567 [ + + - + ]: 43202 : Assert(!(HeapTupleHeaderXminCommitted(tuple) &&
1568 : : !TransactionIdDidCommit(xmin)));
1569 : :
1570 : : /* check for hint bit first, consult clog afterwards */
1571 [ + + ]: 43202 : if (!HeapTupleHeaderXminCommitted(tuple) &&
1572 [ - + ]: 52 : !TransactionIdDidCommit(xmin))
4630 rhaas@postgresql.org 1573 :UBC 0 : return false;
1574 : : /* fall through */
1575 : : }
1576 : : /* beyond our xmax horizon, i.e. invisible */
4630 rhaas@postgresql.org 1577 [ + + ]:CBC 3797 : else if (TransactionIdFollowsOrEquals(xmin, snapshot->xmax))
1578 : : {
1579 : 123 : return false;
1580 : : }
1581 : : /* check if it's a committed transaction in [xmin, xmax) */
4566 bruce@momjian.us 1582 [ - + ]: 3674 : else if (TransactionIdInArray(xmin, snapshot->xip, snapshot->xcnt))
1583 : : {
1584 : : /* fall through */
1585 : : }
1586 : :
1587 : : /*
1588 : : * none of the above, i.e. between [xmin, xmax) but hasn't committed. I.e.
1589 : : * invisible.
1590 : : */
1591 : : else
1592 : : {
4630 rhaas@postgresql.org 1593 :UBC 0 : return false;
1594 : : }
1595 : :
1596 : : /* at this point we know xmin is visible, go on to check xmax */
1597 : :
1598 : : /* xid invalid or aborted */
4630 rhaas@postgresql.org 1599 [ + + ]:CBC 47345 : if (tuple->t_infomask & HEAP_XMAX_INVALID)
1600 : 38903 : return true;
1601 : : /* locked tuples are always visible */
1602 [ + + ]: 8442 : else if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
1603 : 3904 : return true;
1604 : :
1605 : : /*
1606 : : * We can see multis here if we're looking at user tables or if somebody
1607 : : * SELECT ... FOR SHARE/UPDATE a system table.
1608 : : */
1609 [ + + ]: 4538 : else if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1610 : : {
1611 : 49 : xmax = HeapTupleGetUpdateXid(tuple);
1612 : : }
1613 : :
1614 : : /* check if it's one of our txids, toplevel is also in there */
1615 [ + + ]: 4538 : if (TransactionIdInArray(xmax, snapshot->subxip, snapshot->subxcnt))
1616 : : {
1617 : : bool resolved;
1618 : : CommandId cmin;
4566 bruce@momjian.us 1619 : 311 : CommandId cmax = HeapTupleHeaderGetRawCommandId(tuple);
1620 : :
1621 : : /* Lookup actual cmin/cmax values */
4630 rhaas@postgresql.org 1622 : 311 : resolved = ResolveCminCmaxDuringDecoding(HistoricSnapshotGetTupleCids(), snapshot,
1623 : : htup, buffer,
1624 : : &cmin, &cmax);
1625 : :
1626 : : /*
1627 : : * If we haven't resolved the combo CID to cmin/cmax, that means we
1628 : : * have not decoded the combo CID yet. That means the cmax is
1629 : : * definitely in the future, and we're still supposed to see the
1630 : : * tuple.
1631 : : *
1632 : : * XXX This only applies to decoding of in-progress transactions. In
1633 : : * regular logical decoding we only execute this code at commit time,
1634 : : * at which point we should have seen all relevant combo CIDs. So
1635 : : * ideally, we should error out in this case but in practice, this
1636 : : * won't happen. If we are too worried about this then we can add an
1637 : : * elog inside ResolveCminCmaxDuringDecoding.
1638 : : *
1639 : : * XXX For the streaming case, we can track the largest combo CID
1640 : : * assigned, and error out based on this (when unable to resolve combo
1641 : : * CID below that observed maximum value).
1642 : : */
2280 akapila@postgresql.o 1643 [ + + - + ]: 311 : if (!resolved || cmax == InvalidCommandId)
1644 : 11 : return true;
1645 : :
4630 rhaas@postgresql.org 1646 [ + + ]: 300 : if (cmax >= snapshot->curcid)
4566 bruce@momjian.us 1647 : 95 : return true; /* deleted after scan started */
1648 : : else
1649 : 205 : return false; /* deleted before scan started */
1650 : : }
1651 : : /* below xmin horizon, normal transaction state is valid */
4630 rhaas@postgresql.org 1652 [ + + ]: 4227 : else if (TransactionIdPrecedes(xmax, snapshot->xmin))
1653 : : {
1654 [ + + - + ]: 1908 : Assert(!(tuple->t_infomask & HEAP_XMAX_COMMITTED &&
1655 : : !TransactionIdDidCommit(xmax)));
1656 : :
1657 : : /* check hint bit first */
1658 [ + + ]: 1908 : if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
1659 : 1842 : return false;
1660 : :
1661 : : /* check clog */
1662 : 66 : return !TransactionIdDidCommit(xmax);
1663 : : }
1664 : : /* above xmax horizon, we cannot possibly see the deleting transaction */
1665 [ + + ]: 2319 : else if (TransactionIdFollowsOrEquals(xmax, snapshot->xmax))
1666 : 622 : return true;
1667 : : /* xmax is between [xmin, xmax), check known committed array */
1668 [ + - ]: 1697 : else if (TransactionIdInArray(xmax, snapshot->xip, snapshot->xcnt))
1669 : 1697 : return false;
1670 : : /* xmax is between [xmin, xmax), but known not to have committed yet */
1671 : : else
4630 rhaas@postgresql.org 1672 :UBC 0 : return true;
1673 : : }
1674 : :
1675 : : /*
1676 : : * Perform HeapTupleSatisfiesMVCC() on each passed in tuple. This is more
1677 : : * efficient than doing HeapTupleSatisfiesMVCC() one-by-one.
1678 : : *
1679 : : * To be checked tuples are passed via BatchMVCCState->tuples. Each tuple's
1680 : : * visibility is stored in batchmvcc->visible[]. In addition,
1681 : : * ->vistuples_dense is set to contain the offsets of visible tuples.
1682 : : *
1683 : : * The reason this is more efficient than HeapTupleSatisfiesMVCC() is that it
1684 : : * avoids a cross-translation-unit function call for each tuple, allows the
1685 : : * compiler to optimize across calls to HeapTupleSatisfiesMVCC and allows
1686 : : * setting hint bits more efficiently (see the one BufferFinishSetHintBits()
1687 : : * call below).
1688 : : *
1689 : : * Returns the number of visible tuples.
1690 : : */
1691 : : int
297 andres@anarazel.de 1692 :CBC 1682356 : HeapTupleSatisfiesMVCCBatch(Snapshot snapshot, Buffer buffer,
1693 : : int ntups,
1694 : : BatchMVCCState *batchmvcc,
1695 : : OffsetNumber *vistuples_dense)
1696 : : {
1697 : 1682356 : int nvis = 0;
240 1698 : 1682356 : SetHintBitsState state = SHB_INITIAL;
1699 : :
297 1700 [ - + ]: 1682356 : Assert(IsMVCCSnapshot(snapshot));
1701 : :
1702 [ + + ]: 69943616 : for (int i = 0; i < ntups; i++)
1703 : : {
1704 : : bool valid;
1705 : 68261260 : HeapTuple tup = &batchmvcc->tuples[i];
1706 : :
240 1707 : 68261260 : valid = HeapTupleSatisfiesMVCC(tup, snapshot, buffer, &state);
297 1708 : 68261260 : batchmvcc->visible[i] = valid;
1709 : :
1710 [ + + ]: 68261260 : if (likely(valid))
1711 : : {
1712 : 60985129 : vistuples_dense[nvis] = tup->t_self.ip_posid;
1713 : 60985129 : nvis++;
1714 : : }
1715 : : }
1716 : :
240 1717 [ + + ]: 1682356 : if (state == SHB_ENABLED)
1718 : 90856 : BufferFinishSetHintBits(buffer, true, true);
1719 : :
297 1720 : 1682356 : return nvis;
1721 : : }
1722 : :
1723 : : /*
1724 : : * HeapTupleSatisfiesVisibility
1725 : : * True iff heap tuple satisfies a time qual.
1726 : : *
1727 : : * Notes:
1728 : : * Assumes heap tuple is valid, and buffer at least share locked.
1729 : : *
1730 : : * Hint bits in the HeapTuple's t_infomask may be updated as a side effect;
1731 : : * if so, the indicated buffer is marked dirty.
1732 : : */
1733 : : bool
1508 pg@bowt.ie 1734 : 41929165 : HeapTupleSatisfiesVisibility(HeapTuple htup, Snapshot snapshot, Buffer buffer)
1735 : : {
2845 andres@anarazel.de 1736 [ + + + + : 41929165 : switch (snapshot->snapshot_type)
+ + + - ]
1737 : : {
1738 : 20544107 : case SNAPSHOT_MVCC:
240 1739 : 20544107 : return HeapTupleSatisfiesMVCC(htup, snapshot, buffer, NULL);
2845 1740 : 656970 : case SNAPSHOT_SELF:
1508 pg@bowt.ie 1741 : 656970 : return HeapTupleSatisfiesSelf(htup, snapshot, buffer);
2845 andres@anarazel.de 1742 : 12223109 : case SNAPSHOT_ANY:
1508 pg@bowt.ie 1743 : 12223109 : return HeapTupleSatisfiesAny(htup, snapshot, buffer);
2845 andres@anarazel.de 1744 : 100166 : case SNAPSHOT_TOAST:
1508 pg@bowt.ie 1745 : 100166 : return HeapTupleSatisfiesToast(htup, snapshot, buffer);
2845 andres@anarazel.de 1746 : 7828478 : case SNAPSHOT_DIRTY:
1508 pg@bowt.ie 1747 : 7828478 : return HeapTupleSatisfiesDirty(htup, snapshot, buffer);
2845 andres@anarazel.de 1748 : 47508 : case SNAPSHOT_HISTORIC_MVCC:
1508 pg@bowt.ie 1749 : 47508 : return HeapTupleSatisfiesHistoricMVCC(htup, snapshot, buffer);
2845 andres@anarazel.de 1750 : 528827 : case SNAPSHOT_NON_VACUUMABLE:
1508 pg@bowt.ie 1751 : 528827 : return HeapTupleSatisfiesNonVacuumable(htup, snapshot, buffer);
1752 : : }
1753 : :
2845 andres@anarazel.de 1754 :UBC 0 : return false; /* keep compiler quiet */
1755 : : }
|