diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index ef0f7cf..62861fa 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -3834,19 +3834,6 @@ UNION ALL SELECT * FROM measurement_y2008m01;
- If you are using manual VACUUM or
- ANALYZE commands, don't forget that
- you need to run them on each partition individually. A command like:
-
-ANALYZE measurement;
-
- will only process the master table. This is true even for partitioned
- tables.
-
-
-
-
-
INSERT statements with ON CONFLICT>
clauses are unlikely to work as expected, as the ON CONFLICT>
action is only taken in case of unique violations on the specified
@@ -3903,6 +3890,17 @@ ANALYZE measurement;
+
+
+ If you are using manual VACUUM or
+ ANALYZE commands, you must run them on each partition
+ individually.
+
+ANALYZE measurement;
+
+ will only process the master table.
+
+
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index ed3acb1..b6f2543 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -201,8 +201,7 @@ analyze_rel(Oid relid, RangeVar *relation, int options,
* locked the relation.
*/
if (onerel->rd_rel->relkind == RELKIND_RELATION ||
- onerel->rd_rel->relkind == RELKIND_MATVIEW ||
- onerel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+ onerel->rd_rel->relkind == RELKIND_MATVIEW)
{
/* Regular table, so we'll use the regular row acquisition function */
acquirefunc = acquire_sample_rows;
@@ -234,6 +233,12 @@ analyze_rel(Oid relid, RangeVar *relation, int options,
return;
}
}
+ else if (onerel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+ {
+ /*
+ * For partitioned tables, we want to do the recursive ANALYZE below.
+ */
+ }
else
{
/* No need for a WARNING if we already complained during VACUUM */
@@ -253,10 +258,12 @@ analyze_rel(Oid relid, RangeVar *relation, int options,
LWLockRelease(ProcArrayLock);
/*
- * Do the normal non-recursive ANALYZE.
+ * Do the normal non-recursive ANALYZE. We can skip this for partitioned
+ * tables, which don't contain any rows.
*/
- do_analyze_rel(onerel, options, params, va_cols, acquirefunc, relpages,
- false, in_outer_xact, elevel);
+ if (onerel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
+ do_analyze_rel(onerel, options, params, va_cols, acquirefunc,
+ relpages, false, in_outer_xact, elevel);
/*
* If there are child tables, do recursive ANALYZE.
@@ -1318,8 +1325,7 @@ acquire_inherited_sample_rows(Relation onerel, int elevel,
/* Check table type (MATVIEW can't happen, but might as well allow) */
if (childrel->rd_rel->relkind == RELKIND_RELATION ||
- childrel->rd_rel->relkind == RELKIND_MATVIEW ||
- childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+ childrel->rd_rel->relkind == RELKIND_MATVIEW)
{
/* Regular table, so use the regular row acquisition function */
acquirefunc = acquire_sample_rows;
@@ -1351,9 +1357,12 @@ acquire_inherited_sample_rows(Relation onerel, int elevel,
}
else
{
- /* ignore, but release the lock on it */
- Assert(childrel != onerel);
- heap_close(childrel, AccessShareLock);
+ /*
+ * ignore, but release the lock on it. could be a partitioned
+ * table.
+ */
+ if (childrel != onerel)
+ heap_close(childrel, AccessShareLock);
continue;
}
@@ -1366,9 +1375,11 @@ acquire_inherited_sample_rows(Relation onerel, int elevel,
}
/*
- * If we don't have at least two tables to consider, fail.
+ * If we don't have at least one child table to consider, fail. If the
+ * relation is a partitioned table, it's not counted as a child table.
*/
- if (nrels < 2)
+ if ((onerel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE && nrels < 2) ||
+ nrels < 1)
{
ereport(elevel,
(errmsg("skipping analyze of \"%s.%s\" inheritance tree --- this inheritance tree contains no analyzable child tables",
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 3cea220..3170120 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -1349,6 +1349,10 @@ ExecuteTruncate(TruncateStmt *stmt)
{
Relation rel = (Relation) lfirst(cell);
+ /* Skip partitioned tables as there is nothing to do */
+ if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+ continue;
+
/*
* Normally, we need a transaction-safe truncation here. However, if
* the table was either created in the current (sub)transaction or has
@@ -1459,7 +1463,11 @@ truncate_check_rel(Relation rel)
{
AclResult aclresult;
- /* Only allow truncate on regular tables */
+ /*
+ * Only allow truncate on regular tables and partitioned tables (although,
+ * the latter are only being included here for the following checks; no
+ * physical truncation will occur in their case.)
+ */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
ereport(ERROR,
@@ -4006,8 +4014,9 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode)
{
AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
- /* Foreign tables have no storage. */
- if (tab->relkind == RELKIND_FOREIGN_TABLE)
+ /* Foreign tables have no storage, nor do partitioned tables. */
+ if (tab->relkind == RELKIND_FOREIGN_TABLE ||
+ tab->relkind == RELKIND_PARTITIONED_TABLE)
continue;
/*
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 812fb4a..763ffc5 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -32,6 +32,7 @@
#include "access/xact.h"
#include "catalog/namespace.h"
#include "catalog/pg_database.h"
+#include "catalog/pg_inherits_fn.h"
#include "catalog/pg_namespace.h"
#include "commands/cluster.h"
#include "commands/vacuum.h"
@@ -66,7 +67,7 @@ static BufferAccessStrategy vac_strategy;
/* non-export function prototypes */
-static List *get_rel_oids(Oid relid, const RangeVar *vacrel);
+static List *get_rel_oids(Oid relid, const RangeVar *vacrel, bool is_vacuum);
static void vac_truncate_clog(TransactionId frozenXID,
MultiXactId minMulti,
TransactionId lastSaneFrozenXid,
@@ -228,7 +229,7 @@ vacuum(int options, RangeVar *relation, Oid relid, VacuumParams *params,
* Build list of relations to process, unless caller gave us one. (If we
* build one, we put it in vac_context for safekeeping.)
*/
- relations = get_rel_oids(relid, relation);
+ relations = get_rel_oids(relid, relation, (options & VACOPT_VACUUM) != 0);
/*
* Decide whether we need to start/commit our own transactions.
@@ -378,7 +379,7 @@ vacuum(int options, RangeVar *relation, Oid relid, VacuumParams *params,
* per-relation transactions.
*/
static List *
-get_rel_oids(Oid relid, const RangeVar *vacrel)
+get_rel_oids(Oid relid, const RangeVar *vacrel, bool is_vacuum)
{
List *oid_list = NIL;
MemoryContext oldcontext;
@@ -394,6 +395,9 @@ get_rel_oids(Oid relid, const RangeVar *vacrel)
{
/* Process a specific relation */
Oid relid;
+ HeapTuple tuple;
+ Form_pg_class classForm;
+ bool include_parts;
/*
* Since we don't take a lock here, the relation might be gone, or the
@@ -406,9 +410,29 @@ get_rel_oids(Oid relid, const RangeVar *vacrel)
*/
relid = RangeVarGetRelid(vacrel, NoLock, false);
- /* Make a relation list entry for this guy */
+ /*
+ * To check whether the relation is a partitioned table, fetch its
+ * syscache entry.
+ */
+ tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for relation %u", relid);
+ classForm = (Form_pg_class) GETSTRUCT(tuple);
+ include_parts = (classForm->relkind == RELKIND_PARTITIONED_TABLE);
+ ReleaseSysCache(tuple);
+
+ /*
+ * Make relation list entries for this guy and its partitions, if any.
+ * Note that the list returned by find_all_inheritors() include the
+ * passed-in OID at its head. Also note that we did not request a
+ * lock to be taken to match what would be done otherwise.
+ */
oldcontext = MemoryContextSwitchTo(vac_context);
- oid_list = lappend_oid(oid_list, relid);
+ if (include_parts)
+ oid_list = list_concat(oid_list,
+ find_all_inheritors(relid, NoLock, NULL));
+ else
+ oid_list = lappend_oid(oid_list, relid);
MemoryContextSwitchTo(oldcontext);
}
else
@@ -429,8 +453,23 @@ get_rel_oids(Oid relid, const RangeVar *vacrel)
{
Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
+ /*
+ * We include partitioned tables here; depending on which
+ * operation is to be performed, caller will decide whether to
+ * process or ignore them.
+ */
if (classForm->relkind != RELKIND_RELATION &&
- classForm->relkind != RELKIND_MATVIEW)
+ classForm->relkind != RELKIND_MATVIEW &&
+ classForm->relkind != RELKIND_PARTITIONED_TABLE)
+ continue;
+
+ /*
+ * If only ANALYZE is to be performed, there is no need to include
+ * partitions in the list. In a database-wide ANALYZE, we only
+ * update the inheritance statistics of partitioned tables, not
+ * the statistics of individual partitions.
+ */
+ if (!is_vacuum && classForm->relispartition)
continue;
/* Make a relation list entry for this guy */
@@ -1350,6 +1389,21 @@ vacuum_rel(Oid relid, RangeVar *relation, int options, VacuumParams *params)
}
/*
+ * Ignore partitioned tables as there is no work to be done. Since we
+ * release the lock here, it's possible that any partitions added from
+ * this point on will not get processed, but that seems harmless.
+ */
+ if (onerel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+ {
+ relation_close(onerel, lmode);
+ PopActiveSnapshot();
+ CommitTransactionCommand();
+
+ /* It's OK for other commands to look at this table */
+ return true;
+ }
+
+ /*
* Get a session-level lock too. This will protect our access to the
* relation across multiple transactions, so that we can vacuum the
* relation's TOAST table (if any) secure in the knowledge that no one is
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 0c5ffa0..4f3bb2d 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -2513,7 +2513,8 @@ 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_TOASTVALUE);
+ ((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_TOASTVALUE ||
+ ((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_PARTITIONED_TABLE);
relopts = extractRelOptions(tup, pg_class_desc, NULL);
if (relopts == NULL)