? Makefile.global
? backend/postgres
? backend/access/transam/xlog.patch
? backend/catalog/postgres.bki
? backend/catalog/postgres.description
? backend/postmaster/pgarch.c
? backend/utils/mb/conversion_procs/conversion_create.sql
? backend/utils/mb/conversion_procs/ascii_and_mic/libascii_and_mic.so.0.0
? backend/utils/mb/conversion_procs/cyrillic_and_mic/libcyrillic_and_mic.so.0.0
? backend/utils/mb/conversion_procs/euc_cn_and_mic/libeuc_cn_and_mic.so.0.0
? backend/utils/mb/conversion_procs/euc_jp_and_sjis/libeuc_jp_and_sjis.so.0.0
? backend/utils/mb/conversion_procs/euc_kr_and_mic/libeuc_kr_and_mic.so.0.0
? backend/utils/mb/conversion_procs/euc_tw_and_big5/libeuc_tw_and_big5.so.0.0
? backend/utils/mb/conversion_procs/latin2_and_win1250/liblatin2_and_win1250.so.0.0
? backend/utils/mb/conversion_procs/latin_and_mic/liblatin_and_mic.so.0.0
? backend/utils/mb/conversion_procs/utf8_and_ascii/libutf8_and_ascii.so.0.0
? backend/utils/mb/conversion_procs/utf8_and_big5/libutf8_and_big5.so.0.0
? backend/utils/mb/conversion_procs/utf8_and_cyrillic/libutf8_and_cyrillic.so.0.0
? backend/utils/mb/conversion_procs/utf8_and_euc_cn/libutf8_and_euc_cn.so.0.0
? backend/utils/mb/conversion_procs/utf8_and_euc_jp/libutf8_and_euc_jp.so.0.0
? backend/utils/mb/conversion_procs/utf8_and_euc_kr/libutf8_and_euc_kr.so.0.0
? backend/utils/mb/conversion_procs/utf8_and_euc_tw/libutf8_and_euc_tw.so.0.0
? backend/utils/mb/conversion_procs/utf8_and_gb18030/libutf8_and_gb18030.so.0.0
? backend/utils/mb/conversion_procs/utf8_and_gbk/libutf8_and_gbk.so.0.0
? backend/utils/mb/conversion_procs/utf8_and_iso8859/libutf8_and_iso8859.so.0.0
? backend/utils/mb/conversion_procs/utf8_and_iso8859_1/libutf8_and_iso8859_1.so.0.0
? backend/utils/mb/conversion_procs/utf8_and_johab/libutf8_and_johab.so.0.0
? backend/utils/mb/conversion_procs/utf8_and_sjis/libutf8_and_sjis.so.0.0
? backend/utils/mb/conversion_procs/utf8_and_tcvn/libutf8_and_tcvn.so.0.0
? backend/utils/mb/conversion_procs/utf8_and_uhc/libutf8_and_uhc.so.0.0
? backend/utils/mb/conversion_procs/utf8_and_win1250/libutf8_and_win1250.so.0.0
? backend/utils/mb/conversion_procs/utf8_and_win1256/libutf8_and_win1256.so.0.0
? backend/utils/mb/conversion_procs/utf8_and_win874/libutf8_and_win874.so.0.0
? bin/initdb/initdb
? bin/ipcclean/ipcclean
? bin/pg_config/pg_config
? bin/pg_controldata/pg_controldata
? bin/pg_ctl/pg_ctl
? bin/pg_dump/pg_dump
? bin/pg_dump/pg_dumpall
? bin/pg_dump/pg_restore
? bin/pg_resetxlog/pg_resetxlog
? bin/psql/psql
? bin/scripts/clusterdb
? bin/scripts/createdb
? bin/scripts/createlang
? bin/scripts/createuser
? bin/scripts/dropdb
? bin/scripts/droplang
? bin/scripts/dropuser
? bin/scripts/vacuumdb
? include/pg_config.h
? include/pgarch.h
? include/stamp-h
? interfaces/ecpg/compatlib/libecpg_compat.so.1.1
? interfaces/ecpg/ecpglib/libecpg.so.4.2
? interfaces/ecpg/pgtypeslib/libpgtypes.so.1.2
? interfaces/ecpg/preproc/ecpg
? interfaces/libpq/libpq.so.3.2
? pl/plpgsql/src/libplpgsql.so.1.0
? port/pg_config_paths.h
? timezone/zic
Index: backend/access/nbtree/nbtsort.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/access/nbtree/nbtsort.c,v
retrieving revision 1.82
diff -c -r1.82 nbtsort.c
*** backend/access/nbtree/nbtsort.c	2 Jun 2004 17:28:17 -0000	1.82
--- backend/access/nbtree/nbtsort.c	8 Jul 2004 17:52:59 -0000
***************
*** 67,72 ****
--- 67,73 ----
  #include "miscadmin.h"
  #include "storage/smgr.h"
  #include "utils/tuplesort.h"
+ #include "access/xlog.h"
  
  
  /*
***************
*** 220,235 ****
  
  	wstate.index = btspool->index;
  	/*
! 	 * We need to log index creation in WAL iff WAL archiving is enabled
  	 * AND it's not a temp index.
- 	 *
- 	 * XXX when WAL archiving is actually supported, this test will likely
- 	 * need to change; and the hardwired extern is cruddy anyway ...
  	 */
  	{
! 		extern char XLOG_archive_dir[];
! 
! 		wstate.btws_use_wal = XLOG_archive_dir[0] && !wstate.index->rd_istemp;
  	}
  	/* reserve the metapage */
  	wstate.btws_pages_alloced = BTREE_METAPAGE + 1;
--- 221,231 ----
  
  	wstate.index = btspool->index;
  	/*
! 	 * We need to log index creation in WAL if WAL archiving is enabled
  	 * AND it's not a temp index.
  	 */
  	{
!  		wstate.btws_use_wal = XLogArchiveMode && !wstate.index->rd_istemp;
  	}
  	/* reserve the metapage */
  	wstate.btws_pages_alloced = BTREE_METAPAGE + 1;
Index: backend/access/transam/xlog.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/access/transam/xlog.c,v
retrieving revision 1.147
diff -c -r1.147 xlog.c
*** backend/access/transam/xlog.c	1 Jul 2004 00:49:50 -0000	1.147
--- backend/access/transam/xlog.c	8 Jul 2004 17:53:05 -0000
***************
*** 7,13 ****
   * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
   * Portions Copyright (c) 1994, Regents of the University of California
   *
!  * $PostgreSQL: pgsql-server/src/backend/access/transam/xlog.c,v 1.147 2004/07/01 00:49:50 tgl Exp $
   *
   *-------------------------------------------------------------------------
   */
--- 7,13 ----
   * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
   * Portions Copyright (c) 1994, Regents of the University of California
   *
!  * $PostgreSQL: pgsql-server/src/backend/access/transam/xlog.c,v 1.146 2004/06/03 02:08:00 tgl Exp $
   *
   *-------------------------------------------------------------------------
   */
***************
*** 36,47 ****
  #include "storage/proc.h"
  #include "storage/sinval.h"
  #include "storage/spin.h"
  #include "utils/builtins.h"
  #include "utils/guc.h"
  #include "utils/relcache.h"
  #include "miscadmin.h"
  
- 
  /*
   * This chunk of hackery attempts to determine which file sync methods
   * are available on the current platform, and to choose an appropriate
--- 36,47 ----
  #include "storage/proc.h"
  #include "storage/sinval.h"
  #include "storage/spin.h"
+ #include "storage/pmsignal.h"
  #include "utils/builtins.h"
  #include "utils/guc.h"
  #include "utils/relcache.h"
  #include "miscadmin.h"
  
  /*
   * This chunk of hackery attempts to determine which file sync methods
   * are available on the current platform, and to choose an appropriate
***************
*** 85,96 ****
  
  
  /* User-settable parameters */
  int			CheckPointSegments = 3;
  int			XLOGbuffers = 8;
  char	   *XLOG_sync_method = NULL;
  const char	XLOG_sync_method_default[] = DEFAULT_SYNC_METHOD_STR;
