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