@@ -37,6 +37,7 @@ typedef struct
3737 const char * label ;
3838 bool progress ;
3939 bool fastcheckpoint ;
40+ bool includewal ;
4041} basebackup_options ;
4142
4243
@@ -46,11 +47,17 @@ static void _tarWriteHeader(char *filename, char *linktarget,
4647 struct stat * statbuf );
4748static void send_int8_string (StringInfoData * buf , int64 intval );
4849static void SendBackupHeader (List * tablespaces );
49- static void SendBackupDirectory (char * location , char * spcoid );
5050static void base_backup_cleanup (int code , Datum arg );
5151static void perform_base_backup (basebackup_options * opt , DIR * tblspcdir );
5252static void parse_basebackup_options (List * options , basebackup_options * opt );
5353
54+ /*
55+ * Size of each block sent into the tar stream for larger files.
56+ *
57+ * XLogSegSize *MUST* be evenly dividable by this
58+ */
59+ #define TAR_SEND_SIZE 32768
60+
5461typedef struct
5562{
5663 char * oid ;
@@ -78,7 +85,10 @@ base_backup_cleanup(int code, Datum arg)
7885static void
7986perform_base_backup (basebackup_options * opt , DIR * tblspcdir )
8087{
81- do_pg_start_backup (opt -> label , opt -> fastcheckpoint );
88+ XLogRecPtr startptr ;
89+ XLogRecPtr endptr ;
90+
91+ startptr = do_pg_start_backup (opt -> label , opt -> fastcheckpoint );
8292
8393 PG_ENSURE_ERROR_CLEANUP (base_backup_cleanup , (Datum ) 0 );
8494 {
@@ -87,12 +97,6 @@ perform_base_backup(basebackup_options *opt, DIR *tblspcdir)
8797 struct dirent * de ;
8898 tablespaceinfo * ti ;
8999
90-
91- /* Add a node for the base directory */
92- ti = palloc0 (sizeof (tablespaceinfo ));
93- ti -> size = opt -> progress ? sendDir ("." , 1 , true) : -1 ;
94- tablespaces = lappend (tablespaces , ti );
95-
96100 /* Collect information about all tablespaces */
97101 while ((de = ReadDir (tblspcdir , "pg_tblspc" )) != NULL )
98102 {
@@ -120,6 +124,10 @@ perform_base_backup(basebackup_options *opt, DIR *tblspcdir)
120124 tablespaces = lappend (tablespaces , ti );
121125 }
122126
127+ /* Add a node for the base directory at the end */
128+ ti = palloc0 (sizeof (tablespaceinfo ));
129+ ti -> size = opt -> progress ? sendDir ("." , 1 , true) : -1 ;
130+ tablespaces = lappend (tablespaces , ti );
123131
124132 /* Send tablespace header */
125133 SendBackupHeader (tablespaces );
@@ -128,13 +136,102 @@ perform_base_backup(basebackup_options *opt, DIR *tblspcdir)
128136 foreach (lc , tablespaces )
129137 {
130138 tablespaceinfo * ti = (tablespaceinfo * ) lfirst (lc );
139+ StringInfoData buf ;
140+
141+ /* Send CopyOutResponse message */
142+ pq_beginmessage (& buf , 'H' );
143+ pq_sendbyte (& buf , 0 ); /* overall format */
144+ pq_sendint (& buf , 0 , 2 ); /* natts */
145+ pq_endmessage (& buf );
146+
147+ sendDir (ti -> path == NULL ? "." : ti -> path ,
148+ ti -> path == NULL ? 1 : strlen (ti -> path ),
149+ false);
131150
132- SendBackupDirectory (ti -> path , ti -> oid );
151+ /*
152+ * If we're including WAL, and this is the main data directory we
153+ * don't terminate the tar stream here. Instead, we will append
154+ * the xlog files below and terminate it then. This is safe since
155+ * the main data directory is always sent *last*.
156+ */
157+ if (opt -> includewal && ti -> path == NULL )
158+ {
159+ Assert (lnext (lc ) == NULL );
160+ }
161+ else
162+ pq_putemptymessage ('c' ); /* CopyDone */
133163 }
134164 }
135165 PG_END_ENSURE_ERROR_CLEANUP (base_backup_cleanup , (Datum ) 0 );
136166
137- do_pg_stop_backup ();
167+ endptr = do_pg_stop_backup ();
168+
169+ if (opt -> includewal )
170+ {
171+ /*
172+ * We've left the last tar file "open", so we can now append the
173+ * required WAL files to it.
174+ */
175+ uint32 logid ,
176+ logseg ;
177+ uint32 endlogid ,
178+ endlogseg ;
179+ struct stat statbuf ;
180+
181+ MemSet (& statbuf , 0 , sizeof (statbuf ));
182+ statbuf .st_mode = S_IRUSR | S_IWUSR ;
183+ #ifndef WIN32
184+ statbuf .st_uid = geteuid ();
185+ statbuf .st_gid = getegid ();
186+ #endif
187+ statbuf .st_size = XLogSegSize ;
188+ statbuf .st_mtime = time (NULL );
189+
190+ XLByteToSeg (startptr , logid , logseg );
191+ XLByteToPrevSeg (endptr , endlogid , endlogseg );
192+
193+ while (true)
194+ {
195+ /* Send another xlog segment */
196+ char fn [MAXPGPATH ];
197+ int i ;
198+
199+ XLogFilePath (fn , ThisTimeLineID , logid , logseg );
200+ _tarWriteHeader (fn , NULL , & statbuf );
201+
202+ /* Send the actual WAL file contents, block-by-block */
203+ for (i = 0 ; i < XLogSegSize / TAR_SEND_SIZE ; i ++ )
204+ {
205+ char buf [TAR_SEND_SIZE ];
206+ XLogRecPtr ptr ;
207+
208+ ptr .xlogid = logid ;
209+ ptr .xrecoff = logseg * XLogSegSize + TAR_SEND_SIZE * i ;
210+
211+ XLogRead (buf , ptr , TAR_SEND_SIZE );
212+ if (pq_putmessage ('d' , buf , TAR_SEND_SIZE ))
213+ ereport (ERROR ,
214+ (errmsg ("base backup could not send data, aborting backup" )));
215+ }
216+
217+ /*
218+ * Files are always fixed size, and always end on a 512 byte
219+ * boundary, so padding is never necessary.
220+ */
221+
222+
223+ /* Advance to the next WAL file */
224+ NextLogSeg (logid , logseg );
225+
226+ /* Have we reached our stop position yet? */
227+ if (logid > endlogid ||
228+ (logid == endlogid && logseg > endlogseg ))
229+ break ;
230+ }
231+
232+ /* Send CopyDone message for the last tar file */
233+ pq_putemptymessage ('c' );
234+ }
138235}
139236
140237/*
@@ -147,6 +244,7 @@ parse_basebackup_options(List *options, basebackup_options *opt)
147244 bool o_label = false;
148245 bool o_progress = false;
149246 bool o_fast = false;
247+ bool o_wal = false;
150248
151249 MemSet (opt , 0 , sizeof (* opt ));
152250 foreach (lopt , options )
@@ -180,6 +278,15 @@ parse_basebackup_options(List *options, basebackup_options *opt)
180278 opt -> fastcheckpoint = true;
181279 o_fast = true;
182280 }
281+ else if (strcmp (defel -> defname , "wal" ) == 0 )
282+ {
283+ if (o_wal )
284+ ereport (ERROR ,
285+ (errcode (ERRCODE_SYNTAX_ERROR ),
286+ errmsg ("duplicate option \"%s\"" , defel -> defname )));
287+ opt -> includewal = true;
288+ o_wal = true;
289+ }
183290 else
184291 elog (ERROR , "option \"%s\" not recognized" ,
185292 defel -> defname );
@@ -316,26 +423,6 @@ SendBackupHeader(List *tablespaces)
316423 pq_puttextmessage ('C' , "SELECT" );
317424}
318425
319- static void
320- SendBackupDirectory (char * location , char * spcoid )
321- {
322- StringInfoData buf ;
323-
324- /* Send CopyOutResponse message */
325- pq_beginmessage (& buf , 'H' );
326- pq_sendbyte (& buf , 0 ); /* overall format */
327- pq_sendint (& buf , 0 , 2 ); /* natts */
328- pq_endmessage (& buf );
329-
330- /* tar up the data directory if NULL, otherwise the tablespace */
331- sendDir (location == NULL ? "." : location ,
332- location == NULL ? 1 : strlen (location ),
333- false);
334-
335- /* Send CopyDone message */
336- pq_putemptymessage ('c' );
337- }
338-
339426
340427static int64
341428sendDir (char * path , int basepathlen , bool sizeonly )
@@ -506,7 +593,7 @@ static void
506593sendFile (char * filename , int basepathlen , struct stat * statbuf )
507594{
508595 FILE * fp ;
509- char buf [32768 ];
596+ char buf [TAR_SEND_SIZE ];
510597 size_t cnt ;
511598 pgoff_t len = 0 ;
512599 size_t pad ;
0 commit comments