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