@@ -43,6 +43,8 @@ typedef struct vacuumingOptions
43
43
bool freeze ;
44
44
bool disable_page_skipping ;
45
45
bool skip_locked ;
46
+ int min_xid_age ;
47
+ int min_mxid_age ;
46
48
} vacuumingOptions ;
47
49
48
50
@@ -113,6 +115,8 @@ main(int argc, char *argv[])
113
115
{"analyze-in-stages" , no_argument , NULL , 3 },
114
116
{"disable-page-skipping" , no_argument , NULL , 4 },
115
117
{"skip-locked" , no_argument , NULL , 5 },
118
+ {"min-xid-age" , required_argument , NULL , 6 },
119
+ {"min-mxid-age" , required_argument , NULL , 7 },
116
120
{NULL , 0 , NULL , 0 }
117
121
};
118
122
@@ -222,6 +226,24 @@ main(int argc, char *argv[])
222
226
case 5 :
223
227
vacopts .skip_locked = true;
224
228
break ;
229
+ case 6 :
230
+ vacopts .min_xid_age = atoi (optarg );
231
+ if (vacopts .min_xid_age <= 0 )
232
+ {
233
+ fprintf (stderr , _ ("%s: minimum transaction ID age must be at least 1\n" ),
234
+ progname );
235
+ exit (1 );
236
+ }
237
+ break ;
238
+ case 7 :
239
+ vacopts .min_mxid_age = atoi (optarg );
240
+ if (vacopts .min_mxid_age <= 0 )
241
+ {
242
+ fprintf (stderr , _ ("%s: minimum multixact ID age must be at least 1\n" ),
243
+ progname );
244
+ exit (1 );
245
+ }
246
+ break ;
225
247
default :
226
248
fprintf (stderr , _ ("Try \"%s --help\" for more information.\n" ), progname );
227
249
exit (1 );
@@ -370,6 +392,7 @@ vacuum_one_database(const char *dbname, vacuumingOptions *vacopts,
370
392
bool failed = false;
371
393
bool parallel = concurrentCons > 1 ;
372
394
bool tables_listed = false;
395
+ bool has_where = false;
373
396
const char * stage_commands [] = {
374
397
"SET default_statistics_target=1; SET vacuum_cost_delay=0;" ,
375
398
"SET default_statistics_target=10; RESET vacuum_cost_delay;" ,
@@ -403,6 +426,20 @@ vacuum_one_database(const char *dbname, vacuumingOptions *vacopts,
403
426
exit (1 );
404
427
}
405
428
429
+ if (vacopts -> min_xid_age != 0 && PQserverVersion (conn ) < 90600 )
430
+ {
431
+ fprintf (stderr , _ ("%s: cannot use the \"%s\" option on server versions older than PostgreSQL 9.6\n" ),
432
+ progname , "--min-xid-age" );
433
+ exit (1 );
434
+ }
435
+
436
+ if (vacopts -> min_mxid_age != 0 && PQserverVersion (conn ) < 90600 )
437
+ {
438
+ fprintf (stderr , _ ("%s: cannot use the \"%s\" option on server versions older than PostgreSQL 9.6\n" ),
439
+ progname , "--min-mxid-age" );
440
+ exit (1 );
441
+ }
442
+
406
443
if (!quiet )
407
444
{
408
445
if (stage != ANALYZE_NO_STAGE )
@@ -477,7 +514,9 @@ vacuum_one_database(const char *dbname, vacuumingOptions *vacopts,
477
514
appendPQExpBuffer (& catalog_query ,
478
515
" FROM pg_catalog.pg_class c\n"
479
516
" JOIN pg_catalog.pg_namespace ns"
480
- " ON c.relnamespace OPERATOR(pg_catalog.=) ns.oid\n" );
517
+ " ON c.relnamespace OPERATOR(pg_catalog.=) ns.oid\n"
518
+ " LEFT JOIN pg_catalog.pg_class t"
519
+ " ON c.reltoastrelid OPERATOR(pg_catalog.=) t.oid\n" );
481
520
482
521
/* Used to match the tables listed by the user */
483
522
if (tables_listed )
@@ -491,9 +530,43 @@ vacuum_one_database(const char *dbname, vacuumingOptions *vacopts,
491
530
* processed in which case the user will know about it.
492
531
*/
493
532
if (!tables_listed )
533
+ {
494
534
appendPQExpBuffer (& catalog_query , " WHERE c.relkind OPERATOR(pg_catalog.=) ANY (array["
495
535
CppAsString2 (RELKIND_RELATION ) ", "
496
536
CppAsString2 (RELKIND_MATVIEW ) "])\n" );
537
+ has_where = true;
538
+ }
539
+
540
+ /*
541
+ * For --min-xid-age and --min-mxid-age, the age of the relation is the
542
+ * greatest of the ages of the main relation and its associated TOAST
543
+ * table. The commands generated by vacuumdb will also process the TOAST
544
+ * table for the relation if necessary, so it does not need to be
545
+ * considered separately.
546
+ */
547
+ if (vacopts -> min_xid_age != 0 )
548
+ {
549
+ appendPQExpBuffer (& catalog_query ,
550
+ " %s GREATEST(pg_catalog.age(c.relfrozenxid),"
551
+ " pg_catalog.age(t.relfrozenxid)) "
552
+ " OPERATOR(pg_catalog.>=) '%d'::pg_catalog.int4\n"
553
+ " AND c.relfrozenxid OPERATOR(pg_catalog.!=)"
554
+ " '0'::pg_catalog.xid\n" ,
555
+ has_where ? "AND" : "WHERE" , vacopts -> min_xid_age );
556
+ has_where = true;
557
+ }
558
+
559
+ if (vacopts -> min_mxid_age != 0 )
560
+ {
561
+ appendPQExpBuffer (& catalog_query ,
562
+ " %s GREATEST(pg_catalog.mxid_age(c.relminmxid),"
563
+ " pg_catalog.mxid_age(t.relminmxid)) OPERATOR(pg_catalog.>=)"
564
+ " '%d'::pg_catalog.int4\n"
565
+ " AND c.relminmxid OPERATOR(pg_catalog.!=)"
566
+ " '0'::pg_catalog.xid\n" ,
567
+ has_where ? "AND" : "WHERE" , vacopts -> min_mxid_age );
568
+ has_where = true;
569
+ }
497
570
498
571
/*
499
572
* Execute the catalog query. We use the default search_path for this
@@ -1152,6 +1225,8 @@ help(const char *progname)
1152
1225
printf (_ (" -f, --full do full vacuuming\n" ));
1153
1226
printf (_ (" -F, --freeze freeze row transaction information\n" ));
1154
1227
printf (_ (" -j, --jobs=NUM use this many concurrent connections to vacuum\n" ));
1228
+ printf (_ (" --min-mxid-age=MXID_AGE minimum multixact ID age of tables to vacuum\n" ));
1229
+ printf (_ (" --min-xid-age=XID_AGE minimum transaction ID age of tables to vacuum\n" ));
1155
1230
printf (_ (" -q, --quiet don't write any messages\n" ));
1156
1231
printf (_ (" --skip-locked skip relations that cannot be immediately locked\n" ));
1157
1232
printf (_ (" -t, --table='TABLE[(COLUMNS)]' vacuum specific table(s) only\n" ));
0 commit comments