diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index f3327b1fb2f..3473e98cde0 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -15,7 +15,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.154 2005/03/12 21:33:55 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.155 2005/03/23 00:03:28 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -438,23 +438,17 @@ createdb(const CreatedbStmt *stmt)
 		/* Record the filesystem change in XLOG */
 		{
 			xl_dbase_create_rec xlrec;
-			XLogRecData rdata[3];
+			XLogRecData rdata[1];
 
 			xlrec.db_id = dboid;
+			xlrec.tablespace_id = dsttablespace;
+			xlrec.src_db_id = src_dboid;
+			xlrec.src_tablespace_id = srctablespace;
+
 			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;
+			rdata[0].len = sizeof(xl_dbase_create_rec);
+			rdata[0].next = NULL;
 
 			(void) XLogInsert(RM_DBASE_ID, XLOG_DBASE_CREATE, rdata);
 		}
@@ -1076,18 +1070,15 @@ remove_dbtablespaces(Oid db_id)
 		/* Record the filesystem change in XLOG */
 		{
 			xl_dbase_drop_rec xlrec;
-			XLogRecData rdata[2];
+			XLogRecData rdata[1];
 
 			xlrec.db_id = db_id;
+			xlrec.tablespace_id = dsttablespace;
+
 			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;
+			rdata[0].len = sizeof(xl_dbase_drop_rec);
+			rdata[0].next = NULL;
 
 			(void) XLogInsert(RM_DBASE_ID, XLOG_DBASE_DROP, rdata);
 		}
@@ -1190,6 +1181,86 @@ dbase_redo(XLogRecPtr lsn, XLogRecord *record)
 	if (info == XLOG_DBASE_CREATE)
 	{
 		xl_dbase_create_rec *xlrec = (xl_dbase_create_rec *) XLogRecGetData(record);
+		char	   *src_path;
+		char	   *dst_path;
+		struct stat st;
+
+#ifndef WIN32
+		char		buf[2 * MAXPGPATH + 100];
+#endif
+
+		src_path = GetDatabasePath(xlrec->src_db_id, xlrec->src_tablespace_id);
+		dst_path = GetDatabasePath(xlrec->db_id, xlrec->tablespace_id);
+
+		/*
+		 * 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)));
+		}
+
+		/*
+		 * Force dirty buffers out to disk, to ensure source database is
+		 * up-to-date for the copy.  (We really only need to flush buffers for
+		 * the source database, but bufmgr.c provides no API for that.)
+		 */
+		BufferSync();
+
+#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'",
+				 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(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);
+		char	   *dst_path;
+
+		dst_path = GetDatabasePath(xlrec->db_id, xlrec->tablespace_id);
+
+		/*
+		 * Drop pages for this database that are in the shared buffer
+		 * cache
+		 */
+		DropBuffers(xlrec->db_id);
+
+		if (!rmtree(dst_path, true))
+			ereport(WARNING,
+					(errmsg("could not remove database directory \"%s\"",
+							dst_path)));
+	}
+	else if (info == XLOG_DBASE_CREATE_OLD)
+	{
+		xl_dbase_create_rec_old *xlrec = (xl_dbase_create_rec_old *) XLogRecGetData(record);
 		char	   *dst_path = xlrec->src_path + strlen(xlrec->src_path) + 1;
 		struct stat st;
 
@@ -1245,9 +1316,9 @@ dbase_redo(XLogRecPtr lsn, XLogRecord *record)
 		}
 #endif   /* WIN32 */
 	}
-	else if (info == XLOG_DBASE_DROP)
+	else if (info == XLOG_DBASE_DROP_OLD)
 	{
-		xl_dbase_drop_rec *xlrec = (xl_dbase_drop_rec *) XLogRecGetData(record);
+		xl_dbase_drop_rec_old *xlrec = (xl_dbase_drop_rec_old *) XLogRecGetData(record);
 
 		/*
 		 * Drop pages for this database that are in the shared buffer
@@ -1278,14 +1349,29 @@ dbase_desc(char *buf, uint8 xl_info, char *rec)
 	if (info == XLOG_DBASE_CREATE)
 	{
 		xl_dbase_create_rec *xlrec = (xl_dbase_create_rec *) rec;
+
+		sprintf(buf + strlen(buf), "create db: copy dir %u/%u to %u/%u",
+				xlrec->src_db_id, xlrec->src_tablespace_id,
+				xlrec->db_id, xlrec->tablespace_id);
+	}
+	else if (info == XLOG_DBASE_DROP)
+	{
+		xl_dbase_drop_rec *xlrec = (xl_dbase_drop_rec *) rec;
+
+		sprintf(buf + strlen(buf), "drop db: dir %u/%u",
+				xlrec->db_id, xlrec->tablespace_id);
+	}
+	else if (info == XLOG_DBASE_CREATE_OLD)
+	{
+		xl_dbase_create_rec_old *xlrec = (xl_dbase_create_rec_old *) 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)
+	else if (info == XLOG_DBASE_DROP_OLD)
 	{
-		xl_dbase_drop_rec *xlrec = (xl_dbase_drop_rec *) rec;
+		xl_dbase_drop_rec_old *xlrec = (xl_dbase_drop_rec_old *) rec;
 
 		sprintf(buf + strlen(buf), "drop db: %u directory: \"%s\"",
 				xlrec->db_id, xlrec->dir_path);
diff --git a/src/include/commands/dbcommands.h b/src/include/commands/dbcommands.h
index 1709d996289..8bdbd860c4e 100644
--- a/src/include/commands/dbcommands.h
+++ b/src/include/commands/dbcommands.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/commands/dbcommands.h,v 1.36 2004/12/31 22:03:28 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/commands/dbcommands.h,v 1.37 2005/03/23 00:03:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -18,22 +18,47 @@
 #include "nodes/parsenodes.h"
 
 /* XLOG stuff */
-#define XLOG_DBASE_CREATE		0x00
-#define XLOG_DBASE_DROP			0x10
+#define XLOG_DBASE_CREATE_OLD	0x00
+#define XLOG_DBASE_DROP_OLD		0x10
+#define XLOG_DBASE_CREATE		0x20
+#define XLOG_DBASE_DROP			0x30
 
-typedef struct xl_dbase_create_rec
+/*
+ * Note: "old" versions are deprecated and need not be supported beyond 8.0.
+ * Not only are they relatively bulky, but they do the Wrong Thing when a
+ * WAL log is replayed in a data area that's at a different absolute path
+ * than the original.
+ */
+
+typedef struct xl_dbase_create_rec_old
 {
 	/* 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_old;
+
+typedef struct xl_dbase_drop_rec_old
+{
+	/* Records dropping of a single subdirectory incl. contents */
+	Oid			db_id;
+	char		dir_path[1];	/* VARIABLE LENGTH STRING */
+}	xl_dbase_drop_rec_old;
+
+typedef struct xl_dbase_create_rec
+{
+	/* Records copying of a single subdirectory incl. contents */
+	Oid			db_id;
+	Oid			tablespace_id;
+	Oid			src_db_id;
+	Oid			src_tablespace_id;
 }	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 */
+	Oid			tablespace_id;
 }	xl_dbase_drop_rec;
 
 extern void createdb(const CreatedbStmt *stmt);