Add WAL logging for CREATE/DROP DATABASE and CREATE/DROP TABLESPACE.
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 29 Aug 2004 21:08:48 +0000 (21:08 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 29 Aug 2004 21:08:48 +0000 (21:08 +0000)
Fix TablespaceCreateDbspace() to be able to create a dummy directory
in place of a dropped tablespace's symlink.  This eliminates the open
problem of a PANIC during WAL replay when a replayed action attempts
to touch a file in a since-deleted tablespace.  It also makes for a
significant improvement in the usability of PITR replay.

doc/src/sgml/backup.sgml
src/backend/access/transam/rmgr.c
src/backend/commands/dbcommands.c
src/backend/commands/tablespace.c
src/include/access/rmgr.h
src/include/commands/dbcommands.h
src/include/commands/tablespace.h
src/include/port.h
src/port/path.c

index 7a3c4f73ff94226ee257f08d41bcd84e52b5f876..af06249203a19da93aea5d9ea2dddfe47281701d 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/backup.sgml,v 2.46 2004/08/08 04:34:43 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/backup.sgml,v 2.47 2004/08/29 21:08:47 tgl Exp $
 -->
 <chapter id="backup">
  <title>Backup and Restore</title>
@@ -902,17 +902,9 @@ restore_command = 'cp /mnt/server/archivedir/%f %p'
 
    <para>
     At this writing, there are several limitations of the on-line backup
-    technique.  These will probably be fixed in future releases.
+    technique.  These will probably be fixed in future releases:
 
   <itemizedlist>
-   <listitem>
-    <para>
-     The effects of <command>CREATE DATABASE</>, <command>DROP DATABASE</>,
-     <command>CREATE TABLESPACE</>, and <command>DROP TABLESPACE</> are
-     not fully reflected in the WAL log.  It is recommended that you take
-     a new base backup after performing one of these operations.
-    </para>
-   </listitem>
    <listitem>
     <para>
      Operations on non-btree indexes (hash, R-tree, and GiST indexes) are
@@ -932,7 +924,7 @@ restore_command = 'cp /mnt/server/archivedir/%f %p'
     since we may need to fix partially-written disk pages.  It is not
     necessary to store so many page copies for PITR operations, however.
     An area for future development is to compress archived WAL data by
-    removing unnecesssary page copies.
+    removing unnecessary page copies.
    </para>
   </sect2>
  </sect1>
index 575ad7a08912897b58b1bfb818ea72bd4562b5d5..db51f36a009ca0720e94e8d27038cc73d49ae93c 100644 (file)
@@ -3,20 +3,22 @@
  *
  * Resource managers definition
  *
- * $PostgreSQL: pgsql/src/backend/access/transam/rmgr.c,v 1.15 2004/08/23 23:22:44 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/access/transam/rmgr.c,v 1.16 2004/08/29 21:08:47 tgl Exp $
  */
 #include "postgres.h"
 
+#include "access/clog.h"
 #include "access/gist.h"
 #include "access/hash.h"
 #include "access/heapam.h"
 #include "access/nbtree.h"
 #include "access/rtree.h"
-#include "access/clog.h"
 #include "access/xact.h"
 #include "access/xlog_internal.h"
-#include "storage/smgr.h"
+#include "commands/dbcommands.h"
 #include "commands/sequence.h"
+#include "commands/tablespace.h"
+#include "storage/smgr.h"
 
 
 const RmgrData RmgrTable[RM_MAX_ID + 1] = {
@@ -24,8 +26,8 @@ const RmgrData RmgrTable[RM_MAX_ID + 1] = {
    {"Transaction", xact_redo, xact_undo, xact_desc, NULL, NULL},
    {"Storage", smgr_redo, smgr_undo, smgr_desc, NULL, NULL},
    {"CLOG", clog_redo, clog_undo, clog_desc, NULL, NULL},
-   {"Reserved 4", NULL, NULL, NULL, NULL, NULL},
-   {"Reserved 5", NULL, NULL, NULL, NULL, NULL},
+   {"Database", dbase_redo, dbase_undo, dbase_desc, NULL, NULL},
+   {"Tablespace", tblspc_redo, tblspc_undo, tblspc_desc, NULL, NULL},
    {"Reserved 6", NULL, NULL, NULL, NULL, NULL},
    {"Reserved 7", NULL, NULL, NULL, NULL, NULL},
    {"Reserved 8", NULL, NULL, NULL, NULL, NULL},
index f7ef440b02e7774799a527b40a4024c5a8afec7f..aa95d97d0e29e01e1dd126fe9586a72d30a67d91 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.141 2004/08/29 05:06:41 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.142 2004/08/29 21:08:47 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -385,6 +385,30 @@ createdb(const CreatedbStmt *stmt)
                    (errmsg("could not initialize database directory")));
        }
 #endif   /* WIN32 */
+
+       /* Record the filesystem change in XLOG */
+       {
+           xl_dbase_create_rec xlrec;
+           XLogRecData rdata[3];
+
+           xlrec.db_id = dboid;
+           rdata[0].buffer = InvalidBuffer;
+           rdata[0].data = (char *) &xlrec;
+           rdata[0].len = offsetof(xl_dbase_create_rec, src_path);
+           rdata[0].next = &(rdata[1]);
+
+           rdata[1].buffer = InvalidBuffer;
+           rdata[1].data = (char *) srcpath;
+           rdata[1].len = strlen(srcpath) + 1;
+           rdata[1].next = &(rdata[2]);
+
+           rdata[2].buffer = InvalidBuffer;
+           rdata[2].data = (char *) dstpath;
+           rdata[2].len = strlen(dstpath) + 1;
+           rdata[2].next = NULL;
+
+           (void) XLogInsert(RM_DBASE_ID, XLOG_DBASE_CREATE, rdata);
+       }
    }
    heap_endscan(scan);
    heap_close(rel, AccessShareLock);
