15
15
#include "postgres_fe.h"
16
16
17
17
#include <dirent.h>
18
+ #include <time.h>
18
19
#include <sys/stat.h>
19
20
#include <unistd.h>
20
21
@@ -38,6 +39,7 @@ static ControlFileData *ControlFile;
38
39
static char * only_relfilenode = NULL ;
39
40
static bool do_sync = true;
40
41
static bool verbose = false;
42
+ static bool showprogress = false;
41
43
42
44
typedef enum
43
45
{
@@ -60,6 +62,13 @@ static PgChecksumMode mode = PG_MODE_CHECK;
60
62
61
63
static const char * progname ;
62
64
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
+
63
72
static void
64
73
usage (void )
65
74
{
@@ -72,6 +81,7 @@ usage(void)
72
81
printf (_ (" -d, --disable disable data checksums\n" ));
73
82
printf (_ (" -e, --enable enable data checksums\n" ));
74
83
printf (_ (" -N, --no-sync do not wait for changes to be written safely to disk\n" ));
84
+ printf (_ (" -P, --progress show progress information\n" ));
75
85
printf (_ (" -v, --verbose output verbose messages\n" ));
76
86
printf (_ (" -r RELFILENODE check only relation with specified relfilenode\n" ));
77
87
printf (_ (" -V, --version output version information, then exit\n" ));
@@ -98,6 +108,52 @@ static const char *const skip[] = {
98
108
NULL ,
99
109
};
100
110
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
+
101
157
static bool
102
158
skipfile (const char * fn )
103
159
{
@@ -153,6 +209,7 @@ scan_file(const char *fn, BlockNumber segmentno)
153
209
continue ;
154
210
155
211
csum = pg_checksum_page (buf .data , blockno + segmentno * RELSEG_SIZE );
212
+ current_size += r ;
156
213
if (mode == PG_MODE_CHECK )
157
214
{
158
215
if (csum != header -> pd_checksum )
@@ -183,6 +240,9 @@ scan_file(const char *fn, BlockNumber segmentno)
183
240
exit (1 );
184
241
}
185
242
}
243
+
244
+ if (showprogress )
245
+ progress_report (false);
186
246
}
187
247
188
248
if (verbose )
@@ -196,9 +256,17 @@ scan_file(const char *fn, BlockNumber segmentno)
196
256
close (f );
197
257
}
198
258
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 )
201
268
{
269
+ int64 dirsize = 0 ;
202
270
char path [MAXPGPATH ];
203
271
DIR * dir ;
204
272
struct dirent * de ;
@@ -275,16 +343,24 @@ scan_directory(const char *basedir, const char *subdir)
275
343
/* Relfilenode not to be included */
276
344
continue ;
277
345
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 );
279
354
}
280
355
#ifndef WIN32
281
356
else if (S_ISDIR (st .st_mode ) || S_ISLNK (st .st_mode ))
282
357
#else
283
358
else if (S_ISDIR (st .st_mode ) || pgwin32_is_junction (fn ))
284
359
#endif
285
- scan_directory (path , de -> d_name );
360
+ dirsize += scan_directory (path , de -> d_name , sizeonly );
286
361
}
287
362
closedir (dir );
363
+ return dirsize ;
288
364
}
289
365
290
366
int
@@ -296,6 +372,7 @@ main(int argc, char *argv[])
296
372
{"disable" , no_argument , NULL , 'd' },
297
373
{"enable" , no_argument , NULL , 'e' },
298
374
{"no-sync" , no_argument , NULL , 'N' },
375
+ {"progress" , no_argument , NULL , 'P' },
299
376
{"verbose" , no_argument , NULL , 'v' },
300
377
{NULL , 0 , NULL , 0 }
301
378
};
@@ -323,7 +400,7 @@ main(int argc, char *argv[])
323
400
}
324
401
}
325
402
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 )
327
404
{
328
405
switch (c )
329
406
{
@@ -353,6 +430,9 @@ main(int argc, char *argv[])
353
430
}
354
431
only_relfilenode = pstrdup (optarg );
355
432
break ;
433
+ case 'P' :
434
+ showprogress = true;
435
+ break ;
356
436
default :
357
437
fprintf (stderr , _ ("Try \"%s --help\" for more information.\n" ), progname );
358
438
exit (1 );
@@ -447,9 +527,27 @@ main(int argc, char *argv[])
447
527
/* Operate on all files if checking or enabling checksums */
448
528
if (mode == PG_MODE_CHECK || mode == PG_MODE_ENABLE )
449
529
{
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
+ }
453
551
454
552
printf (_ ("Checksum operation completed\n" ));
455
553
printf (_ ("Files scanned: %s\n" ), psprintf (INT64_FORMAT , files ));
0 commit comments