]> granicus.if.org Git - postgresql/commitdiff
Avoid including tablespaces inside PGDATA twice in base backups
authorMagnus Hagander <magnus@hagander.net>
Tue, 7 Jan 2014 16:04:40 +0000 (17:04 +0100)
committerMagnus Hagander <magnus@hagander.net>
Tue, 7 Jan 2014 16:11:51 +0000 (17:11 +0100)
If a tablespace was crated inside PGDATA it was backed up both as part
of the PGDATA backup and as the backup of the tablespace. Avoid this
by skipping any directory inside PGDATA that contains one of the active
tablespaces.

Dimitri Fontaine and Magnus Hagander

src/backend/replication/basebackup.c

index ba8d173357e036e9d9242a2a4a9d640c59780318..7c106904c398158641a564a7f0957030fd3efc9c 100644 (file)
@@ -45,7 +45,7 @@ typedef struct
 } basebackup_options;
 
 
-static int64 sendDir(char *path, int basepathlen, bool sizeonly);
+static int64 sendDir(char *path, int basepathlen, bool sizeonly, List *tablespaces);
 static int64 sendTablespace(char *path, bool sizeonly);
 static bool sendFile(char *readfilename, char *tarfilename,
                 struct stat * statbuf, bool missing_ok);
@@ -72,6 +72,7 @@ typedef struct
 {
        char       *oid;
        char       *path;
+       char       *rpath;                      /* relative path within PGDATA, or NULL */
        int64           size;
 } tablespaceinfo;
 
@@ -100,6 +101,9 @@ perform_base_backup(basebackup_options *opt, DIR *tblspcdir)
        XLogRecPtr      endptr;
        TimeLineID      endtli;
        char       *labelfile;
+       int                     datadirpathlen;
+
+       datadirpathlen = strlen(DataDir);
 
        backup_started_in_recovery = RecoveryInProgress();
 
@@ -119,6 +123,7 @@ perform_base_backup(basebackup_options *opt, DIR *tblspcdir)
                {
                        char            fullpath[MAXPGPATH];
                        char            linkpath[MAXPGPATH];
+                       char       *relpath = NULL;
                        int                     rllen;
 
                        /* Skip special stuff */
@@ -145,9 +150,20 @@ perform_base_backup(basebackup_options *opt, DIR *tblspcdir)
                        }
                        linkpath[rllen] = '\0';
 
+                       /*
+                        * Relpath holds the relative path of the tablespace directory
+                        * when it's located within PGDATA, or NULL if it's located
+                        * elsewhere.
+                        */
+                       if (rllen > datadirpathlen &&
+                               strncmp(linkpath, DataDir, datadirpathlen) == 0 &&
+                               IS_DIR_SEP(linkpath[datadirpathlen]))
+                               relpath = linkpath + datadirpathlen + 1;
+
                        ti = palloc(sizeof(tablespaceinfo));
                        ti->oid = pstrdup(de->d_name);
                        ti->path = pstrdup(linkpath);
+                       ti->rpath = relpath ? pstrdup(relpath) : NULL;
                        ti->size = opt->progress ? sendTablespace(fullpath, true) : -1;
                        tablespaces = lappend(tablespaces, ti);
 #else
@@ -165,7 +181,7 @@ perform_base_backup(basebackup_options *opt, DIR *tblspcdir)
 
                /* Add a node for the base directory at the end */
                ti = palloc0(sizeof(tablespaceinfo));
-               ti->size = opt->progress ? sendDir(".", 1, true) : -1;
+               ti->size = opt->progress ? sendDir(".", 1, true, tablespaces) : -1;
                tablespaces = lappend(tablespaces, ti);
 
                /* Send tablespace header */
@@ -191,7 +207,7 @@ perform_base_backup(basebackup_options *opt, DIR *tblspcdir)
                                sendFileWithContent(BACKUP_LABEL_FILE, labelfile);
 
                                /* ... then the bulk of the files ... */
-                               sendDir(".", 1, false);
+                               sendDir(".", 1, false, tablespaces);
 
                                /* ... and pg_control after everything else. */
                                if (lstat(XLOG_CONTROL_FILE, &statbuf) != 0)
@@ -744,6 +760,8 @@ sendFileWithContent(const char *filename, const char *content)
  * Include the tablespace directory pointed to by 'path' in the output tar
  * stream.     If 'sizeonly' is true, we just calculate a total length and return
  * it, without actually sending anything.
+ *
+ * Only used to send auxiliary tablespaces, not PGDATA.
  */
 static int64
 sendTablespace(char *path, bool sizeonly)
@@ -779,7 +797,7 @@ sendTablespace(char *path, bool sizeonly)
        size = 512;                                     /* Size of the header just added */
 
        /* Send all the files in the tablespace version directory */
-       size += sendDir(pathbuf, strlen(path), sizeonly);
+       size += sendDir(pathbuf, strlen(path), sizeonly, NIL);
 
        return size;
 }
@@ -788,9 +806,12 @@ sendTablespace(char *path, bool sizeonly)
  * Include all files from the given directory in the output tar stream. If
  * 'sizeonly' is true, we just calculate a total length and return it, without
  * actually sending anything.
+ *
+ * Omit any directory in the tablespaces list, to avoid backing up
+ * tablespaces twice when they were created inside PGDATA.
  */
 static int64
-sendDir(char *path, int basepathlen, bool sizeonly)
+sendDir(char *path, int basepathlen, bool sizeonly, List *tablespaces)
 {
        DIR                *dir;
        struct dirent *de;
@@ -924,6 +945,9 @@ sendDir(char *path, int basepathlen, bool sizeonly)
                }
                else if (S_ISDIR(statbuf.st_mode))
                {
+                       bool            skip_this_dir = false;
+                       ListCell   *lc;
+
                        /*
                         * Store a directory entry in the tar file so we can get the
                         * permissions right.
@@ -932,8 +956,29 @@ sendDir(char *path, int basepathlen, bool sizeonly)
                                _tarWriteHeader(pathbuf + basepathlen + 1, NULL, &statbuf);
                        size += 512;            /* Size of the header just added */
 
-                       /* call ourselves recursively for a directory */
-                       size += sendDir(pathbuf, basepathlen, sizeonly);
+                       /*
+                        * Call ourselves recursively for a directory, unless it happens
+                        * to be a separate tablespace located within PGDATA.
+                        */
+                       foreach(lc, tablespaces)
+                       {
+                               tablespaceinfo *ti = (tablespaceinfo *) lfirst(lc);
+
+                               /*
+                                * ti->rpath is the tablespace relative path within PGDATA, or
+                                * NULL if the tablespace has been properly located somewhere
+                                * else.
+                                *
+                                * Skip past the leading "./" in pathbuf when comparing.
+                                */
+                               if (ti->rpath && strcmp(ti->rpath, pathbuf + 2) == 0)
+                               {
+                                       skip_this_dir = true;
+                                       break;
+                               }
+                       }
+                       if (!skip_this_dir)
+                               size += sendDir(pathbuf, basepathlen, sizeonly, tablespaces);
                }
                else if (S_ISREG(statbuf.st_mode))
                {