@@ -970,11 +994,27 @@ remove_dbtablespaces(Oid db_id)
        }
 
        if (!rmtree(dstpath, true))
-       {
            ereport(WARNING,
                    (errmsg("could not remove database directory \"%s\"",
-                           dstpath),
-                    errhint("Look in the postmaster's stderr log for more information.")));
+                           dstpath)));
+
+       /* Record the filesystem change in XLOG */
+       {
+           xl_dbase_drop_rec xlrec;
+           XLogRecData rdata[2];
+
+           xlrec.db_id = db_id;
+           rdata[0].buffer = InvalidBuffer;
+           rdata[0].data = (char *) &xlrec;
+           rdata[0].len = offsetof(xl_dbase_drop_rec, dir_path);
+           rdata[0].next = &(rdata[1]);
+
+           rdata[1].buffer = InvalidBuffer;
+           rdata[1].data = (char *) dstpath;
+           rdata[1].len = strlen(dstpath) + 1;
+           rdata[1].next = NULL;
+
+           (void) XLogInsert(RM_DBASE_ID, XLOG_DBASE_DROP, rdata);
        }
 
        pfree(dstpath);
@@ -1063,3 +1103,105 @@ get_database_name(Oid dbid)
 
    return result;
 }
+
+/*
+ * DATABASE resource manager's routines
+ */
+void
+dbase_redo(XLogRecPtr lsn, XLogRecord *record)
+{
+   uint8       info = record->xl_info & ~XLR_INFO_MASK;
+
+   if (info == XLOG_DBASE_CREATE)
+   {
+       xl_dbase_create_rec *xlrec = (xl_dbase_create_rec *) XLogRecGetData(record);
+       char       *dst_path = xlrec->src_path + strlen(xlrec->src_path) + 1;
+       struct stat st;
+#ifndef WIN32
+       char        buf[2 * MAXPGPATH + 100];
+#endif
+
+       /*
+        * Our theory for replaying a CREATE is to forcibly drop the target
+        * subdirectory if present, then re-copy the source data.  This
+        * may be more work than needed, but it is simple to implement.
+        */
+       if (stat(dst_path, &st) == 0 && S_ISDIR(st.st_mode))
+       {
+           if (!rmtree(dst_path, true))
+               ereport(WARNING,
+                       (errmsg("could not remove database directory \"%s\"",
+                               dst_path)));
+       }
+
+#ifndef WIN32
+       /*
+        * Copy this subdirectory to the new location
+        *
+        * XXX use of cp really makes this code pretty grotty, particularly
+        * with respect to lack of ability to report errors well.  Someday
+        * rewrite to do it for ourselves.
+        */
+
+       /* We might need to use cp -R one day for portability */
+       snprintf(buf, sizeof(buf), "cp -r '%s' '%s'",
+                xlrec->src_path, dst_path);
+       if (system(buf) != 0)
+           ereport(ERROR,
+                   (errmsg("could not initialize database directory"),
+                    errdetail("Failing system command was: %s", buf),
+                    errhint("Look in the postmaster's stderr log for more information.")));
+#else                          /* WIN32 */
+       if (copydir(xlrec->src_path, dst_path) != 0)
+       {
+           /* copydir should already have given details of its troubles */
+           ereport(ERROR,
+                   (errmsg("could not initialize database directory")));
+       }
+#endif   /* WIN32 */
+   }
+   else if (info == XLOG_DBASE_DROP)
+   {
+       xl_dbase_drop_rec *xlrec = (xl_dbase_drop_rec *) XLogRecGetData(record);
+
+       /* Drop pages for this database that are in the shared buffer cache */
+       DropBuffers(xlrec->db_id);
+
+       if (!rmtree(xlrec->dir_path, true))
+           ereport(WARNING,
+                   (errmsg("could not remove database directory \"%s\"",
+                           xlrec->dir_path)));
+   }
+   else
+       elog(PANIC, "dbase_redo: unknown op code %u", info);
+}
+
+void
+dbase_undo(XLogRecPtr lsn, XLogRecord *record)
+{
+   elog(PANIC, "dbase_undo: unimplemented");
+}
+
+void
+dbase_desc(char *buf, uint8 xl_info, char *rec)
+{
+   uint8       info = xl_info & ~XLR_INFO_MASK;
+
+   if (info == XLOG_DBASE_CREATE)
+   {
+       xl_dbase_create_rec *xlrec = (xl_dbase_create_rec *) rec;
+       char       *dst_path = xlrec->src_path + strlen(xlrec->src_path) + 1;
+
+       sprintf(buf + strlen(buf), "create db: %u copy \"%s\" to \"%s\"",
+               xlrec->db_id, xlrec->src_path, dst_path);
+   }
+   else if (info == XLOG_DBASE_DROP)
+   {
+       xl_dbase_drop_rec *xlrec = (xl_dbase_drop_rec *) rec;
+
+       sprintf(buf + strlen(buf), "drop db: %u directory: \"%s\"",
+               xlrec->db_id, xlrec->dir_path);
+   }
+   else
+       strcat(buf, "UNKNOWN");
+}
index 15fe839288216b669cfd4f891b551f731790f504..3b44ebb19f4d6b52b5f42eb7e2834b641a108711 100644 (file)
@@ -45,7 +45,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/tablespace.c,v 1.9 2004/08/29 05:06:41 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/tablespace.c,v 1.10 2004/08/29 21:08:47 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -73,6 +73,7 @@
 #include "utils/syscache.h"
 
 
