diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index e9a31aa257..8d80efd445 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -1308,8 +1308,6 @@ WITH ( MODULUS numeric_literal, REM
If a table parameter value is set and the
equivalent toast. parameter is not, the TOAST table
will use the table's parameter value.
- Specifying these parameters for partitioned tables is not supported,
- but you may specify them for individual leaf partitions.
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 48377ace24..d640aa4bda 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -108,7 +108,7 @@ static relopt_bool boolRelOpts[] =
{
"autovacuum_enabled",
"Enables autovacuum in this relation",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
+ RELOPT_KIND_HEAP | RELOPT_KIND_TOAST | RELOPT_KIND_PARTITIONED,
ShareUpdateExclusiveLock
},
true
@@ -1586,13 +1586,19 @@ build_reloptions(Datum reloptions, bool validate,
bytea *
partitioned_table_reloptions(Datum reloptions, bool validate)
{
+ static const relopt_parse_elt tab[] = {
+ {"autovacuum_enabled", RELOPT_TYPE_BOOL,
+ offsetof(PartitionedTableOptions, autovacuum_enabled)}
+ };
+
/*
* There are no options for partitioned tables yet, but this is able to do
* some validation.
*/
return (bytea *) build_reloptions(reloptions, validate,
RELOPT_KIND_PARTITIONED,
- 0, NULL, 0);
+ sizeof(PartitionedTableOptions),
+ tab, lengthof(tab));
}
/*
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index c1dd8168ca..7bd4950d90 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,85 @@ 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;
+ bool av_enabled;
- /* Check if it needs vacuum or analyze */
- relation_needs_vacanalyze(relid, relopts, classForm, tabentry,
- effective_multixact_freeze_max_age,
- &dovacuum, &doanalyze, &wraparound);
+ /*
+ * Fetch reloptions and check whether partitioned table needs
+ * autovacuum or not.
+ */
+ relopts = extract_autovac_opts(tuple, pg_class_desc);
+ av_enabled = (relopts ? relopts->enabled : true);
- /* Relations that need work are added to table_oids */
- if (dovacuum || doanalyze)
- table_oids = lappend_oid(table_oids, relid);
+ /* 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 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)
+ {
+ if (av_enabled)
+ 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 +2797,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 +2871,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);
- hentry = hash_search(table_toast_map, &relid, HASH_FIND, &found);
- if (found && hentry->ar_hasrelopts)
- avopts = &hentry->ar_reloptions;
+ relation_needs_vacanalyze(relid, avopts, classForm, tabentry,
+ effective_multixact_freeze_max_age,
+ &dovacuum, &doanalyze, &wraparound);
+
+ /* 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;
- /* fetch the pgstat table entry */
- tabentry = get_pgstat_tabentry_relid(relid, classForm->relisshared,
- shared, dbentry);
+ /* 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;
- relation_needs_vacanalyze(relid, avopts, classForm, tabentry,
- effective_multixact_freeze_max_age,
- &dovacuum, &doanalyze, &wraparound);
+ childtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(childOID));
+ childclassForm = (Form_pg_class) GETSTRUCT(childtuple);
- /* ignore ANALYZE for toast tables */
- if (classForm->relkind == RELKIND_TOASTVALUE)
- doanalyze = false;
+ /* Skip foreign partitions */
+ if (childclassForm->relkind == RELKIND_FOREIGN_TABLE)
+ continue;
+
+ /* 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(childOID, avopts, childclassForm, tabentry,
+ effective_multixact_freeze_max_age,
+ &dovacuum, &doanalyze, &wraparound);
+
+ /* Its parents need vacuum or analyze */
+ if (dovacuum || doanalyze)
+ break;
+ }
+ }
/* OK, it needs something done */
if (doanalyze || dovacuum)
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 31d8a1a10e..d007d7feab 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -326,6 +326,16 @@ typedef struct StdRdOptions
((relation)->rd_options ? \
((StdRdOptions *) (relation)->rd_options)->parallel_workers : (defaultpw))
+/*
+ * PartitionedTableOptions
+ * Contents of rd_options for partitioned tables
+ */
+typedef struct PartitionedTableOptions
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ bool autovacuum_enabled;
+} PartitionedTableOptions;
+
/* ViewOptions->check_option values */
typedef enum ViewOptCheckOption
{