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