1515#include "postgres_fe.h"
1616
1717#include <dirent.h>
18+ #include <time.h>
1819#include <sys/stat.h>
1920#include <unistd.h>
2021
@@ -38,6 +39,7 @@ static ControlFileData *ControlFile;
3839static char * only_relfilenode = NULL ;
3940static bool do_sync = true;
4041static bool verbose = false;
42+ static bool showprogress = false;
4143
4244typedef enum
4345{
@@ -60,6 +62,13 @@ static PgChecksumMode mode = PG_MODE_CHECK;
6062
6163static const char * progname ;
6264
65+ /*
66+ * Progress status information.
67+ */
68+ int64 total_size = 0 ;
69+ int64 current_size = 0 ;
70+ static pg_time_t last_progress_report = 0 ;
71+
6372static void
6473usage (void )
6574{
@@ -72,6 +81,7 @@ usage(void)
7281 printf (_ (" -d, --disable disable data checksums\n" ));
7382 printf (_ (" -e, --enable enable data checksums\n" ));
7483 printf (_ (" -N, --no-sync do not wait for changes to be written safely to disk\n" ));
84+ printf (_ (" -P, --progress show progress information\n" ));
7585 printf (_ (" -v, --verbose output verbose messages\n" ));
7686 printf (_ (" -r RELFILENODE check only relation with specified relfilenode\n" ));
7787 printf (_ (" -V, --version output version information, then exit\n" ));
@@ -98,6 +108,52 @@ static const char *const skip[] = {
98108 NULL ,
99109};
100110
111+ /*
112+ * Report current progress status. Parts borrowed from
113+ * src/bin/pg_basebackup.c.
114+ */
115+ static void
116+ progress_report (bool force )
117+ {
118+ int percent ;
119+ char total_size_str [32 ];
120+ char current_size_str [32 ];
121+ pg_time_t now ;
122+
123+ Assert (showprogress );
124+
125+ now = time (NULL );
126+ if (now == last_progress_report && !force )
127+ return ; /* Max once per second */
128+
129+ /* Save current time */
130+ last_progress_report = now ;
131+
132+ /* Adjust total size if current_size is larger */
133+ if (current_size > total_size )
134+ total_size = current_size ;
135+
136+ /* Calculate current percentage of size done */
137+ percent = total_size ? (int ) ((current_size ) * 100 / total_size ) : 0 ;
138+
139+ snprintf (total_size_str , sizeof (total_size_str ), INT64_FORMAT ,
140+ total_size / (1024 * 1024 ));
141+ snprintf (current_size_str , sizeof (current_size_str ), INT64_FORMAT ,
142+ current_size / (1024 * 1024 ));
143+
144+ /*
145+ * Separate step to keep platform-dependent format code out of
146+ * translatable strings. And we only test for INT64_FORMAT availability
147+ * in snprintf, not fprintf.
148+ */
149+ fprintf (stderr , "%*s/%s MB (%d%%) computed" ,
150+ (int ) strlen (current_size_str ), current_size_str , total_size_str ,
151+ percent );
152+
153+ /* Stay on the same line if reporting to a terminal */
154+ fprintf (stderr , isatty (fileno (stderr )) ? "\r" : "\n" );
155+ }
156+
101157static bool
102158skipfile (const char * fn )
103159{
@@ -153,6 +209,7 @@ scan_file(const char *fn, BlockNumber segmentno)
153209 continue ;
154210
155211 csum = pg_checksum_page (buf .data , blockno + segmentno * RELSEG_SIZE );
212+ current_size += r ;
156213 if (mode == PG_MODE_CHECK )
157214 {
158215 if (csum != header -> pd_checksum )
@@ -183,6 +240,9 @@ scan_file(const char *fn, BlockNumber segmentno)
183240 exit (1 );
184241 }
185242 }
243+
244+ if (showprogress )
245+ progress_report (false);
186246 }
187247
188248 if (verbose )
@@ -196,9 +256,17 @@ scan_file(const char *fn, BlockNumber segmentno)
196256 close (f );
197257}
198258
199- static void
200- scan_directory (const char * basedir , const char * subdir )
259+ /*
260+ * Scan the given directory for items which can be checksummed and
261+ * operate on each one of them. If "sizeonly" is true, the size of
262+ * all the items which have checksums is computed and returned back
263+ * to the caller without operating on the files. This is used to compile
264+ * the total size of the data directory for progress reports.
265+ */
266+ static int64
267+ scan_directory (const char * basedir , const char * subdir , bool sizeonly )
201268{
269+ int64 dirsize = 0 ;
202270 char path [MAXPGPATH ];
203271 DIR * dir ;
204272 struct dirent * de ;
@@ -275,16 +343,24 @@ scan_directory(const char *basedir, const char *subdir)
275343 /* Relfilenode not to be included */
276344 continue ;
277345
278- scan_file (fn , segmentno );
346+ dirsize += st .st_size ;
347+
348+ /*
349+ * No need to work on the file when calculating only the size of
350+ * the items in the data folder.
351+ */
352+ if (!sizeonly )
353+ scan_file (fn , segmentno );
279354 }
280355#ifndef WIN32
281356 else if (S_ISDIR (st .st_mode ) || S_ISLNK (st .st_mode ))
282357#else
283358 else if (S_ISDIR (st .st_mode ) || pgwin32_is_junction (fn ))
284359#endif
285- scan_directory (path , de -> d_name );
360+ dirsize += scan_directory (path , de -> d_name , sizeonly );
286361 }
287362 closedir (dir );
363+ return dirsize ;
288364}
289365
290366int
@@ -296,6 +372,7 @@ main(int argc, char *argv[])
296372 {"disable" , no_argument , NULL , 'd' },
297373 {"enable" , no_argument , NULL , 'e' },
298374 {"no-sync" , no_argument , NULL , 'N' },
375+ {"progress" , no_argument , NULL , 'P' },
299376 {"verbose" , no_argument , NULL , 'v' },
300377 {NULL , 0 , NULL , 0 }
301378 };
@@ -323,7 +400,7 @@ main(int argc, char *argv[])
323400 }
324401 }
325402
326- while ((c = getopt_long (argc , argv , "cD:deNr :v" , long_options , & option_index )) != -1 )
403+ while ((c = getopt_long (argc , argv , "cD:deNPr :v" , long_options , & option_index )) != -1 )
327404 {
328405 switch (c )
329406 {
@@ -353,6 +430,9 @@ main(int argc, char *argv[])
353430 }
354431 only_relfilenode = pstrdup (optarg );
355432 break ;
433+ case 'P' :
434+ showprogress = true;
435+ break ;
356436 default :
357437 fprintf (stderr , _ ("Try \"%s --help\" for more information.\n" ), progname );
358438 exit (1 );
@@ -447,9 +527,27 @@ main(int argc, char *argv[])
447527 /* Operate on all files if checking or enabling checksums */
448528 if (mode == PG_MODE_CHECK || mode == PG_MODE_ENABLE )
449529 {
450- scan_directory (DataDir , "global" );
451- scan_directory (DataDir , "base" );
452- scan_directory (DataDir , "pg_tblspc" );
530+ /*
531+ * If progress status information is requested, we need to scan the
532+ * directory tree twice: once to know how much total data needs to be
533+ * processed and once to do the real work.
534+ */
535+ if (showprogress )
536+ {
537+ total_size = scan_directory (DataDir , "global" , true);
538+ total_size += scan_directory (DataDir , "base" , true);
539+ total_size += scan_directory (DataDir , "pg_tblspc" , true);
540+ }
541+
542+ (void ) scan_directory (DataDir , "global" , false);
543+ (void ) scan_directory (DataDir , "base" , false);
544+ (void ) scan_directory (DataDir , "pg_tblspc" , false);
545+
546+ if (showprogress )
547+ {
548+ progress_report (true);
549+ fprintf (stderr , "\n" ); /* Need to move to next line */
550+ }
453551
454552 printf (_ ("Checksum operation completed\n" ));
455553 printf (_ ("Files scanned: %s\n" ), psprintf (INT64_FORMAT , files ));
0 commit comments