+static bool remove_tablespace_directories(Oid tablespaceoid, bool redo);
 static void set_short_version(const char *path);
 static bool directory_is_empty(const char *path);
 
@@ -89,6 +90,13 @@ static bool directory_is_empty(const char *path);
  * isRedo indicates that we are creating an object during WAL replay;
  * we can skip doing locking in that case (and should do so to avoid
  * any possible problems with pg_tablespace not being valid).
+ *
+ * Also, when isRedo is true, we will cope with the possibility of the
+ * tablespace not being there either --- this could happen if we are
+ * replaying an operation on a table in a subsequently-dropped tablespace.
+ * We handle this by making a directory in the place where the tablespace
+ * symlink would normally be.  This isn't an exact replay of course, but
+ * it's the best we can do given the available information.
  */
 void
 TablespaceCreateDbspace(Oid spcNode, Oid dbNode, bool isRedo)
@@ -137,10 +145,29 @@ TablespaceCreateDbspace(Oid spcNode, Oid dbNode, bool isRedo)
            {
                /* OK, go for it */
                if (mkdir(dir, S_IRWXU) < 0)
-                   ereport(ERROR,
-                           (errcode_for_file_access(),
-                         errmsg("could not create directory \"%s\": %m",
-                                dir)));
+               {
+                   char   *parentdir;
+
+                   if (errno != ENOENT || !isRedo)
+                       ereport(ERROR,
+                               (errcode_for_file_access(),
+                                errmsg("could not create directory \"%s\": %m",
+                                       dir)));
+                   /* Try to make parent directory too */
+                   parentdir = pstrdup(dir);
+                   get_parent_directory(parentdir);
+                   if (mkdir(parentdir, S_IRWXU) < 0)
+                       ereport(ERROR,
+                               (errcode_for_file_access(),
+                                errmsg("could not create directory \"%s\": %m",
+                                       parentdir)));
+                   pfree(parentdir);
+                   if (mkdir(dir, S_IRWXU) < 0)
+                       ereport(ERROR,
+                               (errcode_for_file_access(),
+                                errmsg("could not create directory \"%s\": %m",
+                                       dir)));
+               }
            }
 
            /* OK to drop the exclusive lock */
