]> granicus.if.org Git - postgresql/blob - src/backend/storage/lmgr/multi.c
e60ba613ee81b32099bb8aba8b6cb765c4d691d8
[postgresql] / src / backend / storage / lmgr / multi.c
1 /*-------------------------------------------------------------------------
2  *
3  * multi.c
4  *        multi level lock table manager
5  *
6  *        Standard multi-level lock manager as per the Gray paper
7  *        (at least, that is what it is supposed to be).  We implement
8  *        three levels -- RELN, PAGE, TUPLE.  Tuple is actually TID
9  *        a physical record pointer.  It isn't an object id.
10  *
11  * Copyright (c) 1994, Regents of the University of California
12  *
13  *
14  * IDENTIFICATION
15  *        $Header: /cvsroot/pgsql/src/backend/storage/lmgr/Attic/multi.c,v 1.29 1999/05/25 16:11:22 momjian Exp $
16  *
17  * NOTES:
18  *       (1) The lock.c module assumes that the caller here is doing
19  *               two phase locking.
20  *
21  *-------------------------------------------------------------------------
22  */
23 #include <stdio.h>
24 #include <string.h>
25 #include "postgres.h"
26 #include "storage/lmgr.h"
27 #include "storage/multilev.h"
28
29 #include "utils/rel.h"
30 #include "miscadmin.h"                  /* MyDatabaseId */
31
32 static bool MultiAcquire(LOCKMETHOD lockmethod, LOCKTAG *tag,
33                          LOCKMODE lockmode, PG_LOCK_LEVEL level);
34 static bool MultiRelease(LOCKMETHOD lockmethod, LOCKTAG *tag,
35                          LOCKMODE lockmode, PG_LOCK_LEVEL level);
36
37 /*
38  * INTENT indicates to higher level that a lower level lock has been
39  * set.  For example, a write lock on a tuple conflicts with a write
40  * lock on a relation.  This conflict is detected as a WRITE_INTENT/
41  * WRITE conflict between the tuple's intent lock and the relation's
42  * write lock.
43  */
44 static MASK MultiConflicts[] = {
45         (int) NULL,
46         /* All reads and writes at any level conflict with a write lock */
47         (1 << WRITE_LOCK) | (1 << WRITE_INTENT) | (1 << READ_LOCK) | (1 << READ_INTENT),
48         /* read locks conflict with write locks at curr and lower levels */
49         (1 << WRITE_LOCK) | (1 << WRITE_INTENT),
50         /* write intent locks */
51         (1 << READ_LOCK) | (1 << WRITE_LOCK),
52         /* read intent locks */
53         (1 << WRITE_LOCK),
54
55         /*
56          * extend locks for archive storage manager conflict only w/extend
57          * locks
58          */
59         (1 << EXTEND_LOCK)
60 };
61
62 /*
63  * write locks have higher priority than read locks and extend locks.  May
64  * want to treat INTENT locks differently.
65  */
66 static int      MultiPrios[] = {
67         (int) NULL,
68         2,
69         1,
70         2,
71         1,
72         1
73 };
74
75 /*
76  * Lock table identifier for this lock table.  The multi-level
77  * lock table is ONE lock table, not three.
78  */
79 LOCKMETHOD      MultiTableId = (LOCKMETHOD) NULL;
80 LOCKMETHOD      LongTermTableId = (LOCKMETHOD) NULL;
81
82 #ifdef NOT_USED
83 LOCKMETHOD      ShortTermTableId = (LOCKMETHOD) NULL;
84
85 #endif
86
87 /*
88  * Create the lock table described by MultiConflicts and Multiprio.
89  */
90 LOCKMETHOD
91 InitMultiLevelLocks()
92 {
93         int                     lockmethod;
94
95         lockmethod = LockMethodTableInit("MultiLevelLockTable",
96                                                   MultiConflicts, MultiPrios, MAX_LOCKMODES - 1);
97         MultiTableId = lockmethod;
98         if (!(MultiTableId))
99                 elog(ERROR, "InitMultiLocks: couldnt initialize lock table");
100         /* -----------------------
101          * No short term lock table for now.  -Jeff 15 July 1991
102          *
103          * ShortTermTableId = LockMethodTableRename(lockmethod);
104          * if (! (ShortTermTableId)) {
105          *       elog(ERROR,"InitMultiLocks: couldnt rename lock table");
106          * }
107          * -----------------------
108          */
109
110 #ifdef USER_LOCKS
111
112         /*
113          * Allocate another tableId for long-term locks
114          */
115         LongTermTableId = LockMethodTableRename(MultiTableId);
116         if (!(LongTermTableId))
117         {
118                 elog(ERROR,
119                          "InitMultiLevelLocks: couldn't rename long-term lock table");
120         }
121 #endif
122
123         return MultiTableId;
124 }
125
126 /*
127  * MultiLockReln -- lock a relation
128  *
129  * Returns: TRUE if the lock can be set, FALSE otherwise.
130  */
131 bool
132 MultiLockReln(LockInfo lockinfo, LOCKMODE lockmode)
133 {
134         LOCKTAG         tag;
135
136         /*
137          * LOCKTAG has two bytes of padding, unfortunately.  The hash function
138          * will return miss if the padding bytes aren't zero'd.
139          */
140         MemSet(&tag, 0, sizeof(tag));
141         tag.relId = lockinfo->lockRelId.relId;
142         tag.dbId = lockinfo->lockRelId.dbId;
143         return MultiAcquire(MultiTableId, &tag, lockmode, RELN_LEVEL);
144 }
145
146 #ifdef NOT_USED
147 /*
148  * MultiLockTuple -- Lock the TID associated with a tuple
149  *
150  * Returns: TRUE if lock is set, FALSE otherwise.
151  *
152  * Side Effects: causes intention level locks to be set
153  *              at the page and relation level.
154  */
155 bool
156 MultiLockTuple(LockInfo lockinfo, ItemPointer tidPtr, LOCKMODE lockmode)
157 {
158         LOCKTAG         tag;
159
160         /*
161          * LOCKTAG has two bytes of padding, unfortunately.  The hash function
162          * will return miss if the padding bytes aren't zero'd.
163          */
164         MemSet(&tag, 0, sizeof(tag));
165
166         tag.relId = lockinfo->lockRelId.relId;
167         tag.dbId = lockinfo->lockRelId.dbId;
168
169         /* not locking any valid Tuple, just the page */
170         tag.tupleId = *tidPtr;
171         return MultiAcquire(MultiTableId, &tag, lockmode, TUPLE_LEVEL);
172 }
173
174 #endif
175
176 /*
177  * same as above at page level
178  */
179 bool
180 MultiLockPage(LockInfo lockinfo, ItemPointer tidPtr, LOCKMODE lockmode)
181 {
182         LOCKTAG         tag;
183
184         /*
185          * LOCKTAG has two bytes of padding, unfortunately.  The hash function
186          * will return miss if the padding bytes aren't zero'd.
187          */
188         MemSet(&tag, 0, sizeof(tag));
189
190
191         /* ----------------------------
192          * Now we want to set the page offset to be invalid
193          * and lock the block.  There is some confusion here as to what
194          * a page is.  In Postgres a page is an 8k block, however this
195          * block may be partitioned into many subpages which are sometimes
196          * also called pages.  The term is overloaded, so don't be fooled
197          * when we say lock the page we mean the 8k block. -Jeff 16 July 1991
198          * ----------------------------
199          */
200         tag.relId = lockinfo->lockRelId.relId;
201         tag.dbId = lockinfo->lockRelId.dbId;
202         BlockIdCopy(&(tag.tupleId.ip_blkid), &(tidPtr->ip_blkid));
203         return MultiAcquire(MultiTableId, &tag, lockmode, PAGE_LEVEL);
204 }
205
206 /*
207  * MultiAcquire -- acquire multi level lock at requested level
208  *
209  * Returns: TRUE if lock is set, FALSE if not
210  * Side Effects:
211  */
212 static bool
213 MultiAcquire(LOCKMETHOD lockmethod,
214                          LOCKTAG *tag,
215                          LOCKMODE lockmode,
216                          PG_LOCK_LEVEL level)
217 {
218         LOCKMODE        locks[N_LEVELS];
219         int                     i,
220                                 status;
221         LOCKTAG         xxTag,
222                            *tmpTag = &xxTag;
223         int                     retStatus = TRUE;
224
225         /*
226          * Three levels implemented.  If we set a low level (e.g. Tuple) lock,
227          * we must set INTENT locks on the higher levels.  The intent lock
228          * detects conflicts between the low level lock and an existing high
229          * level lock.  For example, setting a write lock on a tuple in a
230          * relation is disallowed if there is an existing read lock on the
231          * entire relation.  The write lock would set a WRITE + INTENT lock on
232          * the relation and that lock would conflict with the read.
233          */
234         switch (level)
235         {
236                 case RELN_LEVEL:
237                         locks[0] = lockmode;
238                         locks[1] = NO_LOCK;
239                         locks[2] = NO_LOCK;
240                         break;
241                 case PAGE_LEVEL:
242                         locks[0] = lockmode + INTENT;
243                         locks[1] = lockmode;
244                         locks[2] = NO_LOCK;
245                         break;
246                 case TUPLE_LEVEL:
247                         locks[0] = lockmode + INTENT;
248                         locks[1] = lockmode + INTENT;
249                         locks[2] = lockmode;
250                         break;
251                 default:
252                         elog(ERROR, "MultiAcquire: bad lock level");
253                         return FALSE;
254         }
255
256         /*
257          * construct a new tag as we go. Always loop through all levels, but
258          * if we arent' seting a low level lock, locks[i] is set to NO_LOCK
259          * for the lower levels.  Always start from the highest level and go
260          * to the lowest level.
261          */
262         MemSet(tmpTag, 0, sizeof(*tmpTag));
263         tmpTag->relId = tag->relId;
264         tmpTag->dbId = tag->dbId;
265
266         for (i = 0; i < N_LEVELS; i++)
267         {
268                 if (locks[i] != NO_LOCK)
269                 {
270                         switch (i)
271                         {
272                                 case RELN_LEVEL:
273                                         /* -------------
274                                          * Set the block # and offset to invalid
275                                          * -------------
276                                          */
277                                         BlockIdSet(&(tmpTag->tupleId.ip_blkid), InvalidBlockNumber);
278                                         tmpTag->tupleId.ip_posid = InvalidOffsetNumber;
279                                         break;
280                                 case PAGE_LEVEL:
281                                         /* -------------
282                                          * Copy the block #, set the offset to invalid
283                                          * -------------
284                                          */
285                                         BlockIdCopy(&(tmpTag->tupleId.ip_blkid),
286                                                                 &(tag->tupleId.ip_blkid));
287                                         tmpTag->tupleId.ip_posid = InvalidOffsetNumber;
288                                         break;
289                                 case TUPLE_LEVEL:
290                                         /* --------------
291                                          * Copy the entire tuple id.
292                                          * --------------
293                                          */
294                                         ItemPointerCopy(&tmpTag->tupleId, &tag->tupleId);
295                                         break;
296                         }
297
298                         status = LockAcquire(lockmethod, tmpTag, locks[i]);
299                         if (!status)
300                         {
301
302                                 /*
303                                  * failed for some reason. Before returning we have to
304                                  * release all of the locks we just acquired.
305                                  * MultiRelease(xx,xx,xx, i) means release starting from
306                                  * the last level lock we successfully acquired
307                                  */
308                                 retStatus = FALSE;
309                                 MultiRelease(lockmethod, tag, lockmode, i);
310                                 /* now leave the loop.  Don't try for any more locks */
311                                 break;
312                         }
313                 }
314         }
315         return retStatus;
316 }
317
318 /* ------------------
319  * Release a page in the multi-level lock table
320  * ------------------
321  */
322 #ifdef NOT_USED
323 bool
324 MultiReleasePage(LockInfo lockinfo, ItemPointer tidPtr, LOCKMODE lockmode)
325 {
326         LOCKTAG         tag;
327
328         /* ------------------
329          * LOCKTAG has two bytes of padding, unfortunately.  The
330          * hash function will return miss if the padding bytes aren't
331          * zero'd.
332          * ------------------
333          */
334         MemSet(&tag, 0, sizeof(LOCKTAG));
335
336         tag.relId = lockinfo->lockRelId.relId;
337         tag.dbId = lockinfo->lockRelId.dbId;
338         BlockIdCopy(&(tag.tupleId.ip_blkid), &(tidPtr->ip_blkid));
339
340         return MultiRelease(MultiTableId, &tag, lockmode, PAGE_LEVEL);
341 }
342
343 #endif
344
345 /* ------------------
346  * Release a relation in the multi-level lock table
347  * ------------------
348  */
349 bool
350 MultiReleaseReln(LockInfo lockinfo, LOCKMODE lockmode)
351 {
352         LOCKTAG         tag;
353
354         /* ------------------
355          * LOCKTAG has two bytes of padding, unfortunately.  The
356          * hash function will return miss if the padding bytes aren't
357          * zero'd.
358          * ------------------
359          */
360         MemSet(&tag, 0, sizeof(LOCKTAG));
361         tag.relId = lockinfo->lockRelId.relId;
362         tag.dbId = lockinfo->lockRelId.dbId;
363
364         return MultiRelease(MultiTableId, &tag, lockmode, RELN_LEVEL);
365 }
366
367 /*
368  * MultiRelease -- release a multi-level lock
369  *
370  * Returns: TRUE if successful, FALSE otherwise.
371  */
372 static bool
373 MultiRelease(LOCKMETHOD lockmethod,
374                          LOCKTAG *tag,
375                          LOCKMODE lockmode,
376                          PG_LOCK_LEVEL level)
377 {
378         LOCKMODE        locks[N_LEVELS];
379         int                     i,
380                                 status;
381         LOCKTAG         xxTag,
382                            *tmpTag = &xxTag;
383
384         /*
385          * same level scheme as MultiAcquire().
386          */
387         switch (level)
388         {
389                 case RELN_LEVEL:
390                         locks[0] = lockmode;
391                         locks[1] = NO_LOCK;
392                         locks[2] = NO_LOCK;
393                         break;
394                 case PAGE_LEVEL:
395                         locks[0] = lockmode + INTENT;
396                         locks[1] = lockmode;
397                         locks[2] = NO_LOCK;
398                         break;
399                 case TUPLE_LEVEL:
400                         locks[0] = lockmode + INTENT;
401                         locks[1] = lockmode + INTENT;
402                         locks[2] = lockmode;
403                         break;
404                 default:
405                         elog(ERROR, "MultiRelease: bad lockmode");
406         }
407
408         /*
409          * again, construct the tag on the fly.  This time, however, we
410          * release the locks in the REVERSE order -- from lowest level to
411          * highest level.
412          *
413          * Must zero out the tag to set padding byes to zero and ensure hashing
414          * consistency.
415          */
416         MemSet(tmpTag, 0, sizeof(*tmpTag));
417         tmpTag->relId = tag->relId;
418         tmpTag->dbId = tag->dbId;
419
420         for (i = (N_LEVELS - 1); i >= 0; i--)
421         {
422                 if (locks[i] != NO_LOCK)
423                 {
424                         switch (i)
425                         {
426                                 case RELN_LEVEL:
427                                         /* -------------
428                                          * Set the block # and offset to invalid
429                                          * -------------
430                                          */
431                                         BlockIdSet(&(tmpTag->tupleId.ip_blkid), InvalidBlockNumber);
432                                         tmpTag->tupleId.ip_posid = InvalidOffsetNumber;
433                                         break;
434                                 case PAGE_LEVEL:
435                                         /* -------------
436                                          * Copy the block #, set the offset to invalid
437                                          * -------------
438                                          */
439                                         BlockIdCopy(&(tmpTag->tupleId.ip_blkid),
440                                                                 &(tag->tupleId.ip_blkid));
441                                         tmpTag->tupleId.ip_posid = InvalidOffsetNumber;
442                                         break;
443                                 case TUPLE_LEVEL:
444                                         ItemPointerCopy(&tmpTag->tupleId, &tag->tupleId);
445                                         break;
446                         }
447                         status = LockRelease(lockmethod, tmpTag, locks[i]);
448                         if (!status)
449                                 elog(ERROR, "MultiRelease: couldn't release after error");
450                 }
451         }
452         /* shouldn't reach here */
453         return false;
454 }