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,
5859exit(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)
7686printf (_ (" --synchronous flush transaction log immediately after writing\n" ));
7787printf (_ (" -v, --verbose output verbose messages\n" ));
7888printf (_ (" -V, --version output version information, then exit\n" ));
89+ printf (_ (" -Z, --compress=0-9 compress logs with given compression level\n" ));
7990printf (_ (" -?, --help show this help, then exit\n" ));
8091printf (_ ("\nConnection options:\n" ));
8192printf (_ (" -d, --dbname=CONNSTR connection string\n" ));
@@ -188,14 +199,31 @@ FindStreamingStart(uint32 *tli)
188199uint32 tli ;
189200XLogSegNo segno ;
190201bool ispartial ;
202+ bool iscompress ;
191203
192204/*
193205 * Check if the filename looks like an xlog file, or a .partial file.
194206 */
195207if (IsXLogFileName (dirent -> d_name ))
208+ {
196209ispartial = false;
210+ iscompress = false;
211+ }
197212else 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+ {
198224ispartial = true;
225+ iscompress = true;
226+ }
199227else
200228continue ;
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{
213247struct stat statbuf ;
214248char fullpath [MAXPGPATH ];
@@ -229,6 +263,47 @@ FindStreamingStart(uint32 *tli)
229263continue ;
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. */
234309if ((segno > high_segno )||
@@ -339,7 +414,8 @@ StreamLog(void)
339414stream .synchronous = synchronous ;
340415stream .do_sync = true;
341416stream .mark_done = false;
342- stream .walmethod = CreateWalDirectoryMethod (basedir ,stream .do_sync );
417+ stream .walmethod = CreateWalDirectoryMethod (basedir ,compresslevel ,
418+ stream .do_sync );
343419stream .partial_suffix = ".partial" ;
344420stream .replication_slot = replication_slot ;
345421stream .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: " ,
426503long_options ,& option_index ))!= -1 )
427504{
428505switch (c )
@@ -472,6 +549,15 @@ main(int argc, char **argv)
472549case 'v' :
473550verbose ++ ;
474551break ;
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 */
476562case 1 :
477563do_create_slot = true;
@@ -538,6 +624,16 @@ main(int argc, char **argv)
538624exit (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 */