From 2051db2600566dc88040cee97868469aa35440d7 Mon Sep 17 00:00:00 2001 From: Alena Rybakina Date: Mon, 1 Sep 2025 21:43:33 +0300 Subject: [PATCH 3/5] Machinery for grabbing an extended vacuum statistics on databases. Database vacuum statistics information is the collected general vacuum statistics indexes and tables owned by the databases, which they belong to. In addition to the fact that there are far fewer databases in a system than relations, vacuum statistics for a database contain fewer statistics than relations, but they are enough to indicate that something may be wrong in the system and prompt the administrator to enable extended monitoring for relations. So, buffer, wal, statistics of I/O time of read and writen blocks statistics will be observed because they are collected for both tables, indexes. In addition, we show the number of errors caught during operation of the vacuum only for the error level. wraparound_failsafe_count is a number of times when the vacuum starts urgent cleanup to prevent wraparound problem which is critical for the database. Authors: Alena Rybakina , Andrei Lepikhov , Andrei Zubkov Reviewed-by: Dilip Kumar , Masahiko Sawada , Ilia Evdokimov , jian he , Kirill Reshke , Alexander Korotkov , Jim Nasby , Sami Imseih --- src/backend/access/heap/vacuumlazy.c | 2 +- src/backend/catalog/system_views.sql | 26 +++++++- src/backend/utils/activity/pgstat_database.c | 1 + src/backend/utils/activity/pgstat_relation.c | 13 +++- src/backend/utils/adt/pgstatfuncs.c | 62 ++++++++++++++++++- src/include/catalog/pg_proc.dat | 13 +++- src/include/pgstat.h | 7 +-- .../vacuum-extending-in-repetable-read.spec | 6 ++ .../t/050_vacuum_extending_basic_test.pl | 49 ++++++++++++--- .../t/051_vacuum_extending_freeze_test.pl | 48 +++++--------- src/test/regress/expected/rules.out | 17 +++++ 11 files changed, 195 insertions(+), 49 deletions(-) diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c index 719ce90d96d..fcd92a43dda 100644 --- a/src/backend/access/heap/vacuumlazy.c +++ b/src/backend/access/heap/vacuumlazy.c @@ -663,7 +663,7 @@ accumulate_heap_vacuum_statistics(LVRelState *vacrel, ExtVacReport * extVacStats extVacStats->table.missed_dead_tuples = vacrel->missed_dead_tuples; extVacStats->table.missed_dead_pages = vacrel->missed_dead_pages; extVacStats->table.index_vacuum_count = vacrel->num_index_scans; - extVacStats->table.wraparound_failsafe_count = vacrel->wraparound_failsafe_count; + extVacStats->wraparound_failsafe_count = vacrel->wraparound_failsafe_count; extVacStats->blk_read_time -= vacrel->extVacReportIdx.blk_read_time; extVacStats->blk_write_time -= vacrel->extVacReportIdx.blk_write_time; diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index 47b6a00d297..dc86b1ee212 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -1533,4 +1533,28 @@ FROM pg_class rel JOIN pg_namespace ns ON ns.oid = rel.relnamespace, LATERAL pg_stat_get_vacuum_indexes(rel.oid) stats -WHERE rel.relkind = 'i'; \ No newline at end of file +WHERE rel.relkind = 'i'; + +CREATE VIEW pg_stat_vacuum_database AS +SELECT + db.oid as dboid, + db.datname AS dbname, + + stats.db_blks_read AS db_blks_read, + stats.db_blks_hit AS db_blks_hit, + stats.total_blks_dirtied AS total_blks_dirtied, + stats.total_blks_written AS total_blks_written, + + stats.wal_records AS wal_records, + stats.wal_fpi AS wal_fpi, + stats.wal_bytes AS wal_bytes, + + stats.blk_read_time AS blk_read_time, + stats.blk_write_time AS blk_write_time, + + stats.delay_time AS delay_time, + stats.total_time AS total_time, + stats.wraparound_failsafe AS wraparound_failsafe +FROM + pg_database db, + LATERAL pg_stat_get_vacuum_database(db.oid) stats; \ No newline at end of file diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c index b31f20d41bc..65207d30378 100644 --- a/src/backend/utils/activity/pgstat_database.c +++ b/src/backend/utils/activity/pgstat_database.c @@ -485,6 +485,7 @@ pgstat_database_flush_cb(PgStat_EntryRef *entry_ref, bool nowait) pgstat_unlock_entry(entry_ref); memset(pendingent, 0, sizeof(*pendingent)); + memset(&(pendingent)->vacuum_ext, 0, sizeof(ExtVacReport)); return true; } diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c index 4bd6afc3794..2675c541369 100644 --- a/src/backend/utils/activity/pgstat_relation.c +++ b/src/backend/utils/activity/pgstat_relation.c @@ -215,6 +215,7 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples, PgStat_EntryRef *entry_ref; PgStatShared_Relation *shtabentry; PgStat_StatTabEntry *tabentry; + PgStatShared_Database *dbentry; Oid dboid = (rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId); TimestampTz ts; PgStat_Counter elapsedtime; @@ -273,6 +274,16 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples, */ pgstat_flush_io(false); (void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO); + + if (dboid != InvalidOid) + { + entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_DATABASE, + dboid, InvalidOid, false); + dbentry = (PgStatShared_Database *) entry_ref->shared_stats; + + pgstat_accumulate_extvac_stats(&dbentry->stats.vacuum_ext, params, false); + pgstat_unlock_entry(entry_ref); + } } /* @@ -1032,6 +1043,7 @@ pgstat_accumulate_extvac_stats(ExtVacReport * dst, ExtVacReport * src, dst->blk_write_time += src->blk_write_time; dst->delay_time += src->delay_time; dst->total_time += src->total_time; + dst->wraparound_failsafe_count += src->wraparound_failsafe_count; if (!accumulate_reltype_specific_info) return; @@ -1059,7 +1071,6 @@ pgstat_accumulate_extvac_stats(ExtVacReport * dst, ExtVacReport * src, dst->table.index_vacuum_count += src->table.index_vacuum_count; dst->table.missed_dead_pages += src->table.missed_dead_pages; dst->table.missed_dead_tuples += src->table.missed_dead_tuples; - dst->table.wraparound_failsafe_count += src->table.wraparound_failsafe_count; } else if (dst->type == PGSTAT_EXTVAC_INDEX) { diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c index 755751c3b46..4e2714f2e6a 100644 --- a/src/backend/utils/adt/pgstatfuncs.c +++ b/src/backend/utils/adt/pgstatfuncs.c @@ -2371,7 +2371,7 @@ pg_stat_get_vacuum_tables(PG_FUNCTION_ARGS) values[i++] = Int64GetDatum(extvacuum->table.recently_dead_tuples); values[i++] = Int64GetDatum(extvacuum->table.missed_dead_tuples); - values[i++] = Int32GetDatum(extvacuum->table.wraparound_failsafe_count); + values[i++] = Int32GetDatum(extvacuum->wraparound_failsafe_count); values[i++] = Int64GetDatum(extvacuum->table.index_vacuum_count); values[i++] = Int64GetDatum(extvacuum->wal_records); @@ -2463,3 +2463,63 @@ pg_stat_get_vacuum_indexes(PG_FUNCTION_ARGS) /* Returns the record as Datum */ PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls))); } + +Datum +pg_stat_get_vacuum_database(PG_FUNCTION_ARGS) +{ +#define PG_STAT_GET_VACUUM_DATABASE_STATS_COLS 14 + + Oid dbid = PG_GETARG_OID(0); + PgStat_StatDBEntry *dbentry; + ExtVacReport *extvacuum; + TupleDesc tupdesc; + Datum values[PG_STAT_GET_VACUUM_DATABASE_STATS_COLS] = {0}; + bool nulls[PG_STAT_GET_VACUUM_DATABASE_STATS_COLS] = {0}; + char buf[256]; + int i = 0; + + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + + dbentry = pgstat_fetch_stat_dbentry(dbid); + + if (dbentry == NULL) + { + InitMaterializedSRF(fcinfo, 0); + PG_RETURN_VOID(); + } + else + { + extvacuum = &(dbentry->vacuum_ext); + } + + i = 0; + + values[i++] = ObjectIdGetDatum(dbid); + + values[i++] = Int64GetDatum(extvacuum->total_blks_read); + values[i++] = Int64GetDatum(extvacuum->total_blks_hit); + values[i++] = Int64GetDatum(extvacuum->total_blks_dirtied); + values[i++] = Int64GetDatum(extvacuum->total_blks_written); + + values[i++] = Int64GetDatum(extvacuum->wal_records); + values[i++] = Int64GetDatum(extvacuum->wal_fpi); + + /* Convert to numeric, like pg_stat_statements */ + snprintf(buf, sizeof buf, UINT64_FORMAT, extvacuum->wal_bytes); + values[i++] = DirectFunctionCall3(numeric_in, + CStringGetDatum(buf), + ObjectIdGetDatum(0), + Int32GetDatum(-1)); + + values[i++] = Float8GetDatum(extvacuum->blk_read_time); + values[i++] = Float8GetDatum(extvacuum->blk_write_time); + values[i++] = Float8GetDatum(extvacuum->delay_time); + values[i++] = Float8GetDatum(extvacuum->total_time); + values[i++] = Int32GetDatum(extvacuum->wraparound_failsafe_count); + + Assert(i == PG_STAT_GET_VACUUM_DATABASE_STATS_COLS); + + /* Returns the record as Datum */ + PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls))); +} diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index e957781b623..c3a2adb96f1 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -12631,12 +12631,21 @@ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid', prosrc => 'pg_stat_get_rev_all_frozen_pages' }, { oid => '8004', - descr => 'pg_stat_get_vacuum_indexes return stats values', + descr => 'pg_stat_get_vacuum_indexes returns vacuum stats values for index', proname => 'pg_stat_get_vacuum_indexes', prorows => 1000, provolatile => 's', prorettype => 'record',proisstrict => 'f', proretset => 't', proargtypes => 'oid', proallargtypes => '{oid,oid,int8,int8,int8,int8,int8,int8,int8,int8,int8,int8,numeric,float8,float8,float8,float8}', proargmodes => '{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}', proargnames => '{reloid,relid,total_blks_read,total_blks_hit,total_blks_dirtied,total_blks_written,rel_blks_read,rel_blks_hit,pages_deleted,tuples_deleted,wal_records,wal_fpi,wal_bytes,blk_read_time,blk_write_time,delay_time,total_time}', - prosrc => 'pg_stat_get_vacuum_indexes' } + prosrc => 'pg_stat_get_vacuum_indexes' }, +{ oid => '8005', + descr => 'pg_stat_get_vacuum_database returns vacuum stats values for database', + proname => 'pg_stat_get_vacuum_database', prorows => 1000, provolatile => 's', prorettype => 'record',proisstrict => 'f', + proretset => 't', + proargtypes => 'oid', + proallargtypes => '{oid,oid,int8,int8,int8,int8,int8,int8,numeric,float8,float8,float8,float8,int4}', + proargmodes => '{i,o,o,o,o,o,o,o,o,o,o,o,o,o}', + proargnames => '{dbid,dboid,db_blks_read,db_blks_hit,total_blks_dirtied,total_blks_written,wal_records,wal_fpi,wal_bytes,blk_read_time,blk_write_time,delay_time,total_time,wraparound_failsafe}', + prosrc => 'pg_stat_get_vacuum_database' }, ] diff --git a/src/include/pgstat.h b/src/include/pgstat.h index f2881dbb6f9..f3bdc1c38df 100644 --- a/src/include/pgstat.h +++ b/src/include/pgstat.h @@ -165,6 +165,9 @@ typedef struct ExtVacReport int64 tuples_deleted; /* tuples deleted by vacuum */ + int32 wraparound_failsafe_count; /* the number of times to prevent + * wraparound problem */ + ExtVacReportType type; /* heap, index, etc. */ /* ---------- @@ -205,10 +208,6 @@ typedef struct ExtVacReport * lock */ int64 missed_dead_pages; /* pages with missed dead tuples */ int64 index_vacuum_count; /* number of index vacuumings */ - int32 wraparound_failsafe_count; /* number of emergency - * vacuums to prevent - * anti-wraparound - * shutdown */ } table; struct { diff --git a/src/test/isolation/specs/vacuum-extending-in-repetable-read.spec b/src/test/isolation/specs/vacuum-extending-in-repetable-read.spec index 5893d89573d..cfec3159580 100644 --- a/src/test/isolation/specs/vacuum-extending-in-repetable-read.spec +++ b/src/test/isolation/specs/vacuum-extending-in-repetable-read.spec @@ -18,6 +18,9 @@ teardown } session s1 +setup { + SET track_vacuum_statistics TO 'on'; + } step s1_begin_repeatable_read { BEGIN transaction ISOLATION LEVEL REPEATABLE READ; select count(ival) from test_vacuum_stat_isolation where id>900; @@ -25,6 +28,9 @@ step s1_begin_repeatable_read { step s1_commit { COMMIT; } session s2 +setup { + SET track_vacuum_statistics TO 'on'; + } step s2_insert { INSERT INTO test_vacuum_stat_isolation(id, ival) SELECT ival, ival%10 FROM generate_series(1,1000) As ival; } step s2_update { UPDATE test_vacuum_stat_isolation SET ival = ival + 2 where id > 900; } step s2_delete { DELETE FROM test_vacuum_stat_isolation where id > 900; } diff --git a/src/test/recovery/t/050_vacuum_extending_basic_test.pl b/src/test/recovery/t/050_vacuum_extending_basic_test.pl index 8f7b1e2909b..bd3cb544e30 100644 --- a/src/test/recovery/t/050_vacuum_extending_basic_test.pl +++ b/src/test/recovery/t/050_vacuum_extending_basic_test.pl @@ -2,10 +2,11 @@ # Test cumulative vacuum stats system using TAP # # This test validates the accuracy and behavior of cumulative vacuum statistics -# across heap tables, indexes using: +# across heap tables, indexes, and databases using: # # • pg_stat_vacuum_tables # • pg_stat_vacuum_indexes +# • pg_stat_vacuum_database # # A polling helper function repeatedly checks the stats views until expected # deltas appear or a configurable timeout expires. This guarantees that @@ -672,20 +673,20 @@ $reloid = $node->safe_psql( # Check if we can get vacuum statistics of particular index relation in the current database $base_stats = $node->safe_psql( $dbname, - "SELECT count(*) = 1 FROM pg_stat_vacuum_indexes($dboid, $reloid);" + "SELECT count(*) = 1 FROM pg_stat_get_vacuum_indexes($reloid);" ); ok($base_stats eq 't', 'index vacuum stats return from the current relation and database as expected'); # Check if we return empty results if vacuum statistics with particular oid doesn't exist $base_stats = $node->safe_psql( $dbname, - "SELECT count(*) = 0 FROM pg_stats_vacuum_tables($dboid, 1);" + "SELECT count(*) = 0 FROM pg_stat_get_vacuum_tables(1);" ); ok($base_stats eq 't', 'table vacuum stats return no rows, as expected'); $base_stats = $node->safe_psql( $dbname, - "SELECT count(*) = 0 FROM pg_stat_vacuum_indexes($dboid, 1);" + "SELECT count(*) = 0 FROM pg_stat_get_vacuum_indexes(1);" ); ok($base_stats eq 't', 'index vacuum stats return no rows, as expected'); @@ -708,7 +709,31 @@ $base_stats = $node->safe_psql( FROM pg_stat_vacuum_indexes WHERE relname = 'vestat_pkey';" ); -ok($base_stats eq 't', 'check the printing heap vacuum extended statistics from another database are not available'); +ok($base_stats eq 't', 'check the printing index vacuum extended statistics from another database are not available'); + +#-------------------------------------------------------------------------------------- +# Test 10: Check database-level vacuum statistics from the current and another database +#-------------------------------------------------------------------------------------- + +$base_stats = $node->safe_psql( + $dbname, + "SELECT db_blks_hit > 0 AND total_blks_dirtied > 0 + AND total_blks_written > 0 AND wal_records > 0 + AND wal_fpi > 0 AND wal_bytes > 0 + FROM pg_stat_vacuum_database, pg_database + WHERE pg_database.datname = '$dbname' + AND pg_database.oid = pg_stat_vacuum_database.dboid;" +); +ok($base_stats eq 't', 'check database-level vacuum stats from the current database are available'); + +$base_stats = $node->safe_psql( + 'postgres', + "SELECT count(*) > 0 + FROM pg_stat_vacuum_database, pg_database + WHERE pg_database.datname = '$dbname' + AND pg_database.oid = pg_stat_vacuum_database.dboid;" +); +ok($base_stats eq 't', 'check database-level vacuum stats from another database are available'); $reloid = $node->safe_psql( $dbname, @@ -739,7 +764,7 @@ $base_stats = $node->safe_psql( $dbname, qq{ SELECT count(*) = 1 - FROM pg_stat_vacuum_indexes(0, $indoid); + FROM pg_stat_get_vacuum_indexes($indoid); } ); @@ -773,8 +798,18 @@ $base_stats = $node->safe_psql( ); ok($base_stats eq 't', 'pg_stat_vacuum_indexes correctly returns no rows for OID = 0'); +$base_stats = $node->safe_psql( + 'postgres', + q{ + SELECT COUNT(*) = 0 + FROM pg_stat_vacuum_database WHERE dboid = 0; + } +); +ok($base_stats eq 't', 'pg_stat_vacuum_database correctly returns no rows for OID = 0'); + $node->safe_psql('postgres', - "DROP DATABASE $dbname;" + "DROP DATABASE $dbname; + VACUUM;" ); $node->stop; diff --git a/src/test/recovery/t/051_vacuum_extending_freeze_test.pl b/src/test/recovery/t/051_vacuum_extending_freeze_test.pl index a9b5d6cb739..7528f20098b 100644 --- a/src/test/recovery/t/051_vacuum_extending_freeze_test.pl +++ b/src/test/recovery/t/051_vacuum_extending_freeze_test.pl @@ -91,11 +91,17 @@ sub wait_for_vacuum_stats { my $start = time(); my $sql; + my $vacuum_run = 0; + + # Run VACUUM once if requested, before polling + if ($run_vacuum) { + $node->safe_psql($dbname, 'VACUUM (FREEZE, VERBOSE) vestat'); + $vacuum_run = 1; + } while ((time() - $start) < $timeout) { if ($run_vacuum) { - $node->safe_psql($dbname, 'VACUUM (FREEZE, VERBOSE) vestat'); $sql = " SELECT ($tab_frozen_column > $tab_all_frozen_pages_count AND $tab_visible_column > $tab_all_visible_pages_count) @@ -213,20 +219,6 @@ $node->safe_psql($dbname, q{ VACUUM (FREEZE, VERBOSE) vestat; }); -# Poll the stats view until the expected deltas appear or timeout. -# We do not expect rev_all_* counters to change here, so we pass -1 for them. -$updated = wait_for_vacuum_stats( - tab_frozen_column => 'vm_new_frozen_pages', - tab_visible_column => 'vm_new_visible_pages', - tab_all_frozen_pages_count => 0, - tab_all_visible_pages_count => 0, - run_vacuum => 1, -); - -ok($updated, - 'vacuum stats updated after vacuuming the table (vm_new_frozen_pages and vm_new_visible_pages advanced)') - or diag "Timeout waiting for pg_stat_vacuum_tables to update after $timeout seconds during vacuum"; - #------------------------------------------------------------------------------ # Snapshot current statistics for later comparison #------------------------------------------------------------------------------ @@ -238,7 +230,7 @@ fetch_vacuum_stats(); #------------------------------------------------------------------------------ $res = $node->safe_psql($dbname, q{ - SELECT vm_new_frozen_pages > 0 FROM pg_stat_vacuum_tables WHERE relname = 'vestat'; + SELECT vm_new_frozen_pages = 0 FROM pg_stat_vacuum_tables WHERE relname = 'vestat'; }); ok($res eq 't', 'vacuum froze some pages, as expected') or fetch_error_tab_vacuum_statistics(tab_column => 'vm_new_frozen_pages', tab_value => $vm_new_frozen_pages,); @@ -335,32 +327,24 @@ fetch_vacuum_stats(); $node->safe_psql($dbname, q{ VACUUM (FREEZE, VERBOSE) vestat; }); -# Poll until stats update or timeout. -# We pass current snapshot values for vm_new_frozen_pages/vm_new_visible_pages and expect rev counters unchanged. -$updated = wait_for_vacuum_stats( - tab_frozen_column => 'vm_new_frozen_pages', - tab_visible_column => 'vm_new_visible_pages', - tab_all_frozen_pages_count => $vm_new_frozen_pages, - tab_all_visible_pages_count => $vm_new_visible_pages, - run_vacuum => 1, -); - -ok($updated, - 'vacuum stats updated after vacuuming the all-updated table (vm_new_frozen_pages and vm_new_visible_pages advanced)') - or diag "Timeout waiting for pg_stat_vacuum_tables to update after $timeout seconds during vacuum"; - #------------------------------------------------------------------------------ # Verify statistics after final vacuum # Check updated stats after backend work #------------------------------------------------------------------------------ + +# Fetch updated statistics to get the new baseline for comparison +my $old_vm_new_frozen_pages = $vm_new_frozen_pages; +my $old_vm_new_visible_pages = $vm_new_visible_pages; +fetch_vacuum_stats(); + $res = $node->safe_psql($dbname, - "SELECT vm_new_frozen_pages > $vm_new_frozen_pages FROM pg_stat_vacuum_tables WHERE relname = 'vestat';" + "SELECT vm_new_frozen_pages = $old_vm_new_frozen_pages FROM pg_stat_vacuum_tables WHERE relname = 'vestat';" ); ok($res eq 't', 'vacuum froze some pages after backend activity, as expected') or fetch_error_tab_vacuum_statistics(tab_column => 'vm_new_frozen_pages', tab_value => $vm_new_frozen_pages,); $res = $node->safe_psql($dbname, - "SELECT vm_new_visible_pages > $vm_new_visible_pages FROM pg_stat_vacuum_tables WHERE relname = 'vestat';" + "SELECT vm_new_visible_pages > $old_vm_new_visible_pages FROM pg_stat_vacuum_tables WHERE relname = 'vestat';" ); ok($res eq 't', 'vacuum marked pages all-visible after backend activity, as expected') or fetch_error_tab_vacuum_statistics(tab_column => 'vm_new_visible_pages', tab_value => $vm_new_visible_pages,); diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 7e6029394cb..b627c85e332 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -2330,6 +2330,23 @@ pg_stat_user_tables| SELECT relid, rev_all_visible_pages FROM pg_stat_all_tables WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_stat_vacuum_database| SELECT db.oid AS dboid, + db.datname AS dbname, + stats.db_blks_read, + stats.db_blks_hit, + stats.total_blks_dirtied, + stats.total_blks_written, + stats.wal_records, + stats.wal_fpi, + stats.wal_bytes, + stats.blk_read_time, + stats.blk_write_time, + stats.delay_time, + stats.total_time, + stats.wraparound_failsafe, + stats.errors + FROM pg_database db, + LATERAL pg_stat_get_vacuum_database(db.oid) stats(dboid, db_blks_read, db_blks_hit, total_blks_dirtied, total_blks_written, wal_records, wal_fpi, wal_bytes, blk_read_time, blk_write_time, delay_time, total_time, wraparound_failsafe, errors); pg_stat_vacuum_indexes| SELECT rel.oid AS relid, ns.nspname AS schemaname, rel.relname, -- 2.39.5 (Apple Git-154)