- char		XLOG_archive_dir[MAXPGPATH];		/* null string means
- 												 * delete 'em */
  
  #ifdef WAL_DEBUG
  bool		XLOG_DEBUG = false;
--- 85,98 ----
  
  
  /* User-settable parameters */
+ bool 			XLogArchiveMode = false;
+ bool 			XLogArchiveDEBUG = false;
+ char 			*XLogArchiveDest;
+ char 			*XLogArchiveProgram;
  int			CheckPointSegments = 3;
  int			XLOGbuffers = 8;
  char	   *XLOG_sync_method = NULL;
  const char	XLOG_sync_method_default[] = DEFAULT_SYNC_METHOD_STR;
  
  #ifdef WAL_DEBUG
  bool		XLOG_DEBUG = false;
***************
*** 127,132 ****
--- 129,154 ----
  
  /* Are we doing recovery by reading XLOG? */
  bool		InRecovery = false;
+ bool        InArchiveRecovery = false;
+ bool        restoreFromArchive = false;
+ bool        InRecoveryCleanup = false;
+ 
+ FILE	   	*archiveStatusLogFD;
+ 
+ static void readRecoveryCommandFile(void);
+ 
+ static 	char recoveryCommandFile[MAXPGPATH];
+ 
+ static  char recoveryRestoreProgram[MAXPGPATH];
+ static  char recoveryTargetTimeStr[MAXPGPATH];
+ static  char recoveryTargetXidStr[MAXPGPATH];
+ 
+ static  bool recoveryTarget = true;
+ static  bool recoveryTargetExact = false;
+ static  bool recoveryTargetInclusive = true;
+ 
+ static  TransactionId   recoveryTargetXid;
+ static  time_t          recoveryTargetTime;
  
  /*
   * MyLastRecPtr points to the start of the last XLOG record inserted by the
***************
*** 393,398 ****
--- 415,421 ----
  
  /* File path names */
  static char XLogDir[MAXPGPATH];
+ static char archiveStatusDir[MAXPGPATH];
  static char ControlFilePath[MAXPGPATH];
  
  /*
***************
*** 434,439 ****
--- 457,465 ----
  
  static bool InRedo = false;
  
+ static void XLogArchiveNotify(uint32 log, uint32 seg);
+ static bool XLogArchiveDone(char xlog[MAXPGPATH]);
+ static void XLogArchiveCleanup(char xlog[32]);
  
  static bool AdvanceXLInsertBuffer(void);
  static bool WasteXLInsertBuffer(void);
***************
*** 444,449 ****
--- 470,476 ----
  					   bool find_free, int max_advance,
  					   bool use_lock);
  static int	XLogFileOpen(uint32 log, uint32 seg, bool econt);
+ static void RestoreRecoveryXlog(char *path, uint32 log, uint32 seg);
  static void PreallocXlogFiles(XLogRecPtr endptr);
  static void MoveOfflineLogs(uint32 log, uint32 seg, XLogRecPtr endptr);
  static XLogRecord *ReadRecord(XLogRecPtr *RecPtr, int emode, char *buffer);
***************
*** 455,464 ****
  static void ReadControlFile(void);
  static char *str_time(time_t tnow);
  static void issue_xlog_fsync(void);
- #ifdef WAL_DEBUG
  static void xlog_outrec(char *buf, XLogRecord *record);
- #endif
- 
  
  /*
   * Insert an XLOG record having the specified RMID and info bytes,
--- 482,488 ----
***************
*** 912,917 ****
--- 936,1073 ----
  }
  
  /*
+  * XLogArchiveNotify
+  *
+  * Writes an archive notification file to the archiveStatusDir
+  *
+  * The name of the notification file is the message that will be picked up
+  * by the archiver, e.g. we write archiveStatusDir/00000001000000C6.ready 
+  * and the archiver then knows to archive XLogDir/00000001000000C6,
+  * then when complete, rename it to archiveStatusDir/00000001000000C6.done
+  *
+  * Called only when in XLogArchiveMode by one backend process
+  * or by Archive Restore in various situations
+  */
+ static void 
+ XLogArchiveNotify(uint32 log, uint32 seg)
+ {
+ 	char		archiveStatusLog[32];
+ 	char		archiveStatusLogPath[MAXPGPATH];
+ 
+ /* insert an otherwise empty file called <XLOG>.ready */
+ 	sprintf(archiveStatusLog, "%08X%08X.ready", log, seg);
+ 	snprintf(archiveStatusLogPath, MAXPGPATH, "%s/%s", archiveStatusDir, archiveStatusLog);
+    	archiveStatusLogFD = NULL;
+ 	archiveStatusLogFD = AllocateFile(archiveStatusLogPath, "w");
+ 	if (archiveStatusLogFD == NULL) {
+ 		ereport(ERROR,
+ 				(errcode_for_file_access(),
+ 	   		     errmsg("could not write archive_status file \"%s\": %m",
+ 				    archiveStatusLogPath)));
+         return;
+     }
+ 	FreeFile(archiveStatusLogFD);
+ 
+ /* the existence of this file is the message to the archiver to identify
+  * which files require archiving
+  *
+  * if this file is written OK, we then signal the ARCHIVER to do its thang
+  */
+ 
+ 	if (XLogArchiveDEBUG) {
+     	sprintf(archiveStatusLog, "%08X%08X", log, seg);
+ 		ereport(LOG,
+     		(errmsg("transaction log file \"%s\" is now ready to be archived", archiveStatusLog)));
+     }
+ 
+     /*
+      * don't send a signal if we know that the archiver isn't there (yet)
+      * - the archiver will see the archive_status file as soon as it starts 
+      */
+     if (!InArchiveRecovery)
+         SendPostmasterSignal(PMSIGNAL_WAKEN_ARCHIVER);
+ 
+ 	return;
+ }
+ 
+ /*
+  * XLogArchiveDone
+  *
+  * Searches for an archive notification file in archiveStatusDir
+  * 
+  * Reads archiveStatusDir looking for a specific filename. If that filename ends with .done
+  * then we know that the filename refers to an xlog in XLogDir that is safe to
+  * recycle. If the filename ends .ready then thats OK, else we have an error.
+  * 
+  * Called only when in XLogArchiveMode by bgwriter (when performing checkpoint)
+  *
+  * XXX code is rehacked from an earlier version, so needs streamlining
+  */
+ static bool 
+ XLogArchiveDone(char xlog[32])
+ {
+ 	char		archiveStatusLogPath[MAXPGPATH];
+ 	struct stat tmpStatBuf;
+ 
+ /* If <XLOG>.done exists then return true
+  */
+ 	snprintf(archiveStatusLogPath, MAXPGPATH, "%s/%s.done", archiveStatusDir, xlog);
+ 	if (stat(archiveStatusLogPath, &tmpStatBuf) == 0) {
+ 		return true;
+ 	} 
+ 	else
+ 		{
+ /*
+  * else if <XLOG>.ready exists then return false and issue WARNING
+  * ...this indicates archiver is either not working at all or
+  * if it is, then its just way too slow or incorrectly configured
+  */
+ 			snprintf(archiveStatusLogPath, MAXPGPATH, "%s/%s.ready", archiveStatusDir, xlog);
+         	if (stat(archiveStatusLogPath, &tmpStatBuf) == 0) {
+ 			    ereport(WARNING,
+     				(errcode(ERRCODE_WARNING),
+ 	       	 	    errmsg("archiving of log file \"%s\" has not yet started", 
+ 						archiveStatusLogPath),
+                     errhint("you should check whether your archive_program"
+                             "\"%s\" is still functioning correctly", XLogArchiveProgram)));
+ 			    return false;
+ 			}
+ 			else
+ 			{
+ /* else issue a WARNING.... a notification file should exist, except when you
+  * have just enabled archive_mode
+  * assume that error is simply a missing file...
+  */ 
+ 			    ereport(WARNING,
+     				(errcode_for_file_access(),
+     			 	errmsg("archive_status file \"%s\" is missing: %m",
+     						archiveStatusLogPath),
+                     errhint("if you have just enabled archive_mode you should:"
+                             "take a full physical backup, then remove \"%s\"",xlog)));
+ 			    return false;
+ 			}
+ 		}
+ }
+ 
+ /*
+  * XLogArchiveCleanup
+  *
+  * Cleanup an archive notification file for a particular xlog in XLogDir
+  * 
+  * Called only when in XLogArchiveMode by bgwriter (when performing checkpoint)
+  *
+  */
+ static void
+ XLogArchiveCleanup(char xlog[32])
+ {
+ 	char	archiveStatusLogPath[MAXPGPATH];
+ 
+ 	snprintf(archiveStatusLogPath, MAXPGPATH, "%s/%s.done", archiveStatusDir, xlog);
+ 	unlink(archiveStatusLogPath);
+ 
+ }
+ 
+ /*
   * Advance the Insert state to the next buffer page, writing out the next
   * buffer if it still contains unwritten data.
   *
***************
*** 1260,1265 ****
--- 1416,1427 ----
  		{
  			issue_xlog_fsync();
  			LogwrtResult.Flush = LogwrtResult.Write;	/* end of current page */
