@ -43,6 +43,8 @@ typedef struct vacuumingOptions
bool freeze ;
bool freeze ;
bool disable_page_skipping ;
bool disable_page_skipping ;
bool skip_locked ;
bool skip_locked ;
int min_xid_age ;
int min_mxid_age ;
} vacuumingOptions ;
} vacuumingOptions ;
@ -113,6 +115,8 @@ main(int argc, char *argv[])
{ " analyze-in-stages " , no_argument , NULL , 3 } ,
{ " analyze-in-stages " , no_argument , NULL , 3 } ,
{ " disable-page-skipping " , no_argument , NULL , 4 } ,
{ " disable-page-skipping " , no_argument , NULL , 4 } ,
{ " skip-locked " , no_argument , NULL , 5 } ,
{ " skip-locked " , no_argument , NULL , 5 } ,
{ " min-xid-age " , required_argument , NULL , 6 } ,
{ " min-mxid-age " , required_argument , NULL , 7 } ,
{ NULL , 0 , NULL , 0 }
{ NULL , 0 , NULL , 0 }
} ;
} ;
@ -222,6 +226,24 @@ main(int argc, char *argv[])
case 5 :
case 5 :
vacopts . skip_locked = true ;
vacopts . skip_locked = true ;
break ;
break ;
case 6 :
vacopts . min_xid_age = atoi ( optarg ) ;
if ( vacopts . min_xid_age < = 0 )
{
fprintf ( stderr , _ ( " %s: minimum transaction ID age must be at least 1 \n " ) ,
progname ) ;
exit ( 1 ) ;
}
break ;
case 7 :
vacopts . min_mxid_age = atoi ( optarg ) ;
if ( vacopts . min_mxid_age < = 0 )
{
fprintf ( stderr , _ ( " %s: minimum multixact ID age must be at least 1 \n " ) ,
progname ) ;
exit ( 1 ) ;
}
break ;
default :
default :
fprintf ( stderr , _ ( " Try \" %s --help \" for more information. \n " ) , progname ) ;
fprintf ( stderr , _ ( " Try \" %s --help \" for more information. \n " ) , progname ) ;
exit ( 1 ) ;
exit ( 1 ) ;
@ -370,6 +392,7 @@ vacuum_one_database(const char *dbname, vacuumingOptions *vacopts,
bool failed = false ;
bool failed = false ;
bool parallel = concurrentCons > 1 ;
bool parallel = concurrentCons > 1 ;
bool tables_listed = false ;
bool tables_listed = false ;
bool has_where = false ;
const char * stage_commands [ ] = {
const char * stage_commands [ ] = {
" SET default_statistics_target=1; SET vacuum_cost_delay=0; " ,
" SET default_statistics_target=1; SET vacuum_cost_delay=0; " ,
" SET default_statistics_target=10; RESET vacuum_cost_delay; " ,
" SET default_statistics_target=10; RESET vacuum_cost_delay; " ,
@ -403,6 +426,20 @@ vacuum_one_database(const char *dbname, vacuumingOptions *vacopts,
exit ( 1 ) ;
exit ( 1 ) ;
}
}
if ( vacopts - > min_xid_age ! = 0 & & PQserverVersion ( conn ) < 90600 )
{
fprintf ( stderr , _ ( " %s: cannot use the \" %s \" option on server versions older than PostgreSQL 9.6 \n " ) ,
progname , " --min-xid-age " ) ;
exit ( 1 ) ;
}
if ( vacopts - > min_mxid_age ! = 0 & & PQserverVersion ( conn ) < 90600 )
{
fprintf ( stderr , _ ( " %s: cannot use the \" %s \" option on server versions older than PostgreSQL 9.6 \n " ) ,
progname , " --min-mxid-age " ) ;
exit ( 1 ) ;
}
if ( ! quiet )
if ( ! quiet )
{
{
if ( stage ! = ANALYZE_NO_STAGE )
if ( stage ! = ANALYZE_NO_STAGE )
@ -477,7 +514,9 @@ vacuum_one_database(const char *dbname, vacuumingOptions *vacopts,
appendPQExpBuffer ( & catalog_query ,
appendPQExpBuffer ( & catalog_query ,
" FROM pg_catalog.pg_class c \n "
" FROM pg_catalog.pg_class c \n "
" JOIN pg_catalog.pg_namespace ns "
" JOIN pg_catalog.pg_namespace ns "
" ON c.relnamespace OPERATOR(pg_catalog.=) ns.oid \n " ) ;
" ON c.relnamespace OPERATOR(pg_catalog.=) ns.oid \n "
" LEFT JOIN pg_catalog.pg_class t "
" ON c.reltoastrelid OPERATOR(pg_catalog.=) t.oid \n " ) ;
/* Used to match the tables listed by the user */
/* Used to match the tables listed by the user */
if ( tables_listed )
if ( tables_listed )
@ -491,9 +530,43 @@ vacuum_one_database(const char *dbname, vacuumingOptions *vacopts,
* processed in which case the user will know about it .
* processed in which case the user will know about it .
*/
*/
if ( ! tables_listed )
if ( ! tables_listed )
{
appendPQExpBuffer ( & catalog_query , " WHERE c.relkind OPERATOR(pg_catalog.=) ANY (array[ "
appendPQExpBuffer ( & catalog_query , " WHERE c.relkind OPERATOR(pg_catalog.=) ANY (array[ "
CppAsString2 ( RELKIND_RELATION ) " , "
CppAsString2 ( RELKIND_RELATION ) " , "
CppAsString2 ( RELKIND_MATVIEW ) " ]) \n " ) ;
CppAsString2 ( RELKIND_MATVIEW ) " ]) \n " ) ;
has_where = true ;
}
/*
* For - - min - xid - age and - - min - mxid - age , the age of the relation is the
* greatest of the ages of the main relation and its associated TOAST
* table . The commands generated by vacuumdb will also process the TOAST
* table for the relation if necessary , so it does not need to be
* considered separately .
*/
if ( vacopts - > min_xid_age ! = 0 )
{
appendPQExpBuffer ( & catalog_query ,
" %s GREATEST(pg_catalog.age(c.relfrozenxid), "
" pg_catalog.age(t.relfrozenxid)) "
" OPERATOR(pg_catalog.>=) '%d'::pg_catalog.int4 \n "
" AND c.relfrozenxid OPERATOR(pg_catalog.!=) "
" '0'::pg_catalog.xid \n " ,
has_where ? " AND " : " WHERE " , vacopts - > min_xid_age ) ;
has_where = true ;
}
if ( vacopts - > min_mxid_age ! = 0 )
{
appendPQExpBuffer ( & catalog_query ,
" %s GREATEST(pg_catalog.mxid_age(c.relminmxid), "
" pg_catalog.mxid_age(t.relminmxid)) OPERATOR(pg_catalog.>=) "
" '%d'::pg_catalog.int4 \n "
" AND c.relminmxid OPERATOR(pg_catalog.!=) "
" '0'::pg_catalog.xid \n " ,
has_where ? " AND " : " WHERE " , vacopts - > min_mxid_age ) ;
has_where = true ;
}
/*
/*
* Execute the catalog query . We use the default search_path for this
* Execute the catalog query . We use the default search_path for this
@ -1152,6 +1225,8 @@ help(const char *progname)
printf ( _ ( " -f, --full do full vacuuming \n " ) ) ;
printf ( _ ( " -f, --full do full vacuuming \n " ) ) ;
printf ( _ ( " -F, --freeze freeze row transaction information \n " ) ) ;
printf ( _ ( " -F, --freeze freeze row transaction information \n " ) ) ;
printf ( _ ( " -j, --jobs=NUM use this many concurrent connections to vacuum \n " ) ) ;
printf ( _ ( " -j, --jobs=NUM use this many concurrent connections to vacuum \n " ) ) ;
printf ( _ ( " --min-mxid-age=MXID_AGE minimum multixact ID age of tables to vacuum \n " ) ) ;
printf ( _ ( " --min-xid-age=XID_AGE minimum transaction ID age of tables to vacuum \n " ) ) ;
printf ( _ ( " -q, --quiet don't write any messages \n " ) ) ;
printf ( _ ( " -q, --quiet don't write any messages \n " ) ) ;
printf ( _ ( " --skip-locked skip relations that cannot be immediately locked \n " ) ) ;
printf ( _ ( " --skip-locked skip relations that cannot be immediately locked \n " ) ) ;
printf ( _ ( " -t, --table='TABLE[(COLUMNS)]' vacuum specific table(s) only \n " ) ) ;
printf ( _ ( " -t, --table='TABLE[(COLUMNS)]' vacuum specific table(s) only \n " ) ) ;