77 * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
88 * Portions Copyright (c) 1994, Regents of the University of California
99 *
10- * $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.381 2010/03/15 18:49:17 sriggs Exp $
10+ * $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.382 2010/03/18 09:17:18 heikki Exp $
1111 *
1212 *-------------------------------------------------------------------------
1313 */
@@ -171,6 +171,7 @@ static bool restoredFromArchive = false;
171171/* options taken from recovery.conf for archive recovery */
172172static char * recoveryRestoreCommand = NULL ;
173173static char * recoveryEndCommand = NULL ;
174+ static char * restartPointCommand = NULL ;
174175static bool recoveryTarget = false;
175176static bool recoveryTargetExact = false;
176177static bool recoveryTargetInclusive = true;
@@ -370,6 +371,11 @@ typedef struct XLogCtlData
370371int XLogCacheBlck ;/* highest allocated xlog buffer index */
371372TimeLineID ThisTimeLineID ;
372373TimeLineID RecoveryTargetTLI ;
374+ /*
375+ * restartPointCommand is read from recovery.conf but needs to be in
376+ * shared memory so that the bgwriter process can access it.
377+ */
378+ char restartPointCommand [MAXPGPATH ];
373379
374380/*
375381 * SharedRecoveryInProgress indicates if we're still in crash or archive
@@ -520,7 +526,8 @@ static bool XLogPageRead(XLogRecPtr *RecPtr, int emode, bool fetching_ckpt,
520526static void XLogFileClose (void );
521527static bool RestoreArchivedFile (char * path ,const char * xlogfname ,
522528const char * recovername ,off_t expectedSize );
523- static void ExecuteRecoveryEndCommand (void );
529+ static void ExecuteRecoveryCommand (char * command ,char * commandName ,
530+ bool failOnerror );
524531static void PreallocXlogFiles (XLogRecPtr endptr );
525532static void RemoveOldXlogFiles (uint32 log ,uint32 seg ,XLogRecPtr endptr );
526533static void ValidateXLOGDirectoryStructure (void );
@@ -2990,12 +2997,19 @@ RestoreArchivedFile(char *path, const char *xlogfname,
29902997}
29912998
29922999/*
2993- * Attempt to execute the recovery_end_command.
3000+ * Attempt to execute an external shell command during recovery.
3001+ *
3002+ * 'command' is the shell command to be executed, 'commandName' is a
3003+ * human-readable name describing the command emitted in the logs. If
3004+ * 'failonSignal' is true and the command is killed by a signal, a FATAL
3005+ * error is thrown. Otherwise a WARNING is emitted.
3006+ *
3007+ * This is currently used for restore_end_command and restartpoint_command.
29943008 */
29953009static void
2996- ExecuteRecoveryEndCommand ( void )
3010+ ExecuteRecoveryCommand ( char * command , char * commandName , bool failOnSignal )
29973011{
2998- char xlogRecoveryEndCmd [MAXPGPATH ];
3012+ char xlogRecoveryCmd [MAXPGPATH ];
29993013char lastRestartPointFname [MAXPGPATH ];
30003014char * dp ;
30013015char * endp ;
@@ -3005,43 +3019,29 @@ ExecuteRecoveryEndCommand(void)
30053019uint32 restartLog ;
30063020uint32 restartSeg ;
30073021
3008- Assert (recoveryEndCommand );
3022+ Assert (command && commandName );
30093023
30103024/*
30113025 * Calculate the archive file cutoff point for use during log shipping
30123026 * replication. All files earlier than this point can be deleted from the
30133027 * archive, though there is no requirement to do so.
3014- *
3015- * We initialise this with the filename of an InvalidXLogRecPtr, which
3016- * will prevent the deletion of any WAL files from the archive because of
3017- * the alphabetic sorting property of WAL filenames.
3018- *
3019- * Once we have successfully located the redo pointer of the checkpoint
3020- * from which we start recovery we never request a file prior to the redo
3021- * pointer of the last restartpoint. When redo begins we know that we have
3022- * successfully located it, so there is no need for additional status
3023- * flags to signify the point when we can begin deleting WAL files from
3024- * the archive.
30253028 */
3026- if (InRedo )
3027- {
3028- XLByteToSeg (ControlFile -> checkPointCopy .redo ,
3029- restartLog ,restartSeg );
3030- XLogFileName (lastRestartPointFname ,
3031- ControlFile -> checkPointCopy .ThisTimeLineID ,
3032- restartLog ,restartSeg );
3033- }
3034- else
3035- XLogFileName (lastRestartPointFname ,0 ,0 ,0 );
3029+ LWLockAcquire (ControlFileLock ,LW_SHARED );
3030+ XLByteToSeg (ControlFile -> checkPointCopy .redo ,
3031+ restartLog ,restartSeg );
3032+ XLogFileName (lastRestartPointFname ,
3033+ ControlFile -> checkPointCopy .ThisTimeLineID ,
3034+ restartLog ,restartSeg );
3035+ LWLockRelease (ControlFileLock );
30363036
30373037/*
30383038 * construct the command to be executed
30393039 */
3040- dp = xlogRecoveryEndCmd ;
3041- endp = xlogRecoveryEndCmd + MAXPGPATH - 1 ;
3040+ dp = xlogRecoveryCmd ;
3041+ endp = xlogRecoveryCmd + MAXPGPATH - 1 ;
30423042* endp = '\0' ;
30433043
3044- for (sp = recoveryEndCommand ;* sp ;sp ++ )
3044+ for (sp = command ;* sp ;sp ++ )
30453045{
30463046if (* sp == '%' )
30473047{
@@ -3075,13 +3075,12 @@ ExecuteRecoveryEndCommand(void)
30753075* dp = '\0' ;
30763076
30773077ereport (DEBUG3 ,
3078- (errmsg_internal ("executing recovery end command \"%s\"" ,
3079- xlogRecoveryEndCmd )));
3078+ (errmsg_internal ("executing %s \"%s\"" ,commandName ,command )));
30803079
30813080/*
30823081 * execute the constructed command
30833082 */
3084- rc = system (xlogRecoveryEndCmd );
3083+ rc = system (xlogRecoveryCmd );
30853084if (rc != 0 )
30863085{
30873086/*
@@ -3091,9 +3090,13 @@ ExecuteRecoveryEndCommand(void)
30913090 */
30923091signaled = WIFSIGNALED (rc )|| WEXITSTATUS (rc )> 125 ;
30933092
3094- ereport (signaled ?FATAL :WARNING ,
3095- (errmsg ("recovery_end_command \"%s\": return code %d" ,
3096- xlogRecoveryEndCmd ,rc )));
3093+ /*
3094+ * translator: First %s represents a recovery.conf parameter name like
3095+ * "recovery_end_command", and the 2nd is the value of that parameter.
3096+ */
3097+ ereport ((signaled && failOnSignal ) ?FATAL :WARNING ,
3098+ (errmsg ("%s \"%s\": return code %d" ,commandName ,
3099+ command ,rc )));
30973100}
30983101}
30993102
@@ -4936,6 +4939,13 @@ readRecoveryCommandFile(void)
49364939(errmsg ("recovery_end_command = '%s'" ,
49374940recoveryEndCommand )));
49384941}
4942+ else if (strcmp (tok1 ,"restartpoint_command" )== 0 )
4943+ {
4944+ restartPointCommand = pstrdup (tok2 );
4945+ ereport (DEBUG2 ,
4946+ (errmsg ("restartpoint_command = '%s'" ,
4947+ restartPointCommand )));
4948+ }
49394949else if (strcmp (tok1 ,"recovery_target_timeline" )== 0 )
49404950{
49414951rtliGiven = true;
@@ -5505,8 +5515,14 @@ StartupXLOG(void)
55055515recoveryTargetTLI ,
55065516ControlFile -> checkPointCopy .ThisTimeLineID )));
55075517
5508- /* Save the selected recovery target timeline ID in shared memory */
5518+ /*
5519+ * Save the selected recovery target timeline ID and restartpoint_command
5520+ * in shared memory so that other processes can see them
5521+ */
55095522XLogCtl -> RecoveryTargetTLI = recoveryTargetTLI ;
5523+ strncpy (XLogCtl -> restartPointCommand ,
5524+ restartPointCommand ?restartPointCommand :"" ,
5525+ sizeof (XLogCtl -> restartPointCommand ));
55105526
55115527if (read_backup_label (& checkPointLoc ))
55125528{
@@ -6129,7 +6145,9 @@ StartupXLOG(void)
61296145 * And finally, execute the recovery_end_command, if any.
61306146 */
61316147if (recoveryEndCommand )
6132- ExecuteRecoveryEndCommand ();
6148+ ExecuteRecoveryCommand (recoveryEndCommand ,
6149+ "recovery_end_command" ,
6150+ true);
61336151}
61346152
61356153/*
@@ -7318,6 +7336,15 @@ CreateRestartPoint(int flags)
73187336timestamptz_to_str (GetLatestXLogTime ()))));
73197337
73207338LWLockRelease (CheckpointLock );
7339+
7340+ /*
7341+ * Finally, execute restartpoint_command, if any.
7342+ */
7343+ if (XLogCtl -> restartPointCommand [0 ])
7344+ ExecuteRecoveryCommand (XLogCtl -> restartPointCommand ,
7345+ "restartpoint_command" ,
7346+ false);
7347+
73217348return true;
73227349}
73237350