+ 
+             /* 
+              * write archive_status file to notify that xlog is ready to archive
+              */
+             if (XLogArchiveMode)
+                 XLogArchiveNotify(openLogId, openLogSeg);
  		}
  
  		if (ispartialpage)
***************
*** 1619,1625 ****
  					   bool use_lock)
  {
  	char		path[MAXPGPATH];
! 	struct stat stat_buf;
  
  	XLogFileName(path, log, seg);
  
--- 1781,1787 ----
  					   bool use_lock)
  {
  	char		path[MAXPGPATH];
! 	struct stat tmpStatBuf;
  
  	XLogFileName(path, log, seg);
  
***************
*** 1637,1643 ****
  	else
  	{
  		/* Find a free slot to put it in */
! 		while (stat(path, &stat_buf) == 0)
  		{
  			if (--max_advance < 0)
  			{
--- 1799,1805 ----
  	else
  	{
  		/* Find a free slot to put it in */
! 		while (stat(path, &tmpStatBuf) == 0)
  		{
  			if (--max_advance < 0)
  			{
***************
*** 1686,1692 ****
  	char		path[MAXPGPATH];
  	int			fd;
  
! 	XLogFileName(path, log, seg);
  
  	fd = BasicOpenFile(path, O_RDWR | PG_BINARY | XLOG_SYNC_BIT,
  					   S_IRUSR | S_IWUSR);
--- 1848,1856 ----
  	char		path[MAXPGPATH];
  	int			fd;
  
!     XLogFileName(path, log, seg);
!  	if (restoreFromArchive)
!         RestoreRecoveryXlog(path, log, seg);
  
  	fd = BasicOpenFile(path, O_RDWR | PG_BINARY | XLOG_SYNC_BIT,
  					   S_IRUSR | S_IWUSR);
***************
*** 1706,1715 ****
--- 1870,2101 ----
  				   path, log, seg)));
  	}
  
+     /*
+      * if we switched back to local xlogs after having been
+      * restoring from archive, we need to make sure that the
+      * local files dont get removed by end-of-recovery checkpoint
+      * in case we need to re-run the recovery
+      *
+      * we want to copy these away as soon as possible, so set
+      * the archive_status flag to .ready for them
+      * in case admin isn't cautious enough to have done this anyway
+      */
+  	if (InArchiveRecovery && !restoreFromArchive) {
+        	snprintf(path, MAXPGPATH, "%s/%08X%08X.ready", archiveStatusDir, log, seg);
+        	archiveStatusLogFD = NULL;
+         archiveStatusLogFD = AllocateFile(path, "w");
+ 	    if (archiveStatusLogFD == NULL)
+             ereport(ERROR,
+ 	   	       (errcode_for_file_access(),
+ 	           errmsg("could not write archive_status file \"%s\" ",
+ 			        path)));
+ 	    FreeFile(archiveStatusLogFD);
+     }        
+  
  	return (fd);
  }
  
  /*
+  * Get next logfile segment to allow recovery
+  *
+  */
+ static void
+ RestoreRecoveryXlog(char *path, uint32 log, uint32 seg)
+ {
+     char tmpXlog[32];
+     char restoreXlog[32];
+     char tmppath[MAXPGPATH];
+     char xlogRestoreCmd[MAXPGPATH];
+     char recoveryXlog[MAXPGPATH];
+     char lastrecoXlog[MAXPGPATH];
+     int         rc;
+ 	struct stat tmpStatBuf;
+     uint32 prevlog, prevseg;
+ 
+     /* 
+      * If a RecoveryFile exists, then we know we are in media recovery
+      * in which case we choose to recover files from archive, even
+      * though a file of that name may already exist in XLogDir
+      *
+      * By doing this, we do not effect crash recovery code path
+      * when we are not in archive_mode
+      *
+      * We take the archived file because, at the point we took backup,
+      * the current xlog will most probably be only partially full, 
+      * so we MUST refer to the full version of this file and 
+      * NOT the version of the file that exists with the backup.
+      *
+      * We could try to optimize this slightly by checking the local
+      * copy lastchange timestamp against the archived copy, 
+      * but we have no API to do this, nor can we guarantee that the
+      * lastchange timestamp was preserved correctly when we copied
+      * to archive. Our aim is robustness, so we elect not to do this.
+      *
+      * Try to copy full xlog from archive to pg_xlog, if it is available
+      * If that succeeds, we pass the RecoveryXlog filepath back for opening
+      * If that fails, then we try to read a local file if one exists.
+      * This allows us to cater for situations where the current xlog
+      * is still available locally and hasn't yet made it to archive.
+      * This could happen if:
+      * - we decide to recover database to undo user data changes
+      * - we have XLogDir on a different disk and the main DataDir drive
+      *   fails, leaving us with just the XLogDir
+      *
+      * Notice that we don't actually overwrite any files when we copy back
+      * from archive because the recoveryRestoreProgram may inadvertently
+      * restore inappropriate xlogs, or they may be corrupt, so we may
+      * have to fallback to the segments remaining in current XLogDir later.
+      * The copy-from-archive xlog is always the same, ensuring that we
+      * don't run out of disk space on long recoveries.
+      *
+      * simon@2ndquadrant.com
+      */
+     
+         snprintf(recoveryXlog, MAXPGPATH, "%s/RECOVERYXLOG", XLogDir);
+     	snprintf(lastrecoXlog, MAXPGPATH, "%s/LASTRECOXLOG", XLogDir);
+ 
+         if (stat(recoveryXlog, &tmpStatBuf) == 0) {            
+             /*
+              * save a copy of the last xlog, before we try to restore
+              * if the restore fails, we will need it to become current xlog
+              */
+             rc = rename(recoveryXlog, lastrecoXlog);
+             if (rc !=0)
+         		ereport(WARNING,
+             		(errcode_for_file_access(),
+                     errmsg("rename failed \"%s\" \"%s\":%m",
+                         recoveryXlog, lastrecoXlog)));
+             /*
+              * if it fails, ignore it - we'll create one soon...
+              */
+         }
+ 
+         /*
+          * Copy xlog from archive_dest to XLogDir
+          */
+         sprintf(restoreXlog, "%08X%08X", log, seg);
+       	snprintf(xlogRestoreCmd, MAXPGPATH, recoveryRestoreProgram, 
+                    XLogArchiveDest, restoreXlog, recoveryXlog);
+         if (XLogArchiveDEBUG)
+ 			ereport(LOG,
+                 (errmsg("restore_program=\"%s\"",
+                     xlogRestoreCmd)));
+ 
+         rc = system(xlogRestoreCmd);
+         if (rc!=0) {
+             /*
+              * remember, we rollforward UNTIL the restore fails
+              * so failure here is just part of the process...
+              * that makes it difficult to determine whether the restore
+              * failed because there isn't an archive to restore, or
+              * because the administrator has specified the restore
+              * program incorrectly...
+              * we could try to restore the testfile that the archiver writes
+              * when it starts up, but the absence of that file isn't
+              * very reliable evidence that the restore itself is broken,
+              * so just trust that the administrator has it correctly,
+              * XXX enhance that later
+              */
+ 			ereport(LOG,
+                 (errmsg("could not restore \"%s\" from archive",
+                     restoreXlog)));
+             /*
+              * if an archived file is not available, there might just be 
+              * a partially full version of this file still in XLogDir
+              * so return this as the filename to open.
+              * In many recovery scenarios we expect this to fail also...
+              * ...note that once we return to local mode, we could
+              * find more than one xlog there that were not in the archive,
+              * if for example, the archiver had stopped working prior
+              * to the event which led to recovery
+              */
+             snprintf(recoveryXlog, MAXPGPATH, "%s/%s", XLogDir, restoreXlog);
+             restoreFromArchive = false;
+             if (stat(recoveryXlog, &tmpStatBuf) == 0) {
+     			ereport(LOG,
+                     (errmsg("recovery will continue using local copy of \"%s\"",
+                         restoreXlog)));
+             }
+             /*
+              * if this file isn't available, then we need to setup the previous
+              * restored xlog to be the last and current xlog, if it exists
+              * remember: we've been restoring from recoverXlog, which isn't
+              * named the same as the normal xlog chain...
+              * also remember to output a corresponding archive_status of .done
+              */
+             else if ((stat(lastrecoXlog, &tmpStatBuf) == 0) && log==0 && seg > 0) {
+                 prevlog = log;
+                 prevseg = seg;
+         	    PrevLogSeg(prevlog, prevseg);
+                 XLogFileName(tmppath, prevlog, prevseg);
+                 if (XLogArchiveDEBUG)
+         			ereport(LOG,
+                         (errmsg("moving last restored xlog to \"%s\"", tmppath)));
+                 rc = rename(lastrecoXlog, tmppath);
+                 if (rc!=0) {
+             		ereport(LOG,
+                 		(errcode_for_file_access(),
+                         errmsg("rename failed \"%s\" \"%s\":%m",
+                             lastrecoXlog, tmppath)));
+             	    ereport(PANIC,
+         		        (errcode_for_file_access(),
+             	        errmsg("could not open file \"%s\" (log file %u, segment %u): %m",
+                             tmpXlog, log, seg)));
+                 }
+ 
+                 /* 
+                  * write out an archive_status file for previous xlog
+                  * to allow xlog to be recycled when recovered database
+                  * is all up and working again
+                  * ...looks wrong, but checkpointer is smart enough
+                  * not to archive the current xlog!
+                  */
+             	sprintf(tmpXlog, "%08X%08X", prevlog, prevseg);
+             	snprintf(tmppath, MAXPGPATH, "%s/%s.done", archiveStatusDir, tmpXlog);
+                	archiveStatusLogFD = NULL;
+             	archiveStatusLogFD = AllocateFile(tmppath, "w");
+ 	            if (archiveStatusLogFD == NULL)
+                     ereport(ERROR,
+ 	       			    (errcode_for_file_access(),
+ 	       		         errmsg("could not write archive_status file \"%s\" ",
+ 	       			        tmppath)));
+ 	            FreeFile(archiveStatusLogFD);
+             }
+             /* 
+              * there is NO else here...we just return the filename
+              * knowing that it isn't there...which then throws the usual error,
+              * will end with a clear message as to why...but not a problem
+              */
+         }
+         else {
+         /* restore success */
+             /* 
+              * if backup restored an xlog, yet we didnt use the local copy
+              * because we used the xlog version of that name from the
+              * archive instead, we need to write out an archive_status for
+              * it to show it can be recycled later
+              */
+             XLogFileName(tmppath, log, seg);
+             if (stat(tmppath, &tmpStatBuf) == 0) {
+                	sprintf(tmpXlog, "%08X%08X", log, seg);
+                	snprintf(tmppath, MAXPGPATH, "%s/%s.done", archiveStatusDir, tmpXlog);
+                	archiveStatusLogFD = NULL;
+                	archiveStatusLogFD = AllocateFile(tmppath, "w");
+         	    if (archiveStatusLogFD == NULL)
+             		ereport(ERROR,
+         				(errcode_for_file_access(),
+         	   		     errmsg("could not write archive_status file \"%s\": %m",
+     	  			        tmppath)));
+     	        FreeFile(archiveStatusLogFD);
+             }
+    			ereport(LOG,
+                 (errmsg("restored log file \"%s\" from archive", restoreXlog)));
+         }
+         strcpy(path, recoveryXlog);                
+         return;
+ }
+ 
+ /*
   * Preallocate log files beyond the specified log endpoint, according to
   * the XLOGfile user parameter.
   */
***************
*** 1747,1752 ****
--- 2133,2139 ----
  	struct dirent *xlde;
  	char		lastoff[32];
  	char		path[MAXPGPATH];
+     bool        recycle=false;
  
  	XLByteToPrevSeg(endptr, endlogId, endlogSeg);
  
***************
*** 1762,1786 ****
  	errno = 0;
  	while ((xlde = readdir(xldir)) != NULL)
  	{
  		if (strlen(xlde->d_name) == 16 &&
  			strspn(xlde->d_name, "0123456789ABCDEF") == 16 &&
  			strcmp(xlde->d_name, lastoff) <= 0)
  		{
  			snprintf(path, MAXPGPATH, "%s/%s", XLogDir, xlde->d_name);
! 			if (XLOG_archive_dir[0])
! 			{
! 				ereport(LOG,
! 						(errmsg("archiving transaction log file \"%s\"",
! 								xlde->d_name)));
! 				elog(WARNING, "archiving log files is not implemented");
! 			}
! 			else
  			{
  				/*
  				 * Before deleting the file, see if it can be recycled as
  				 * a future log segment.  We allow recycling segments up
! 				 * to XLOGfileslop segments beyond the current XLOG
! 				 * location.
  				 */
  				if (InstallXLogFileSegment(endlogId, endlogSeg, path,
  										   true, XLOGfileslop,
--- 2149,2191 ----
  	errno = 0;
  	while ((xlde = readdir(xldir)) != NULL)
  	{
+ 		/* if correct length and alphanumeric makeup of file looks correct
+ 		 * use the alphanumeric sorting property of the filenames to decide
+ 		 * which ones are earlier than the lastoff transaction log
+ 		 * ...maybe should read lastwrite datetime of lastoff, then check that
+ 		 * only files last written earlier than this are removed/recycled
+ 		 */
  		if (strlen(xlde->d_name) == 16 &&
  			strspn(xlde->d_name, "0123456789ABCDEF") == 16 &&
  			strcmp(xlde->d_name, lastoff) <= 0)
  		{
  			snprintf(path, MAXPGPATH, "%s/%s", XLogDir, xlde->d_name);
! 			if (XLogArchiveMode) {
!                 if (InRecoveryCleanup)
!                     /*
!                      * this allows recycling of transaction logs
!                      * during the shutdown checkpoint at end of recovery
!                      * - we may have restored logs that were not used
!                      * in the recovery sequence, and so will not have
!                      * had an archive_status file written for them. 
!                      * - end-of-recovery doesn't clean up ALL xlogs,
!                      * which is why we also write archive_status files
!                      * as well as doing this
!                      */
!                     recycle=true;
!                 else
!                     recycle=XLogArchiveDone(xlde->d_name);
!             }
!             else
!                 recycle=false;
! 
! 			if ( recycle )
  			{
  				/*
  				 * Before deleting the file, see if it can be recycled as
  				 * a future log segment.  We allow recycling segments up
! 				 * until there are XLOGfileslop segments beyond the
! 				 * current XLOG location, otherwise they are removed.
  				 */
  				if (InstallXLogFileSegment(endlogId, endlogSeg, path,
  										   true, XLOGfileslop,
***************
*** 1794,1803 ****
  				{
  					/* No need for any more future segments... */
  					ereport(LOG,
! 						  (errmsg("removing transaction log file \"%s\"",
  								  xlde->d_name)));
  					unlink(path);
  				}
  			}
  		}
  		errno = 0;
--- 2199,2209 ----
  				{
  					/* No need for any more future segments... */
  					ereport(LOG,
! 						  (errmsg("too many transaction log files, removing \"%s\"",
  								  xlde->d_name)));
  					unlink(path);
  				}
+                 XLogArchiveCleanup(xlde->d_name);
  			}
  		}
  		errno = 0;
***************
*** 2255,2260 ****
--- 2661,2667 ----
  {
  	/* Init XLOG file paths */
  	snprintf(XLogDir, MAXPGPATH, "%s/pg_xlog", DataDir);
+ 	snprintf(archiveStatusDir, MAXPGPATH, "%s/archive_status", XLogDir);
  	snprintf(ControlFilePath, MAXPGPATH, "%s/global/pg_control", DataDir);
  }
  
***************
*** 2772,2777 ****
--- 3179,3257 ----
  }
  
  /*
+  * read in restore command from recovery.conf
+  *
+  * XXX longer term intention is to expand this to 
+  * cater for additional parameters and controls
+  * possibly using a bison grammar to control it
+  */
+ static void
+ readRecoveryCommandFile(void)
+ {
+     FILE     *fd;
+     char    *tok1 = NULL;
+     char    *tok2 = NULL;
+     char    *val = NULL;
+     char    cmdline[MAXPGPATH];
+     bool    syntaxError = false;
+ 
+     fd = AllocateFile(recoveryCommandFile, "r");
+ 	if (fd == NULL) {
+     		ereport(FATAL,
+     			(errcode_for_file_access(),
+ 				errmsg("could not open recovery command file \"%s\"",recoveryCommandFile)));
+     		return;
+     }
+     /*  
+      * expecting |restore_program = Qcommand stringQ|
+      * e.g.      |restore_program = 'cp %s/%s %s'|
+      * where | denote the beginning and end of the string
+      */
+     while (!syntaxError && fgets(cmdline, MAXPGPATH, fd) != NULL) {
+ 
+         tok1 = strtok(cmdline, "'");
+         tok2 = strtok(NULL, "'");
+     
+         if (tok1 != NULL && tok2 != NULL) {
+             val = tok2;
+             tok1 = strtok(cmdline, " =");
+             if (strcmp(tok1,"restore_program") == 0) {
+                 strcpy(recoveryRestoreProgram, tok2);
+             	ereport(LOG,
+                     (errmsg("restore_program %s", recoveryRestoreProgram)));
+                     recoveryTarget = false;
+             }
+             else if (strcmp(tok1,"recover_to_time") == 0) {
+                     strcpy(recoveryTargetTimeStr, tok2);
+                 	ereport(LOG,(errmsg("recover_to_time %s", recoveryTargetTimeStr)));
+                     recoveryTarget = true;
+                     recoveryTargetExact = false;
+                  }
+             else if (strcmp(tok1,"recover_to_txnid") == 0) {
+                     strcpy(recoveryTargetXidStr, tok2);
+                 	ereport(LOG,(errmsg("recover_to_txn %s", recoveryTargetXidStr)));
+                     recoveryTarget = true;
+                     recoveryTargetExact = true;
+                  }
+             else
+                 syntaxError = true; 
+         }
+         else
+             syntaxError = true;        
+     }
+ 
+ 	FreeFile(fd);
+ 
+     if (syntaxError)
+         ereport(FATAL,
+ 		  (errmsg("syntax error in \"%s\"", recoveryCommandFile),
+ 		 errhint("Syntax needs should be similar to "
+                     "\"restore_program = 'cp %%s/%%s %%s'\"")));
+ 
+     return;
+ }
+ 
+ /*
   * This must be called ONCE during postmaster or standalone-backend startup
   */
  void
***************
*** 2787,2792 ****
--- 3267,3273 ----
  	XLogRecord *record;
  	char	   *buffer;
  	uint32		freespace;
+    	struct stat tmpStatBuf;
  
  	/* Use malloc() to ensure record buffer is MAXALIGNED */
  	buffer = (char *) malloc(_INTL_MAXLOGRECSZ);
***************
*** 2833,2838 ****
--- 3314,3353 ----
  		pg_usleep(60000000L);
  #endif
  
+     /*
+      * Check now for recovery.conf
+      *
+      * if this file exists, it demonstrates the intention of the administrator
+      * to recover this database using archived xlogs
+      *
+      * we do this now because the first xlog is about to be opened for the
+      * first time. We've read the checkpoint pointer from the control file
+      * and we are about to use that to open the xlog it points to, and
+      * will begin rollforward recovery from that point
+      */
+   	snprintf(recoveryCommandFile, MAXPGPATH, "%s/recovery.conf", DataDir);
+     if (stat(recoveryCommandFile, &tmpStatBuf) == 0) {
+ 
+      	readRecoveryCommandFile();
+         /*
+          * clearly indicate our state
+          */
+         InArchiveRecovery = true;
+         /*
+          * set initial state for checking transaction logs
+          * this may change if the archive runs dry while still InArchiveRecovery
+          */
+         restoreFromArchive = true;
+ 
+     	ereport(LOG,
+     		(errmsg("recovery command file found...starting archive recovery")));
+ 
+         if (XLogArchiveDEBUG)
+ 			ereport(LOG,
+                 (errmsg("restore_program=\"%s\"",
+                     recoveryRestoreProgram)));
+     }
+ 
  	/*
  	 * Get the last valid checkpoint record.  If the latest one according
  	 * to pg_control is broken, try the next-to-last one.
***************
*** 2863,2874 ****
  	LastRec = RecPtr = checkPointLoc;
  	memcpy(&checkPoint, XLogRecGetData(record), sizeof(CheckPoint));
  	wasShutdown = (record->xl_info == XLOG_CHECKPOINT_SHUTDOWN);
! 
  	ereport(LOG,
  			(errmsg("redo record is at %X/%X; undo record is at %X/%X; shutdown %s",
  					checkPoint.redo.xlogid, checkPoint.redo.xrecoff,
  					checkPoint.undo.xlogid, checkPoint.undo.xrecoff,
! 					wasShutdown ? "TRUE" : "FALSE")));
  	ereport(LOG,
  			(errmsg("next transaction ID: %u; next OID: %u",
  					checkPoint.nextXid, checkPoint.nextOid)));
--- 3378,3397 ----
  	LastRec = RecPtr = checkPointLoc;
  	memcpy(&checkPoint, XLogRecGetData(record), sizeof(CheckPoint));
  	wasShutdown = (record->xl_info == XLOG_CHECKPOINT_SHUTDOWN);
!     /*
!      * we report the state of the control_file, not the checkpoint, why?
!      * wasShutdown refers to whether the last checkpoint was a 
!      * shutdown checkpoint, NOT whether the database was shutdown
!      * correctly according to control file. This distinction is only
!      * important InArchiveRecovery, since otherwise we could
!      * report that the database was shutdown, when the control file disagrees
!      */
  	ereport(LOG,
  			(errmsg("redo record is at %X/%X; undo record is at %X/%X; shutdown %s",
  					checkPoint.redo.xlogid, checkPoint.redo.xrecoff,
  					checkPoint.undo.xlogid, checkPoint.undo.xrecoff,
!                     (ControlFile->state == DB_SHUTDOWNED) ? "TRUE" : "FALSE")));
! 
  	ereport(LOG,
  			(errmsg("next transaction ID: %u; next OID: %u",
  					checkPoint.nextXid, checkPoint.nextOid)));
***************
*** 2915,2921 ****
  	/* REDO */
  	if (InRecovery)
  	{
! 		int			rmid;
  
  		ereport(LOG,
  				(errmsg("database system was not properly shut down; "
--- 3438,3453 ----
  	/* REDO */
  	if (InRecovery)
  	{
! 		int			      rmid;
!     	char		      recoveryLogPath[MAXPGPATH];
!         bool              recoveryLogDEBUG = false;
!         bool              recoveryStop = false;
!         int               recoveryLogFD = -1;
!         char              *recbuf = NULL;
!        	uint8	          record_info;
! 		xl_xact_commit    *recordXactCommitData;
! 		xl_xact_abort     *recordXactAbortData;
!         time_t            recordXtime;
  
  		ereport(LOG,
  				(errmsg("database system was not properly shut down; "
***************
*** 2935,2941 ****
  
  		/* Is REDO required ? */
  		if (XLByteLT(checkPoint.redo, RecPtr))
! 			record = ReadRecord(&(checkPoint.redo), PANIC, buffer);
  		else
  		{
  			/* read past CheckPoint record */
--- 3467,3473 ----
  
  		/* Is REDO required ? */
  		if (XLByteLT(checkPoint.redo, RecPtr))
!     		record = ReadRecord(&(checkPoint.redo), PANIC, buffer);
  		else
  		{
  			/* read past CheckPoint record */
***************
*** 2948,2953 ****
--- 3480,3506 ----
  			ereport(LOG,
  					(errmsg("redo starts at %X/%X",
  							ReadRecPtr.xlogid, ReadRecPtr.xrecoff)));
+ #ifdef WAL_DEBUG
+             if (XLOG_DEBUG)
+                recoveryLogDEBUG = true;
+ #endif
+             if (XLogArchiveDEBUG)            
+                recoveryLogDEBUG = true;
+ 
+             recoveryLogDEBUG = true;
+             if (recoveryLogDEBUG) {
+            		recbuf = (char *) malloc(BLCKSZ);
+                 snprintf(recoveryLogPath, MAXPGPATH, "%s/recovery.log", DataDir);
+                 unlink(recoveryLogPath);
+                 recoveryLogFD = BasicOpenFile(recoveryLogPath, O_RDWR | O_CREAT | O_EXCL,
+ 					S_IRUSR | S_IWUSR);
+                 if (recoveryLogFD < 0)
+                     recoveryLogDEBUG = false;
+             }
+ 
+             /*
+              * main redo apply loop
+              */
  			do
  			{
  				/* nextXid must be beyond record's xid */
***************
*** 2958,2990 ****
  					TransactionIdAdvance(ShmemVariableCache->nextXid);
  				}
  
! #ifdef WAL_DEBUG
! 				if (XLOG_DEBUG)
  				{
! 					char		buf[8192];
! 
! 					sprintf(buf, "REDO @ %X/%X; LSN %X/%X: ",
  							ReadRecPtr.xlogid, ReadRecPtr.xrecoff,
  							EndRecPtr.xlogid, EndRecPtr.xrecoff);
! 					xlog_outrec(buf, record);
! 					strcat(buf, " - ");
! 					RmgrTable[record->xl_rmid].rm_desc(buf,
  								record->xl_info, XLogRecGetData(record));
! 					elog(LOG, "%s", buf);
  				}
- #endif
  
  				if (record->xl_info & XLR_BKP_BLOCK_MASK)
  					RestoreBkpBlocks(record, EndRecPtr);
  
- 				RmgrTable[record->xl_rmid].rm_redo(EndRecPtr, record);
- 				record = ReadRecord(NULL, LOG, buffer);
- 			} while (record != NULL);
  			ereport(LOG,
  					(errmsg("redo done at %X/%X",
  							ReadRecPtr.xlogid, ReadRecPtr.xrecoff)));
  			LastRec = ReadRecPtr;
  			InRedo = false;
  		}
  		else
  			ereport(LOG,
--- 3511,3622 ----
  					TransactionIdAdvance(ShmemVariableCache->nextXid);
  				}
  
! 				if (recoveryLogDEBUG)
  				{
! 					sprintf(recbuf, "\nREDO @ %X/%X; LSN %X/%X: ",
  							ReadRecPtr.xlogid, ReadRecPtr.xrecoff,
  							EndRecPtr.xlogid, EndRecPtr.xrecoff);
! 					xlog_outrec(recbuf, record);
! 					strcat(recbuf, " - ");
!                     if (record->xl_rmid == 0)
! 					   RmgrTable[record->xl_rmid].rm_desc(recbuf,
  								record->xl_info, XLogRecGetData(record));
!                     
!                     write(recoveryLogFD, recbuf, strlen(recbuf));
  				}
  
  				if (record->xl_info & XLR_BKP_BLOCK_MASK)
  					RestoreBkpBlocks(record, EndRecPtr);
+                 /*
+                  * Have we reached our recovery target?
+                  * check whether this is the last record we should apply -
+                  * we can only stop at transaction end record types,
+                  * COMMIT or ABORT, which are only ever associated
+                  * with the XLOG resource manager (0)
+                  */
+                 if (recoveryTarget == true   &&
+                     record->xl_rmid == 0) {
+ 
+                    	record_info = record->xl_info & ~XLR_INFO_MASK;
+ 
+                 	if (record_info == XLOG_XACT_COMMIT ||
+                 	    record_info == XLOG_XACT_ABORT) {
+ 
+                         if (recoveryTargetExact) {
+                             /* 
+                              * there can be only one transaction end record
+                              * with this exact transactionid
+                              *
+                              * when testing for an xid, we MUST test for 
+                              * equality only, since transactions are numbered
+                              * in the order they start, not the order they
+                              * complete. A higher numbered xid will complete
+                              * before you about 50% of the time...
+                              */
+                             if (record->xl_xid == recoveryTargetXid)
+                                 recoveryStop = true;
+                         }
+                         else {
+                             /* 
+                              * there can be many transactions that
+                              * share the same commit time, so
+                              * we stop after the last one, if we are
+                              * inclusive, or stop at the first one
+                              * if we are exclusive
+                              *
+                              * timestamp is first part of record body
+                              */
+                            	if (record_info == XLOG_XACT_COMMIT) {
+                                 recordXactCommitData = 
+                                     (xl_xact_commit *) XLogRecGetData(record);
+                                 recordXtime = recordXactCommitData->xtime;
+                             }
+                             else {
+                                 recordXactAbortData = 
+                                     (xl_xact_abort *) XLogRecGetData(record);
+                                 recordXtime = recordXactAbortData->xtime;
+                             }
+  
+                             if (recoveryTargetInclusive) {
+                                 if ((unsigned int)(recordXtime) ==
+                                     (unsigned int)(recoveryTargetTime))
+                                     recoveryStop = true;
+                             }
+                             else {
+                                 if ((unsigned int)(recordXtime) >
+                                     (unsigned int)(recoveryTargetTime))
+                                     recoveryStop = true;
+                             }
+                         }
+                     }
+                 }
+ 
+                 if (!recoveryStop && recoveryTargetInclusive) {
+     				RmgrTable[record->xl_rmid].rm_redo(EndRecPtr, record);
+        				record = ReadRecord(NULL, LOG, buffer);
+                 }
+ 
+ 			} while (record != NULL && !recoveryStop);
+             /*
+              * end of main redo apply
+              */
+ 
+             if (recoveryLogFD >= 0) {
+                 close(recoveryLogFD);
+                 free(recbuf);
+             }
  
  			ereport(LOG,
  					(errmsg("redo done at %X/%X",
  							ReadRecPtr.xlogid, ReadRecPtr.xrecoff)));
  			LastRec = ReadRecPtr;
  			InRedo = false;
+             if (InArchiveRecovery) {
+                 InRecoveryCleanup = true;
+                 InArchiveRecovery = false;
+                 /* LastRec.xlogid = LastRec.xlogid + TIMELINE_INCREMENT */
+             }
+             Assert(restoreFromArchive == false);
  		}
  		else
  			ereport(LOG,
***************
*** 3106,3118 ****
  		 * Note that we write a shutdown checkpoint.  This is correct since
  		 * the records following it will use SUI one more than what is
  		 * shown in the checkpoint's ThisStartUpID.
! 		 *
  		 * In case we had to use the secondary checkpoint, make sure that it
  		 * will still be shown as the secondary checkpoint after this
  		 * CreateCheckPoint operation; we don't want the broken primary
! 		 * checkpoint to become prevCheckPoint...
  		 */
! 		ControlFile->checkPoint = checkPointLoc;
  		CreateCheckPoint(true, true);
  
  		/*
--- 3738,3757 ----
  		 * Note that we write a shutdown checkpoint.  This is correct since
  		 * the records following it will use SUI one more than what is
  		 * shown in the checkpoint's ThisStartUpID.
! 		 */
! 		ControlFile->checkPoint = checkPointLoc;
! 		/*
  		 * In case we had to use the secondary checkpoint, make sure that it
  		 * will still be shown as the secondary checkpoint after this
  		 * CreateCheckPoint operation; we don't want the broken primary
! 		 * checkpoint to become prevCheckPoint...unless we have just
!          * performed an archive recovery, in which case reset the
!          * secondary checkpoint as well...to avoid going back over
!          * old ground
  		 */
!         if (InRecoveryCleanup)
!     		ControlFile->prevCheckPoint = checkPointLoc;
! 
  		CreateCheckPoint(true, true);
  
  		/*
***************
*** 3149,3154 ****
--- 3788,3799 ----
  	 * Okay, we're officially UP.
  	 */
  	InRecovery = false;
+     if (InRecoveryCleanup) {
+         unlink(recoveryCommandFile);
+         InRecoveryCleanup = false;
+ 		ereport(LOG,
+ 			(errmsg("archive recovery complete")));
+     }
  
  	ControlFile->state = DB_IN_PRODUCTION;
  	ControlFile->time = time(NULL);
***************
*** 3706,3712 ****
  		strcat(buf, "UNKNOWN");
  }
  
- #ifdef WAL_DEBUG
  static void
  xlog_outrec(char *buf, XLogRecord *record)
  {
--- 4351,4356 ----
***************
*** 3731,3738 ****
  	sprintf(buf + strlen(buf), ": %s",
  			RmgrTable[record->xl_rmid].rm_name);
  }
- #endif /* WAL_DEBUG */
- 
  
  /*
   * GUC support
--- 4375,4380 ----
Index: backend/postmaster/Makefile
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/postmaster/Makefile,v
retrieving revision 1.15
diff -c -r1.15 Makefile
*** backend/postmaster/Makefile	29 May 2004 22:48:19 -0000	1.15
--- backend/postmaster/Makefile	8 Jul 2004 17:53:05 -0000
***************
*** 12,18 ****
  top_builddir = ../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = postmaster.o bgwriter.o pgstat.o
  
  all: SUBSYS.o
  
--- 12,18 ----
  top_builddir = ../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = postmaster.o bgwriter.o pgstat.o pgarch.o
  
  all: SUBSYS.o
  
Index: backend/postmaster/postmaster.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/postmaster/postmaster.c,v
retrieving revision 1.405
diff -c -r1.405 postmaster.c
*** backend/postmaster/postmaster.c	24 Jun 2004 21:02:55 -0000	1.405
--- backend/postmaster/postmaster.c	8 Jul 2004 17:53:09 -0000
***************
*** 117,123 ****
  #include "utils/ps_status.h"
  #include "bootstrap/bootstrap.h"
  #include "pgstat.h"
! 
  
  /*
   * List of active backends (or child processes anyway; we don't actually
--- 117,123 ----
  #include "utils/ps_status.h"
  #include "bootstrap/bootstrap.h"
  #include "pgstat.h"
! #include "pgarch.h"
  
  /*
   * List of active backends (or child processes anyway; we don't actually
***************
*** 198,203 ****
--- 198,204 ----
  /* PIDs of special child processes; 0 when not running */
  static pid_t StartupPID = 0,
  			BgWriterPID = 0,
+             PgArchPID = 0,
  			PgStatPID = 0;
  
  /* Startup/shutdown state */
***************
*** 1147,1152 ****
--- 1148,1158 ----
  				kill(BgWriterPID, SIGUSR2);
  		}
  
+ 		/* If we have lost the archiver, try to start a new one */
+ 		if (XLogArchiveMode && PgArchPID == 0 && 
+             StartupPID == 0 && !FatalError && Shutdown == NoShutdown)
+ 			PgArchPID = pgarch_start();
+  
  		/* If we have lost the stats collector, try to start a new one */
  		if (PgStatPID == 0 &&
  			StartupPID == 0 && !FatalError && Shutdown == NoShutdown)
***************
*** 1751,1756 ****
--- 1757,1765 ----
  			/* Tell pgstat to shut down too; nothing left for it to do */
  			if (PgStatPID != 0)
  				kill(PgStatPID, SIGQUIT);
+ 			/* Tell pgarch to shut down too; nothing left for it to do */
+ 			if (PgArchPID != 0)
+ 				kill(PgArchPID, SIGQUIT);
  			break;
  
  		case SIGINT:
***************
*** 1795,1800 ****
--- 1804,1812 ----
  			/* Tell pgstat to shut down too; nothing left for it to do */
  			if (PgStatPID != 0)
  				kill(PgStatPID, SIGQUIT);
+ 			/* Tell pgarch to shut down too; nothing left for it to do */
+ 			if (PgArchPID != 0)
+ 				kill(PgArchPID, SIGQUIT);
  			break;
  
  		case SIGQUIT:
***************
*** 1812,1817 ****
--- 1824,1831 ----
  				kill(BgWriterPID, SIGQUIT);
  			if (PgStatPID != 0)
  				kill(PgStatPID, SIGQUIT);
+ 			if (PgArchPID != 0)
+ 				kill(PgArchPID, SIGQUIT);
  			if (DLGetHead(BackendList))
  				SignalChildren(SIGQUIT);
  			ExitPostmaster(0);
***************
*** 1901,1908 ****
  			 */
  			if (Shutdown > NoShutdown && BgWriterPID != 0)
  				kill(BgWriterPID, SIGUSR2);
! 			else if (PgStatPID == 0 && Shutdown == NoShutdown)
! 				PgStatPID = pgstat_start();
  
  			continue;
  		}
--- 1915,1926 ----
  			 */
  			if (Shutdown > NoShutdown && BgWriterPID != 0)
  				kill(BgWriterPID, SIGUSR2);
! 			else if (Shutdown == NoShutdown) {
!                     if (PgStatPID == 0)
!         				PgStatPID = pgstat_start();
!                     if (PgArchPID == 0)
!         				PgArchPID = pgarch_start();
!             }
  
  			continue;
  		}
***************
*** 1951,1956 ****
--- 1969,1990 ----
  		}
  
  		/*
+ 		 * Was it the archiver?  If so, just try to start a new
+ 		 * one; no need to force reset of the rest of the system.  (If fail,
+ 		 * we'll try again in future cycles of the main loop.)
+ 		 */
+ 		if (PgArchPID != 0 && pid == PgArchPID)
+ 		{
+ 			PgArchPID = 0;
+ 			if (exitstatus != 0)
+ 				LogChildExit(LOG, gettext("archiver process"),
+ 							 pid, exitstatus);
+ 			if (StartupPID == 0 && !FatalError && Shutdown == NoShutdown)
+ 				PgArchPID = pgarch_start();
+ 			continue;
+ 		}
+ 
+ 		/*
  		 * Else do standard backend child cleanup.
  		 */
  		CleanupProc(pid, exitstatus);
***************
*** 2132,2137 ****
--- 2166,2182 ----
  		kill(PgStatPID, SIGQUIT);
  	}
  
+ 	/* Force a power-cycle of the pgarch processes too */
+ 	/* (Shouldn't be necessary, but just for luck) */
+ 	if (PgArchPID != 0 && !FatalError)
+ 	{
+ 		ereport(DEBUG2,
+ 				(errmsg_internal("sending %s to process %d",
+ 								 "SIGQUIT",
+ 								 (int) PgArchPID)));
+ 		kill(PgArchPID, SIGQUIT);
+ 	}
+ 
  	FatalError = true;
  }
  
***************
*** 2881,2886 ****
--- 2926,2943 ----
  		if (Shutdown <= SmartShutdown)
  			SignalChildren(SIGUSR1);
  	}
+  
+  	if (CheckPostmasterSignal(PMSIGNAL_WAKEN_ARCHIVER))
+  	{
+  		/*
+  		 * Send SIGUSR1 to ARCHIVER process, to wake it up and begin
+  		 * archiving next transaction log file. Backend should only
+          * send if in XLogArchiveMode...
+  		 */
+  		if (XLogArchiveMode && Shutdown == NoShutdown) {
+             kill(PgArchPID,SIGUSR1);
+ 		}
+     }
  
  	PG_SETMASK(&UnBlockSig);
  
Index: backend/storage/smgr/md.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/storage/smgr/md.c,v
retrieving revision 1.108
diff -c -r1.108 md.c
*** backend/storage/smgr/md.c	18 Jun 2004 06:13:37 -0000	1.108
--- backend/storage/smgr/md.c	8 Jul 2004 17:53:10 -0000
***************
*** 162,167 ****
--- 162,169 ----
  
  	if (fd < 0)
  	{
+         elog(LOG,"path= %s, errno = %u", path, errno);
+ 
  		int			save_errno = errno;
  
  		/*
Index: backend/utils/misc/guc.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/utils/misc/guc.c,v
retrieving revision 1.213
diff -c -r1.213 guc.c
*** backend/utils/misc/guc.c	5 Jul 2004 23:14:14 -0000	1.213
--- backend/utils/misc/guc.c	8 Jul 2004 17:53:29 -0000
***************
*** 333,338 ****
--- 333,354 ----
  
  static struct config_bool ConfigureNamesBool[] =
  {
+  	{
+  		{"archive_mode", PGC_POSTMASTER, WAL_SETTINGS,
+  			gettext_noop("Enable archiving of full transaction log files to a specified archival destination."),
+  			NULL
+  		},
+  		&XLogArchiveMode,
+  		false, NULL, NULL
+  	},
+  	{
+  		{"archive_debug", PGC_SIGHUP, WAL_SETTINGS,
+  			gettext_noop("Provide debug output for archive activities."),
+  			NULL
+  		},
+  		&XLogArchiveDEBUG,
+  		false, NULL, NULL
+  	},
  	{
  		{"enable_seqscan", PGC_USERSET, QUERY_TUNING_METHOD,
  			gettext_noop("Enables the planner's use of sequential-scan plans."),
***************
*** 1362,1367 ****
--- 1378,1401 ----
  
  static struct config_string ConfigureNamesString[] =
  {
+  	{
+  		{"archive_dest", PGC_POSTMASTER, WAL_SETTINGS,
+  			gettext_noop("Specifies where to archive WAL logs."),
+  			gettext_noop("A directory or specific location for archiving transation log files from PostgreSQL")
+  		},
+  		&XLogArchiveDest,
+  		"", NULL, NULL
+  	},
+  
+  	{
+  		{"archive_program", PGC_POSTMASTER, WAL_SETTINGS,
+  			gettext_noop("Archive program"),
+  			gettext_noop("The external program that will be called to execute the archival process")
+  		},
+  		&XLogArchiveProgram,
+  		"", NULL, NULL
+  	},
+ 
  	{
  		{"client_encoding", PGC_USERSET, CLIENT_CONN_LOCALE,
  			gettext_noop("Sets the client's character set encoding."),
Index: backend/utils/misc/postgresql.conf.sample
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/utils/misc/postgresql.conf.sample,v
retrieving revision 1.113
diff -c -r1.113 postgresql.conf.sample
*** backend/utils/misc/postgresql.conf.sample	7 Apr 2004 05:05:50 -0000	1.113
--- backend/utils/misc/postgresql.conf.sample	8 Jul 2004 17:53:29 -0000
***************
*** 103,108 ****
--- 103,119 ----
  
  
  #---------------------------------------------------------------------------
+ # ARCHIVING
+ #---------------------------------------------------------------------------
+ 
+ # - Settings -
+ 
+ #archive_mode = true		# enables archiving of full txn log files
+ #archive_dest = '/tmp'        # specifies destination of archive files
+ #archive_program = 'cp %s %s'   # external archiving program command line
+ 
+ 
+ #---------------------------------------------------------------------------
  # QUERY TUNING
  #---------------------------------------------------------------------------
  
Index: bin/initdb/initdb.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/bin/initdb/initdb.c,v
retrieving revision 1.41
diff -c -r1.41 initdb.c
*** bin/initdb/initdb.c	1 Jul 2004 00:51:36 -0000	1.41
--- bin/initdb/initdb.c	8 Jul 2004 17:53:29 -0000
***************
*** 1828,1834 ****
  	char	   *pgdenv;			/* PGDATA value got from sent to
  								 * environment */
  	char	   *subdirs[] =
! 	{"global", "pg_xlog", "pg_clog", "pg_subtrans", "base", "base/1", "pg_tblspc"};
  
  	progname = get_progname(argv[0]);
  	set_pglocale_pgservice(argv[0], "initdb");
--- 1828,1835 ----
  	char	   *pgdenv;			/* PGDATA value got from sent to
  								 * environment */
  	char	   *subdirs[] =
! 
! 	{"global", "pg_xlog", "pg_xlog/archive_status", "pg_clog", "pg_subtrans", "base", "base/1", "pg_tblspc"};
  
  	progname = get_progname(argv[0]);
  	set_pglocale_pgservice(argv[0], "initdb");
Index: include/access/xlog.h
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/include/access/xlog.h,v
retrieving revision 1.52
diff -c -r1.52 xlog.h
*** include/access/xlog.h	1 Jul 2004 00:51:38 -0000	1.52
--- include/access/xlog.h	8 Jul 2004 17:53:29 -0000
***************
*** 210,215 ****
--- 210,219 ----
  extern int	XLOGbuffers;
  extern char *XLOG_sync_method;
  extern const char XLOG_sync_method_default[];
+ extern bool 			XLogArchiveMode;
+ extern bool 			XLogArchiveDEBUG;
+ extern char 			*XLogArchiveDest;
+ extern char 			*XLogArchiveProgram;
  
  #ifdef WAL_DEBUG
  extern bool	XLOG_DEBUG;
Index: include/storage/pmsignal.h
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/include/storage/pmsignal.h,v
retrieving revision 1.8
diff -c -r1.8 pmsignal.h
*** include/storage/pmsignal.h	29 May 2004 22:48:23 -0000	1.8
--- include/storage/pmsignal.h	8 Jul 2004 17:53:29 -0000
***************
*** 24,29 ****
--- 24,30 ----
  {
  	PMSIGNAL_PASSWORD_CHANGE,	/* pg_pwd file has changed */
  	PMSIGNAL_WAKEN_CHILDREN,	/* send a SIGUSR1 signal to all backends */
+   	PMSIGNAL_WAKEN_ARCHIVER,	/* send a NOTIFY signal to ARCHIVER */
  
  	NUM_PMSIGNALS				/* Must be last value of enum! */
  } PMSignalReason;
