3434/* Global options */
3535static char * basedir = NULL ;
3636static int verbose = 0 ;
37+ static int compresslevel = 0 ;
3738static int noloop = 0 ;
3839static int standby_message_timeout = 10 * 1000 ; /* 10 sec = default */
3940static volatile bool time_to_abort = false;
@@ -58,6 +59,15 @@ static bool stop_streaming(XLogRecPtr segendpos, uint32 timeline,
5859 exit(code); \
5960 }
6061
62+ /* Routines to evaluate segment file format */
63+ #define IsCompressXLogFileName (fname ) \
64+ (strlen(fname) == XLOG_FNAME_LEN + strlen(".gz") && \
65+ strspn(fname, "0123456789ABCDEF") == XLOG_FNAME_LEN && \
66+ strcmp((fname) + XLOG_FNAME_LEN, ".gz") == 0)
67+ #define IsPartialCompressXLogFileName (fname ) \
68+ (strlen(fname) == XLOG_FNAME_LEN + strlen(".gz.partial") && \
69+ strspn(fname, "0123456789ABCDEF") == XLOG_FNAME_LEN && \
70+ strcmp((fname) + XLOG_FNAME_LEN, ".gz.partial") == 0)
6171
6272static void
6373usage (void )
@@ -76,6 +86,7 @@ usage(void)
7686 printf (_ (" --synchronous flush transaction log immediately after writing\n" ));
7787 printf (_ (" -v, --verbose output verbose messages\n" ));
7888 printf (_ (" -V, --version output version information, then exit\n" ));
89+ printf (_ (" -Z, --compress=0-9 compress logs with given compression level\n" ));
7990 printf (_ (" -?, --help show this help, then exit\n" ));
8091 printf (_ ("\nConnection options:\n" ));
8192 printf (_ (" -d, --dbname=CONNSTR connection string\n" ));
@@ -188,14 +199,31 @@ FindStreamingStart(uint32 *tli)
188199 uint32 tli ;
189200 XLogSegNo segno ;
190201 bool ispartial ;
202+ bool iscompress ;
191203
192204 /*
193205 * Check if the filename looks like an xlog file, or a .partial file.
194206 */
195207 if (IsXLogFileName (dirent -> d_name ))
208+ {
196209 ispartial = false;
210+ iscompress = false;
211+ }
197212 else if (IsPartialXLogFileName (dirent -> d_name ))
213+ {
214+ ispartial = true;
215+ iscompress = false;
216+ }
217+ else if (IsCompressXLogFileName (dirent -> d_name ))
218+ {
219+ ispartial = false;
220+ iscompress = true;
221+ }
222+ else if (IsPartialCompressXLogFileName (dirent -> d_name ))
223+ {
198224 ispartial = true;
225+ iscompress = true;
226+ }
199227 else
200228 continue ;
201229
@@ -206,9 +234,15 @@ FindStreamingStart(uint32 *tli)
206234
207235 /*
208236 * Check that the segment has the right size, if it's supposed to be
209- * completed.
237+ * completed. For non-compressed segments just check the on-disk size
238+ * and see if it matches a completed segment.
239+ * For compressed segments, look at the last 4 bytes of the compressed
240+ * file, which is where the uncompressed size is located for gz files
241+ * with a size lower than 4GB, and then compare it to the size of a
242+ * completed segment. The 4 last bytes correspond to the ISIZE member
243+ * according to http://www.zlib.org/rfc-gzip.html.
210244 */
211- if (!ispartial )
245+ if (!ispartial && ! iscompress )
212246 {
213247 struct stat statbuf ;
214248 char fullpath [MAXPGPATH ];
@@ -229,6 +263,47 @@ FindStreamingStart(uint32 *tli)
229263 continue ;
230264 }
231265 }
266+ else if (!ispartial && iscompress )
267+ {
268+ int fd ;
269+ char buf [4 ];
270+ int bytes_out ;
271+ char fullpath [MAXPGPATH ];
272+
273+ snprintf (fullpath , sizeof (fullpath ), "%s/%s" , basedir , dirent -> d_name );
274+
275+ fd = open (fullpath , O_RDONLY | PG_BINARY );
276+ if (fd < 0 )
277+ {
278+ fprintf (stderr , _ ("%s: could not open compressed file \"%s\": %s\n" ),
279+ progname , fullpath , strerror (errno ));
280+ disconnect_and_exit (1 );
281+ }
282+ if (lseek (fd , (off_t )(-4 ), SEEK_END ) < 0 )
283+ {
284+ fprintf (stderr , _ ("%s: could not seek compressed file \"%s\": %s\n" ),
285+ progname , fullpath , strerror (errno ));
286+ disconnect_and_exit (1 );
287+ }
288+ if (read (fd , (char * ) buf , sizeof (buf )) != sizeof (buf ))
289+ {
290+ fprintf (stderr , _ ("%s: could not read compressed file \"%s\": %s\n" ),
291+ progname , fullpath , strerror (errno ));
292+ disconnect_and_exit (1 );
293+ }
294+
295+ close (fd );
296+ bytes_out = (buf [3 ] << 24 ) | (buf [2 ] << 16 ) |
297+ (buf [1 ] << 8 ) | buf [0 ];
298+
299+ if (bytes_out != XLOG_SEG_SIZE )
300+ {
301+ fprintf (stderr ,
302+ _ ("%s: compressed segment file \"%s\" has incorrect uncompressed size %d, skipping\n" ),
303+ progname , dirent -> d_name , bytes_out );
304+ continue ;
305+ }
306+ }
232307
233308 /* Looks like a valid segment. Remember that we saw it. */
234309 if ((segno > high_segno ) ||
@@ -339,7 +414,8 @@ StreamLog(void)
339414 stream .synchronous = synchronous ;
340415 stream .do_sync = true;
341416 stream .mark_done = false;
342- stream .walmethod = CreateWalDirectoryMethod (basedir , stream .do_sync );
417+ stream .walmethod = CreateWalDirectoryMethod (basedir , compresslevel ,
418+ stream .do_sync );
343419 stream .partial_suffix = ".partial" ;
344420 stream .replication_slot = replication_slot ;
345421 stream .temp_slot = false;
@@ -392,6 +468,7 @@ main(int argc, char **argv)
392468 {"status-interval" , required_argument , NULL , 's' },
393469 {"slot" , required_argument , NULL , 'S' },
394470 {"verbose" , no_argument , NULL , 'v' },
471+ {"compress" , required_argument , NULL , 'Z' },
395472/* action */
396473 {"create-slot" , no_argument , NULL , 1 },
397474 {"drop-slot" , no_argument , NULL , 2 },
@@ -422,7 +499,7 @@ main(int argc, char **argv)
422499 }
423500 }
424501
425- while ((c = getopt_long (argc , argv , "D:d:h:p:U:s:S:nwWv " ,
502+ while ((c = getopt_long (argc , argv , "D:d:h:p:U:s:S:nwWvZ: " ,
426503 long_options , & option_index )) != -1 )
427504 {
428505 switch (c )
@@ -472,6 +549,15 @@ main(int argc, char **argv)
472549 case 'v' :
473550 verbose ++ ;
474551 break ;
552+ case 'Z' :
553+ compresslevel = atoi (optarg );
554+ if (compresslevel < 0 || compresslevel > 9 )
555+ {
556+ fprintf (stderr , _ ("%s: invalid compression level \"%s\"\n" ),
557+ progname , optarg );
558+ exit (1 );
559+ }
560+ break ;
475561/* action */
476562 case 1 :
477563 do_create_slot = true;
@@ -538,6 +624,16 @@ main(int argc, char **argv)
538624 exit (1 );
539625 }
540626
627+ #ifndef HAVE_LIBZ
628+ if (compresslevel != 0 )
629+ {
630+ fprintf (stderr ,
631+ _ ("%s: this build does not support compression\n" ),
632+ progname );
633+ exit (1 );
634+ }
635+ #endif
636+
541637 /*
542638 * Check existence of destination folder.
543639 */
0 commit comments