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)
7281printf (_ (" -d, --disable disable data checksums\n" ));
7382printf (_ (" -e, --enable enable data checksums\n" ));
7483printf (_ (" -N, --no-sync do not wait for changes to be written safely to disk\n" ));
84+ printf (_ (" -P, --progress show progress information\n" ));
7585printf (_ (" -v, --verbose output verbose messages\n" ));
7686printf (_ (" -r RELFILENODE check only relation with specified relfilenode\n" ));
7787printf (_ (" -V, --version output version information, then exit\n" ));
@@ -98,6 +108,52 @@ static const char *const skip[] = {
98108NULL ,
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)
153209continue ;
154210
155211csum = pg_checksum_page (buf .data ,blockno + segmentno * RELSEG_SIZE );
212+ current_size += r ;
156213if (mode == PG_MODE_CHECK )
157214{
158215if (csum != header -> pd_checksum )
@@ -183,6 +240,9 @@ scan_file(const char *fn, BlockNumber segmentno)
183240exit (1 );
184241}
185242}
243+
244+ if (showprogress )
245+ progress_report (false);
186246}
187247
188248if (verbose )
@@ -196,9 +256,17 @@ scan_file(const char *fn, BlockNumber segmentno)
196256close (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 ;
202270char path [MAXPGPATH ];
203271DIR * dir ;
204272struct dirent * de ;
@@ -275,16 +343,24 @@ scan_directory(const char *basedir, const char *subdir)
275343/* Relfilenode not to be included */
276344continue ;
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
281356else if (S_ISDIR (st .st_mode )|| S_ISLNK (st .st_mode ))
282357#else
283358else 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}
287362closedir (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{
328405switch (c )
329406{
@@ -353,6 +430,9 @@ main(int argc, char *argv[])
353430}
354431only_relfilenode = pstrdup (optarg );
355432break ;
433+ case 'P' :
434+ showprogress = true;
435+ break ;
356436default :
357437fprintf (stderr ,_ ("Try \"%s --help\" for more information.\n" ),progname );
358438exit (1 );
@@ -447,9 +527,27 @@ main(int argc, char *argv[])
447527/* Operate on all files if checking or enabling checksums */
448528if (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
454552printf (_ ("Checksum operation completed\n" ));
455553printf (_ ("Files scanned: %s\n" ),psprintf (INT64_FORMAT ,files ));