@@ -282,11 +309,7 @@ CreateTableSpace(CreateTableSpaceStmt *stmt)
 
    tuple = heap_formtuple(rel->rd_att, values, nulls);
 
-   tablespaceoid = newoid();
-
-   HeapTupleSetOid(tuple, tablespaceoid);
-
-   simple_heap_insert(rel, tuple);
+   tablespaceoid = simple_heap_insert(rel, tuple);
 
    CatalogUpdateIndexes(rel, tuple);
 
@@ -332,10 +355,30 @@ CreateTableSpace(CreateTableSpaceStmt *stmt)
                 errmsg("could not create symbolic link \"%s\": %m",
                        linkloc)));
 
+   /* Record the filesystem change in XLOG */
+   {
+       xl_tblspc_create_rec xlrec;
+       XLogRecData rdata[2];
+
+       xlrec.ts_id = tablespaceoid;
+       rdata[0].buffer = InvalidBuffer;
+       rdata[0].data = (char *) &xlrec;
+       rdata[0].len = offsetof(xl_tblspc_create_rec, ts_path);
+       rdata[0].next = &(rdata[1]);
+
+       rdata[1].buffer = InvalidBuffer;
+       rdata[1].data = (char *) location;
+       rdata[1].len = strlen(location) + 1;
+       rdata[1].next = NULL;
+
+       (void) XLogInsert(RM_TBLSPC_ID, XLOG_TBLSPC_CREATE, rdata);
+   }
+
    pfree(linkloc);
    pfree(location);
 
-   heap_close(rel, RowExclusiveLock);
+   /* We keep the lock on pg_tablespace until commit */
+   heap_close(rel, NoLock);
 
 #else                          /* !HAVE_SYMLINK */
    ereport(ERROR,
@@ -358,11 +401,7 @@ DropTableSpace(DropTableSpaceStmt *stmt)
    Relation    rel;
    HeapTuple   tuple;
    ScanKeyData entry[1];
-   char       *location;
    Oid         tablespaceoid;
-   DIR        *dirdesc;
-   struct dirent *de;
-   char       *subfile;
 
    /* don't call this in a transaction block */
    PreventTransactionChain((void *) stmt, "DROP TABLESPACE");
@@ -404,7 +443,63 @@ DropTableSpace(DropTableSpaceStmt *stmt)
        aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_TABLESPACE,
                       tablespacename);
 
