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