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