Header And Logo

PostgreSQL
| The world's most advanced open source database.

reinit.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * reinit.c
00004  *    Reinitialization of unlogged relations
00005  *
00006  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00007  * Portions Copyright (c) 1994, Regents of the University of California
00008  *
00009  * IDENTIFICATION
00010  *    src/backend/storage/file/reinit.c
00011  *
00012  *-------------------------------------------------------------------------
00013  */
00014 
00015 #include "postgres.h"
00016 
00017 #include <unistd.h>
00018 
00019 #include "catalog/catalog.h"
00020 #include "common/relpath.h"
00021 #include "storage/copydir.h"
00022 #include "storage/fd.h"
00023 #include "storage/reinit.h"
00024 #include "utils/hsearch.h"
00025 #include "utils/memutils.h"
00026 
00027 static void ResetUnloggedRelationsInTablespaceDir(const char *tsdirname,
00028                                       int op);
00029 static void ResetUnloggedRelationsInDbspaceDir(const char *dbspacedirname,
00030                                    int op);
00031 static bool parse_filename_for_nontemp_relation(const char *name,
00032                                     int *oidchars, ForkNumber *fork);
00033 
00034 typedef struct
00035 {
00036     char        oid[OIDCHARS + 1];
00037 } unlogged_relation_entry;
00038 
00039 /*
00040  * Reset unlogged relations from before the last restart.
00041  *
00042  * If op includes UNLOGGED_RELATION_CLEANUP, we remove all forks of any
00043  * relation with an "init" fork, except for the "init" fork itself.
00044  *
00045  * If op includes UNLOGGED_RELATION_INIT, we copy the "init" fork to the main
00046  * fork.
00047  */
00048 void
00049 ResetUnloggedRelations(int op)
00050 {
00051     char        temp_path[MAXPGPATH];
00052     DIR        *spc_dir;
00053     struct dirent *spc_de;
00054     MemoryContext tmpctx,
00055                 oldctx;
00056 
00057     /* Log it. */
00058     elog(DEBUG1, "resetting unlogged relations: cleanup %d init %d",
00059          (op & UNLOGGED_RELATION_CLEANUP) != 0,
00060          (op & UNLOGGED_RELATION_INIT) != 0);
00061 
00062     /*
00063      * Just to be sure we don't leak any memory, let's create a temporary
00064      * memory context for this operation.
00065      */
00066     tmpctx = AllocSetContextCreate(CurrentMemoryContext,
00067                                    "ResetUnloggedRelations",
00068                                    ALLOCSET_DEFAULT_MINSIZE,
00069                                    ALLOCSET_DEFAULT_INITSIZE,
00070                                    ALLOCSET_DEFAULT_MAXSIZE);
00071     oldctx = MemoryContextSwitchTo(tmpctx);
00072 
00073     /*
00074      * First process unlogged files in pg_default ($PGDATA/base)
00075      */
00076     ResetUnloggedRelationsInTablespaceDir("base", op);
00077 
00078     /*
00079      * Cycle through directories for all non-default tablespaces.
00080      */
00081     spc_dir = AllocateDir("pg_tblspc");
00082 
00083     while ((spc_de = ReadDir(spc_dir, "pg_tblspc")) != NULL)
00084     {
00085         if (strcmp(spc_de->d_name, ".") == 0 ||
00086             strcmp(spc_de->d_name, "..") == 0)
00087             continue;
00088 
00089         snprintf(temp_path, sizeof(temp_path), "pg_tblspc/%s/%s",
00090                  spc_de->d_name, TABLESPACE_VERSION_DIRECTORY);
00091         ResetUnloggedRelationsInTablespaceDir(temp_path, op);
00092     }
00093 
00094     FreeDir(spc_dir);
00095 
00096     /*
00097      * Restore memory context.
00098      */
00099     MemoryContextSwitchTo(oldctx);
00100     MemoryContextDelete(tmpctx);
00101 }
00102 
00103 /* Process one tablespace directory for ResetUnloggedRelations */
00104 static void
00105 ResetUnloggedRelationsInTablespaceDir(const char *tsdirname, int op)
00106 {
00107     DIR        *ts_dir;
00108     struct dirent *de;
00109     char        dbspace_path[MAXPGPATH];
00110 
00111     ts_dir = AllocateDir(tsdirname);
00112     if (ts_dir == NULL)
00113     {
00114         /* anything except ENOENT is fishy */
00115         if (errno != ENOENT)
00116             elog(LOG,
00117                  "could not open tablespace directory \"%s\": %m",
00118                  tsdirname);
00119         return;
00120     }
00121 
00122     while ((de = ReadDir(ts_dir, tsdirname)) != NULL)
00123     {
00124         int         i = 0;
00125 
00126         /*
00127          * We're only interested in the per-database directories, which have
00128          * numeric names.  Note that this code will also (properly) ignore "."
00129          * and "..".
00130          */
00131         while (isdigit((unsigned char) de->d_name[i]))
00132             ++i;
00133         if (de->d_name[i] != '\0' || i == 0)
00134             continue;
00135 
00136         snprintf(dbspace_path, sizeof(dbspace_path), "%s/%s",
00137                  tsdirname, de->d_name);
00138         ResetUnloggedRelationsInDbspaceDir(dbspace_path, op);
00139     }
00140 
00141     FreeDir(ts_dir);
00142 }
00143 
00144 /* Process one per-dbspace directory for ResetUnloggedRelations */
00145 static void
00146 ResetUnloggedRelationsInDbspaceDir(const char *dbspacedirname, int op)
00147 {
00148     DIR        *dbspace_dir;
00149     struct dirent *de;
00150     char        rm_path[MAXPGPATH];
00151 
00152     /* Caller must specify at least one operation. */
00153     Assert((op & (UNLOGGED_RELATION_CLEANUP | UNLOGGED_RELATION_INIT)) != 0);
00154 
00155     /*
00156      * Cleanup is a two-pass operation.  First, we go through and identify all
00157      * the files with init forks.  Then, we go through again and nuke
00158      * everything with the same OID except the init fork.
00159      */
00160     if ((op & UNLOGGED_RELATION_CLEANUP) != 0)
00161     {
00162         HTAB       *hash = NULL;
00163         HASHCTL     ctl;
00164 
00165         /* Open the directory. */
00166         dbspace_dir = AllocateDir(dbspacedirname);
00167         if (dbspace_dir == NULL)
00168         {
00169             elog(LOG,
00170                  "could not open dbspace directory \"%s\": %m",
00171                  dbspacedirname);
00172             return;
00173         }
00174 
00175         /*
00176          * It's possible that someone could create a ton of unlogged relations
00177          * in the same database & tablespace, so we'd better use a hash table
00178          * rather than an array or linked list to keep track of which files
00179          * need to be reset.  Otherwise, this cleanup operation would be
00180          * O(n^2).
00181          */
00182         ctl.keysize = sizeof(unlogged_relation_entry);
00183         ctl.entrysize = sizeof(unlogged_relation_entry);
00184         hash = hash_create("unlogged hash", 32, &ctl, HASH_ELEM);
00185 
00186         /* Scan the directory. */
00187         while ((de = ReadDir(dbspace_dir, dbspacedirname)) != NULL)
00188         {
00189             ForkNumber  forkNum;
00190             int         oidchars;
00191             unlogged_relation_entry ent;
00192 
00193             /* Skip anything that doesn't look like a relation data file. */
00194             if (!parse_filename_for_nontemp_relation(de->d_name, &oidchars,
00195                                                      &forkNum))
00196                 continue;
00197 
00198             /* Also skip it unless this is the init fork. */
00199             if (forkNum != INIT_FORKNUM)
00200                 continue;
00201 
00202             /*
00203              * Put the OID portion of the name into the hash table, if it
00204              * isn't already.
00205              */
00206             memset(ent.oid, 0, sizeof(ent.oid));
00207             memcpy(ent.oid, de->d_name, oidchars);
00208             hash_search(hash, &ent, HASH_ENTER, NULL);
00209         }
00210 
00211         /* Done with the first pass. */
00212         FreeDir(dbspace_dir);
00213 
00214         /*
00215          * If we didn't find any init forks, there's no point in continuing;
00216          * we can bail out now.
00217          */
00218         if (hash_get_num_entries(hash) == 0)
00219         {
00220             hash_destroy(hash);
00221             return;
00222         }
00223 
00224         /*
00225          * Now, make a second pass and remove anything that matches. First,
00226          * reopen the directory.
00227          */
00228         dbspace_dir = AllocateDir(dbspacedirname);
00229         if (dbspace_dir == NULL)
00230         {
00231             elog(LOG,
00232                  "could not open dbspace directory \"%s\": %m",
00233                  dbspacedirname);
00234             hash_destroy(hash);
00235             return;
00236         }
00237 
00238         /* Scan the directory. */
00239         while ((de = ReadDir(dbspace_dir, dbspacedirname)) != NULL)
00240         {
00241             ForkNumber  forkNum;
00242             int         oidchars;
00243             bool        found;
00244             unlogged_relation_entry ent;
00245 
00246             /* Skip anything that doesn't look like a relation data file. */
00247             if (!parse_filename_for_nontemp_relation(de->d_name, &oidchars,
00248                                                      &forkNum))
00249                 continue;
00250 
00251             /* We never remove the init fork. */
00252             if (forkNum == INIT_FORKNUM)
00253                 continue;
00254 
00255             /*
00256              * See whether the OID portion of the name shows up in the hash
00257              * table.
00258              */
00259             memset(ent.oid, 0, sizeof(ent.oid));
00260             memcpy(ent.oid, de->d_name, oidchars);
00261             hash_search(hash, &ent, HASH_FIND, &found);
00262 
00263             /* If so, nuke it! */
00264             if (found)
00265             {
00266                 snprintf(rm_path, sizeof(rm_path), "%s/%s",
00267                          dbspacedirname, de->d_name);
00268 
00269                 /*
00270                  * It's tempting to actually throw an error here, but since
00271                  * this code gets run during database startup, that could
00272                  * result in the database failing to start.  (XXX Should we do
00273                  * it anyway?)
00274                  */
00275                 if (unlink(rm_path))
00276                     elog(LOG, "could not unlink file \"%s\": %m", rm_path);
00277                 else
00278                     elog(DEBUG2, "unlinked file \"%s\"", rm_path);
00279             }
00280         }
00281 
00282         /* Cleanup is complete. */
00283         FreeDir(dbspace_dir);
00284         hash_destroy(hash);
00285     }
00286 
00287     /*
00288      * Initialization happens after cleanup is complete: we copy each init
00289      * fork file to the corresponding main fork file.  Note that if we are
00290      * asked to do both cleanup and init, we may never get here: if the
00291      * cleanup code determines that there are no init forks in this dbspace,
00292      * it will return before we get to this point.
00293      */
00294     if ((op & UNLOGGED_RELATION_INIT) != 0)
00295     {
00296         /* Open the directory. */
00297         dbspace_dir = AllocateDir(dbspacedirname);
00298         if (dbspace_dir == NULL)
00299         {
00300             /* we just saw this directory, so it really ought to be there */
00301             elog(LOG,
00302                  "could not open dbspace directory \"%s\": %m",
00303                  dbspacedirname);
00304             return;
00305         }
00306 
00307         /* Scan the directory. */
00308         while ((de = ReadDir(dbspace_dir, dbspacedirname)) != NULL)
00309         {
00310             ForkNumber  forkNum;
00311             int         oidchars;
00312             char        oidbuf[OIDCHARS + 1];
00313             char        srcpath[MAXPGPATH];
00314             char        dstpath[MAXPGPATH];
00315 
00316             /* Skip anything that doesn't look like a relation data file. */
00317             if (!parse_filename_for_nontemp_relation(de->d_name, &oidchars,
00318                                                      &forkNum))
00319                 continue;
00320 
00321             /* Also skip it unless this is the init fork. */
00322             if (forkNum != INIT_FORKNUM)
00323                 continue;
00324 
00325             /* Construct source pathname. */
00326             snprintf(srcpath, sizeof(srcpath), "%s/%s",
00327                      dbspacedirname, de->d_name);
00328 
00329             /* Construct destination pathname. */
00330             memcpy(oidbuf, de->d_name, oidchars);
00331             oidbuf[oidchars] = '\0';
00332             snprintf(dstpath, sizeof(dstpath), "%s/%s%s",
00333                      dbspacedirname, oidbuf, de->d_name + oidchars + 1 +
00334                      strlen(forkNames[INIT_FORKNUM]));
00335 
00336             /* OK, we're ready to perform the actual copy. */
00337             elog(DEBUG2, "copying %s to %s", srcpath, dstpath);
00338             copy_file(srcpath, dstpath);
00339         }
00340 
00341         FreeDir(dbspace_dir);
00342     }
00343 }
00344 
00345 /*
00346  * Basic parsing of putative relation filenames.
00347  *
00348  * This function returns true if the file appears to be in the correct format
00349  * for a non-temporary relation and false otherwise.
00350  *
00351  * NB: If this function returns true, the caller is entitled to assume that
00352  * *oidchars has been set to the a value no more than OIDCHARS, and thus
00353  * that a buffer of OIDCHARS+1 characters is sufficient to hold the OID
00354  * portion of the filename.  This is critical to protect against a possible
00355  * buffer overrun.
00356  */
00357 static bool
00358 parse_filename_for_nontemp_relation(const char *name, int *oidchars,
00359                                     ForkNumber *fork)
00360 {
00361     int         pos;
00362 
00363     /* Look for a non-empty string of digits (that isn't too long). */
00364     for (pos = 0; isdigit((unsigned char) name[pos]); ++pos)
00365         ;
00366     if (pos == 0 || pos > OIDCHARS)
00367         return false;
00368     *oidchars = pos;
00369 
00370     /* Check for a fork name. */
00371     if (name[pos] != '_')
00372         *fork = MAIN_FORKNUM;
00373     else
00374     {
00375         int         forkchar;
00376 
00377         forkchar = forkname_chars(&name[pos + 1], fork);
00378         if (forkchar <= 0)
00379             return false;
00380         pos += forkchar + 1;
00381     }
00382 
00383     /* Check for a segment number. */
00384     if (name[pos] == '.')
00385     {
00386         int         segchar;
00387 
00388         for (segchar = 1; isdigit((unsigned char) name[pos + segchar]); ++segchar)
00389             ;
00390         if (segchar <= 1)
00391             return false;
00392         pos += segchar;
00393     }
00394 
00395     /* Now we should be at the end. */
00396     if (name[pos] != '\0')
00397         return false;
00398     return true;
00399 }