]> granicus.if.org Git - postgresql/blob - src/backend/access/transam/varsup.c
Nested transactions. There is still much left to do, especially on the
[postgresql] / src / backend / access / transam / varsup.c
1 /*-------------------------------------------------------------------------
2  *
3  * varsup.c
4  *        postgres OID & XID variables support routines
5  *
6  * Copyright (c) 2000-2003, PostgreSQL Global Development Group
7  *
8  * IDENTIFICATION
9  *        $PostgreSQL: pgsql/src/backend/access/transam/varsup.c,v 1.56 2004/07/01 00:49:42 tgl Exp $
10  *
11  *-------------------------------------------------------------------------
12  */
13
14 #include "postgres.h"
15
16 #include "access/clog.h"
17 #include "access/subtrans.h"
18 #include "access/transam.h"
19 #include "storage/ipc.h"
20 #include "storage/proc.h"
21
22
23 /* Number of OIDs to prefetch (preallocate) per XLOG write */
24 #define VAR_OID_PREFETCH                8192
25
26 /* pointer to "variable cache" in shared memory (set up by shmem.c) */
27 VariableCache ShmemVariableCache = NULL;
28
29
30 /*
31  * Allocate the next XID for my new transaction.
32  */
33 TransactionId
34 GetNewTransactionId(bool isSubXact)
35 {
36         TransactionId xid;
37
38         /*
39          * During bootstrap initialization, we return the special bootstrap
40          * transaction id.
41          */
42         if (AMI_OVERRIDE)
43                 return BootstrapTransactionId;
44
45         LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
46
47         xid = ShmemVariableCache->nextXid;
48
49         /*
50          * If we are allocating the first XID of a new page of the commit
51          * log, zero out that commit-log page before returning. We must do
52          * this while holding XidGenLock, else another xact could acquire and
53          * commit a later XID before we zero the page.  Fortunately, a page of
54          * the commit log holds 32K or more transactions, so we don't have to
55          * do this very often.
56          *
57          * Extend pg_subtrans too.
58          */
59         ExtendCLOG(xid);
60         ExtendSUBTRANS(xid);
61
62         /*
63          * Now advance the nextXid counter.  This must not happen until after
64          * we have successfully completed ExtendCLOG() --- if that routine fails,
65          * we want the next incoming transaction to try it again.  We cannot
66          * assign more XIDs until there is CLOG space for them.
67          */
68         TransactionIdAdvance(ShmemVariableCache->nextXid);
69
70         /*
71          * Must set MyProc->xid before releasing XidGenLock.  This ensures
72          * that when GetSnapshotData calls ReadNewTransactionId, all active
73          * XIDs before the returned value of nextXid are already present in
74          * the shared PGPROC array.  Else we have a race condition.
75          *
76          * XXX by storing xid into MyProc without acquiring SInvalLock, we are
77          * relying on fetch/store of an xid to be atomic, else other backends
78          * might see a partially-set xid here.  But holding both locks at once
79          * would be a nasty concurrency hit (and in fact could cause a
80          * deadlock against GetSnapshotData).  So for now, assume atomicity.
81          * Note that readers of PGPROC xid field should be careful to fetch
82          * the value only once, rather than assume they can read it multiple
83          * times and get the same answer each time.
84          *
85          * A solution to the atomic-store problem would be to give each PGPROC
86          * its own spinlock used only for fetching/storing that PGPROC's xid.
87          * (SInvalLock would then mean primarily that PGPROCs couldn't be added/
88          * removed while holding the lock.)
89          *
90          * We don't want a subtransaction to update the stored Xid; we'll check
91          * if a transaction Xid is a running subxact by checking pg_subtrans.
92          */
93         if (MyProc != NULL && !isSubXact)
94                 MyProc->xid = xid;
95
96         LWLockRelease(XidGenLock);
97
98         return xid;
99 }
100
101 /*
102  * Read nextXid but don't allocate it.
103  */
104 TransactionId
105 ReadNewTransactionId(void)
106 {
107         TransactionId xid;
108
109         LWLockAcquire(XidGenLock, LW_SHARED);
110         xid = ShmemVariableCache->nextXid;
111         LWLockRelease(XidGenLock);
112
113         return xid;
114 }
115
116 /* ----------------------------------------------------------------
117  *                                      object id generation support
118  * ----------------------------------------------------------------
119  */
120
121 static Oid      lastSeenOid = InvalidOid;
122
123 Oid
124 GetNewObjectId(void)
125 {
126         Oid                     result;
127
128         LWLockAcquire(OidGenLock, LW_EXCLUSIVE);
129
130         /*
131          * Check for wraparound of the OID counter.  We *must* not return 0
132          * (InvalidOid); and as long as we have to check that, it seems a good
133          * idea to skip over everything below BootstrapObjectIdData too. (This
134          * basically just reduces the odds of OID collision right after a wrap
135          * occurs.)  Note we are relying on unsigned comparison here.
136          */
137         if (ShmemVariableCache->nextOid < ((Oid) BootstrapObjectIdData))
138         {
139                 ShmemVariableCache->nextOid = BootstrapObjectIdData;
140                 ShmemVariableCache->oidCount = 0;
141         }
142
143         /* If we run out of logged for use oids then we must log more */
144         if (ShmemVariableCache->oidCount == 0)
145         {
146                 XLogPutNextOid(ShmemVariableCache->nextOid + VAR_OID_PREFETCH);
147                 ShmemVariableCache->oidCount = VAR_OID_PREFETCH;
148         }
149
150         result = ShmemVariableCache->nextOid;
151
152         (ShmemVariableCache->nextOid)++;
153         (ShmemVariableCache->oidCount)--;
154
155         LWLockRelease(OidGenLock);
156
157         lastSeenOid = result;
158
159         return result;
160 }
161
162 void
163 CheckMaxObjectId(Oid assigned_oid)
164 {
165         if (lastSeenOid != InvalidOid && assigned_oid < lastSeenOid)
166                 return;
167
168         LWLockAcquire(OidGenLock, LW_EXCLUSIVE);
169
170         if (assigned_oid < ShmemVariableCache->nextOid)
171         {
172                 lastSeenOid = ShmemVariableCache->nextOid - 1;
173                 LWLockRelease(OidGenLock);
174                 return;
175         }
176
177         /* If we are in the logged oid range, just bump nextOid up */
178         if (assigned_oid <= ShmemVariableCache->nextOid +
179                 ShmemVariableCache->oidCount - 1)
180         {
181                 ShmemVariableCache->oidCount -=
182                         assigned_oid - ShmemVariableCache->nextOid + 1;
183                 ShmemVariableCache->nextOid = assigned_oid + 1;
184                 LWLockRelease(OidGenLock);
185                 return;
186         }
187
188         /*
189          * We have exceeded the logged oid range. We should lock the database
190          * and kill all other backends but we are loading oid's that we can
191          * not guarantee are unique anyway, so we must rely on the user.
192          */
193
194         XLogPutNextOid(assigned_oid + VAR_OID_PREFETCH);
195         ShmemVariableCache->nextOid = assigned_oid + 1;
196         ShmemVariableCache->oidCount = VAR_OID_PREFETCH - 1;
197
198         LWLockRelease(OidGenLock);
199 }