]> granicus.if.org Git - postgresql/blob - src/backend/storage/smgr/md.c
fsync patch from openlink
[postgresql] / src / backend / storage / smgr / md.c
1 /*-------------------------------------------------------------------------
2  *
3  * md.c--
4  *    This code manages relations that reside on magnetic disk.
5  *
6  * Copyright (c) 1994, Regents of the University of California
7  *
8  *
9  * IDENTIFICATION
10  *    $Header: /cvsroot/pgsql/src/backend/storage/smgr/md.c,v 1.3 1996/07/15 19:22:12 scrappy Exp $
11  *
12  *-------------------------------------------------------------------------
13  */
14 #include <stdio.h>              /* for sprintf() */
15 #include <sys/file.h>
16
17 #include "postgres.h"
18 #include "miscadmin.h"  /* for DataDir */
19
20 #include "machine.h"
21 #include "storage/smgr.h"       /* where the declarations go */
22 #include "storage/block.h"
23 #include "storage/fd.h"
24 #include "utils/mcxt.h"
25 #include "utils/rel.h"
26 #include "utils/elog.h"
27 #include "utils/palloc.h"
28 #include "catalog/catalog.h"
29
30 #undef DIAGNOSTIC
31
32 /*
33  *  The magnetic disk storage manager keeps track of open file descriptors
34  *  in its own descriptor pool.  This happens for two reasons.  First, at
35  *  transaction boundaries, we walk the list of descriptors and flush
36  *  anything that we've dirtied in the current transaction.  Second, we
37  *  have to support relations of > 4GBytes.  In order to do this, we break
38  *  relations up into chunks of < 2GBytes and store one chunk in each of
39  *  several files that represent the relation.
40  */
41
42 typedef struct _MdfdVec {
43     int                 mdfd_vfd; /* fd number in vfd pool */
44     uint16              mdfd_flags; /* clean, dirty */
45     int                 mdfd_lstbcnt; /* most recent block count */
46     struct _MdfdVec     *mdfd_chain; /* for large relations */
47 } MdfdVec;
48
49 static int      Nfds = 100;
50 static MdfdVec  *Md_fdvec = (MdfdVec *) NULL;
51 static int      CurFd = 0;
52 static MemoryContext    MdCxt;
53
54 #define MDFD_DIRTY      (uint16) 0x01
55
56 #define RELSEG_SIZE     262144          /* (2 ** 31) / 8192 -- 2GB file */
57
58 /* routines declared here */
59 static MdfdVec  *_mdfd_openseg(Relation reln, int segno, int oflags);
60 static MdfdVec  *_mdfd_getseg(Relation reln, int blkno, int oflag);
61 static int _fdvec_ext(void);
62 static BlockNumber _mdnblocks(File file, Size blcksz);
63
64 /*
65  *  mdinit() -- Initialize private state for magnetic disk storage manager.
66  *
67  *      We keep a private table of all file descriptors.  Whenever we do
68  *      a write to one, we mark it dirty in our table.  Whenever we force
69  *      changes to disk, we mark the file descriptor clean.  At transaction
70  *      commit, we force changes to disk for all dirty file descriptors.
71  *      This routine allocates and initializes the table.
72  *
73  *      Returns SM_SUCCESS or SM_FAIL with errno set as appropriate.
74  */
75 int
76 mdinit()
77 {
78     MemoryContext oldcxt;
79
80     MdCxt = (MemoryContext) CreateGlobalMemory("MdSmgr");
81     if (MdCxt == (MemoryContext) NULL)
82         return (SM_FAIL);
83
84     oldcxt = MemoryContextSwitchTo(MdCxt);
85     Md_fdvec = (MdfdVec *) palloc(Nfds * sizeof(MdfdVec));
86     (void) MemoryContextSwitchTo(oldcxt);
87
88     if (Md_fdvec == (MdfdVec *) NULL)
89         return (SM_FAIL);
90
91     memset(Md_fdvec, 0, Nfds * sizeof(MdfdVec)); 
92
93     return (SM_SUCCESS);
94 }
95
96 int
97 mdcreate(Relation reln)
98 {
99     int fd, vfd;
100     int tmp;
101     char *path;
102     extern bool IsBootstrapProcessingMode();
103
104     path = relpath(&(reln->rd_rel->relname.data[0]));
105     fd = FileNameOpenFile(path, O_RDWR|O_CREAT|O_EXCL, 0600);
106
107     /*
108      *  If the file already exists and is empty, we pretend that the
109      *  create succeeded.  During bootstrap processing, we skip that check,
110      *  because pg_time, pg_variable, and pg_log get created before their
111      *  .bki file entries are processed.
112      */
113
114     if (fd < 0) {
115         if ((fd = FileNameOpenFile(path, O_RDWR, 0600)) >= 0) {
116             if (!IsBootstrapProcessingMode() &&
117                 FileRead(fd, (char *) &tmp, sizeof(tmp)) != 0) {
118                 FileClose(fd);
119                 return (-1);
120             }
121         }
122     }
123
124     if (CurFd >= Nfds) {
125         if (_fdvec_ext() == SM_FAIL)
126             return (-1);
127     }
128
129     Md_fdvec[CurFd].mdfd_vfd = fd;
130     Md_fdvec[CurFd].mdfd_flags = (uint16) 0;
131     Md_fdvec[CurFd].mdfd_chain = (MdfdVec *) NULL;
132     Md_fdvec[CurFd].mdfd_lstbcnt = 0;
133
134     vfd = CurFd++;
135
136     return (vfd);
137 }
138
139 /*
140  *  mdunlink() -- Unlink a relation.
141  */
142 int
143 mdunlink(Relation reln)
144 {
145     int fd;
146     int i;
147     MdfdVec *v, *ov;
148     MemoryContext oldcxt;
149     char fname[NAMEDATALEN];    
150     char tname[NAMEDATALEN+10]; /* leave room for overflow suffixes*/
151
152  /* On Windows NT you can't unlink a file if it is open so we have
153  ** to do this.
154  */
155 #ifdef WIN32
156     (void) mdclose(reln);
157 #endif /* WIN32 */
158  
159
160     memset(fname,0, NAMEDATALEN);
161     strncpy(fname, RelationGetRelationName(reln)->data, NAMEDATALEN);
162
163     if (FileNameUnlink(fname) < 0)
164         return (SM_FAIL);
165
166     /* unlink all the overflow files for large relations */
167     for (i = 1; ; i++) {
168 #ifdef WIN32
169        (void) mdclose(reln);
170 #endif /* WIN32 */
171         sprintf(tname, "%s.%d", fname, i);
172         if (FileNameUnlink(tname) < 0)
173             break;
174     }
175
176     /* finally, clean out the mdfd vector */
177     fd = RelationGetFile(reln);
178     Md_fdvec[fd].mdfd_flags = (uint16) 0;
179
180     oldcxt = MemoryContextSwitchTo(MdCxt);
181     for (v = &Md_fdvec[fd]; v != (MdfdVec *) NULL; ) {
182         ov = v;
183         v = v->mdfd_chain;
184         if (ov != &Md_fdvec[fd])
185             pfree(ov);
186     }
187     Md_fdvec[fd].mdfd_chain = (MdfdVec *) NULL;
188     (void) MemoryContextSwitchTo(oldcxt);
189
190     return (SM_SUCCESS);
191 }
192
193 /*
194  *  mdextend() -- Add a block to the specified relation.
195  *
196  *      This routine returns SM_FAIL or SM_SUCCESS, with errno set as
197  *      appropriate.
198  */
199 int
200 mdextend(Relation reln, char *buffer)
201 {
202     long pos;
203     int nblocks;
204     MdfdVec *v;
205
206     nblocks = mdnblocks(reln);
207     v = _mdfd_getseg(reln, nblocks, O_CREAT);
208
209     if ((pos = FileSeek(v->mdfd_vfd, 0L, SEEK_END)) < 0)
210         return (SM_FAIL);
211
212     if (FileWrite(v->mdfd_vfd, buffer, BLCKSZ) != BLCKSZ)
213         return (SM_FAIL);
214
215     /* remember that we did a write, so we can sync at xact commit */
216     v->mdfd_flags |= MDFD_DIRTY;
217
218     /* try to keep the last block count current, though it's just a hint */
219     if ((v->mdfd_lstbcnt = (++nblocks % RELSEG_SIZE)) == 0)
220         v->mdfd_lstbcnt = RELSEG_SIZE;
221
222 #ifdef DIAGNOSTIC
223     if (_mdnblocks(v->mdfd_vfd, BLCKSZ) > RELSEG_SIZE
224         || v->mdfd_lstbcnt > RELSEG_SIZE)
225         elog(FATAL, "segment too big!");
226 #endif
227
228     return (SM_SUCCESS);
229 }
230
231 /*
232  *  mdopen() -- Open the specified relation.
233  */
234 int
235 mdopen(Relation reln)
236 {
237     char *path;
238     int fd;
239     int vfd;
240
241     if (CurFd >= Nfds) {
242         if (_fdvec_ext() == SM_FAIL)
243             return (-1);
244     }
245
246     path = relpath(&(reln->rd_rel->relname.data[0]));
247
248     fd = FileNameOpenFile(path, O_RDWR, 0600);
249
250     /* this should only happen during bootstrap processing */
251     if (fd < 0)
252         fd = FileNameOpenFile(path, O_RDWR|O_CREAT|O_EXCL, 0600);
253
254     Md_fdvec[CurFd].mdfd_vfd = fd;
255     Md_fdvec[CurFd].mdfd_flags = (uint16) 0;
256     Md_fdvec[CurFd].mdfd_chain = (MdfdVec *) NULL;
257     Md_fdvec[CurFd].mdfd_lstbcnt = _mdnblocks(fd, BLCKSZ);
258
259 #ifdef DIAGNOSTIC
260     if (Md_fdvec[CurFd].mdfd_lstbcnt > RELSEG_SIZE)
261         elog(FATAL, "segment too big on relopen!");
262 #endif
263
264     vfd = CurFd++;
265
266     return (vfd);
267 }
268
269 /*
270  *  mdclose() -- Close the specified relation.
271  *
272  *      Returns SM_SUCCESS or SM_FAIL with errno set as appropriate.
273  */
274 int
275 mdclose(Relation reln)
276 {
277     int fd;
278     MdfdVec *v;
279
280     fd = RelationGetFile(reln);
281
282     for (v = &Md_fdvec[fd]; v != (MdfdVec *) NULL; v = v->mdfd_chain) {
283
284         /* may be closed already */
285         if (v->mdfd_vfd < 0)
286             continue;
287
288         /*
289          *  We sync the file descriptor so that we don't need to reopen it at
290          *  transaction commit to force changes to disk.
291          */
292
293         FileSync(v->mdfd_vfd);
294         FileClose(v->mdfd_vfd);
295
296         /* mark this file descriptor as clean in our private table */
297         v->mdfd_flags &= ~MDFD_DIRTY;
298     }
299
300     return (SM_SUCCESS);
301 }
302
303 /*
304  *  mdread() -- Read the specified block from a relation.
305  *
306  *      Returns SM_SUCCESS or SM_FAIL.
307  */
308 int
309 mdread(Relation reln, BlockNumber blocknum, char *buffer)
310 {
311     int status;
312     long seekpos;
313     int nbytes;
314     MdfdVec *v;
315
316     v = _mdfd_getseg(reln, blocknum, 0);
317
318     seekpos = (long) (BLCKSZ * (blocknum % RELSEG_SIZE));
319
320 #ifdef DIAGNOSTIC
321     if (seekpos >= BLCKSZ * RELSEG_SIZE)
322         elog(FATAL, "seekpos too big!");
323 #endif
324
325     if (FileSeek(v->mdfd_vfd, seekpos, SEEK_SET) != seekpos) {
326         return (SM_FAIL);
327     }
328
329     status = SM_SUCCESS;
330     if ((nbytes = FileRead(v->mdfd_vfd, buffer, BLCKSZ)) != BLCKSZ) {
331         if (nbytes == 0) {
332           memset(buffer, 0, BLCKSZ); 
333         } else {
334             status = SM_FAIL;
335         }
336     }
337
338     return (status);
339 }
340
341 /*
342  *  mdwrite() -- Write the supplied block at the appropriate location.
343  *
344  *      Returns SM_SUCCESS or SM_FAIL.
345  */
346 int
347 mdwrite(Relation reln, BlockNumber blocknum, char *buffer)
348 {
349     int status;
350     long seekpos;
351     MdfdVec *v;
352
353     v = _mdfd_getseg(reln, blocknum, 0);
354
355     seekpos = (long) (BLCKSZ * (blocknum % RELSEG_SIZE));
356 #ifdef DIAGNOSTIC
357     if (seekpos >= BLCKSZ * RELSEG_SIZE)
358         elog(FATAL, "seekpos too big!");
359 #endif
360
361     if (FileSeek(v->mdfd_vfd, seekpos, SEEK_SET) != seekpos) {
362         return (SM_FAIL);
363     }
364
365     status = SM_SUCCESS;
366     if (FileWrite(v->mdfd_vfd, buffer, BLCKSZ) != BLCKSZ)
367         status = SM_FAIL;
368
369     v->mdfd_flags |= MDFD_DIRTY;
370
371     return (status);
372 }
373
374 /*
375  *  mdflush() -- Synchronously write a block to disk.
376  *
377  *      This is exactly like mdwrite(), but doesn't return until the file
378  *      system buffer cache has been flushed.
379  */
380 int
381 mdflush(Relation reln, BlockNumber blocknum, char *buffer)
382 {
383     int status;
384     long seekpos;
385     MdfdVec *v;
386
387     v = _mdfd_getseg(reln, blocknum, 0);
388
389     seekpos = (long) (BLCKSZ * (blocknum % RELSEG_SIZE));
390 #ifdef DIAGNOSTIC
391     if (seekpos >= BLCKSZ * RELSEG_SIZE)
392         elog(FATAL, "seekpos too big!");
393 #endif
394
395     if (FileSeek(v->mdfd_vfd, seekpos, SEEK_SET) != seekpos) {
396         return (SM_FAIL);
397     }
398
399     /* write and sync the block */
400     status = SM_SUCCESS;
401     if (FileWrite(v->mdfd_vfd, buffer, BLCKSZ) != BLCKSZ
402         || FileSync(v->mdfd_vfd) < 0)
403         status = SM_FAIL;
404
405     /*
406      *  By here, the block is written and changes have been forced to stable
407      *  storage.  Mark the descriptor as clean until the next write, so we
408      *  don't sync it again unnecessarily at transaction commit.
409      */
410
411     v->mdfd_flags &= ~MDFD_DIRTY;
412
413     return (status);
414 }
415
416 /*
417  *  mdblindwrt() -- Write a block to disk blind.
418  *
419  *      We have to be able to do this using only the name and OID of
420  *      the database and relation in which the block belongs.  This
421  *      is a synchronous write.
422  */
423 int
424 mdblindwrt(char *dbstr,
425            char *relstr,
426            Oid dbid,
427            Oid relid,
428            BlockNumber blkno,
429            char *buffer)
430 {
431     int fd;
432     int segno;
433     long seekpos;
434     int status;
435     char *path;
436     int nchars;
437
438     /* be sure we have enough space for the '.segno', if any */
439     segno = blkno / RELSEG_SIZE;
440     if (segno > 0)
441         nchars = 10;
442     else
443         nchars = 0;
444
445     /* construct the path to the file and open it */
446     if (dbid == (Oid) 0) {
447         path = (char *) palloc(strlen(DataDir) + sizeof(NameData) + 2 + nchars);
448         if (segno == 0)
449             sprintf(path, "%s/%.*s", DataDir, NAMEDATALEN, relstr);
450         else
451             sprintf(path, "%s/%.*s.%d", DataDir, NAMEDATALEN, relstr, segno);
452     } else {
453         path = (char *) palloc(strlen(DataDir) + strlen("/base/") + 2 * sizeof(NameData) + 2 + nchars);
454         if (segno == 0)
455             sprintf(path, "%s/base/%.*s/%.*s", DataDir, NAMEDATALEN, 
456                         dbstr, NAMEDATALEN, relstr);
457         else
458             sprintf(path, "%s/base/%.*s/%.*s.%d", DataDir, NAMEDATALEN, dbstr,
459                         NAMEDATALEN, relstr, segno);
460     }
461
462     if ((fd = open(path, O_RDWR, 0600)) < 0)
463         return (SM_FAIL);
464
465     /* seek to the right spot */
466     seekpos = (long) (BLCKSZ * (blkno % RELSEG_SIZE));
467     if (lseek(fd, seekpos, SEEK_SET) != seekpos) {
468         (void) close(fd);
469         return (SM_FAIL);
470     }
471
472     status = SM_SUCCESS;
473
474     /* write and sync the block */
475 #ifdef OPENLINK_PATCHES
476     if (write(fd, buffer, BLCKSZ) != BLCKSZ || (pg_fsync(fd) < 0))
477 #else
478     if (write(fd, buffer, BLCKSZ) != BLCKSZ || fsync(fd) < 0)
479         status = SM_FAIL;
480 #endif
481
482     if (close(fd) < 0)
483         status = SM_FAIL;
484
485     pfree(path);
486
487     return (status);
488 }
489
490 /*
491  *  mdnblocks() -- Get the number of blocks stored in a relation.
492  *
493  *      Returns # of blocks or -1 on error.
494  */
495 int
496 mdnblocks(Relation reln)
497 {
498     int fd;
499     MdfdVec *v;
500     int nblocks;
501     int segno;
502
503     fd = RelationGetFile(reln);
504     v = &Md_fdvec[fd];
505
506 #ifdef DIAGNOSTIC
507     if (_mdnblocks(v->mdfd_vfd, BLCKSZ) > RELSEG_SIZE)
508         elog(FATAL, "segment too big in getseg!");
509 #endif
510
511     segno = 0;
512     for (;;) {
513         if (v->mdfd_lstbcnt == RELSEG_SIZE
514             || (nblocks = _mdnblocks(v->mdfd_vfd, BLCKSZ)) == RELSEG_SIZE) {
515
516             v->mdfd_lstbcnt = RELSEG_SIZE;
517             segno++;
518
519             if (v->mdfd_chain == (MdfdVec *) NULL) {
520                 v->mdfd_chain = _mdfd_openseg(reln, segno, O_CREAT);
521                 if (v->mdfd_chain == (MdfdVec *) NULL)
522                     elog(WARN, "cannot count blocks for %.16s -- open failed",
523                                 RelationGetRelationName(reln));
524             }
525
526             v = v->mdfd_chain;
527         } else {
528             return ((segno * RELSEG_SIZE) + nblocks);
529         }
530     }
531 }
532
533 /*
534  *  mdcommit() -- Commit a transaction.
535  *
536  *      All changes to magnetic disk relations must be forced to stable
537  *      storage.  This routine makes a pass over the private table of
538  *      file descriptors.  Any descriptors to which we have done writes,
539  *      but not synced, are synced here.
540  *
541  *      Returns SM_SUCCESS or SM_FAIL with errno set as appropriate.
542  */
543 int
544 mdcommit()
545 {
546     int i;
547     MdfdVec *v;
548
549     for (i = 0; i < CurFd; i++) {
550         for (v = &Md_fdvec[i]; v != (MdfdVec *) NULL; v = v->mdfd_chain) {
551             if (v->mdfd_flags & MDFD_DIRTY) {
552                 if (FileSync(v->mdfd_vfd) < 0)
553                     return (SM_FAIL);
554
555                 v->mdfd_flags &= ~MDFD_DIRTY;
556             }
557         }
558     }
559
560     return (SM_SUCCESS);
561 }
562
563 /*
564  *  mdabort() -- Abort a transaction.
565  *
566  *      Changes need not be forced to disk at transaction abort.  We mark
567  *      all file descriptors as clean here.  Always returns SM_SUCCESS.
568  */
569 int
570 mdabort()
571 {
572     int i;
573     MdfdVec *v;
574
575     for (i = 0; i < CurFd; i++) {
576         for (v = &Md_fdvec[i]; v != (MdfdVec *) NULL; v = v->mdfd_chain) {
577             v->mdfd_flags &= ~MDFD_DIRTY;
578         }
579     }
580
581     return (SM_SUCCESS);
582 }
583
584 /*
585  *  _fdvec_ext() -- Extend the md file descriptor vector.
586  *
587  *      The file descriptor vector must be large enough to hold at least
588  *      'fd' entries.
589  */
590 static
591 int _fdvec_ext()
592 {
593     MdfdVec *nvec;
594     MemoryContext oldcxt;
595
596     Nfds *= 2;
597
598     oldcxt = MemoryContextSwitchTo(MdCxt);
599
600     nvec = (MdfdVec *) palloc(Nfds * sizeof(MdfdVec));
601     memset(nvec, 0, Nfds * sizeof(MdfdVec)); 
602     memmove(nvec, (char *) Md_fdvec, (Nfds / 2) * sizeof(MdfdVec)); 
603     pfree(Md_fdvec);
604
605     (void) MemoryContextSwitchTo(oldcxt);
606
607     Md_fdvec = nvec;
608
609     return (SM_SUCCESS);
610 }
611
612 static MdfdVec *
613 _mdfd_openseg(Relation reln, int segno, int oflags)
614 {
615     MemoryContext oldcxt;
616     MdfdVec *v;
617     int fd;
618     bool dofree;
619     char *path, *fullpath;
620
621     /* be sure we have enough space for the '.segno', if any */
622     path = relpath(RelationGetRelationName(reln)->data);
623
624     dofree = false;
625     if (segno > 0) {
626         dofree = true;
627         fullpath = (char *) palloc(strlen(path) + 12);
628         sprintf(fullpath, "%s.%d", path, segno);
629     } else
630         fullpath = path;
631
632     /* open the file */
633     fd = PathNameOpenFile(fullpath, O_RDWR|oflags, 0600);
634
635     if (dofree)
636         pfree(fullpath);
637
638     if (fd < 0)
639         return ((MdfdVec *) NULL);
640
641     /* allocate an mdfdvec entry for it */
642     oldcxt = MemoryContextSwitchTo(MdCxt);
643     v = (MdfdVec *) palloc(sizeof(MdfdVec));
644     (void) MemoryContextSwitchTo(oldcxt);
645
646     /* fill the entry */
647     v->mdfd_vfd = fd;
648     v->mdfd_flags = (uint16) 0;
649     v->mdfd_chain = (MdfdVec *) NULL;
650     v->mdfd_lstbcnt = _mdnblocks(fd, BLCKSZ);
651
652 #ifdef DIAGNOSTIC
653     if (v->mdfd_lstbcnt > RELSEG_SIZE)
654         elog(FATAL, "segment too big on open!");
655 #endif
656
657     /* all done */
658     return (v);
659 }
660
661 static MdfdVec *
662 _mdfd_getseg(Relation reln, int blkno, int oflag)
663 {
664     MdfdVec *v;
665     int segno;
666     int fd;
667     int i;
668
669     fd = RelationGetFile(reln);
670     if (fd < 0) {
671         if ((fd = mdopen(reln)) < 0)
672             elog(WARN, "cannot open relation %.16s",
673                         RelationGetRelationName(reln));
674         reln->rd_fd = fd;
675     }
676
677     for (v = &Md_fdvec[fd], segno = blkno / RELSEG_SIZE, i = 1;
678          segno > 0;
679          i++, segno--) {
680
681         if (v->mdfd_chain == (MdfdVec *) NULL) {
682             v->mdfd_chain = _mdfd_openseg(reln, i, oflag);
683
684             if (v->mdfd_chain == (MdfdVec *) NULL)
685                 elog(WARN, "cannot open segment %d of relation %.16s",
686                             i, RelationGetRelationName(reln));
687         }
688         v = v->mdfd_chain;
689     }
690
691     return (v);
692 }
693
694 static BlockNumber
695 _mdnblocks(File file, Size blcksz)
696 {
697     long len;
698     
699     len = FileSeek(file, 0L, SEEK_END) - 1;
700     return((BlockNumber)((len < 0) ? 0 : 1 + len / blcksz));
701 }