-   location = (char *) palloc(strlen(DataDir) + 16 + 10 + 1);
+   /*
+    * Remove the pg_tablespace tuple (this will roll back if we fail below)
+    */
+   simple_heap_delete(rel, &tuple->t_self);
+
+   heap_endscan(scandesc);
+
+   /*
+    * Try to remove the physical infrastructure
+    */
+   if (!remove_tablespace_directories(tablespaceoid, false))
+       ereport(ERROR,
+               (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+                errmsg("tablespace \"%s\" is not empty",
+                       tablespacename)));
+
+   /* Record the filesystem change in XLOG */
+   {
+       xl_tblspc_drop_rec xlrec;
+       XLogRecData rdata[1];
+
+       xlrec.ts_id = tablespaceoid;
+       rdata[0].buffer = InvalidBuffer;
+       rdata[0].data = (char *) &xlrec;
+       rdata[0].len = sizeof(xl_tblspc_drop_rec);
+       rdata[0].next = NULL;
+
+       (void) XLogInsert(RM_TBLSPC_ID, XLOG_TBLSPC_DROP, rdata);
+   }
+
+   /* We keep the lock on pg_tablespace until commit */
+   heap_close(rel, NoLock);
+
+#else                          /* !HAVE_SYMLINK */
+   ereport(ERROR,
+           (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+            errmsg("tablespaces are not supported on this platform")));
+#endif   /* HAVE_SYMLINK */
+}
+
+/*
+ * remove_tablespace_directories: attempt to remove filesystem infrastructure
+ *
+ * Returns TRUE if successful, FALSE if some subdirectory is not empty
+ *
+ * redo indicates we are redoing a drop from XLOG; okay if nothing there
+ */
+static bool
+remove_tablespace_directories(Oid tablespaceoid, bool redo)
+{
+   char       *location;
+   DIR        *dirdesc;
+   struct dirent *de;
+   char       *subfile;
+   struct stat st;
+
+   location = (char *) palloc(strlen(DataDir) + 11 + 10 + 1);
    sprintf(location, "%s/pg_tblspc/%u", DataDir, tablespaceoid);
 
    /*
@@ -422,10 +517,17 @@ DropTableSpace(DropTableSpaceStmt *stmt)
     */
    dirdesc = AllocateDir(location);
    if (dirdesc == NULL)
+   {
+       if (redo && errno == ENOENT)
+       {
+           pfree(location);
+           return true;
+       }
        ereport(ERROR,
                (errcode_for_file_access(),
                 errmsg("could not open directory \"%s\": %m",
                        location)));
+   }
 
    errno = 0;
    while ((de = readdir(dirdesc)) != NULL)
@@ -444,10 +546,10 @@ DropTableSpace(DropTableSpaceStmt *stmt)
 
        /* This check is just to deliver a friendlier error message */
        if (!directory_is_empty(subfile))
-           ereport(ERROR,
-                   (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-                    errmsg("tablespace \"%s\" is not empty",
-                           tablespacename)));
+       {
+           FreeDir(dirdesc);
+           return false;
+       }
 
        /* Do the real deed */
        if (rmdir(subfile) < 0)
@@ -457,6 +559,7 @@ DropTableSpace(DropTableSpaceStmt *stmt)
                            subfile)));
 
        pfree(subfile);
+       errno = 0;
    }
 #ifdef WIN32
 
@@ -475,54 +578,51 @@ DropTableSpace(DropTableSpaceStmt *stmt)
    FreeDir(dirdesc);
 
    /*
-    * Okay, try to unlink PG_VERSION and then remove the symlink.
+    * Okay, try to unlink PG_VERSION (we allow it to not be there, even
+    * in non-REDO case, for robustness).
     */
    subfile = palloc(strlen(location) + 11 + 1);
    sprintf(subfile, "%s/PG_VERSION", location);
 
    if (unlink(subfile) < 0)
-       ereport(ERROR,
-               (errcode_for_file_access(),
-                errmsg("could not unlink file \"%s\": %m",
-                       subfile)));
-
-#ifndef WIN32
-   if (unlink(location) < 0)
-       ereport(ERROR,
-               (errcode_for_file_access(),
-                errmsg("could not unlink symbolic link \"%s\": %m",
-                       location)));
-#else
-   /* The junction is a directory, not a file */
-   if (rmdir(location) < 0)
-       ereport(ERROR,
-               (errcode_for_file_access(),
-                errmsg("could not remove junction dir \"%s\": %m",
-                       location)));
-#endif
+   {
+       if (errno != ENOENT)
+           ereport(ERROR,
+                   (errcode_for_file_access(),
+                    errmsg("could not unlink file \"%s\": %m",
+                           subfile)));
+   }
 
    pfree(subfile);
