Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * snapmgr.c
4 : : * PostgreSQL snapshot manager
5 : : *
6 : : * We keep track of snapshots in two ways: those "registered" by resowner.c,
7 : : * and the "active snapshot" stack. All snapshots in either of them live in
8 : : * persistent memory. When a snapshot is no longer in any of these lists
9 : : * (tracked by separate refcounts on each snapshot), its memory can be freed.
10 : : *
11 : : * The FirstXactSnapshot, if any, is treated a bit specially: we increment its
12 : : * regd_count and list it in RegisteredSnapshots, but this reference is not
13 : : * tracked by a resource owner. We used to use the TopTransactionResourceOwner
14 : : * to track this snapshot reference, but that introduces logical circularity
15 : : * and thus makes it impossible to clean up in a sane fashion. It's better to
16 : : * handle this reference as an internally-tracked registration, so that this
17 : : * module is entirely lower-level than ResourceOwners.
18 : : *
19 : : * Likewise, any snapshots that have been exported by pg_export_snapshot
20 : : * have regd_count = 1 and are listed in RegisteredSnapshots, but are not
21 : : * tracked by any resource owner.
22 : : *
23 : : * Likewise, the CatalogSnapshot is listed in RegisteredSnapshots when it
24 : : * is valid, but is not tracked by any resource owner.
25 : : *
26 : : * The same is true for historic snapshots used during logical decoding,
27 : : * their lifetime is managed separately (as they live longer than one xact.c
28 : : * transaction).
29 : : *
30 : : * These arrangements let us reset MyProc->xmin when there are no snapshots
31 : : * referenced by this transaction, and advance it when the one with oldest
32 : : * Xmin is no longer referenced. For simplicity however, only registered
33 : : * snapshots not active snapshots participate in tracking which one is oldest;
34 : : * we don't try to change MyProc->xmin except when the active-snapshot
35 : : * stack is empty.
36 : : *
37 : : *
38 : : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
39 : : * Portions Copyright (c) 1994, Regents of the University of California
40 : : *
41 : : * IDENTIFICATION
42 : : * src/backend/utils/time/snapmgr.c
43 : : *
44 : : *-------------------------------------------------------------------------
45 : : */
46 : : #include "postgres.h"
47 : :
48 : : #include <sys/stat.h>
49 : : #include <unistd.h>
50 : :
51 : : #include "access/subtrans.h"
52 : : #include "access/transam.h"
53 : : #include "access/xact.h"
54 : : #include "datatype/timestamp.h"
55 : : #include "lib/pairingheap.h"
56 : : #include "miscadmin.h"
57 : : #include "port/pg_lfind.h"
58 : : #include "storage/fd.h"
59 : : #include "storage/predicate.h"
60 : : #include "storage/proc.h"
61 : : #include "storage/procarray.h"
62 : : #include "utils/builtins.h"
63 : : #include "utils/memutils.h"
64 : : #include "utils/resowner.h"
65 : : #include "utils/snapmgr.h"
66 : : #include "utils/syscache.h"
67 : :
68 : :
69 : : /*
70 : : * CurrentSnapshot points to the only snapshot taken in transaction-snapshot
71 : : * mode, and to the latest one taken in a read-committed transaction.
72 : : * SecondarySnapshot is a snapshot that's always up-to-date as of the current
73 : : * instant, even in transaction-snapshot mode. It should only be used for
74 : : * special-purpose code (say, RI checking.) CatalogSnapshot points to an
75 : : * MVCC snapshot intended to be used for catalog scans; we must invalidate it
76 : : * whenever a system catalog change occurs.
77 : : *
78 : : * These SnapshotData structs are static to simplify memory allocation
79 : : * (see the hack in GetSnapshotData to avoid repeated malloc/free).
80 : : */
81 : : static SnapshotData CurrentSnapshotData = {SNAPSHOT_MVCC};
82 : : static SnapshotData SecondarySnapshotData = {SNAPSHOT_MVCC};
83 : : SnapshotData CatalogSnapshotData = {SNAPSHOT_MVCC};
84 : : SnapshotData SnapshotSelfData = {SNAPSHOT_SELF};
85 : : SnapshotData SnapshotAnyData = {SNAPSHOT_ANY};
86 : :
87 : : /* Pointers to valid snapshots */
88 : : static Snapshot CurrentSnapshot = NULL;
89 : : static Snapshot SecondarySnapshot = NULL;
90 : : static Snapshot CatalogSnapshot = NULL;
91 : : static Snapshot HistoricSnapshot = NULL;
92 : :
93 : : /*
94 : : * These are updated by GetSnapshotData. We initialize them this way
95 : : * for the convenience of TransactionIdIsInProgress: even in bootstrap
96 : : * mode, we don't want it to say that BootstrapTransactionId is in progress.
97 : : */
98 : : TransactionId TransactionXmin = FirstNormalTransactionId;
99 : : TransactionId RecentXmin = FirstNormalTransactionId;
100 : :
101 : : /* (table, ctid) => (cmin, cmax) mapping during timetravel */
102 : : static HTAB *tuplecid_data = NULL;
103 : :
104 : : /*
105 : : * Elements of the active snapshot stack.
106 : : *
107 : : * Each element here accounts for exactly one active_count on SnapshotData.
108 : : *
109 : : * NB: the code assumes that elements in this list are in non-increasing
110 : : * order of as_level; also, the list must be NULL-terminated.
111 : : */
112 : : typedef struct ActiveSnapshotElt
113 : : {
114 : : Snapshot as_snap;
115 : : int as_level;
116 : : struct ActiveSnapshotElt *as_next;
117 : : } ActiveSnapshotElt;
118 : :
119 : : /* Top of the stack of active snapshots */
120 : : static ActiveSnapshotElt *ActiveSnapshot = NULL;
121 : :
122 : : /* Bottom of the stack of active snapshots */
123 : : static ActiveSnapshotElt *OldestActiveSnapshot = NULL;
124 : :
125 : : /*
126 : : * Currently registered Snapshots. Ordered in a heap by xmin, so that we can
127 : : * quickly find the one with lowest xmin, to advance our MyProc->xmin.
128 : : */
129 : : static int xmin_cmp(const pairingheap_node *a, const pairingheap_node *b,
130 : : void *arg);
131 : :
132 : : static pairingheap RegisteredSnapshots = {&xmin_cmp, NULL, NULL};
133 : :
134 : : /* first GetTransactionSnapshot call in a transaction? */
135 : : bool FirstSnapshotSet = false;
136 : :
137 : : /*
138 : : * Remember the serializable transaction snapshot, if any. We cannot trust
139 : : * FirstSnapshotSet in combination with IsolationUsesXactSnapshot(), because
140 : : * GUC may be reset before us, changing the value of IsolationUsesXactSnapshot.
141 : : */
142 : : static Snapshot FirstXactSnapshot = NULL;
143 : :
144 : : /* Define pathname of exported-snapshot files */
145 : : #define SNAPSHOT_EXPORT_DIR "pg_snapshots"
146 : :
147 : : /* Structure holding info about exported snapshot. */
148 : : typedef struct ExportedSnapshot
149 : : {
150 : : char *snapfile;
151 : : Snapshot snapshot;
152 : : } ExportedSnapshot;
153 : :
154 : : /* Current xact's exported snapshots (a list of ExportedSnapshot structs) */
155 : : static List *exportedSnapshots = NIL;
156 : :
157 : : /* Prototypes for local functions */
158 : : static Snapshot CopySnapshot(Snapshot snapshot);
159 : : static void UnregisterSnapshotNoOwner(Snapshot snapshot);
160 : : static void FreeSnapshot(Snapshot snapshot);
161 : : static void SnapshotResetXmin(void);
162 : :
163 : : /* ResourceOwner callbacks to track snapshot references */
164 : : static void ResOwnerReleaseSnapshot(Datum res);
165 : :
166 : : static const ResourceOwnerDesc snapshot_resowner_desc =
167 : : {
168 : : .name = "snapshot reference",
169 : : .release_phase = RESOURCE_RELEASE_AFTER_LOCKS,
170 : : .release_priority = RELEASE_PRIO_SNAPSHOT_REFS,
171 : : .ReleaseResource = ResOwnerReleaseSnapshot,
172 : : .DebugPrint = NULL /* the default message is fine */
173 : : };
174 : :
175 : : /* Convenience wrappers over ResourceOwnerRemember/Forget */
176 : : static inline void
158 heikki.linnakangas@i 177 :GNC 6339438 : ResourceOwnerRememberSnapshot(ResourceOwner owner, Snapshot snap)
178 : : {
179 : 6339438 : ResourceOwnerRemember(owner, PointerGetDatum(snap), &snapshot_resowner_desc);
180 : 6339438 : }
181 : : static inline void
182 : 6312058 : ResourceOwnerForgetSnapshot(ResourceOwner owner, Snapshot snap)
183 : : {
184 : 6312058 : ResourceOwnerForget(owner, PointerGetDatum(snap), &snapshot_resowner_desc);
185 : 6312058 : }
186 : :
187 : : /*
188 : : * Snapshot fields to be serialized.
189 : : *
190 : : * Only these fields need to be sent to the cooperating backend; the
191 : : * remaining ones can (and must) be set by the receiver upon restore.
192 : : */
193 : : typedef struct SerializedSnapshotData
194 : : {
195 : : TransactionId xmin;
196 : : TransactionId xmax;
197 : : uint32 xcnt;
198 : : int32 subxcnt;
199 : : bool suboverflowed;
200 : : bool takenDuringRecovery;
201 : : CommandId curcid;
202 : : TimestampTz whenTaken;
203 : : XLogRecPtr lsn;
204 : : } SerializedSnapshotData;
205 : :
206 : : /*
207 : : * GetTransactionSnapshot
208 : : * Get the appropriate snapshot for a new query in a transaction.
209 : : *
210 : : * Note that the return value may point at static storage that will be modified
211 : : * by future calls and by CommandCounterIncrement(). Callers should call
212 : : * RegisterSnapshot or PushActiveSnapshot on the returned snap if it is to be
213 : : * used very long.
214 : : */
215 : : Snapshot
5863 alvherre@alvh.no-ip. 216 :CBC 884909 : GetTransactionSnapshot(void)
217 : : {
218 : : /*
219 : : * Return historic snapshot if doing logical decoding. We'll never need a
220 : : * non-historic transaction snapshot in this (sub-)transaction, so there's
221 : : * no need to be careful to set one up for later calls to
222 : : * GetTransactionSnapshot().
223 : : */
3695 rhaas@postgresql.org 224 [ - + ]: 884909 : if (HistoricSnapshotActive())
225 : : {
3695 rhaas@postgresql.org 226 [ # # ]:UBC 0 : Assert(!FirstSnapshotSet);
227 : 0 : return HistoricSnapshot;
228 : : }
229 : :
230 : : /* First call in transaction? */
5816 alvherre@alvh.no-ip. 231 [ + + ]:CBC 884909 : if (!FirstSnapshotSet)
232 : : {
233 : : /*
234 : : * Don't allow catalog snapshot to be older than xact snapshot. Must
235 : : * do this first to allow the empty-heap Assert to succeed.
236 : : */
2707 tgl@sss.pgh.pa.us 237 : 333728 : InvalidateCatalogSnapshot();
238 : :
3375 heikki.linnakangas@i 239 [ - + ]: 333728 : Assert(pairingheap_is_empty(&RegisteredSnapshots));
4584 tgl@sss.pgh.pa.us 240 [ - + ]: 333728 : Assert(FirstXactSnapshot == NULL);
241 : :
3272 rhaas@postgresql.org 242 [ - + ]: 333728 : if (IsInParallelMode())
3272 rhaas@postgresql.org 243 [ # # ]:UBC 0 : elog(ERROR,
244 : : "cannot take query snapshot during a parallel operation");
245 : :
246 : : /*
247 : : * In transaction-snapshot mode, the first snapshot must live until
248 : : * end of xact regardless of what the caller does with it, so we must
249 : : * make a copy of it rather than returning CurrentSnapshotData
250 : : * directly. Furthermore, if we're running in serializable mode,
251 : : * predicate.c needs to wrap the snapshot fetch in its own processing.
252 : : */
4964 mail@joeconway.com 253 [ + + ]:CBC 333728 : if (IsolationUsesXactSnapshot())
254 : : {
255 : : /* First, create the snapshot in CurrentSnapshotData */
4815 heikki.linnakangas@i 256 [ + + ]: 2693 : if (IsolationIsSerializable())
4584 tgl@sss.pgh.pa.us 257 : 1652 : CurrentSnapshot = GetSerializableTransactionSnapshot(&CurrentSnapshotData);
258 : : else
4815 heikki.linnakangas@i 259 : 1041 : CurrentSnapshot = GetSnapshotData(&CurrentSnapshotData);
260 : : /* Make a saved copy */
4584 tgl@sss.pgh.pa.us 261 : 2693 : CurrentSnapshot = CopySnapshot(CurrentSnapshot);
262 : 2693 : FirstXactSnapshot = CurrentSnapshot;
263 : : /* Mark it as "registered" in FirstXactSnapshot */
264 : 2693 : FirstXactSnapshot->regd_count++;
3375 heikki.linnakangas@i 265 : 2693 : pairingheap_add(&RegisteredSnapshots, &FirstXactSnapshot->ph_node);
266 : : }
267 : : else
4815 268 : 331035 : CurrentSnapshot = GetSnapshotData(&CurrentSnapshotData);
269 : :
270 : 333728 : FirstSnapshotSet = true;
5816 alvherre@alvh.no-ip. 271 : 333728 : return CurrentSnapshot;
272 : : }
273 : :
4964 mail@joeconway.com 274 [ + + ]: 551181 : if (IsolationUsesXactSnapshot())
5816 alvherre@alvh.no-ip. 275 : 100425 : return CurrentSnapshot;
276 : :
277 : : /* Don't allow catalog snapshot to be older than xact snapshot. */
2707 tgl@sss.pgh.pa.us 278 : 450756 : InvalidateCatalogSnapshot();
279 : :
5816 alvherre@alvh.no-ip. 280 : 450756 : CurrentSnapshot = GetSnapshotData(&CurrentSnapshotData);
281 : :
282 : 450756 : return CurrentSnapshot;
283 : : }
284 : :
285 : : /*
286 : : * GetLatestSnapshot
287 : : * Get a snapshot that is up-to-date as of the current instant,
288 : : * even if we are executing in transaction-snapshot mode.
289 : : */
290 : : Snapshot
5863 291 : 148250 : GetLatestSnapshot(void)
292 : : {
293 : : /*
294 : : * We might be able to relax this, but nothing that could otherwise work
295 : : * needs it.
296 : : */
3272 rhaas@postgresql.org 297 [ - + ]: 148250 : if (IsInParallelMode())
3272 rhaas@postgresql.org 298 [ # # ]:UBC 0 : elog(ERROR,
299 : : "cannot update SecondarySnapshot during a parallel operation");
300 : :
301 : : /*
302 : : * So far there are no cases requiring support for GetLatestSnapshot()
303 : : * during logical decoding, but it wouldn't be hard to add if required.
304 : : */
3695 rhaas@postgresql.org 305 [ - + ]:CBC 148250 : Assert(!HistoricSnapshotActive());
306 : :
307 : : /* If first call in transaction, go ahead and set the xact snapshot */
5816 alvherre@alvh.no-ip. 308 [ + + ]: 148250 : if (!FirstSnapshotSet)
4305 tgl@sss.pgh.pa.us 309 : 50 : return GetTransactionSnapshot();
310 : :
5816 alvherre@alvh.no-ip. 311 : 148200 : SecondarySnapshot = GetSnapshotData(&SecondarySnapshotData);
312 : :
313 : 148200 : return SecondarySnapshot;
314 : : }
315 : :
316 : : /*
317 : : * GetOldestSnapshot
318 : : *
319 : : * Get the transaction's oldest known snapshot, as judged by the LSN.
320 : : * Will return NULL if there are no active or registered snapshots.
321 : : */
322 : : Snapshot
2811 rhaas@postgresql.org 323 : 21430 : GetOldestSnapshot(void)
324 : : {
325 : 21430 : Snapshot OldestRegisteredSnapshot = NULL;
326 : 21430 : XLogRecPtr RegisteredLSN = InvalidXLogRecPtr;
327 : :
328 [ + + ]: 21430 : if (!pairingheap_is_empty(&RegisteredSnapshots))
329 : : {
330 : 21233 : OldestRegisteredSnapshot = pairingheap_container(SnapshotData, ph_node,
331 : : pairingheap_first(&RegisteredSnapshots));
332 : 21233 : RegisteredLSN = OldestRegisteredSnapshot->lsn;
333 : : }
334 : :
335 [ + + ]: 21430 : if (OldestActiveSnapshot != NULL)
336 : : {
2807 tgl@sss.pgh.pa.us 337 : 21425 : XLogRecPtr ActiveLSN = OldestActiveSnapshot->as_snap->lsn;
338 : :
339 [ - + - - ]: 21425 : if (XLogRecPtrIsInvalid(RegisteredLSN) || RegisteredLSN > ActiveLSN)
340 : 21425 : return OldestActiveSnapshot->as_snap;
341 : : }
342 : :
2811 rhaas@postgresql.org 343 : 5 : return OldestRegisteredSnapshot;
344 : : }
345 : :
346 : : /*
347 : : * GetCatalogSnapshot
348 : : * Get a snapshot that is sufficiently up-to-date for scan of the
349 : : * system catalog with the specified OID.
350 : : */
351 : : Snapshot
3939 352 : 5796280 : GetCatalogSnapshot(Oid relid)
353 : : {
354 : : /*
355 : : * Return historic snapshot while we're doing logical decoding, so we can
356 : : * see the appropriate state of the catalog.
357 : : *
358 : : * This is the primary reason for needing to reset the system caches after
359 : : * finishing decoding.
360 : : */
3695 361 [ + + ]: 5796280 : if (HistoricSnapshotActive())
362 : 13335 : return HistoricSnapshot;
363 : :
364 : 5782945 : return GetNonHistoricCatalogSnapshot(relid);
365 : : }
366 : :
367 : : /*
368 : : * GetNonHistoricCatalogSnapshot
369 : : * Get a snapshot that is sufficiently up-to-date for scan of the system
370 : : * catalog with the specified OID, even while historic snapshots are set
371 : : * up.
372 : : */
373 : : Snapshot
374 : 5784293 : GetNonHistoricCatalogSnapshot(Oid relid)
375 : : {
376 : : /*
377 : : * If the caller is trying to scan a relation that has no syscache, no
378 : : * catcache invalidations will be sent when it is updated. For a few key
379 : : * relations, snapshot invalidations are sent instead. If we're trying to
380 : : * scan a relation for which neither catcache nor snapshot invalidations
381 : : * are sent, we must refresh the snapshot every time.
382 : : */
2707 tgl@sss.pgh.pa.us 383 [ + + ]: 5784293 : if (CatalogSnapshot &&
384 [ + + ]: 5049286 : !RelationInvalidatesSnapshotsOnly(relid) &&
3939 rhaas@postgresql.org 385 [ + + ]: 4388500 : !RelationHasSysCache(relid))
2707 tgl@sss.pgh.pa.us 386 : 208966 : InvalidateCatalogSnapshot();
387 : :
388 [ + + ]: 5784293 : if (CatalogSnapshot == NULL)
389 : : {
390 : : /* Get new snapshot. */
3939 rhaas@postgresql.org 391 : 943973 : CatalogSnapshot = GetSnapshotData(&CatalogSnapshotData);
392 : :
393 : : /*
394 : : * Make sure the catalog snapshot will be accounted for in decisions
395 : : * about advancing PGPROC->xmin. We could apply RegisterSnapshot, but
396 : : * that would result in making a physical copy, which is overkill; and
397 : : * it would also create a dependency on some resource owner, which we
398 : : * do not want for reasons explained at the head of this file. Instead
399 : : * just shove the CatalogSnapshot into the pairing heap manually. This
400 : : * has to be reversed in InvalidateCatalogSnapshot, of course.
401 : : *
402 : : * NB: it had better be impossible for this to throw error, since the
403 : : * CatalogSnapshot pointer is already valid.
404 : : */
2707 tgl@sss.pgh.pa.us 405 : 943973 : pairingheap_add(&RegisteredSnapshots, &CatalogSnapshot->ph_node);
406 : : }
407 : :
3939 rhaas@postgresql.org 408 : 5784293 : return CatalogSnapshot;
409 : : }
410 : :
411 : : /*
412 : : * InvalidateCatalogSnapshot
413 : : * Mark the current catalog snapshot, if any, as invalid
414 : : *
415 : : * We could change this API to allow the caller to provide more fine-grained
416 : : * invalidation details, so that a change to relation A wouldn't prevent us
417 : : * from using our cached snapshot to scan relation B, but so far there's no
418 : : * evidence that the CPU cycles we spent tracking such fine details would be
419 : : * well-spent.
420 : : */
421 : : void
3165 andres@anarazel.de 422 : 13002321 : InvalidateCatalogSnapshot(void)
423 : : {
2707 tgl@sss.pgh.pa.us 424 [ + + ]: 13002321 : if (CatalogSnapshot)
425 : : {
426 : 943971 : pairingheap_remove(&RegisteredSnapshots, &CatalogSnapshot->ph_node);
427 : 943971 : CatalogSnapshot = NULL;
428 : 943971 : SnapshotResetXmin();
429 : : }
430 : 13002321 : }
431 : :
432 : : /*
433 : : * InvalidateCatalogSnapshotConditionally
434 : : * Drop catalog snapshot if it's the only one we have
435 : : *
436 : : * This is called when we are about to wait for client input, so we don't
437 : : * want to continue holding the catalog snapshot if it might mean that the
438 : : * global xmin horizon can't advance. However, if there are other snapshots
439 : : * still active or registered, the catalog snapshot isn't likely to be the
440 : : * oldest one, so we might as well keep it.
441 : : */
442 : : void
443 : 343900 : InvalidateCatalogSnapshotConditionally(void)
444 : : {
445 [ + + ]: 343900 : if (CatalogSnapshot &&
446 [ + + ]: 46112 : ActiveSnapshot == NULL &&
447 [ + - + + ]: 45299 : pairingheap_is_singular(&RegisteredSnapshots))
448 : 7901 : InvalidateCatalogSnapshot();
3939 rhaas@postgresql.org 449 : 343900 : }
450 : :
451 : : /*
452 : : * SnapshotSetCommandId
453 : : * Propagate CommandCounterIncrement into the static snapshots, if set
454 : : */
455 : : void
5816 alvherre@alvh.no-ip. 456 : 517996 : SnapshotSetCommandId(CommandId curcid)
457 : : {
458 [ + + ]: 517996 : if (!FirstSnapshotSet)
459 : 8498 : return;
460 : :
461 [ + - ]: 509498 : if (CurrentSnapshot)
462 : 509498 : CurrentSnapshot->curcid = curcid;
463 [ + + ]: 509498 : if (SecondarySnapshot)
464 : 80084 : SecondarySnapshot->curcid = curcid;
465 : : /* Should we do the same with CatalogSnapshot? */
466 : : }
467 : :
468 : : /*
469 : : * SetTransactionSnapshot
470 : : * Set the transaction's snapshot from an imported MVCC snapshot.
471 : : *
472 : : * Note that this is very closely tied to GetTransactionSnapshot --- it
473 : : * must take care of all the same considerations as the first-snapshot case
474 : : * in GetTransactionSnapshot.
475 : : */
476 : : static void
2496 andres@anarazel.de 477 : 1509 : SetTransactionSnapshot(Snapshot sourcesnap, VirtualTransactionId *sourcevxid,
478 : : int sourcepid, PGPROC *sourceproc)
479 : : {
480 : : /* Caller should have checked this already */
4558 tgl@sss.pgh.pa.us 481 [ - + ]: 1509 : Assert(!FirstSnapshotSet);
482 : :
483 : : /* Better do this to ensure following Assert succeeds. */
2707 484 : 1509 : InvalidateCatalogSnapshot();
485 : :
3375 heikki.linnakangas@i 486 [ - + ]: 1509 : Assert(pairingheap_is_empty(&RegisteredSnapshots));
4558 tgl@sss.pgh.pa.us 487 [ - + ]: 1509 : Assert(FirstXactSnapshot == NULL);
3686 rhaas@postgresql.org 488 [ - + ]: 1509 : Assert(!HistoricSnapshotActive());
489 : :
490 : : /*
491 : : * Even though we are not going to use the snapshot it computes, we must
492 : : * call GetSnapshotData, for two reasons: (1) to be sure that
493 : : * CurrentSnapshotData's XID arrays have been allocated, and (2) to update
494 : : * the state for GlobalVis*.
495 : : */
4558 tgl@sss.pgh.pa.us 496 : 1509 : CurrentSnapshot = GetSnapshotData(&CurrentSnapshotData);
497 : :
498 : : /*
499 : : * Now copy appropriate fields from the source snapshot.
500 : : */
501 : 1509 : CurrentSnapshot->xmin = sourcesnap->xmin;
502 : 1509 : CurrentSnapshot->xmax = sourcesnap->xmax;
503 : 1509 : CurrentSnapshot->xcnt = sourcesnap->xcnt;
504 [ - + ]: 1509 : Assert(sourcesnap->xcnt <= GetMaxSnapshotXidCount());
773 505 [ + + ]: 1509 : if (sourcesnap->xcnt > 0)
506 : 344 : memcpy(CurrentSnapshot->xip, sourcesnap->xip,
507 : 344 : sourcesnap->xcnt * sizeof(TransactionId));
4558 508 : 1509 : CurrentSnapshot->subxcnt = sourcesnap->subxcnt;
509 [ - + ]: 1509 : Assert(sourcesnap->subxcnt <= GetMaxSnapshotSubxidCount());
773 510 [ + + ]: 1509 : if (sourcesnap->subxcnt > 0)
511 : 4 : memcpy(CurrentSnapshot->subxip, sourcesnap->subxip,
512 : 4 : sourcesnap->subxcnt * sizeof(TransactionId));
4558 513 : 1509 : CurrentSnapshot->suboverflowed = sourcesnap->suboverflowed;
514 : 1509 : CurrentSnapshot->takenDuringRecovery = sourcesnap->takenDuringRecovery;
515 : : /* NB: curcid should NOT be copied, it's a local matter */
516 : :
1336 andres@anarazel.de 517 : 1509 : CurrentSnapshot->snapXactCompletionCount = 0;
518 : :
519 : : /*
520 : : * Now we have to fix what GetSnapshotData did with MyProc->xmin and
521 : : * TransactionXmin. There is a race condition: to make sure we are not
522 : : * causing the global xmin to go backwards, we have to test that the
523 : : * source transaction is still running, and that has to be done
524 : : * atomically. So let procarray.c do it.
525 : : *
526 : : * Note: in serializable mode, predicate.c will do this a second time. It
527 : : * doesn't seem worth contorting the logic here to avoid two calls,
528 : : * especially since it's not clear that predicate.c *must* do this.
529 : : */
3272 rhaas@postgresql.org 530 [ + + ]: 1509 : if (sourceproc != NULL)
531 : : {
532 [ - + ]: 1491 : if (!ProcArrayInstallRestoredXmin(CurrentSnapshot->xmin, sourceproc))
3272 rhaas@postgresql.org 533 [ # # ]:UBC 0 : ereport(ERROR,
534 : : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
535 : : errmsg("could not import the requested snapshot"),
536 : : errdetail("The source transaction is not running anymore.")));
537 : : }
2496 andres@anarazel.de 538 [ - + ]:CBC 18 : else if (!ProcArrayInstallImportedXmin(CurrentSnapshot->xmin, sourcevxid))
4558 tgl@sss.pgh.pa.us 539 [ # # ]:UBC 0 : ereport(ERROR,
540 : : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
541 : : errmsg("could not import the requested snapshot"),
542 : : errdetail("The source process with PID %d is not running anymore.",
543 : : sourcepid)));
544 : :
545 : : /*
546 : : * In transaction-snapshot mode, the first snapshot must live until end of
547 : : * xact, so we must make a copy of it. Furthermore, if we're running in
548 : : * serializable mode, predicate.c needs to do its own processing.
549 : : */
4558 tgl@sss.pgh.pa.us 550 [ + + ]:CBC 1509 : if (IsolationUsesXactSnapshot())
551 : : {
552 [ + + ]: 207 : if (IsolationIsSerializable())
2496 andres@anarazel.de 553 : 13 : SetSerializableTransactionSnapshot(CurrentSnapshot, sourcevxid,
554 : : sourcepid);
555 : : /* Make a saved copy */
4558 tgl@sss.pgh.pa.us 556 : 207 : CurrentSnapshot = CopySnapshot(CurrentSnapshot);
557 : 207 : FirstXactSnapshot = CurrentSnapshot;
558 : : /* Mark it as "registered" in FirstXactSnapshot */
559 : 207 : FirstXactSnapshot->regd_count++;
3375 heikki.linnakangas@i 560 : 207 : pairingheap_add(&RegisteredSnapshots, &FirstXactSnapshot->ph_node);
561 : : }
562 : :
4558 tgl@sss.pgh.pa.us 563 : 1509 : FirstSnapshotSet = true;
564 : 1509 : }
565 : :
566 : : /*
567 : : * CopySnapshot
568 : : * Copy the given snapshot.
569 : : *
570 : : * The copy is palloc'd in TopTransactionContext and has initial refcounts set
571 : : * to 0. The returned snapshot has the copied flag set.
572 : : */
573 : : static Snapshot
5863 alvherre@alvh.no-ip. 574 : 6639654 : CopySnapshot(Snapshot snapshot)
575 : : {
576 : : Snapshot newsnap;
577 : : Size subxipoff;
578 : : Size size;
579 : :
5816 580 [ - + ]: 6639654 : Assert(snapshot != InvalidSnapshot);
581 : :
582 : : /* We allocate any XID arrays needed in the same palloc block. */
5863 583 : 6639654 : size = subxipoff = sizeof(SnapshotData) +
584 : 6639654 : snapshot->xcnt * sizeof(TransactionId);
585 [ + + ]: 6639654 : if (snapshot->subxcnt > 0)
586 : 46860 : size += snapshot->subxcnt * sizeof(TransactionId);
587 : :
5816 588 : 6639654 : newsnap = (Snapshot) MemoryContextAlloc(TopTransactionContext, size);
5863 589 : 6639654 : memcpy(newsnap, snapshot, sizeof(SnapshotData));
590 : :
5816 591 : 6639654 : newsnap->regd_count = 0;
592 : 6639654 : newsnap->active_count = 0;
593 : 6639654 : newsnap->copied = true;
1336 andres@anarazel.de 594 : 6639654 : newsnap->snapXactCompletionCount = 0;
595 : :
596 : : /* setup XID array */
5863 alvherre@alvh.no-ip. 597 [ + + ]: 6639654 : if (snapshot->xcnt > 0)
598 : : {
599 : 1632864 : newsnap->xip = (TransactionId *) (newsnap + 1);
600 : 1632864 : memcpy(newsnap->xip, snapshot->xip,
601 : 1632864 : snapshot->xcnt * sizeof(TransactionId));
602 : : }
603 : : else
604 : 5006790 : newsnap->xip = NULL;
605 : :
606 : : /*
607 : : * Setup subXID array. Don't bother to copy it if it had overflowed,
608 : : * though, because it's not used anywhere in that case. Except if it's a
609 : : * snapshot taken during recovery; all the top-level XIDs are in subxip as
610 : : * well in that case, so we mustn't lose them.
611 : : */
5230 simon@2ndQuadrant.co 612 [ + + ]: 6639654 : if (snapshot->subxcnt > 0 &&
613 [ - + - - ]: 46860 : (!snapshot->suboverflowed || snapshot->takenDuringRecovery))
614 : : {
5863 alvherre@alvh.no-ip. 615 : 46860 : newsnap->subxip = (TransactionId *) ((char *) newsnap + subxipoff);
616 : 46860 : memcpy(newsnap->subxip, snapshot->subxip,
617 : 46860 : snapshot->subxcnt * sizeof(TransactionId));
618 : : }
619 : : else
620 : 6592794 : newsnap->subxip = NULL;
621 : :
622 : 6639654 : return newsnap;
623 : : }
624 : :
625 : : /*
626 : : * FreeSnapshot
627 : : * Free the memory associated with a snapshot.
628 : : */
629 : : static void
5816 630 : 6616826 : FreeSnapshot(Snapshot snapshot)
631 : : {
632 [ - + ]: 6616826 : Assert(snapshot->regd_count == 0);
633 [ - + ]: 6616826 : Assert(snapshot->active_count == 0);
5756 634 [ - + ]: 6616826 : Assert(snapshot->copied);
635 : :
5816 636 : 6616826 : pfree(snapshot);
637 : 6616826 : }
638 : :
639 : : /*
640 : : * PushActiveSnapshot
641 : : * Set the given snapshot as the current active snapshot
642 : : *
643 : : * If the passed snapshot is a statically-allocated one, or it is possibly
644 : : * subject to a future command counter update, create a new long-lived copy
645 : : * with active refcount=1. Otherwise, only increment the refcount.
646 : : */
647 : : void
572 pg@bowt.ie 648 : 944472 : PushActiveSnapshot(Snapshot snapshot)
649 : : {
650 : 944472 : PushActiveSnapshotWithLevel(snapshot, GetCurrentTransactionNestLevel());
926 tgl@sss.pgh.pa.us 651 : 944472 : }
652 : :
653 : : /*
654 : : * PushActiveSnapshotWithLevel
655 : : * Set the given snapshot as the current active snapshot
656 : : *
657 : : * Same as PushActiveSnapshot except that caller can specify the
658 : : * transaction nesting level that "owns" the snapshot. This level
659 : : * must not be deeper than the current top of the snapshot stack.
660 : : */
661 : : void
572 pg@bowt.ie 662 : 1070072 : PushActiveSnapshotWithLevel(Snapshot snapshot, int snap_level)
663 : : {
664 : : ActiveSnapshotElt *newactive;
665 : :
666 [ - + ]: 1070072 : Assert(snapshot != InvalidSnapshot);
926 tgl@sss.pgh.pa.us 667 [ + + - + ]: 1070072 : Assert(ActiveSnapshot == NULL || snap_level >= ActiveSnapshot->as_level);
668 : :
5816 alvherre@alvh.no-ip. 669 : 1070072 : newactive = MemoryContextAlloc(TopTransactionContext, sizeof(ActiveSnapshotElt));
670 : :
671 : : /*
672 : : * Checking SecondarySnapshot is probably useless here, but it seems
673 : : * better to be sure.
674 : : */
572 pg@bowt.ie 675 [ + + + + ]: 1070072 : if (snapshot == CurrentSnapshot || snapshot == SecondarySnapshot ||
676 [ - + ]: 204407 : !snapshot->copied)
677 : 865665 : newactive->as_snap = CopySnapshot(snapshot);
678 : : else
679 : 204407 : newactive->as_snap = snapshot;
680 : :
5816 alvherre@alvh.no-ip. 681 : 1070072 : newactive->as_next = ActiveSnapshot;
926 tgl@sss.pgh.pa.us 682 : 1070072 : newactive->as_level = snap_level;
683 : :
5816 alvherre@alvh.no-ip. 684 : 1070072 : newactive->as_snap->active_count++;
685 : :
686 : 1070072 : ActiveSnapshot = newactive;
2811 rhaas@postgresql.org 687 [ + + ]: 1070072 : if (OldestActiveSnapshot == NULL)
688 : 826785 : OldestActiveSnapshot = ActiveSnapshot;
5816 alvherre@alvh.no-ip. 689 : 1070072 : }
690 : :
691 : : /*
692 : : * PushCopiedSnapshot
693 : : * As above, except forcibly copy the presented snapshot.
694 : : *
695 : : * This should be used when the ActiveSnapshot has to be modifiable, for
696 : : * example if the caller intends to call UpdateActiveSnapshotCommandId.
697 : : * The new snapshot will be released when popped from the stack.
698 : : */
699 : : void
4794 tgl@sss.pgh.pa.us 700 : 55108 : PushCopiedSnapshot(Snapshot snapshot)
701 : : {
702 : 55108 : PushActiveSnapshot(CopySnapshot(snapshot));
703 : 55108 : }
704 : :
705 : : /*
706 : : * UpdateActiveSnapshotCommandId
707 : : *
708 : : * Update the current CID of the active snapshot. This can only be applied
709 : : * to a snapshot that is not referenced elsewhere.
710 : : */
711 : : void
712 : 50936 : UpdateActiveSnapshotCommandId(void)
713 : : {
714 : : CommandId save_curcid,
715 : : curcid;
716 : :
717 [ - + ]: 50936 : Assert(ActiveSnapshot != NULL);
718 [ - + ]: 50936 : Assert(ActiveSnapshot->as_snap->active_count == 1);
719 [ - + ]: 50936 : Assert(ActiveSnapshot->as_snap->regd_count == 0);
720 : :
721 : : /*
722 : : * Don't allow modification of the active snapshot during parallel
723 : : * operation. We share the snapshot to worker backends at the beginning
724 : : * of parallel operation, so any change to the snapshot can lead to
725 : : * inconsistencies. We have other defenses against
726 : : * CommandCounterIncrement, but there are a few places that call this
727 : : * directly, so we put an additional guard here.
728 : : */
3272 rhaas@postgresql.org 729 : 50936 : save_curcid = ActiveSnapshot->as_snap->curcid;
730 : 50936 : curcid = GetCurrentCommandId(false);
731 [ + + - + ]: 50936 : if (IsInParallelMode() && save_curcid != curcid)
3272 rhaas@postgresql.org 732 [ # # ]:UBC 0 : elog(ERROR, "cannot modify commandid in active snapshot during a parallel operation");
3272 rhaas@postgresql.org 733 :CBC 50936 : ActiveSnapshot->as_snap->curcid = curcid;
5816 alvherre@alvh.no-ip. 734 : 50936 : }
735 : :
736 : : /*
737 : : * PopActiveSnapshot
738 : : *
739 : : * Remove the topmost snapshot from the active snapshot stack, decrementing the
740 : : * reference count, and free it if this was the last reference.
741 : : */
742 : : void
743 : 1044024 : PopActiveSnapshot(void)
744 : : {
745 : : ActiveSnapshotElt *newstack;
746 : :
747 : 1044024 : newstack = ActiveSnapshot->as_next;
748 : :
749 [ - + ]: 1044024 : Assert(ActiveSnapshot->as_snap->active_count > 0);
750 : :
751 : 1044024 : ActiveSnapshot->as_snap->active_count--;
752 : :
753 [ + + ]: 1044024 : if (ActiveSnapshot->as_snap->active_count == 0 &&
754 [ + + ]: 1031151 : ActiveSnapshot->as_snap->regd_count == 0)
755 : 780650 : FreeSnapshot(ActiveSnapshot->as_snap);
756 : :
757 : 1044024 : pfree(ActiveSnapshot);
758 : 1044024 : ActiveSnapshot = newstack;
2811 rhaas@postgresql.org 759 [ + + ]: 1044024 : if (ActiveSnapshot == NULL)
760 : 807880 : OldestActiveSnapshot = NULL;
761 : :
5816 alvherre@alvh.no-ip. 762 : 1044024 : SnapshotResetXmin();
5863 763 : 1044024 : }
764 : :
765 : : /*
766 : : * GetActiveSnapshot
767 : : * Return the topmost snapshot in the Active stack.
768 : : */
769 : : Snapshot
5816 770 : 1172235 : GetActiveSnapshot(void)
771 : : {
772 [ - + ]: 1172235 : Assert(ActiveSnapshot != NULL);
773 : :
774 : 1172235 : return ActiveSnapshot->as_snap;
775 : : }
776 : :
777 : : /*
778 : : * ActiveSnapshotSet
779 : : * Return whether there is at least one snapshot in the Active stack
780 : : */
781 : : bool
782 : 766809 : ActiveSnapshotSet(void)
783 : : {
784 : 766809 : return ActiveSnapshot != NULL;
785 : : }
786 : :
787 : : /*
788 : : * RegisterSnapshot
789 : : * Register a snapshot as being in use by the current resource owner
790 : : *
791 : : * If InvalidSnapshot is passed, it is not registered.
792 : : */
793 : : Snapshot
794 : 6948794 : RegisterSnapshot(Snapshot snapshot)
795 : : {
5610 796 [ + + ]: 6948794 : if (snapshot == InvalidSnapshot)
797 : 609466 : return InvalidSnapshot;
798 : :
799 : 6339328 : return RegisterSnapshotOnOwner(snapshot, CurrentResourceOwner);
800 : : }
801 : :
802 : : /*
803 : : * RegisterSnapshotOnOwner
804 : : * As above, but use the specified resource owner
805 : : */
806 : : Snapshot
807 : 6339438 : RegisterSnapshotOnOwner(Snapshot snapshot, ResourceOwner owner)
808 : : {
809 : : Snapshot snap;
810 : :
5816 811 [ - + ]: 6339438 : if (snapshot == InvalidSnapshot)
5816 alvherre@alvh.no-ip. 812 :UBC 0 : return InvalidSnapshot;
813 : :
814 : : /* Static snapshot? Create a persistent copy */
5619 alvherre@alvh.no-ip. 815 [ + + ]:CBC 6339438 : snap = snapshot->copied ? snapshot : CopySnapshot(snapshot);
816 : :
817 : : /* and tell resowner.c about it */
158 heikki.linnakangas@i 818 :GNC 6339438 : ResourceOwnerEnlarge(owner);
5619 alvherre@alvh.no-ip. 819 :CBC 6339438 : snap->regd_count++;
5610 820 : 6339438 : ResourceOwnerRememberSnapshot(owner, snap);
821 : :
3375 heikki.linnakangas@i 822 [ + + ]: 6339438 : if (snap->regd_count == 1)
823 : 6018169 : pairingheap_add(&RegisteredSnapshots, &snap->ph_node);
824 : :
5619 alvherre@alvh.no-ip. 825 : 6339438 : return snap;
826 : : }
827 : :
828 : : /*
829 : : * UnregisterSnapshot
830 : : *
831 : : * Decrement the reference count of a snapshot, remove the corresponding
832 : : * reference from CurrentResourceOwner, and free the snapshot if no more
833 : : * references remain.
834 : : */
835 : : void
5816 836 : 6876733 : UnregisterSnapshot(Snapshot snapshot)
837 : : {
5610 838 [ + + ]: 6876733 : if (snapshot == NULL)
839 : 582747 : return;
840 : :
841 : 6293986 : UnregisterSnapshotFromOwner(snapshot, CurrentResourceOwner);
842 : : }
843 : :
844 : : /*
845 : : * UnregisterSnapshotFromOwner
846 : : * As above, but use the specified resource owner
847 : : */
848 : : void
849 : 6312058 : UnregisterSnapshotFromOwner(Snapshot snapshot, ResourceOwner owner)
850 : : {
5619 851 [ - + ]: 6312058 : if (snapshot == NULL)
5816 alvherre@alvh.no-ip. 852 :UBC 0 : return;
853 : :
158 heikki.linnakangas@i 854 :GNC 6312058 : ResourceOwnerForgetSnapshot(owner, snapshot);
855 : 6312058 : UnregisterSnapshotNoOwner(snapshot);
856 : : }
857 : :
858 : : static void
859 : 6339431 : UnregisterSnapshotNoOwner(Snapshot snapshot)
860 : : {
5619 alvherre@alvh.no-ip. 861 [ - + ]:CBC 6339431 : Assert(snapshot->regd_count > 0);
3375 heikki.linnakangas@i 862 [ - + ]: 6339431 : Assert(!pairingheap_is_empty(&RegisteredSnapshots));
863 : :
864 : 6339431 : snapshot->regd_count--;
865 [ + + ]: 6339431 : if (snapshot->regd_count == 0)
866 : 6018165 : pairingheap_remove(&RegisteredSnapshots, &snapshot->ph_node);
867 : :
868 [ + + + + ]: 6339431 : if (snapshot->regd_count == 0 && snapshot->active_count == 0)
869 : : {
5619 alvherre@alvh.no-ip. 870 : 5833341 : FreeSnapshot(snapshot);
871 : 5833341 : SnapshotResetXmin();
872 : : }
5816 alvherre@alvh.no-ip. 873 :GIC 6339431 : }
874 : :
875 : : /*
876 : : * Comparison function for RegisteredSnapshots heap. Snapshots are ordered
877 : : * by xmin, so that the snapshot with smallest xmin is at the top.
878 : : */
879 : : static int
3375 heikki.linnakangas@i 880 :CBC 5969309 : xmin_cmp(const pairingheap_node *a, const pairingheap_node *b, void *arg)
881 : : {
882 : 5969309 : const SnapshotData *asnap = pairingheap_const_container(SnapshotData, ph_node, a);
883 : 5969309 : const SnapshotData *bsnap = pairingheap_const_container(SnapshotData, ph_node, b);
884 : :
885 [ + + ]: 5969309 : if (TransactionIdPrecedes(asnap->xmin, bsnap->xmin))
886 : 53714 : return 1;
887 [ + + ]: 5915595 : else if (TransactionIdFollows(asnap->xmin, bsnap->xmin))
888 : 8404 : return -1;
889 : : else
890 : 5907191 : return 0;
891 : : }
892 : :
893 : : /*
894 : : * SnapshotResetXmin
895 : : *
896 : : * If there are no more snapshots, we can reset our PGPROC->xmin to
897 : : * InvalidTransactionId. Note we can do this without locking because we assume
898 : : * that storing an Xid is atomic.
899 : : *
900 : : * Even if there are some remaining snapshots, we may be able to advance our
901 : : * PGPROC->xmin to some degree. This typically happens when a portal is
902 : : * dropped. For efficiency, we only consider recomputing PGPROC->xmin when
903 : : * the active snapshot stack is empty; this allows us not to need to track
904 : : * which active snapshot is oldest.
905 : : *
906 : : * Note: it's tempting to use GetOldestSnapshot() here so that we can include
907 : : * active snapshots in the calculation. However, that compares by LSN not
908 : : * xmin so it's not entirely clear that it's the same thing. Also, we'd be
909 : : * critically dependent on the assumption that the bottommost active snapshot
910 : : * stack entry has the oldest xmin. (Current uses of GetOldestSnapshot() are
911 : : * not actually critical, but this would be.)
912 : : */
913 : : static void
5816 alvherre@alvh.no-ip. 914 : 7849151 : SnapshotResetXmin(void)
915 : : {
916 : : Snapshot minSnapshot;
917 : :
3375 heikki.linnakangas@i 918 [ + + ]: 7849151 : if (ActiveSnapshot != NULL)
919 : 5479128 : return;
920 : :
921 [ + + ]: 2370023 : if (pairingheap_is_empty(&RegisteredSnapshots))
922 : : {
1340 andres@anarazel.de 923 : 789072 : MyProc->xmin = InvalidTransactionId;
3375 heikki.linnakangas@i 924 : 789072 : return;
925 : : }
926 : :
927 : 1580951 : minSnapshot = pairingheap_container(SnapshotData, ph_node,
928 : : pairingheap_first(&RegisteredSnapshots));
929 : :
1340 andres@anarazel.de 930 [ + + ]: 1580951 : if (TransactionIdPrecedes(MyProc->xmin, minSnapshot->xmin))
931 : 4038 : MyProc->xmin = minSnapshot->xmin;
932 : : }
933 : :
934 : : /*
935 : : * AtSubCommit_Snapshot
936 : : */
937 : : void
5816 alvherre@alvh.no-ip. 938 : 5376 : AtSubCommit_Snapshot(int level)
939 : : {
940 : : ActiveSnapshotElt *active;
941 : :
942 : : /*
943 : : * Relabel the active snapshots set in this subtransaction as though they
944 : : * are owned by the parent subxact.
945 : : */
946 [ + + ]: 5376 : for (active = ActiveSnapshot; active != NULL; active = active->as_next)
947 : : {
948 [ + - ]: 4531 : if (active->as_level < level)
949 : 4531 : break;
5816 alvherre@alvh.no-ip. 950 :UBC 0 : active->as_level = level - 1;
951 : : }
5816 alvherre@alvh.no-ip. 952 :CBC 5376 : }
953 : :
954 : : /*
955 : : * AtSubAbort_Snapshot
956 : : * Clean up snapshots after a subtransaction abort
957 : : */
958 : : void
959 : 4575 : AtSubAbort_Snapshot(int level)
960 : : {
961 : : /* Forget the active snapshots set by this subtransaction */
962 [ + + + + ]: 11985 : while (ActiveSnapshot && ActiveSnapshot->as_level >= level)
963 : : {
964 : : ActiveSnapshotElt *next;
965 : :
966 : 2835 : next = ActiveSnapshot->as_next;
967 : :
968 : : /*
969 : : * Decrement the snapshot's active count. If it's still registered or
970 : : * marked as active by an outer subtransaction, we can't free it yet.
971 : : */
972 [ - + ]: 2835 : Assert(ActiveSnapshot->as_snap->active_count >= 1);
973 : 2835 : ActiveSnapshot->as_snap->active_count -= 1;
974 : :
975 [ + - ]: 2835 : if (ActiveSnapshot->as_snap->active_count == 0 &&
976 [ + - ]: 2835 : ActiveSnapshot->as_snap->regd_count == 0)
977 : 2835 : FreeSnapshot(ActiveSnapshot->as_snap);
978 : :
979 : : /* and free the stack element */
980 : 2835 : pfree(ActiveSnapshot);
981 : :
982 : 2835 : ActiveSnapshot = next;
2811 rhaas@postgresql.org 983 [ + + ]: 2835 : if (ActiveSnapshot == NULL)
984 : 121 : OldestActiveSnapshot = NULL;
985 : : }
986 : :
5816 alvherre@alvh.no-ip. 987 : 4575 : SnapshotResetXmin();
988 : 4575 : }
989 : :
990 : : /*
991 : : * AtEOXact_Snapshot
992 : : * Snapshot manager's cleanup function for end of transaction
993 : : */
994 : : void
2565 simon@2ndQuadrant.co 995 : 433759 : AtEOXact_Snapshot(bool isCommit, bool resetXmin)
996 : : {
997 : : /*
998 : : * In transaction-snapshot mode we must release our privately-managed
999 : : * reference to the transaction snapshot. We must remove it from
1000 : : * RegisteredSnapshots to keep the check below happy. But we don't bother
1001 : : * to do FreeSnapshot, for two reasons: the memory will go away with
1002 : : * TopTransactionContext anyway, and if someone has left the snapshot
1003 : : * stacked as active, we don't want the code below to be chasing through a
1004 : : * dangling pointer.
1005 : : */
4584 tgl@sss.pgh.pa.us 1006 [ + + ]: 433759 : if (FirstXactSnapshot != NULL)
1007 : : {
1008 [ - + ]: 2900 : Assert(FirstXactSnapshot->regd_count > 0);
3375 heikki.linnakangas@i 1009 [ - + ]: 2900 : Assert(!pairingheap_is_empty(&RegisteredSnapshots));
1010 : 2900 : pairingheap_remove(&RegisteredSnapshots, &FirstXactSnapshot->ph_node);
1011 : : }
4584 tgl@sss.pgh.pa.us 1012 : 433759 : FirstXactSnapshot = NULL;
1013 : :
1014 : : /*
1015 : : * If we exported any snapshots, clean them up.
1016 : : */
4558 1017 [ + + ]: 433759 : if (exportedSnapshots != NIL)
1018 : : {
1019 : : ListCell *lc;
1020 : :
1021 : : /*
1022 : : * Get rid of the files. Unlink failure is only a WARNING because (1)
1023 : : * it's too late to abort the transaction, and (2) leaving a leaked
1024 : : * file around has little real consequence anyway.
1025 : : *
1026 : : * We also need to remove the snapshots from RegisteredSnapshots to
1027 : : * prevent a warning below.
1028 : : *
1029 : : * As with the FirstXactSnapshot, we don't need to free resources of
1030 : : * the snapshot itself as it will go away with the memory context.
1031 : : */
3375 heikki.linnakangas@i 1032 [ + - + + : 18 : foreach(lc, exportedSnapshots)
+ + ]
1033 : : {
2489 tgl@sss.pgh.pa.us 1034 : 9 : ExportedSnapshot *esnap = (ExportedSnapshot *) lfirst(lc);
1035 : :
2496 andres@anarazel.de 1036 [ - + ]: 9 : if (unlink(esnap->snapfile))
2496 andres@anarazel.de 1037 [ # # ]:UBC 0 : elog(WARNING, "could not unlink file \"%s\": %m",
1038 : : esnap->snapfile);
1039 : :
2496 andres@anarazel.de 1040 :CBC 9 : pairingheap_remove(&RegisteredSnapshots,
1041 : 9 : &esnap->snapshot->ph_node);
1042 : : }
1043 : :
4558 tgl@sss.pgh.pa.us 1044 : 9 : exportedSnapshots = NIL;
1045 : : }
1046 : :
1047 : : /* Drop catalog snapshot if any */
2707 1048 : 433759 : InvalidateCatalogSnapshot();
1049 : :
1050 : : /* On commit, complain about leftover snapshots */
5816 alvherre@alvh.no-ip. 1051 [ + + ]: 433759 : if (isCommit)
1052 : : {
1053 : : ActiveSnapshotElt *active;
1054 : :
3375 heikki.linnakangas@i 1055 [ - + ]: 410920 : if (!pairingheap_is_empty(&RegisteredSnapshots))
3375 heikki.linnakangas@i 1056 [ # # ]:UBC 0 : elog(WARNING, "registered snapshots seem to remain after cleanup");
1057 : :
1058 : : /* complain about unpopped active snapshots */
5816 alvherre@alvh.no-ip. 1059 [ - + ]:CBC 410920 : for (active = ActiveSnapshot; active != NULL; active = active->as_next)
5648 alvherre@alvh.no-ip. 1060 [ # # ]:UBC 0 : elog(WARNING, "snapshot %p still active", active);
1061 : : }
1062 : :
1063 : : /*
1064 : : * And reset our state. We don't need to free the memory explicitly --
1065 : : * it'll go away with TopTransactionContext.
1066 : : */
5816 alvherre@alvh.no-ip. 1067 :CBC 433759 : ActiveSnapshot = NULL;
2811 rhaas@postgresql.org 1068 : 433759 : OldestActiveSnapshot = NULL;
3375 heikki.linnakangas@i 1069 : 433759 : pairingheap_reset(&RegisteredSnapshots);
1070 : :
5816 alvherre@alvh.no-ip. 1071 : 433759 : CurrentSnapshot = NULL;
1072 : 433759 : SecondarySnapshot = NULL;
1073 : :
1074 : 433759 : FirstSnapshotSet = false;
1075 : :
1076 : : /*
1077 : : * During normal commit processing, we call ProcArrayEndTransaction() to
1078 : : * reset the MyProc->xmin. That call happens prior to the call to
1079 : : * AtEOXact_Snapshot(), so we need not touch xmin here at all.
1080 : : */
2565 simon@2ndQuadrant.co 1081 [ + + ]: 433759 : if (resetXmin)
1082 : 23240 : SnapshotResetXmin();
1083 : :
1340 andres@anarazel.de 1084 [ + + - + ]: 433759 : Assert(resetXmin || MyProc->xmin == 0);
5863 alvherre@alvh.no-ip. 1085 : 433759 : }
1086 : :
1087 : :
1088 : : /*
1089 : : * ExportSnapshot
1090 : : * Export the snapshot to a file so that other backends can import it.
1091 : : * Returns the token (the file name) that can be used to import this
1092 : : * snapshot.
1093 : : */
1094 : : char *
4558 tgl@sss.pgh.pa.us 1095 : 9 : ExportSnapshot(Snapshot snapshot)
1096 : : {
1097 : : TransactionId topXid;
1098 : : TransactionId *children;
1099 : : ExportedSnapshot *esnap;
1100 : : int nchildren;
1101 : : int addTopXid;
1102 : : StringInfoData buf;
1103 : : FILE *f;
1104 : : int i;
1105 : : MemoryContext oldcxt;
1106 : : char path[MAXPGPATH];
1107 : : char pathtmp[MAXPGPATH];
1108 : :
1109 : : /*
1110 : : * It's tempting to call RequireTransactionBlock here, since it's not very
1111 : : * useful to export a snapshot that will disappear immediately afterwards.
1112 : : * However, we haven't got enough information to do that, since we don't
1113 : : * know if we're at top level or not. For example, we could be inside a
1114 : : * plpgsql function that is going to fire off other transactions via
1115 : : * dblink. Rather than disallow perfectly legitimate usages, don't make a
1116 : : * check.
1117 : : *
1118 : : * Also note that we don't make any restriction on the transaction's
1119 : : * isolation level; however, importers must check the level if they are
1120 : : * serializable.
1121 : : */
1122 : :
1123 : : /*
1124 : : * Get our transaction ID if there is one, to include in the snapshot.
1125 : : */
2496 andres@anarazel.de 1126 : 9 : topXid = GetTopTransactionIdIfAny();
1127 : :
1128 : : /*
1129 : : * We cannot export a snapshot from a subtransaction because there's no
1130 : : * easy way for importers to verify that the same subtransaction is still
1131 : : * running.
1132 : : */
4558 tgl@sss.pgh.pa.us 1133 [ - + ]: 9 : if (IsSubTransaction())
4558 tgl@sss.pgh.pa.us 1134 [ # # ]:UBC 0 : ereport(ERROR,
1135 : : (errcode(ERRCODE_ACTIVE_SQL_TRANSACTION),
1136 : : errmsg("cannot export a snapshot from a subtransaction")));
1137 : :
1138 : : /*
1139 : : * We do however allow previous committed subtransactions to exist.
1140 : : * Importers of the snapshot must see them as still running, so get their
1141 : : * XIDs to add them to the snapshot.
1142 : : */
4558 tgl@sss.pgh.pa.us 1143 :CBC 9 : nchildren = xactGetCommittedChildren(&children);
1144 : :
1145 : : /*
1146 : : * Generate file path for the snapshot. We start numbering of snapshots
1147 : : * inside the transaction from 1.
1148 : : */
2496 andres@anarazel.de 1149 : 9 : snprintf(path, sizeof(path), SNAPSHOT_EXPORT_DIR "/%08X-%08X-%d",
42 heikki.linnakangas@i 1150 :GNC 9 : MyProc->vxid.procNumber, MyProc->vxid.lxid,
1151 : 9 : list_length(exportedSnapshots) + 1);
1152 : :
1153 : : /*
1154 : : * Copy the snapshot into TopTransactionContext, add it to the
1155 : : * exportedSnapshots list, and mark it pseudo-registered. We do this to
1156 : : * ensure that the snapshot's xmin is honored for the rest of the
1157 : : * transaction.
1158 : : */
4558 tgl@sss.pgh.pa.us 1159 :CBC 9 : snapshot = CopySnapshot(snapshot);
1160 : :
1161 : 9 : oldcxt = MemoryContextSwitchTo(TopTransactionContext);
2496 andres@anarazel.de 1162 : 9 : esnap = (ExportedSnapshot *) palloc(sizeof(ExportedSnapshot));
1163 : 9 : esnap->snapfile = pstrdup(path);
1164 : 9 : esnap->snapshot = snapshot;
1165 : 9 : exportedSnapshots = lappend(exportedSnapshots, esnap);
4558 tgl@sss.pgh.pa.us 1166 : 9 : MemoryContextSwitchTo(oldcxt);
1167 : :
1168 : 9 : snapshot->regd_count++;
3375 heikki.linnakangas@i 1169 : 9 : pairingheap_add(&RegisteredSnapshots, &snapshot->ph_node);
1170 : :
1171 : : /*
1172 : : * Fill buf with a text serialization of the snapshot, plus identification
1173 : : * data about this transaction. The format expected by ImportSnapshot is
1174 : : * pretty rigid: each line must be fieldname:value.
1175 : : */
4558 tgl@sss.pgh.pa.us 1176 : 9 : initStringInfo(&buf);
1177 : :
42 heikki.linnakangas@i 1178 :GNC 9 : appendStringInfo(&buf, "vxid:%d/%u\n", MyProc->vxid.procNumber, MyProc->vxid.lxid);
2496 andres@anarazel.de 1179 :CBC 9 : appendStringInfo(&buf, "pid:%d\n", MyProcPid);
4558 tgl@sss.pgh.pa.us 1180 : 9 : appendStringInfo(&buf, "dbid:%u\n", MyDatabaseId);
1181 : 9 : appendStringInfo(&buf, "iso:%d\n", XactIsoLevel);
1182 : 9 : appendStringInfo(&buf, "ro:%d\n", XactReadOnly);
1183 : :
1184 : 9 : appendStringInfo(&buf, "xmin:%u\n", snapshot->xmin);
1185 : 9 : appendStringInfo(&buf, "xmax:%u\n", snapshot->xmax);
1186 : :
1187 : : /*
1188 : : * We must include our own top transaction ID in the top-xid data, since
1189 : : * by definition we will still be running when the importing transaction
1190 : : * adopts the snapshot, but GetSnapshotData never includes our own XID in
1191 : : * the snapshot. (There must, therefore, be enough room to add it.)
1192 : : *
1193 : : * However, it could be that our topXid is after the xmax, in which case
1194 : : * we shouldn't include it because xip[] members are expected to be before
1195 : : * xmax. (We need not make the same check for subxip[] members, see
1196 : : * snapshot.h.)
1197 : : */
2496 andres@anarazel.de 1198 : 9 : addTopXid = (TransactionIdIsValid(topXid) &&
2489 tgl@sss.pgh.pa.us 1199 [ - + - - ]: 9 : TransactionIdPrecedes(topXid, snapshot->xmax)) ? 1 : 0;
4558 1200 : 9 : appendStringInfo(&buf, "xcnt:%d\n", snapshot->xcnt + addTopXid);
1201 [ - + ]: 9 : for (i = 0; i < snapshot->xcnt; i++)
4558 tgl@sss.pgh.pa.us 1202 :UBC 0 : appendStringInfo(&buf, "xip:%u\n", snapshot->xip[i]);
4558 tgl@sss.pgh.pa.us 1203 [ - + ]:CBC 9 : if (addTopXid)
4558 tgl@sss.pgh.pa.us 1204 :UBC 0 : appendStringInfo(&buf, "xip:%u\n", topXid);
1205 : :
1206 : : /*
1207 : : * Similarly, we add our subcommitted child XIDs to the subxid data. Here,
1208 : : * we have to cope with possible overflow.
1209 : : */
4558 tgl@sss.pgh.pa.us 1210 [ + - - + ]:CBC 18 : if (snapshot->suboverflowed ||
1211 : 9 : snapshot->subxcnt + nchildren > GetMaxSnapshotSubxidCount())
4558 tgl@sss.pgh.pa.us 1212 :UBC 0 : appendStringInfoString(&buf, "sof:1\n");
1213 : : else
1214 : : {
4558 tgl@sss.pgh.pa.us 1215 :CBC 9 : appendStringInfoString(&buf, "sof:0\n");
1216 : 9 : appendStringInfo(&buf, "sxcnt:%d\n", snapshot->subxcnt + nchildren);
1217 [ - + ]: 9 : for (i = 0; i < snapshot->subxcnt; i++)
4558 tgl@sss.pgh.pa.us 1218 :UBC 0 : appendStringInfo(&buf, "sxp:%u\n", snapshot->subxip[i]);
4558 tgl@sss.pgh.pa.us 1219 [ - + ]:CBC 9 : for (i = 0; i < nchildren; i++)
4558 tgl@sss.pgh.pa.us 1220 :UBC 0 : appendStringInfo(&buf, "sxp:%u\n", children[i]);
1221 : : }
4558 tgl@sss.pgh.pa.us 1222 :CBC 9 : appendStringInfo(&buf, "rec:%u\n", snapshot->takenDuringRecovery);
1223 : :
1224 : : /*
1225 : : * Now write the text representation into a file. We first write to a
1226 : : * ".tmp" filename, and rename to final filename if no error. This
1227 : : * ensures that no other backend can read an incomplete file
1228 : : * (ImportSnapshot won't allow it because of its valid-characters check).
1229 : : */
2496 andres@anarazel.de 1230 : 9 : snprintf(pathtmp, sizeof(pathtmp), "%s.tmp", path);
4558 tgl@sss.pgh.pa.us 1231 [ - + ]: 9 : if (!(f = AllocateFile(pathtmp, PG_BINARY_W)))
4558 tgl@sss.pgh.pa.us 1232 [ # # ]:UBC 0 : ereport(ERROR,
1233 : : (errcode_for_file_access(),
1234 : : errmsg("could not create file \"%s\": %m", pathtmp)));
1235 : :
4558 tgl@sss.pgh.pa.us 1236 [ - + ]:CBC 9 : if (fwrite(buf.data, buf.len, 1, f) != 1)
4558 tgl@sss.pgh.pa.us 1237 [ # # ]:UBC 0 : ereport(ERROR,
1238 : : (errcode_for_file_access(),
1239 : : errmsg("could not write to file \"%s\": %m", pathtmp)));
1240 : :
1241 : : /* no fsync() since file need not survive a system crash */
1242 : :
4558 tgl@sss.pgh.pa.us 1243 [ - + ]:CBC 9 : if (FreeFile(f))
4558 tgl@sss.pgh.pa.us 1244 [ # # ]:UBC 0 : ereport(ERROR,
1245 : : (errcode_for_file_access(),
1246 : : errmsg("could not write to file \"%s\": %m", pathtmp)));
1247 : :
1248 : : /*
1249 : : * Now that we have written everything into a .tmp file, rename the file
1250 : : * to remove the .tmp suffix.
1251 : : */
4558 tgl@sss.pgh.pa.us 1252 [ - + ]:CBC 9 : if (rename(pathtmp, path) < 0)
4558 tgl@sss.pgh.pa.us 1253 [ # # ]:UBC 0 : ereport(ERROR,
1254 : : (errcode_for_file_access(),
1255 : : errmsg("could not rename file \"%s\" to \"%s\": %m",
1256 : : pathtmp, path)));
1257 : :
1258 : : /*
1259 : : * The basename of the file is what we return from pg_export_snapshot().
1260 : : * It's already in path in a textual format and we know that the path
1261 : : * starts with SNAPSHOT_EXPORT_DIR. Skip over the prefix and the slash
1262 : : * and pstrdup it so as not to return the address of a local variable.
1263 : : */
4558 tgl@sss.pgh.pa.us 1264 :CBC 9 : return pstrdup(path + strlen(SNAPSHOT_EXPORT_DIR) + 1);
1265 : : }
1266 : :
1267 : : /*
1268 : : * pg_export_snapshot
1269 : : * SQL-callable wrapper for ExportSnapshot.
1270 : : */
1271 : : Datum
1272 : 9 : pg_export_snapshot(PG_FUNCTION_ARGS)
1273 : : {
1274 : : char *snapshotName;
1275 : :
1276 : 9 : snapshotName = ExportSnapshot(GetActiveSnapshot());
1277 : 9 : PG_RETURN_TEXT_P(cstring_to_text(snapshotName));
1278 : : }
1279 : :
1280 : :
1281 : : /*
1282 : : * Parsing subroutines for ImportSnapshot: parse a line with the given
1283 : : * prefix followed by a value, and advance *s to the next line. The
1284 : : * filename is provided for use in error messages.
1285 : : */
1286 : : static int
1287 : 126 : parseIntFromText(const char *prefix, char **s, const char *filename)
1288 : : {
1289 : 126 : char *ptr = *s;
1290 : 126 : int prefixlen = strlen(prefix);
1291 : : int val;
1292 : :
1293 [ - + ]: 126 : if (strncmp(ptr, prefix, prefixlen) != 0)
4558 tgl@sss.pgh.pa.us 1294 [ # # ]:UBC 0 : ereport(ERROR,
1295 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
1296 : : errmsg("invalid snapshot data in file \"%s\"", filename)));
4558 tgl@sss.pgh.pa.us 1297 :CBC 126 : ptr += prefixlen;
1298 [ - + ]: 126 : if (sscanf(ptr, "%d", &val) != 1)
4558 tgl@sss.pgh.pa.us 1299 [ # # ]:UBC 0 : ereport(ERROR,
1300 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
1301 : : errmsg("invalid snapshot data in file \"%s\"", filename)));
4558 tgl@sss.pgh.pa.us 1302 :CBC 126 : ptr = strchr(ptr, '\n');
1303 [ - + ]: 126 : if (!ptr)
4558 tgl@sss.pgh.pa.us 1304 [ # # ]:UBC 0 : ereport(ERROR,
1305 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
1306 : : errmsg("invalid snapshot data in file \"%s\"", filename)));
4558 tgl@sss.pgh.pa.us 1307 :CBC 126 : *s = ptr + 1;
1308 : 126 : return val;
1309 : : }
1310 : :
1311 : : static TransactionId
1312 : 54 : parseXidFromText(const char *prefix, char **s, const char *filename)
1313 : : {
1314 : 54 : char *ptr = *s;
1315 : 54 : int prefixlen = strlen(prefix);
1316 : : TransactionId val;
1317 : :
1318 [ - + ]: 54 : if (strncmp(ptr, prefix, prefixlen) != 0)
4558 tgl@sss.pgh.pa.us 1319 [ # # ]:UBC 0 : ereport(ERROR,
1320 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
1321 : : errmsg("invalid snapshot data in file \"%s\"", filename)));
4558 tgl@sss.pgh.pa.us 1322 :CBC 54 : ptr += prefixlen;
1323 [ - + ]: 54 : if (sscanf(ptr, "%u", &val) != 1)
4558 tgl@sss.pgh.pa.us 1324 [ # # ]:UBC 0 : ereport(ERROR,
1325 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
1326 : : errmsg("invalid snapshot data in file \"%s\"", filename)));
4558 tgl@sss.pgh.pa.us 1327 :CBC 54 : ptr = strchr(ptr, '\n');
1328 [ - + ]: 54 : if (!ptr)
4558 tgl@sss.pgh.pa.us 1329 [ # # ]:UBC 0 : ereport(ERROR,
1330 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
1331 : : errmsg("invalid snapshot data in file \"%s\"", filename)));
4558 tgl@sss.pgh.pa.us 1332 :CBC 54 : *s = ptr + 1;
1333 : 54 : return val;
1334 : : }
1335 : :
1336 : : static void
2496 andres@anarazel.de 1337 : 18 : parseVxidFromText(const char *prefix, char **s, const char *filename,
1338 : : VirtualTransactionId *vxid)
1339 : : {
1340 : 18 : char *ptr = *s;
1341 : 18 : int prefixlen = strlen(prefix);
1342 : :
1343 [ - + ]: 18 : if (strncmp(ptr, prefix, prefixlen) != 0)
2496 andres@anarazel.de 1344 [ # # ]:UBC 0 : ereport(ERROR,
1345 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
1346 : : errmsg("invalid snapshot data in file \"%s\"", filename)));
2496 andres@anarazel.de 1347 :CBC 18 : ptr += prefixlen;
42 heikki.linnakangas@i 1348 [ - + ]:GNC 18 : if (sscanf(ptr, "%d/%u", &vxid->procNumber, &vxid->localTransactionId) != 2)
2496 andres@anarazel.de 1349 [ # # ]:UBC 0 : ereport(ERROR,
1350 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
1351 : : errmsg("invalid snapshot data in file \"%s\"", filename)));
2496 andres@anarazel.de 1352 :CBC 18 : ptr = strchr(ptr, '\n');
1353 [ - + ]: 18 : if (!ptr)
2496 andres@anarazel.de 1354 [ # # ]:UBC 0 : ereport(ERROR,
1355 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
1356 : : errmsg("invalid snapshot data in file \"%s\"", filename)));
2496 andres@anarazel.de 1357 :CBC 18 : *s = ptr + 1;
1358 : 18 : }
1359 : :
1360 : : /*
1361 : : * ImportSnapshot
1362 : : * Import a previously exported snapshot. The argument should be a
1363 : : * filename in SNAPSHOT_EXPORT_DIR. Load the snapshot from that file.
1364 : : * This is called by "SET TRANSACTION SNAPSHOT 'foo'".
1365 : : */
1366 : : void
4558 tgl@sss.pgh.pa.us 1367 : 24 : ImportSnapshot(const char *idstr)
1368 : : {
1369 : : char path[MAXPGPATH];
1370 : : FILE *f;
1371 : : struct stat stat_buf;
1372 : : char *filebuf;
1373 : : int xcnt;
1374 : : int i;
1375 : : VirtualTransactionId src_vxid;
1376 : : int src_pid;
1377 : : Oid src_dbid;
1378 : : int src_isolevel;
1379 : : bool src_readonly;
1380 : : SnapshotData snapshot;
1381 : :
1382 : : /*
1383 : : * Must be at top level of a fresh transaction. Note in particular that
1384 : : * we check we haven't acquired an XID --- if we have, it's conceivable
1385 : : * that the snapshot would show it as not running, making for very screwy
1386 : : * behavior.
1387 : : */
1388 [ + - + - ]: 48 : if (FirstSnapshotSet ||
1389 [ - + ]: 48 : GetTopTransactionIdIfAny() != InvalidTransactionId ||
1390 : 24 : IsSubTransaction())
4558 tgl@sss.pgh.pa.us 1391 [ # # ]:UBC 0 : ereport(ERROR,
1392 : : (errcode(ERRCODE_ACTIVE_SQL_TRANSACTION),
1393 : : errmsg("SET TRANSACTION SNAPSHOT must be called before any query")));
1394 : :
1395 : : /*
1396 : : * If we are in read committed mode then the next query would execute with
1397 : : * a new snapshot thus making this function call quite useless.
1398 : : */
4558 tgl@sss.pgh.pa.us 1399 [ - + ]:CBC 24 : if (!IsolationUsesXactSnapshot())
4558 tgl@sss.pgh.pa.us 1400 [ # # ]:UBC 0 : ereport(ERROR,
1401 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1402 : : errmsg("a snapshot-importing transaction must have isolation level SERIALIZABLE or REPEATABLE READ")));
1403 : :
1404 : : /*
1405 : : * Verify the identifier: only 0-9, A-F and hyphens are allowed. We do
1406 : : * this mainly to prevent reading arbitrary files.
1407 : : */
4558 tgl@sss.pgh.pa.us 1408 [ + + ]:CBC 24 : if (strspn(idstr, "0123456789ABCDEF-") != strlen(idstr))
4558 tgl@sss.pgh.pa.us 1409 [ + - ]:GBC 3 : ereport(ERROR,
1410 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1411 : : errmsg("invalid snapshot identifier: \"%s\"", idstr)));
1412 : :
1413 : : /* OK, read the file */
4558 tgl@sss.pgh.pa.us 1414 :CBC 21 : snprintf(path, MAXPGPATH, SNAPSHOT_EXPORT_DIR "/%s", idstr);
1415 : :
1416 : 21 : f = AllocateFile(path, PG_BINARY_R);
1417 [ + + ]: 21 : if (!f)
1418 : : {
1419 : : /*
1420 : : * If file is missing while identifier has a correct format, avoid
1421 : : * system errors.
1422 : : */
208 michael@paquier.xyz 1423 [ + - ]:GNC 3 : if (errno == ENOENT)
1424 [ + - ]: 3 : ereport(ERROR,
1425 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
1426 : : errmsg("snapshot \"%s\" does not exist", idstr)));
1427 : : else
208 michael@paquier.xyz 1428 [ # # ]:UNC 0 : ereport(ERROR,
1429 : : (errcode_for_file_access(),
1430 : : errmsg("could not open file \"%s\" for reading: %m",
1431 : : path)));
1432 : : }
1433 : :
1434 : : /* get the size of the file so that we know how much memory we need */
4558 tgl@sss.pgh.pa.us 1435 [ - + ]:CBC 18 : if (fstat(fileno(f), &stat_buf))
4558 tgl@sss.pgh.pa.us 1436 [ # # ]:UBC 0 : elog(ERROR, "could not stat file \"%s\": %m", path);
1437 : :
1438 : : /* and read the file into a palloc'd string */
4558 tgl@sss.pgh.pa.us 1439 :CBC 18 : filebuf = (char *) palloc(stat_buf.st_size + 1);
1440 [ - + ]: 18 : if (fread(filebuf, stat_buf.st_size, 1, f) != 1)
4558 tgl@sss.pgh.pa.us 1441 [ # # ]:UBC 0 : elog(ERROR, "could not read file \"%s\": %m", path);
1442 : :
4558 tgl@sss.pgh.pa.us 1443 :CBC 18 : filebuf[stat_buf.st_size] = '\0';
1444 : :
1445 : 18 : FreeFile(f);
1446 : :
1447 : : /*
1448 : : * Construct a snapshot struct by parsing the file content.
1449 : : */
1450 : 18 : memset(&snapshot, 0, sizeof(snapshot));
1451 : :
2496 andres@anarazel.de 1452 : 18 : parseVxidFromText("vxid:", &filebuf, path, &src_vxid);
1453 : 18 : src_pid = parseIntFromText("pid:", &filebuf, path);
1454 : : /* we abuse parseXidFromText a bit here ... */
4558 tgl@sss.pgh.pa.us 1455 : 18 : src_dbid = parseXidFromText("dbid:", &filebuf, path);
1456 : 18 : src_isolevel = parseIntFromText("iso:", &filebuf, path);
1457 : 18 : src_readonly = parseIntFromText("ro:", &filebuf, path);
1458 : :
1880 michael@paquier.xyz 1459 : 18 : snapshot.snapshot_type = SNAPSHOT_MVCC;
1460 : :
4558 tgl@sss.pgh.pa.us 1461 : 18 : snapshot.xmin = parseXidFromText("xmin:", &filebuf, path);
1462 : 18 : snapshot.xmax = parseXidFromText("xmax:", &filebuf, path);
1463 : :
1464 : 18 : snapshot.xcnt = xcnt = parseIntFromText("xcnt:", &filebuf, path);
1465 : :
1466 : : /* sanity-check the xid count before palloc */
1467 [ + - - + ]: 18 : if (xcnt < 0 || xcnt > GetMaxSnapshotXidCount())
4558 tgl@sss.pgh.pa.us 1468 [ # # ]:UBC 0 : ereport(ERROR,
1469 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
1470 : : errmsg("invalid snapshot data in file \"%s\"", path)));
1471 : :
4558 tgl@sss.pgh.pa.us 1472 :CBC 18 : snapshot.xip = (TransactionId *) palloc(xcnt * sizeof(TransactionId));
1473 [ - + ]: 18 : for (i = 0; i < xcnt; i++)
4558 tgl@sss.pgh.pa.us 1474 :UBC 0 : snapshot.xip[i] = parseXidFromText("xip:", &filebuf, path);
1475 : :
4558 tgl@sss.pgh.pa.us 1476 :CBC 18 : snapshot.suboverflowed = parseIntFromText("sof:", &filebuf, path);
1477 : :
1478 [ + - ]: 18 : if (!snapshot.suboverflowed)
1479 : : {
1480 : 18 : snapshot.subxcnt = xcnt = parseIntFromText("sxcnt:", &filebuf, path);
1481 : :
1482 : : /* sanity-check the xid count before palloc */
1483 [ + - - + ]: 18 : if (xcnt < 0 || xcnt > GetMaxSnapshotSubxidCount())
4558 tgl@sss.pgh.pa.us 1484 [ # # ]:UBC 0 : ereport(ERROR,
1485 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
1486 : : errmsg("invalid snapshot data in file \"%s\"", path)));
1487 : :
4558 tgl@sss.pgh.pa.us 1488 :CBC 18 : snapshot.subxip = (TransactionId *) palloc(xcnt * sizeof(TransactionId));
1489 [ - + ]: 18 : for (i = 0; i < xcnt; i++)
4558 tgl@sss.pgh.pa.us 1490 :UBC 0 : snapshot.subxip[i] = parseXidFromText("sxp:", &filebuf, path);
1491 : : }
1492 : : else
1493 : : {
1494 : 0 : snapshot.subxcnt = 0;
1495 : 0 : snapshot.subxip = NULL;
1496 : : }
1497 : :
4558 tgl@sss.pgh.pa.us 1498 :CBC 18 : snapshot.takenDuringRecovery = parseIntFromText("rec:", &filebuf, path);
1499 : :
1500 : : /*
1501 : : * Do some additional sanity checking, just to protect ourselves. We
1502 : : * don't trouble to check the array elements, just the most critical
1503 : : * fields.
1504 : : */
2496 andres@anarazel.de 1505 [ + - + - ]: 18 : if (!VirtualTransactionIdIsValid(src_vxid) ||
4558 tgl@sss.pgh.pa.us 1506 : 18 : !OidIsValid(src_dbid) ||
1507 [ + - ]: 18 : !TransactionIdIsNormal(snapshot.xmin) ||
1508 [ - + ]: 18 : !TransactionIdIsNormal(snapshot.xmax))
4558 tgl@sss.pgh.pa.us 1509 [ # # ]:UBC 0 : ereport(ERROR,
1510 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
1511 : : errmsg("invalid snapshot data in file \"%s\"", path)));
1512 : :
1513 : : /*
1514 : : * If we're serializable, the source transaction must be too, otherwise
1515 : : * predicate.c has problems (SxactGlobalXmin could go backwards). Also, a
1516 : : * non-read-only transaction can't adopt a snapshot from a read-only
1517 : : * transaction, as predicate.c handles the cases very differently.
1518 : : */
4558 tgl@sss.pgh.pa.us 1519 [ - + ]:CBC 18 : if (IsolationIsSerializable())
1520 : : {
4558 tgl@sss.pgh.pa.us 1521 [ # # ]:UBC 0 : if (src_isolevel != XACT_SERIALIZABLE)
1522 [ # # ]: 0 : ereport(ERROR,
1523 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1524 : : errmsg("a serializable transaction cannot import a snapshot from a non-serializable transaction")));
1525 [ # # # # ]: 0 : if (src_readonly && !XactReadOnly)
1526 [ # # ]: 0 : ereport(ERROR,
1527 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1528 : : errmsg("a non-read-only serializable transaction cannot import a snapshot from a read-only transaction")));
1529 : : }
1530 : :
1531 : : /*
1532 : : * We cannot import a snapshot that was taken in a different database,
1533 : : * because vacuum calculates OldestXmin on a per-database basis; so the
1534 : : * source transaction's xmin doesn't protect us from data loss. This
1535 : : * restriction could be removed if the source transaction were to mark its
1536 : : * xmin as being globally applicable. But that would require some
1537 : : * additional syntax, since that has to be known when the snapshot is
1538 : : * initially taken. (See pgsql-hackers discussion of 2011-10-21.)
1539 : : */
4558 tgl@sss.pgh.pa.us 1540 [ - + ]:CBC 18 : if (src_dbid != MyDatabaseId)
4558 tgl@sss.pgh.pa.us 1541 [ # # ]:UBC 0 : ereport(ERROR,
1542 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1543 : : errmsg("cannot import a snapshot from a different database")));
1544 : :
1545 : : /* OK, install the snapshot */
2496 andres@anarazel.de 1546 :CBC 18 : SetTransactionSnapshot(&snapshot, &src_vxid, src_pid, NULL);
4558 tgl@sss.pgh.pa.us 1547 : 18 : }
1548 : :
1549 : : /*
1550 : : * XactHasExportedSnapshots
1551 : : * Test whether current transaction has exported any snapshots.
1552 : : */
1553 : : bool
1554 : 414 : XactHasExportedSnapshots(void)
1555 : : {
1556 : 414 : return (exportedSnapshots != NIL);
1557 : : }
1558 : :
1559 : : /*
1560 : : * DeleteAllExportedSnapshotFiles
1561 : : * Clean up any files that have been left behind by a crashed backend
1562 : : * that had exported snapshots before it died.
1563 : : *
1564 : : * This should be called during database startup or crash recovery.
1565 : : */
1566 : : void
1567 : 242 : DeleteAllExportedSnapshotFiles(void)
1568 : : {
1569 : : char buf[MAXPGPATH + sizeof(SNAPSHOT_EXPORT_DIR)];
1570 : : DIR *s_dir;
1571 : : struct dirent *s_de;
1572 : :
1573 : : /*
1574 : : * Problems in reading the directory, or unlinking files, are reported at
1575 : : * LOG level. Since we're running in the startup process, ERROR level
1576 : : * would prevent database start, and it's not important enough for that.
1577 : : */
2323 1578 : 242 : s_dir = AllocateDir(SNAPSHOT_EXPORT_DIR);
1579 : :
1580 [ + + ]: 726 : while ((s_de = ReadDirExtended(s_dir, SNAPSHOT_EXPORT_DIR, LOG)) != NULL)
1581 : : {
4558 1582 [ + + ]: 484 : if (strcmp(s_de->d_name, ".") == 0 ||
1583 [ + - ]: 242 : strcmp(s_de->d_name, "..") == 0)
1584 : 484 : continue;
1585 : :
2560 peter_e@gmx.net 1586 :UBC 0 : snprintf(buf, sizeof(buf), SNAPSHOT_EXPORT_DIR "/%s", s_de->d_name);
1587 : :
2323 tgl@sss.pgh.pa.us 1588 [ # # ]: 0 : if (unlink(buf) != 0)
1589 [ # # ]: 0 : ereport(LOG,
1590 : : (errcode_for_file_access(),
1591 : : errmsg("could not remove file \"%s\": %m", buf)));
1592 : : }
1593 : :
4558 tgl@sss.pgh.pa.us 1594 :CBC 242 : FreeDir(s_dir);
1595 : 242 : }
1596 : :
1597 : : /*
1598 : : * ThereAreNoPriorRegisteredSnapshots
1599 : : * Is the registered snapshot count less than or equal to one?
1600 : : *
1601 : : * Don't use this to settle important decisions. While zero registrations and
1602 : : * no ActiveSnapshot would confirm a certain idleness, the system makes no
1603 : : * guarantees about the significance of one registered snapshot.
1604 : : */
1605 : : bool
4152 simon@2ndQuadrant.co 1606 : 30 : ThereAreNoPriorRegisteredSnapshots(void)
1607 : : {
3375 heikki.linnakangas@i 1608 [ - + ]: 30 : if (pairingheap_is_empty(&RegisteredSnapshots) ||
3375 heikki.linnakangas@i 1609 [ # # # # ]:UBC 0 : pairingheap_is_singular(&RegisteredSnapshots))
4152 simon@2ndQuadrant.co 1610 :CBC 30 : return true;
1611 : :
4152 simon@2ndQuadrant.co 1612 :UBC 0 : return false;
1613 : : }
1614 : :
1615 : : /*
1616 : : * HaveRegisteredOrActiveSnapshot
1617 : : * Is there any registered or active snapshot?
1618 : : *
1619 : : * NB: Unless pushed or active, the cached catalog snapshot will not cause
1620 : : * this function to return true. That allows this function to be used in
1621 : : * checks enforcing a longer-lived snapshot.
1622 : : */
1623 : : bool
785 andres@anarazel.de 1624 :CBC 21599 : HaveRegisteredOrActiveSnapshot(void)
1625 : : {
1626 [ + + ]: 21599 : if (ActiveSnapshot != NULL)
1627 : 21425 : return true;
1628 : :
1629 : : /*
1630 : : * The catalog snapshot is in RegisteredSnapshots when valid, but can be
1631 : : * removed at any time due to invalidation processing. If explicitly
1632 : : * registered more than one snapshot has to be in RegisteredSnapshots.
1633 : : */
729 tgl@sss.pgh.pa.us 1634 [ + + ]: 174 : if (CatalogSnapshot != NULL &&
1635 [ + - - + ]: 5 : pairingheap_is_singular(&RegisteredSnapshots))
785 andres@anarazel.de 1636 :UBC 0 : return false;
1637 : :
729 tgl@sss.pgh.pa.us 1638 :CBC 174 : return !pairingheap_is_empty(&RegisteredSnapshots);
1639 : : }
1640 : :
1641 : :
1642 : : /*
1643 : : * Setup a snapshot that replaces normal catalog snapshots that allows catalog
1644 : : * access to behave just like it did at a certain point in the past.
1645 : : *
1646 : : * Needed for logical decoding.
1647 : : */
1648 : : void
3695 rhaas@postgresql.org 1649 : 4260 : SetupHistoricSnapshot(Snapshot historic_snapshot, HTAB *tuplecids)
1650 : : {
1651 [ - + ]: 4260 : Assert(historic_snapshot != NULL);
1652 : :
1653 : : /* setup the timetravel snapshot */
1654 : 4260 : HistoricSnapshot = historic_snapshot;
1655 : :
1656 : : /* setup (cmin, cmax) lookup hash */
1657 : 4260 : tuplecid_data = tuplecids;
1658 : 4260 : }
1659 : :
1660 : :
1661 : : /*
1662 : : * Make catalog snapshots behave normally again.
1663 : : */
1664 : : void
1665 : 4254 : TeardownHistoricSnapshot(bool is_error)
1666 : : {
1667 : 4254 : HistoricSnapshot = NULL;
1668 : 4254 : tuplecid_data = NULL;
1669 : 4254 : }
1670 : :
1671 : : bool
1672 : 8616000 : HistoricSnapshotActive(void)
1673 : : {
1674 : 8616000 : return HistoricSnapshot != NULL;
1675 : : }
1676 : :
1677 : : HTAB *
1678 : 790 : HistoricSnapshotGetTupleCids(void)
1679 : : {
1680 [ - + ]: 790 : Assert(HistoricSnapshotActive());
1681 : 790 : return tuplecid_data;
1682 : : }
1683 : :
1684 : : /*
1685 : : * EstimateSnapshotSpace
1686 : : * Returns the size needed to store the given snapshot.
1687 : : *
1688 : : * We are exporting only required fields from the Snapshot, stored in
1689 : : * SerializedSnapshotData.
1690 : : */
1691 : : Size
572 pg@bowt.ie 1692 : 933 : EstimateSnapshotSpace(Snapshot snapshot)
1693 : : {
1694 : : Size size;
1695 : :
1696 [ - + ]: 933 : Assert(snapshot != InvalidSnapshot);
1697 [ - + ]: 933 : Assert(snapshot->snapshot_type == SNAPSHOT_MVCC);
1698 : :
1699 : : /* We allocate any XID arrays needed in the same palloc block. */
3272 rhaas@postgresql.org 1700 : 933 : size = add_size(sizeof(SerializedSnapshotData),
572 pg@bowt.ie 1701 : 933 : mul_size(snapshot->xcnt, sizeof(TransactionId)));
1702 [ + + ]: 933 : if (snapshot->subxcnt > 0 &&
1703 [ - + - - ]: 2 : (!snapshot->suboverflowed || snapshot->takenDuringRecovery))
3272 rhaas@postgresql.org 1704 : 2 : size = add_size(size,
572 pg@bowt.ie 1705 : 2 : mul_size(snapshot->subxcnt, sizeof(TransactionId)));
1706 : :
3272 rhaas@postgresql.org 1707 : 933 : return size;
1708 : : }
1709 : :
1710 : : /*
1711 : : * SerializeSnapshot
1712 : : * Dumps the serialized snapshot (extracted from given snapshot) onto the
1713 : : * memory location at start_address.
1714 : : */
1715 : : void
1716 : 904 : SerializeSnapshot(Snapshot snapshot, char *start_address)
1717 : : {
1718 : : SerializedSnapshotData serialized_snapshot;
1719 : :
1720 [ - + ]: 904 : Assert(snapshot->subxcnt >= 0);
1721 : :
1722 : : /* Copy all required fields */
2600 noah@leadboat.com 1723 : 904 : serialized_snapshot.xmin = snapshot->xmin;
1724 : 904 : serialized_snapshot.xmax = snapshot->xmax;
1725 : 904 : serialized_snapshot.xcnt = snapshot->xcnt;
1726 : 904 : serialized_snapshot.subxcnt = snapshot->subxcnt;
1727 : 904 : serialized_snapshot.suboverflowed = snapshot->suboverflowed;
1728 : 904 : serialized_snapshot.takenDuringRecovery = snapshot->takenDuringRecovery;
1729 : 904 : serialized_snapshot.curcid = snapshot->curcid;
1730 : 904 : serialized_snapshot.whenTaken = snapshot->whenTaken;
1731 : 904 : serialized_snapshot.lsn = snapshot->lsn;
1732 : :
1733 : : /*
1734 : : * Ignore the SubXID array if it has overflowed, unless the snapshot was
1735 : : * taken during recovery - in that case, top-level XIDs are in subxip as
1736 : : * well, and we mustn't lose them.
1737 : : */
2486 simon@2ndQuadrant.co 1738 [ - + - - ]: 904 : if (serialized_snapshot.suboverflowed && !snapshot->takenDuringRecovery)
2486 simon@2ndQuadrant.co 1739 :UBC 0 : serialized_snapshot.subxcnt = 0;
1740 : :
1741 : : /* Copy struct to possibly-unaligned buffer */
2600 noah@leadboat.com 1742 :CBC 904 : memcpy(start_address,
1743 : : &serialized_snapshot, sizeof(SerializedSnapshotData));
1744 : :
1745 : : /* Copy XID array */
3272 rhaas@postgresql.org 1746 [ + + ]: 904 : if (snapshot->xcnt > 0)
2600 noah@leadboat.com 1747 : 417 : memcpy((TransactionId *) (start_address +
1748 : : sizeof(SerializedSnapshotData)),
3272 rhaas@postgresql.org 1749 : 417 : snapshot->xip, snapshot->xcnt * sizeof(TransactionId));
1750 : :
1751 : : /*
1752 : : * Copy SubXID array. Don't bother to copy it if it had overflowed,
1753 : : * though, because it's not used anywhere in that case. Except if it's a
1754 : : * snapshot taken during recovery; all the top-level XIDs are in subxip as
1755 : : * well in that case, so we mustn't lose them.
1756 : : */
2600 noah@leadboat.com 1757 [ + + ]: 904 : if (serialized_snapshot.subxcnt > 0)
1758 : : {
3249 bruce@momjian.us 1759 : 2 : Size subxipoff = sizeof(SerializedSnapshotData) +
331 tgl@sss.pgh.pa.us 1760 : 2 : snapshot->xcnt * sizeof(TransactionId);
1761 : :
2600 noah@leadboat.com 1762 : 2 : memcpy((TransactionId *) (start_address + subxipoff),
3272 rhaas@postgresql.org 1763 : 2 : snapshot->subxip, snapshot->subxcnt * sizeof(TransactionId));
1764 : : }
1765 : 904 : }
1766 : :
1767 : : /*
1768 : : * RestoreSnapshot
1769 : : * Restore a serialized snapshot from the specified address.
1770 : : *
1771 : : * The copy is palloc'd in TopTransactionContext and has initial refcounts set
1772 : : * to 0. The returned snapshot has the copied flag set.
1773 : : */
1774 : : Snapshot
1775 : 3314 : RestoreSnapshot(char *start_address)
1776 : : {
1777 : : SerializedSnapshotData serialized_snapshot;
1778 : : Size size;
1779 : : Snapshot snapshot;
1780 : : TransactionId *serialized_xids;
1781 : :
2600 noah@leadboat.com 1782 : 3314 : memcpy(&serialized_snapshot, start_address,
1783 : : sizeof(SerializedSnapshotData));
3272 rhaas@postgresql.org 1784 : 3314 : serialized_xids = (TransactionId *)
1785 : : (start_address + sizeof(SerializedSnapshotData));
1786 : :
1787 : : /* We allocate any XID arrays needed in the same palloc block. */
1788 : 3314 : size = sizeof(SnapshotData)
2600 noah@leadboat.com 1789 : 3314 : + serialized_snapshot.xcnt * sizeof(TransactionId)
1790 : 3314 : + serialized_snapshot.subxcnt * sizeof(TransactionId);
1791 : :
1792 : : /* Copy all required fields */
3272 rhaas@postgresql.org 1793 : 3314 : snapshot = (Snapshot) MemoryContextAlloc(TopTransactionContext, size);
1910 andres@anarazel.de 1794 : 3314 : snapshot->snapshot_type = SNAPSHOT_MVCC;
2600 noah@leadboat.com 1795 : 3314 : snapshot->xmin = serialized_snapshot.xmin;
1796 : 3314 : snapshot->xmax = serialized_snapshot.xmax;
3272 rhaas@postgresql.org 1797 : 3314 : snapshot->xip = NULL;
2600 noah@leadboat.com 1798 : 3314 : snapshot->xcnt = serialized_snapshot.xcnt;
3272 rhaas@postgresql.org 1799 : 3314 : snapshot->subxip = NULL;
2600 noah@leadboat.com 1800 : 3314 : snapshot->subxcnt = serialized_snapshot.subxcnt;
1801 : 3314 : snapshot->suboverflowed = serialized_snapshot.suboverflowed;
1802 : 3314 : snapshot->takenDuringRecovery = serialized_snapshot.takenDuringRecovery;
1803 : 3314 : snapshot->curcid = serialized_snapshot.curcid;
1804 : 3314 : snapshot->whenTaken = serialized_snapshot.whenTaken;
1805 : 3314 : snapshot->lsn = serialized_snapshot.lsn;
1336 andres@anarazel.de 1806 : 3314 : snapshot->snapXactCompletionCount = 0;
1807 : :
1808 : : /* Copy XIDs, if present. */
2600 noah@leadboat.com 1809 [ + + ]: 3314 : if (serialized_snapshot.xcnt > 0)
1810 : : {
3272 rhaas@postgresql.org 1811 : 1081 : snapshot->xip = (TransactionId *) (snapshot + 1);
1812 : 1081 : memcpy(snapshot->xip, serialized_xids,
2600 noah@leadboat.com 1813 : 1081 : serialized_snapshot.xcnt * sizeof(TransactionId));
1814 : : }
1815 : :
1816 : : /* Copy SubXIDs, if present. */
1817 [ + + ]: 3314 : if (serialized_snapshot.subxcnt > 0)
1818 : : {
2844 rhaas@postgresql.org 1819 : 9 : snapshot->subxip = ((TransactionId *) (snapshot + 1)) +
2600 noah@leadboat.com 1820 : 9 : serialized_snapshot.xcnt;
1821 : 9 : memcpy(snapshot->subxip, serialized_xids + serialized_snapshot.xcnt,
1822 : 9 : serialized_snapshot.subxcnt * sizeof(TransactionId));
1823 : : }
1824 : :
1825 : : /* Set the copied flag so that the caller will set refcounts correctly. */
3272 rhaas@postgresql.org 1826 : 3314 : snapshot->regd_count = 0;
1827 : 3314 : snapshot->active_count = 0;
1828 : 3314 : snapshot->copied = true;
1829 : :
1830 : 3314 : return snapshot;
1831 : : }
1832 : :
1833 : : /*
1834 : : * Install a restored snapshot as the transaction snapshot.
1835 : : *
1836 : : * The second argument is of type void * so that snapmgr.h need not include
1837 : : * the declaration for PGPROC.
1838 : : */
1839 : : void
1399 andres@anarazel.de 1840 : 1491 : RestoreTransactionSnapshot(Snapshot snapshot, void *source_pgproc)
1841 : : {
1842 : 1491 : SetTransactionSnapshot(snapshot, NULL, InvalidPid, source_pgproc);
3272 rhaas@postgresql.org 1843 : 1491 : }
1844 : :
1845 : : /*
1846 : : * XidInMVCCSnapshot
1847 : : * Is the given XID still-in-progress according to the snapshot?
1848 : : *
1849 : : * Note: GetSnapshotData never stores either top xid or subxids of our own
1850 : : * backend into a snapshot, so these xids will not be reported as "running"
1851 : : * by this function. This is OK for current uses, because we always check
1852 : : * TransactionIdIsCurrentTransactionId first, except when it's known the
1853 : : * XID could not be ours anyway.
1854 : : */
1855 : : bool
1910 andres@anarazel.de 1856 : 55866563 : XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot)
1857 : : {
1858 : : /*
1859 : : * Make a quick range check to eliminate most XIDs without looking at the
1860 : : * xip arrays. Note that this is OK even if we convert a subxact XID to
1861 : : * its parent below, because a subxact with XID < xmin has surely also got
1862 : : * a parent with XID < xmin, while one with XID >= xmax must belong to a
1863 : : * parent that was not yet committed at the time of this snapshot.
1864 : : */
1865 : :
1866 : : /* Any xid < xmin is not in-progress */
1867 [ + + ]: 55866563 : if (TransactionIdPrecedes(xid, snapshot->xmin))
1868 : 52121414 : return false;
1869 : : /* Any xid >= xmax is in-progress */
1870 [ + + ]: 3745149 : if (TransactionIdFollowsOrEquals(xid, snapshot->xmax))
1871 : 10653 : return true;
1872 : :
1873 : : /*
1874 : : * Snapshot information is stored slightly differently in snapshots taken
1875 : : * during recovery.
1876 : : */
1877 [ + + ]: 3734496 : if (!snapshot->takenDuringRecovery)
1878 : : {
1879 : : /*
1880 : : * If the snapshot contains full subxact data, the fastest way to
1881 : : * check things is just to compare the given XID against both subxact
1882 : : * XIDs and top-level XIDs. If the snapshot overflowed, we have to
1883 : : * use pg_subtrans to convert a subxact XID to its parent XID, but
1884 : : * then we need only look at top-level XIDs not subxacts.
1885 : : */
1886 [ + + ]: 3734495 : if (!snapshot->suboverflowed)
1887 : : {
1888 : : /* we have full data, so search subxip */
620 john.naylor@postgres 1889 [ + + ]: 3734145 : if (pg_lfind32(xid, snapshot->subxip, snapshot->subxcnt))
1890 : 224 : return true;
1891 : :
1892 : : /* not there, fall through to search xip[] */
1893 : : }
1894 : : else
1895 : : {
1896 : : /*
1897 : : * Snapshot overflowed, so convert xid to top-level. This is safe
1898 : : * because we eliminated too-old XIDs above.
1899 : : */
1910 andres@anarazel.de 1900 : 350 : xid = SubTransGetTopmostTransaction(xid);
1901 : :
1902 : : /*
1903 : : * If xid was indeed a subxact, we might now have an xid < xmin,
1904 : : * so recheck to avoid an array scan. No point in rechecking
1905 : : * xmax.
1906 : : */
1907 [ - + ]: 350 : if (TransactionIdPrecedes(xid, snapshot->xmin))
1910 andres@anarazel.de 1908 :UBC 0 : return false;
1909 : : }
1910 : :
620 john.naylor@postgres 1911 [ + + ]:CBC 3734271 : if (pg_lfind32(xid, snapshot->xip, snapshot->xcnt))
1912 : 8966 : return true;
1913 : : }
1914 : : else
1915 : : {
1916 : : /*
1917 : : * In recovery we store all xids in the subxip array because it is by
1918 : : * far the bigger array, and we mostly don't know which xids are
1919 : : * top-level and which are subxacts. The xip array is empty.
1920 : : *
1921 : : * We start by searching subtrans, if we overflowed.
1922 : : */
1910 andres@anarazel.de 1923 [ - + ]: 1 : if (snapshot->suboverflowed)
1924 : : {
1925 : : /*
1926 : : * Snapshot overflowed, so convert xid to top-level. This is safe
1927 : : * because we eliminated too-old XIDs above.
1928 : : */
1910 andres@anarazel.de 1929 :UBC 0 : xid = SubTransGetTopmostTransaction(xid);
1930 : :
1931 : : /*
1932 : : * If xid was indeed a subxact, we might now have an xid < xmin,
1933 : : * so recheck to avoid an array scan. No point in rechecking
1934 : : * xmax.
1935 : : */
1936 [ # # ]: 0 : if (TransactionIdPrecedes(xid, snapshot->xmin))
1937 : 0 : return false;
1938 : : }
1939 : :
1940 : : /*
1941 : : * We now have either a top-level xid higher than xmin or an
1942 : : * indeterminate xid. We don't know whether it's top level or subxact
1943 : : * but it doesn't matter. If it's present, the xid is visible.
1944 : : */
620 john.naylor@postgres 1945 [ - + ]:CBC 1 : if (pg_lfind32(xid, snapshot->subxip, snapshot->subxcnt))
620 john.naylor@postgres 1946 :LBC (2) : return true;
1947 : : }
1948 : :
1910 andres@anarazel.de 1949 :CBC 3725306 : return false;
1950 : : }
1951 : :
1952 : : /* ResourceOwner callbacks */
1953 : :
1954 : : static void
158 heikki.linnakangas@i 1955 :GNC 27373 : ResOwnerReleaseSnapshot(Datum res)
1956 : : {
1957 : 27373 : UnregisterSnapshotNoOwner((Snapshot) DatumGetPointer(res));
1958 : 27373 : }
|