75
75
#include "catalog/dependency.h"
76
76
#include "catalog/namespace.h"
77
77
#include "catalog/pg_database.h"
78
+ #include "catalog/pg_inherits.h"
78
79
#include "commands/dbcommands.h"
79
80
#include "commands/vacuum.h"
80
81
#include "lib/ilist.h"
@@ -1969,6 +1970,7 @@ do_autovacuum(void)
1969
1970
int effective_multixact_freeze_max_age ;
1970
1971
bool did_vacuum = false;
1971
1972
bool found_concurrent_worker = false;
1973
+ bool updated = false;
1972
1974
int i ;
1973
1975
1974
1976
/*
@@ -2054,12 +2056,19 @@ do_autovacuum(void)
2054
2056
/*
2055
2057
* Scan pg_class to determine which tables to vacuum.
2056
2058
*
2057
- * We do this in two passes: on the first one we collect the list of plain
2058
- * relations and materialized views, and on the second one we collect
2059
- * TOAST tables. The reason for doing the second pass is that during it we
2060
- * want to use the main relation's pg_class.reloptions entry if the TOAST
2061
- * table does not have any, and we cannot obtain it unless we know
2062
- * beforehand what's the main table OID.
2059
+ * We do this in three passes: First we let pgstat collector know about
2060
+ * the partitioned table ancestors of all partitions that have recently
2061
+ * acquired rows for analyze. This informs the second pass about the
2062
+ * total number of tuple count in partitioning hierarchies.
2063
+ *
2064
+ * On the second pass, we collect the list of plain relations,
2065
+ * materialized views and partitioned tables. On the third one we collect
2066
+ * TOAST tables.
2067
+ *
2068
+ * The reason for doing the third pass is that during it we want to use
2069
+ * the main relation's pg_class.reloptions entry if the TOAST table does
2070
+ * not have any, and we cannot obtain it unless we know beforehand what's
2071
+ * the main table OID.
2063
2072
*
2064
2073
* We need to check TOAST tables separately because in cases with short,
2065
2074
* wide tables there might be proportionally much more activity in the
@@ -2068,7 +2077,44 @@ do_autovacuum(void)
2068
2077
relScan = table_beginscan_catalog (classRel , 0 , NULL );
2069
2078
2070
2079
/*
2071
- * On the first pass, we collect main tables to vacuum, and also the main
2080
+ * First pass: before collecting the list of tables to vacuum, let stat
2081
+ * collector know about partitioned-table ancestors of each partition.
2082
+ */
2083
+ while ((tuple = heap_getnext (relScan , ForwardScanDirection )) != NULL )
2084
+ {
2085
+ Form_pg_class classForm = (Form_pg_class ) GETSTRUCT (tuple );
2086
+ Oid relid = classForm -> oid ;
2087
+ PgStat_StatTabEntry * tabentry ;
2088
+
2089
+ /* Only consider permanent leaf partitions */
2090
+ if (!classForm -> relispartition ||
2091
+ classForm -> relkind == RELKIND_PARTITIONED_TABLE ||
2092
+ classForm -> relpersistence == RELPERSISTENCE_TEMP )
2093
+ continue ;
2094
+
2095
+ /*
2096
+ * No need to do this for partitions that haven't acquired any rows.
2097
+ */
2098
+ tabentry = pgstat_fetch_stat_tabentry (relid );
2099
+ if (tabentry &&
2100
+ tabentry -> changes_since_analyze -
2101
+ tabentry -> changes_since_analyze_reported > 0 )
2102
+ {
2103
+ pgstat_report_anl_ancestors (relid );
2104
+ updated = true;
2105
+ }
2106
+ }
2107
+
2108
+ /* Acquire fresh stats for the next passes, if needed */
2109
+ if (updated )
2110
+ {
2111
+ autovac_refresh_stats ();
2112
+ dbentry = pgstat_fetch_stat_dbentry (MyDatabaseId );
2113
+ shared = pgstat_fetch_stat_dbentry (InvalidOid );
2114
+ }
2115
+
2116
+ /*
2117
+ * On the second pass, we collect main tables to vacuum, and also the main
2072
2118
* table relid to TOAST relid mapping.
2073
2119
*/
2074
2120
while ((tuple = heap_getnext (relScan , ForwardScanDirection )) != NULL )
@@ -2082,7 +2128,8 @@ do_autovacuum(void)
2082
2128
bool wraparound ;
2083
2129
2084
2130
if (classForm -> relkind != RELKIND_RELATION &&
2085
- classForm -> relkind != RELKIND_MATVIEW )
2131
+ classForm -> relkind != RELKIND_MATVIEW &&
2132
+ classForm -> relkind != RELKIND_PARTITIONED_TABLE )
2086
2133
continue ;
2087
2134
2088
2135
relid = classForm -> oid ;
@@ -2157,7 +2204,7 @@ do_autovacuum(void)
2157
2204
2158
2205
table_endscan (relScan );
2159
2206
2160
- /* second pass: check TOAST tables */
2207
+ /* third pass: check TOAST tables */
2161
2208
ScanKeyInit (& key ,
2162
2209
Anum_pg_class_relkind ,
2163
2210
BTEqualStrategyNumber , F_CHAREQ ,
@@ -2745,6 +2792,7 @@ extract_autovac_opts(HeapTuple tup, TupleDesc pg_class_desc)
2745
2792
2746
2793
Assert (((Form_pg_class ) GETSTRUCT (tup ))-> relkind == RELKIND_RELATION ||
2747
2794
((Form_pg_class ) GETSTRUCT (tup ))-> relkind == RELKIND_MATVIEW ||
2795
+ ((Form_pg_class ) GETSTRUCT (tup ))-> relkind == RELKIND_PARTITIONED_TABLE ||
2748
2796
((Form_pg_class ) GETSTRUCT (tup ))-> relkind == RELKIND_TOASTVALUE );
2749
2797
2750
2798
relopts = extractRelOptions (tup , pg_class_desc , NULL );
@@ -3161,7 +3209,44 @@ relation_needs_vacanalyze(Oid relid,
3161
3209
*/
3162
3210
if (PointerIsValid (tabentry ) && AutoVacuumingActive ())
3163
3211
{
3164
- reltuples = classForm -> reltuples ;
3212
+ if (classForm -> relkind != RELKIND_PARTITIONED_TABLE )
3213
+ {
3214
+ reltuples = classForm -> reltuples ;
3215
+ }
3216
+ else
3217
+ {
3218
+ /*
3219
+ * If the relation is a partitioned table, we must add up
3220
+ * children's reltuples.
3221
+ */
3222
+ List * children ;
3223
+ ListCell * lc ;
3224
+
3225
+ reltuples = 0 ;
3226
+
3227
+ /* Find all members of inheritance set taking AccessShareLock */
3228
+ children = find_all_inheritors (relid , AccessShareLock , NULL );
3229
+
3230
+ foreach (lc , children )
3231
+ {
3232
+ Oid childOID = lfirst_oid (lc );
3233
+ HeapTuple childtuple ;
3234
+ Form_pg_class childclass ;
3235
+
3236
+ childtuple = SearchSysCache1 (RELOID , ObjectIdGetDatum (childOID ));
3237
+ childclass = (Form_pg_class ) GETSTRUCT (childtuple );
3238
+
3239
+ /* Skip a partitioned table and foreign partitions */
3240
+ if (RELKIND_HAS_STORAGE (childclass -> relkind ))
3241
+ {
3242
+ /* Sum up the child's reltuples for its parent table */
3243
+ reltuples += childclass -> reltuples ;
3244
+ }
3245
+ ReleaseSysCache (childtuple );
3246
+ }
3247
+
3248
+ list_free (children );
3249
+ }
3165
3250
vactuples = tabentry -> n_dead_tuples ;
3166
3251
instuples = tabentry -> inserts_since_vacuum ;
3167
3252
anltuples = tabentry -> changes_since_analyze ;
0 commit comments