-   pfree(location);
 
    /*
-    * We have successfully destroyed the infrastructure ... there is now
-    * no way to roll back the DROP ... so proceed to remove the
-    * pg_tablespace tuple.
+    * Okay, try to remove the symlink.  We must however deal with the
+    * possibility that it's a directory instead of a symlink --- this
+    * could happen during WAL replay (see TablespaceCreateDbspace),
+    * and it is also the normal case on Windows.
     */
-   simple_heap_delete(rel, &tuple->t_self);
-
-   heap_endscan(scandesc);
+   if (lstat(location, &st) == 0 && S_ISDIR(st.st_mode))
+   {
+       if (rmdir(location) < 0)
+           ereport(ERROR,
+                   (errcode_for_file_access(),
+                    errmsg("could not remove directory \"%s\": %m",
+                           location)));
+   }
+   else
+   {
+       if (unlink(location) < 0)
+           ereport(ERROR,
+                   (errcode_for_file_access(),
+                    errmsg("could not unlink symbolic link \"%s\": %m",
+                           location)));
+   }
 
-   heap_close(rel, ExclusiveLock);
+   pfree(location);
 
-#else                          /* !HAVE_SYMLINK */
-   ereport(ERROR,
-           (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-            errmsg("tablespaces are not supported on this platform")));
-#endif   /* HAVE_SYMLINK */
+   return true;
 }
 
-
 /*
  * write out the PG_VERSION file in the specified directory
  */
@@ -843,3 +943,88 @@ AlterTableSpaceOwner(const char *name, AclId newOwnerSysId)
    heap_endscan(scandesc);
    heap_close(rel, NoLock);
 }
+
+/*
+ * TABLESPACE resource manager's routines
+ */
+void
+tblspc_redo(XLogRecPtr lsn, XLogRecord *record)
+{
+   uint8       info = record->xl_info & ~XLR_INFO_MASK;
+
+   if (info == XLOG_TBLSPC_CREATE)
+   {
+       xl_tblspc_create_rec *xlrec = (xl_tblspc_create_rec *) XLogRecGetData(record);
+       char       *location = xlrec->ts_path;
+       char       *linkloc;
+
+       /*
+        * Attempt to coerce target directory to safe permissions.  If this
+        * fails, it doesn't exist or has the wrong owner.
+        */
+       if (chmod(location, 0700) != 0)
+           ereport(ERROR,
+                   (errcode_for_file_access(),
+                    errmsg("could not set permissions on directory \"%s\": %m",
+                           location)));
+
+       /* Create or re-create the PG_VERSION file in the target directory */
+       set_short_version(location);
+
+       /* Create the symlink if not already present */
+       linkloc = (char *) palloc(strlen(DataDir) + 11 + 10 + 1);
+       sprintf(linkloc, "%s/pg_tblspc/%u", DataDir, xlrec->ts_id);
+
+       if (symlink(location, linkloc) < 0)
+       {
+           if (errno != EEXIST)
+               ereport(ERROR,
+                       (errcode_for_file_access(),
+                        errmsg("could not create symbolic link \"%s\": %m",
+                               linkloc)));
+       }
+
+       pfree(linkloc);
+   }
+   else if (info == XLOG_TBLSPC_DROP)
+   {
+       xl_tblspc_drop_rec *xlrec = (xl_tblspc_drop_rec *) XLogRecGetData(record);
+
+       if (!remove_tablespace_directories(xlrec->ts_id, true))
+           ereport(ERROR,
+                   (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+                    errmsg("tablespace %u is not empty",
+                           xlrec->ts_id)));
+   }
+   else
+       elog(PANIC, "tblspc_redo: unknown op code %u", info);
+}
+
+void
+tblspc_undo(XLogRecPtr lsn, XLogRecord *record)
+{
+   elog(PANIC, "tblspc_undo: unimplemented");
+}
+
+void
+tblspc_desc(char *buf, uint8 xl_info, char *rec)
+{
+   uint8       info = xl_info & ~XLR_INFO_MASK;
+
+   if (info == XLOG_TBLSPC_CREATE)
+   {
+       xl_tblspc_create_rec *xlrec = (xl_tblspc_create_rec *) rec;
+
+       sprintf(buf + strlen(buf), "create ts: %u \"%s\"",
+               xlrec->ts_id, xlrec->ts_path);
+   }
+   else if (info == XLOG_TBLSPC_DROP)
+   {
+       xl_tblspc_drop_rec *xlrec = (xl_tblspc_drop_rec *) rec;
+
+       sprintf(buf + strlen(buf), "drop ts: %u",
+               xlrec->ts_id);
+   }
+   else
+       strcat(buf, "UNKNOWN");
+}
index d43f6fdcaf151d43aa025d4d2e85cd3a2c274a94..b01f80588731ebfd618e03476df1e3bea1bdd645 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Resource managers definition
  *
