diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c index c1dd8168ca..d4ce99a9f5 100644 --- a/src/backend/postmaster/autovacuum.c +++ b/src/backend/postmaster/autovacuum.c @@ -75,6 +75,7 @@ #include "catalog/dependency.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" +#include "catalog/pg_inherits.h" #include "commands/dbcommands.h" #include "commands/vacuum.h" #include "lib/ilist.h" @@ -2036,11 +2037,11 @@ do_autovacuum(void) * Scan pg_class to determine which tables to vacuum. * * We do this in two passes: on the first one we collect the list of plain - * relations and materialized views, and on the second one we collect - * TOAST tables. The reason for doing the second pass is that during it we - * want to use the main relation's pg_class.reloptions entry if the TOAST - * table does not have any, and we cannot obtain it unless we know - * beforehand what's the main table OID. + * relations, materialized views and partitioned tables, and on the second + * one we collect TOAST tables. The reason for doing the second pass is that + * during it we want to use the main relation's pg_class.reloptions entry + * if the TOAST table does not have any, and we cannot obtain it unless we + * know beforehand what's the main table OID. * * We need to check TOAST tables separately because in cases with short, * wide tables there might be proportionally much more activity in the @@ -2063,7 +2064,12 @@ do_autovacuum(void) bool wraparound; if (classForm->relkind != RELKIND_RELATION && - classForm->relkind != RELKIND_MATVIEW) + classForm->relkind != RELKIND_MATVIEW && + classForm->relkind != RELKIND_PARTITIONED_TABLE) + continue; + + /* Collect partitioned tables, not partitions. So skip them. */ + if (classForm->relispartition) continue; relid = classForm->oid; @@ -2092,19 +2098,76 @@ do_autovacuum(void) continue; } - /* Fetch reloptions and the pgstat entry for this table */ - relopts = extract_autovac_opts(tuple, pg_class_desc); - tabentry = get_pgstat_tabentry_relid(relid, classForm->relisshared, - shared, dbentry); + if (classForm->relkind == RELKIND_RELATION || + classForm->relkind == RELKIND_MATVIEW) + { + /* Fetch reloptions and the pgstat entry for this table */ + relopts = extract_autovac_opts(tuple, pg_class_desc); + tabentry = get_pgstat_tabentry_relid(relid, classForm->relisshared, + shared, dbentry); + + /* Check if it needs vacuum or analyze */ + relation_needs_vacanalyze(relid, relopts, classForm, tabentry, + effective_multixact_freeze_max_age, + &dovacuum, &doanalyze, &wraparound); + + /* Relations that need work are added to table_oids */ + if (dovacuum || doanalyze) + table_oids = lappend_oid(table_oids, relid); + } + else + { + /* + * If the relation is a partitioned table, we check if its children + * need vacuum or analyze. All children excluding foreign partitions + * need to do that are added to the table_oids list. At least one + * child is added the list, the partitioned table become an object + * for autovacuum. + */ + List *tableOIDs; + ListCell *lc; + List *child_oids = NIL; - /* Check if it needs vacuum or analyze */ - relation_needs_vacanalyze(relid, relopts, classForm, tabentry, - effective_multixact_freeze_max_age, - &dovacuum, &doanalyze, &wraparound); + /* Find all members of inheritance set taking AccessShareLock */ + tableOIDs = find_all_inheritors(relid, AccessShareLock, NULL); - /* Relations that need work are added to table_oids */ - if (dovacuum || doanalyze) - table_oids = lappend_oid(table_oids, relid); + foreach(lc, tableOIDs) + { + Oid childOID = lfirst_oid(lc); + HeapTuple childtuple; + Form_pg_class childclassForm; + + /* Ignore the parent table */ + if (childOID == relid) + continue; + + childtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(childOID)); + childclassForm = (Form_pg_class) GETSTRUCT(childtuple); + + /* Skip foreign partitions */ + if (childclassForm->relkind == RELKIND_FOREIGN_TABLE) + continue; + + /* Fetch reloptions and the pgstat entry for this table */ + relopts = extract_autovac_opts(childtuple, pg_class_desc); + tabentry = get_pgstat_tabentry_relid(childOID, + childclassForm->relisshared, + shared, dbentry); + + relation_needs_vacanalyze(childOID, relopts, childclassForm, tabentry, + effective_multixact_freeze_max_age, + &dovacuum, &doanalyze, &wraparound); + + if (dovacuum || doanalyze) + child_oids = lappend_oid(child_oids, childOID); + } + + if (child_oids) + { + table_oids = lappend_oid(table_oids, relid); + table_oids = list_concat(table_oids, child_oids); + } + } /* * Remember TOAST associations for the second pass. Note: we must do @@ -2725,6 +2788,7 @@ extract_autovac_opts(HeapTuple tup, TupleDesc pg_class_desc) Assert(((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_RELATION || ((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_MATVIEW || + ((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_PARTITIONED_TABLE || ((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_TOASTVALUE); relopts = extractRelOptions(tup, pg_class_desc, NULL); @@ -2798,33 +2862,79 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map, return NULL; classForm = (Form_pg_class) GETSTRUCT(classTup); - /* - * Get the applicable reloptions. If it is a TOAST table, try to get the - * main table reloptions if the toast table itself doesn't have. - */ - avopts = extract_autovac_opts(classTup, pg_class_desc); - if (classForm->relkind == RELKIND_TOASTVALUE && - avopts == NULL && table_toast_map != NULL) + if (classForm->relkind != RELKIND_PARTITIONED_TABLE) { - av_relation *hentry; - bool found; + /* + * Get the applicable reloptions. If it is a TOAST table, try to get the + * main table reloptions if the toast table itself doesn't have. + */ + avopts = extract_autovac_opts(classTup, pg_class_desc); + if (classForm->relkind == RELKIND_TOASTVALUE && + avopts == NULL && table_toast_map != NULL) + { + av_relation *hentry; + bool found; + + hentry = hash_search(table_toast_map, &relid, HASH_FIND, &found); + if (found && hentry->ar_hasrelopts) + avopts = &hentry->ar_reloptions; + } + + /* fetch the pgstat table entry */ + tabentry = get_pgstat_tabentry_relid(relid, classForm->relisshared, + shared, dbentry); + + relation_needs_vacanalyze(relid, avopts, classForm, tabentry, + effective_multixact_freeze_max_age, + &dovacuum, &doanalyze, &wraparound); - hentry = hash_search(table_toast_map, &relid, HASH_FIND, &found); - if (found && hentry->ar_hasrelopts) - avopts = &hentry->ar_reloptions; + /* ignore ANALYZE for toast tables */ + if (classForm->relkind == RELKIND_TOASTVALUE) + doanalyze = false; } + else + { + /* + * If the relation is partitioned and doesn't have any foreign tables + * we check its children again. + */ + List *tableOIDs; + ListCell *lc; + + /* Find all members of inheritance set taking AccessShareLock */ + tableOIDs = find_all_inheritors(relid, AccessShareLock, NULL); + + foreach(lc, tableOIDs) + { + Oid childOID = lfirst_oid(lc); + HeapTuple childtuple; + Form_pg_class childclassForm; + + /* Ignore the parent table */ + if (childOID == relid) + continue; + + childtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(childOID)); + childclassForm = (Form_pg_class) GETSTRUCT(childtuple); + + /* Skip foreign partitions */ + if (childclassForm->relkind == RELKIND_FOREIGN_TABLE) + continue; - /* fetch the pgstat table entry */ - tabentry = get_pgstat_tabentry_relid(relid, classForm->relisshared, - shared, dbentry); + /* Fetch reloptions and the pgstat entry */ + avopts = extract_autovac_opts(childtuple, pg_class_desc); + tabentry = get_pgstat_tabentry_relid(childOID, childclassForm->relisshared, + shared, dbentry); - relation_needs_vacanalyze(relid, avopts, classForm, tabentry, - effective_multixact_freeze_max_age, - &dovacuum, &doanalyze, &wraparound); + relation_needs_vacanalyze(childOID, avopts, childclassForm, tabentry, + effective_multixact_freeze_max_age, + &dovacuum, &doanalyze, &wraparound); - /* ignore ANALYZE for toast tables */ - if (classForm->relkind == RELKIND_TOASTVALUE) - doanalyze = false; + /* Its parents need vacuum or analyze */ + if (dovacuum || doanalyze) + break; + } + } /* OK, it needs something done */ if (doanalyze || dovacuum)