@@ -56,14 +56,13 @@ static void base_backup_cleanup(int code, Datum arg);
5656static void perform_base_backup (basebackup_options * opt , DIR * tblspcdir );
5757static void parse_basebackup_options (List * options , basebackup_options * opt );
5858static void SendXlogRecPtrResult (XLogRecPtr ptr );
59+ static int compareWalFileNames (const void * a , const void * b );
5960
6061/* Was the backup currently in-progress initiated in recovery mode? */
6162static bool backup_started_in_recovery = false;
6263
6364/*
6465 * Size of each block sent into the tar stream for larger files.
65- *
66- * XLogSegSize *MUST* be evenly dividable by this
6766 */
6867#define TAR_SEND_SIZE 32768
6968
@@ -227,64 +226,201 @@ perform_base_backup(basebackup_options *opt, DIR *tblspcdir)
227226 * We've left the last tar file "open", so we can now append the
228227 * required WAL files to it.
229228 */
230- XLogSegNo logsegno ;
231- XLogSegNo endlogsegno ;
229+ char pathbuf [MAXPGPATH ];
230+ XLogSegNo segno ;
231+ XLogSegNo startsegno ;
232+ XLogSegNo endsegno ;
232233 struct stat statbuf ;
234+ List * historyFileList = NIL ;
235+ List * walFileList = NIL ;
236+ char * * walFiles ;
237+ int nWalFiles ;
238+ char firstoff [MAXFNAMELEN ];
239+ char lastoff [MAXFNAMELEN ];
240+ DIR * dir ;
241+ struct dirent * de ;
242+ int i ;
243+ ListCell * lc ;
244+ TimeLineID tli ;
233245
234- MemSet (& statbuf , 0 , sizeof (statbuf ));
235- statbuf .st_mode = S_IRUSR | S_IWUSR ;
236- #ifndef WIN32
237- statbuf .st_uid = geteuid ();
238- statbuf .st_gid = getegid ();
239- #endif
240- statbuf .st_size = XLogSegSize ;
241- statbuf .st_mtime = time (NULL );
246+ /*
247+ * I'd rather not worry about timelines here, so scan pg_xlog and
248+ * include all WAL files in the range between 'startptr' and 'endptr',
249+ * regardless of the timeline the file is stamped with. If there are
250+ * some spurious WAL files belonging to timelines that don't belong
251+ * in this server's history, they will be included too. Normally there
252+ * shouldn't be such files, but if there are, there's little harm in
253+ * including them.
254+ */
255+ XLByteToSeg (startptr , startsegno );
256+ XLogFileName (firstoff , ThisTimeLineID , startsegno );
257+ XLByteToPrevSeg (endptr , endsegno );
258+ XLogFileName (lastoff , ThisTimeLineID , endsegno );
259+
260+ dir = AllocateDir ("pg_xlog" );
261+ if (!dir )
262+ ereport (ERROR ,
263+ (errmsg ("could not open directory \"%s\": %m" , "pg_xlog" )));
264+ while ((de = ReadDir (dir , "pg_xlog" )) != NULL )
265+ {
266+ /* Does it look like a WAL segment, and is it in the range? */
267+ if (strlen (de -> d_name ) == 24 &&
268+ strspn (de -> d_name , "0123456789ABCDEF" ) == 24 &&
269+ strcmp (de -> d_name + 8 , firstoff + 8 ) >= 0 &&
270+ strcmp (de -> d_name + 8 , lastoff + 8 ) <= 0 )
271+ {
272+ walFileList = lappend (walFileList , pstrdup (de -> d_name ));
273+ }
274+ /* Does it look like a timeline history file? */
275+ else if (strlen (de -> d_name ) == 8 + strlen (".history" ) &&
276+ strspn (de -> d_name , "0123456789ABCDEF" ) == 8 &&
277+ strcmp (de -> d_name + 8 , ".history" ) == 0 )
278+ {
279+ historyFileList = lappend (historyFileList , pstrdup (de -> d_name ));
280+ }
281+ }
282+ FreeDir (dir );
242283
243- XLByteToSeg (startptr , logsegno );
244- XLByteToPrevSeg (endptr , endlogsegno );
284+ /*
285+ * Before we go any further, check that none of the WAL segments we
286+ * need were removed.
287+ */
288+ CheckXLogRemoved (startsegno , ThisTimeLineID );
245289
246- while (true)
290+ /*
291+ * Put the WAL filenames into an array, and sort. We send the files
292+ * in order from oldest to newest, to reduce the chance that a file
293+ * is recycled before we get a chance to send it over.
294+ */
295+ nWalFiles = list_length (walFileList );
296+ walFiles = palloc (nWalFiles * sizeof (char * ));
297+ i = 0 ;
298+ foreach (lc , walFileList )
247299 {
248- /* Send another xlog segment */
249- char fn [ MAXPGPATH ];
250- int i ;
300+ walFiles [ i ++ ] = lfirst ( lc );
301+ }
302+ qsort ( walFiles , nWalFiles , sizeof ( char * ), compareWalFileNames ) ;
251303
252- XLogFilePath (fn , ThisTimeLineID , logsegno );
253- _tarWriteHeader (fn , NULL , & statbuf );
304+ /*
305+ * Sanity check: the first and last segment should cover startptr and
306+ * endptr, with no gaps in between.
307+ */
308+ XLogFromFileName (walFiles [0 ], & tli , & segno );
309+ if (segno != startsegno )
310+ {
311+ char startfname [MAXFNAMELEN ];
312+ XLogFileName (startfname , ThisTimeLineID , startsegno );
313+ ereport (ERROR ,
314+ (errmsg ("could not find WAL file %s" , startfname )));
315+ }
316+ for (i = 0 ; i < nWalFiles ; i ++ )
317+ {
318+ XLogSegNo currsegno = segno ;
319+ XLogSegNo nextsegno = segno + 1 ;
254320
255- /* Send the actual WAL file contents, block-by-block */
256- for ( i = 0 ; i < XLogSegSize / TAR_SEND_SIZE ; i ++ )
321+ XLogFromFileName ( walFiles [ i ], & tli , & segno );
322+ if (!( nextsegno == segno || currsegno == segno ) )
257323 {
258- char buf [TAR_SEND_SIZE ];
259- XLogRecPtr ptr ;
324+ char nextfname [MAXFNAMELEN ];
325+ XLogFileName (nextfname , ThisTimeLineID , nextsegno );
326+ ereport (ERROR ,
327+ (errmsg ("could not find WAL file %s" , nextfname )));
328+ }
329+ }
330+ if (segno != endsegno )
331+ {
332+ char endfname [MAXFNAMELEN ];
333+ XLogFileName (endfname , ThisTimeLineID , endsegno );
334+ ereport (ERROR ,
335+ (errmsg ("could not find WAL file %s" , endfname )));
336+ }
337+
338+ /* Ok, we have everything we need. Send the WAL files. */
339+ for (i = 0 ; i < nWalFiles ; i ++ )
340+ {
341+ FILE * fp ;
342+ char buf [TAR_SEND_SIZE ];
343+ size_t cnt ;
344+ pgoff_t len = 0 ;
260345
261- XLogSegNoOffsetToRecPtr (logsegno , TAR_SEND_SIZE * i , ptr );
346+ snprintf (pathbuf , MAXPGPATH , XLOGDIR "/%s" , walFiles [i ]);
347+ XLogFromFileName (walFiles [i ], & tli , & segno );
262348
349+ fp = AllocateFile (pathbuf , "rb" );
350+ if (fp == NULL )
351+ {
263352 /*
264- * Some old compilers, e.g. gcc 2.95.3/x86, think that passing
265- * a struct in the same function as a longjump might clobber a
266- * variable. bjm 2011-02-04
267- * http://lists.apple.com/archives/xcode-users/2003/Dec//msg000
268- * 51.html
353+ * Most likely reason for this is that the file was already
354+ * removed by a checkpoint, so check for that to get a better
355+ * error message.
269356 */
270- XLogRead (buf , ThisTimeLineID , ptr , TAR_SEND_SIZE );
271- if (pq_putmessage ('d' , buf , TAR_SEND_SIZE ))
357+ CheckXLogRemoved (segno , tli );
358+
359+ ereport (ERROR ,
360+ (errcode_for_file_access (),
361+ errmsg ("could not open file \"%s\": %m" , pathbuf )));
362+ }
363+
364+ if (fstat (fileno (fp ), & statbuf ) != 0 )
365+ ereport (ERROR ,
366+ (errcode_for_file_access (),
367+ errmsg ("could not stat file \"%s\": %m" ,
368+ pathbuf )));
369+ if (statbuf .st_size != XLogSegSize )
370+ {
371+ CheckXLogRemoved (segno , tli );
372+ ereport (ERROR ,
373+ (errcode_for_file_access (),
374+ errmsg ("unexpected WAL file size \"%s\"" , walFiles [i ])));
375+ }
376+
377+ _tarWriteHeader (pathbuf , NULL , & statbuf );
378+
379+ while ((cnt = fread (buf , 1 , Min (sizeof (buf ), XLogSegSize - len ), fp )) > 0 )
380+ {
381+ CheckXLogRemoved (segno , tli );
382+ /* Send the chunk as a CopyData message */
383+ if (pq_putmessage ('d' , buf , cnt ))
272384 ereport (ERROR ,
273385 (errmsg ("base backup could not send data, aborting backup" )));
386+
387+ len += cnt ;
388+ if (len == XLogSegSize )
389+ break ;
274390 }
275391
276- /*
277- * Files are always fixed size, and always end on a 512 byte
278- * boundary, so padding is never necessary.
279- */
392+ if (len != XLogSegSize )
393+ {
394+ CheckXLogRemoved (segno , tli );
395+ ereport (ERROR ,
396+ (errcode_for_file_access (),
397+ errmsg ("unexpected WAL file size \"%s\"" , walFiles [i ])));
398+ }
280399
400+ /* XLogSegSize is a multiple of 512, so no need for padding */
401+ FreeFile (fp );
402+ }
281403
282- /* Advance to the next WAL file */
283- logsegno ++ ;
404+ /*
405+ * Send timeline history files too. Only the latest timeline history
406+ * file is required for recovery, and even that only if there happens
407+ * to be a timeline switch in the first WAL segment that contains the
408+ * checkpoint record, or if we're taking a base backup from a standby
409+ * server and the target timeline changes while the backup is taken.
410+ * But they are small and highly useful for debugging purposes, so
411+ * better include them all, always.
412+ */
413+ foreach (lc , historyFileList )
414+ {
415+ char * fname = lfirst (lc );
416+ snprintf (pathbuf , MAXPGPATH , XLOGDIR "/%s" , fname );
284417
285- /* Have we reached our stop position yet? */
286- if (logsegno > endlogsegno )
287- break ;
418+ if (lstat (pathbuf , & statbuf ) != 0 )
419+ ereport (ERROR ,
420+ (errcode_for_file_access (),
421+ errmsg ("could not stat file \"%s\": %m" , pathbuf )));
422+
423+ sendFile (pathbuf , pathbuf , & statbuf , false);
288424 }
289425
290426 /* Send CopyDone message for the last tar file */
@@ -293,6 +429,19 @@ perform_base_backup(basebackup_options *opt, DIR *tblspcdir)
293429 SendXlogRecPtrResult (endptr );
294430}
295431
432+ /*
433+ * qsort comparison function, to compare log/seg portion of WAL segment
434+ * filenames, ignoring the timeline portion.
435+ */
436+ static int
437+ compareWalFileNames (const void * a , const void * b )
438+ {
439+ char * fna = * ((char * * ) a );
440+ char * fnb = * ((char * * ) b );
441+
442+ return strcmp (fna + 8 , fnb + 8 );
443+ }
444+
296445/*
297446 * Parse the base backup options passed down by the parser
298447 */
0 commit comments