- * $PostgreSQL: pgsql/src/include/access/rmgr.h,v 1.12 2004/08/23 23:22:45 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/access/rmgr.h,v 1.13 2004/08/29 21:08:47 tgl Exp $
  */
 #ifndef RMGR_H
 #define RMGR_H
@@ -17,6 +17,8 @@ typedef uint8 RmgrId;
 #define RM_XACT_ID             1
 #define RM_SMGR_ID             2
 #define RM_CLOG_ID             3
+#define RM_DBASE_ID                4
+#define RM_TBLSPC_ID           5
 #define RM_HEAP_ID             10
 #define RM_BTREE_ID                11
 #define RM_HASH_ID             12
index f036a38247d62ae98f8e6f483cdca0ff62995bee..d684511a297f54e0667dea00c9892e09948038bd 100644 (file)
@@ -1,21 +1,41 @@
 /*-------------------------------------------------------------------------
  *
  * dbcommands.h
- *
+ *     Database management commands (create/drop database).
  *
  *
  * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/commands/dbcommands.h,v 1.33 2004/08/29 04:13:05 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/commands/dbcommands.h,v 1.34 2004/08/29 21:08:47 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef DBCOMMANDS_H
 #define DBCOMMANDS_H
 
+#include "access/xlog.h"
 #include "nodes/parsenodes.h"
 
+/* XLOG stuff */
+#define XLOG_DBASE_CREATE      0x00
+#define XLOG_DBASE_DROP            0x10
+
+typedef struct xl_dbase_create_rec
+{
+   /* Records copying of a single subdirectory incl. contents */
+   Oid         db_id;
+   char        src_path[1];    /* VARIABLE LENGTH STRING */
+   /* dst_path follows src_path */
+} xl_dbase_create_rec;
+
+typedef struct xl_dbase_drop_rec
+{
+   /* Records dropping of a single subdirectory incl. contents */
+   Oid         db_id;
+   char        dir_path[1];    /* VARIABLE LENGTH STRING */
+} xl_dbase_drop_rec;
+
 extern void createdb(const CreatedbStmt *stmt);
 extern void dropdb(const char *dbname);
 extern void RenameDatabase(const char *oldname, const char *newname);
@@ -25,4 +45,8 @@ extern void AlterDatabaseOwner(const char *dbname, AclId newOwnerSysId);
 extern Oid get_database_oid(const char *dbname);
 extern char *get_database_name(Oid dbid);
 
+extern void dbase_redo(XLogRecPtr lsn, XLogRecord *rptr);
+extern void dbase_undo(XLogRecPtr lsn, XLogRecord *rptr);
+extern void dbase_desc(char *buf, uint8 xl_info, char *rec);
+
 #endif   /* DBCOMMANDS_H */
index 42093c515054e5a6bc79c09b6aff088db13f42c8..8aba919ab3a2179e7fdea8156b6d67721d46c8c4 100644 (file)
@@ -1,32 +1,49 @@
 /*-------------------------------------------------------------------------
  *
  * tablespace.h
- *   prototypes for tablespace.c.
+ *     Tablespace management commands (create/drop tablespace).
  *
  *
  * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/commands/tablespace.h,v 1.3 2004/07/11 19:52:52 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/commands/tablespace.h,v 1.4 2004/08/29 21:08:47 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef TABLESPACE_H
 #define TABLESPACE_H
 
+#include "access/xlog.h"
 #include "nodes/parsenodes.h"
 
-extern void CreateTableSpace(CreateTableSpaceStmt *stmt);
+/* XLOG stuff */
+#define XLOG_TBLSPC_CREATE     0x00
+#define XLOG_TBLSPC_DROP       0x10
+
+typedef struct xl_tblspc_create_rec
+{
+   Oid         ts_id;
+   char        ts_path[1];     /* VARIABLE LENGTH STRING */
+} xl_tblspc_create_rec;
+
+typedef struct xl_tblspc_drop_rec
+{
+   Oid         ts_id;
+} xl_tblspc_drop_rec;
 
+extern void CreateTableSpace(CreateTableSpaceStmt *stmt);
 extern void DropTableSpace(DropTableSpaceStmt *stmt);
+extern void RenameTableSpace(const char *oldname, const char *newname);
+extern void AlterTableSpaceOwner(const char *name, AclId newOwnerSysId);
 
 extern void TablespaceCreateDbspace(Oid spcNode, Oid dbNode, bool isRedo);
 
 extern Oid get_tablespace_oid(const char *tablespacename);
-
 extern char *get_tablespace_name(Oid spc_oid);
 
-extern void RenameTableSpace(const char *oldname, const char *newname);
-extern void AlterTableSpaceOwner(const char *name, AclId newOwnerSysId);
+extern void tblspc_redo(XLogRecPtr lsn, XLogRecord *rptr);
+extern void tblspc_undo(XLogRecPtr lsn, XLogRecord *rptr);
+extern void tblspc_desc(char *buf, uint8 xl_info, char *rec);
 
 #endif   /* TABLESPACE_H */
index cd9225748e8f978a1c67a40c6179dc6465a261f3..e3e4eda45691503cb1018a7c3ef35e64064f4b93 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/port.h,v 1.56 2004/08/29 05:06:55 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/port.h,v 1.57 2004/08/29 21:08:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -51,6 +51,7 @@ extern void get_pkglib_path(const char *my_exec_path, char *ret_path);
 extern void get_locale_path(const char *my_exec_path, char *ret_path);
 extern void set_pglocale_pgservice(const char *argv0, const char *app);
 extern bool get_home_path(char *ret_path);
+extern void get_parent_directory(char *path);
 
 /*
  * is_absolute_path
index dae4eeab099abb89cd82805c32a309e6f8be569d..8017417b7080bc745fbcfde2c4236f09f03feb36 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/port/path.c,v 1.33 2004/08/29 05:07:02 momjian Exp $
+ *   $PostgreSQL: pgsql/src/port/path.c,v 1.34 2004/08/29 21:08:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -324,6 +324,39 @@ get_locale_path(const char *my_exec_path, char *ret_path)
 }
 
 
+/*
+ * get_home_path
+ */
+bool
+get_home_path(char *ret_path)
+{
+   if (getenv(HOMEDIR) == NULL)
+   {
+       *ret_path = '\0';
+       return false;
+   }
+   else
+   {
+       StrNCpy(ret_path, getenv(HOMEDIR), MAXPGPATH);
+       canonicalize_path(ret_path);
+       return true;
+   }
+}
+
+
+/*
+ * get_parent_directory
+ *
+ * Modify the given string in-place to name the parent directory of the
+ * named file.
+ */
+void
+get_parent_directory(char *path)
+{
+   trim_directory(path);
+   trim_trailing_separator(path);
+}
+
 
 /*
  * set_pglocale_pgservice
@@ -373,27 +406,6 @@ set_pglocale_pgservice(const char *argv0, const char *app)
 }
 
 
-/*
- * get_include_path
- */
-bool
-get_home_path(char *ret_path)
-{
-   if (getenv(HOMEDIR) == NULL)
-   {
-       *ret_path = '\0';
-       return false;
-   }
-   else
-   {
-       StrNCpy(ret_path, getenv(HOMEDIR), MAXPGPATH);
-       canonicalize_path(ret_path);
-       return true;
-   }
-}
-
-
-
 /*
  * make_relative - adjust path to be relative to bin/
  */