diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index ef0f7cf727..09b5b3ff70 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -3792,8 +3792,7 @@ UNION ALL SELECT * FROM measurement_y2008m01;
Caveats
- The following caveats apply to partitioned tables implemented using either
- method (unless noted otherwise):
+ The following caveats apply to using inheritance to implement partitioning:
@@ -3803,13 +3802,6 @@ UNION ALL SELECT * FROM measurement_y2008m01;
partitions and creates and/or modifies associated objects than
to write each by hand.
-
-
- This is not a problem with partitioned tables though, as trying to
- create a partition that overlaps with one of the existing partitions
- results in an error, so it is impossible to end up with partitions
- that overlap one another.
-
@@ -3822,14 +3814,6 @@ UNION ALL SELECT * FROM measurement_y2008m01;
on the partition tables, but it makes management of the structure
much more complicated.
-
-
- This problem exists even for partitioned tables. An UPDATE>
- that causes a row to move from one partition to another fails, because
- the new value of the row fails to satisfy the implicit partition
- constraint of the original partition. This might change in future
- releases.
-
@@ -3840,8 +3824,7 @@ UNION ALL SELECT * FROM measurement_y2008m01;
ANALYZE measurement;
- will only process the master table. This is true even for partitioned
- tables.
+ will only process the master table.
@@ -3852,11 +3835,27 @@ ANALYZE measurement;
action is only taken in case of unique violations on the specified
target relation, not its child relations.
+
+
+
+
+ The following caveats apply to partitioned tables created with the
+ explicit syntax:
+
+
+
+ An UPDATE> that causes a row to move from one partition to
+ another fails, because the new value of the row fails to satisfy the
+ implicit partition constraint of the original partition. This might
+ change in future releases.
+
+
+
+
INSERT statements with ON CONFLICT>
- clause are currently not allowed on partitioned tables, that is,
- cause error when specified.
+ clause are currently not allowed on partitioned tables.
@@ -3864,7 +3863,8 @@ ANALYZE measurement;
- The following caveats apply to constraint exclusion:
+ The following caveats apply to constraint exclusion, which is currently
+ used by both inheritance and partitioned tables:
@@ -3898,8 +3898,7 @@ ANALYZE measurement;
during constraint exclusion, so large numbers of partitions are likely
to increase query planning time considerably. Partitioning using
these techniques will work well with up to perhaps a hundred partitions;
- don't try to use many thousands of partitions. This restriction applies
- both to inheritance and explicit partitioning syntax.
+ don't try to use many thousands of partitions.
diff --git a/doc/src/sgml/ref/analyze.sgml b/doc/src/sgml/ref/analyze.sgml
index 27ab4fca42..49727e22df 100644
--- a/doc/src/sgml/ref/analyze.sgml
+++ b/doc/src/sgml/ref/analyze.sgml
@@ -63,8 +63,11 @@ ANALYZE [ VERBOSE ] [ table_name [
The name (possibly schema-qualified) of a specific table to
- analyze. If omitted, all regular tables (but not foreign tables)
- in the current database are analyzed.
+ analyze. If omitted, all regular tables, partitioned tables, and
+ and materialized views in the current database are analyzed (but not
+ foreign tables). If the specified table is a partitioned table, both the
+ inheritance statistics of the partitioned table as a whole and
+ statistics of the individual partitions are updated.
diff --git a/doc/src/sgml/ref/vacuum.sgml b/doc/src/sgml/ref/vacuum.sgml
index f18180a2fa..543ebcf649 100644
--- a/doc/src/sgml/ref/vacuum.sgml
+++ b/doc/src/sgml/ref/vacuum.sgml
@@ -153,7 +153,9 @@ VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] ANALYZE [
The name (optionally schema-qualified) of a specific table to
- vacuum. Defaults to all tables in the current database.
+ vacuum. If omitted, all regular tables and materialized views in the
+ current database are vacuumed. If the specified table is a partitioned
+ table, all of its leaf partitions are vacuumed.
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index ed3acb1673..a70c760341 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.
@@ -1260,6 +1267,7 @@ acquire_inherited_sample_rows(Relation onerel, int elevel,
nrels,
i;
ListCell *lc;
+ bool has_child;
/*
* Find all members of inheritance set. We only need AccessShareLock on
@@ -1297,6 +1305,7 @@ acquire_inherited_sample_rows(Relation onerel, int elevel,
relblocks = (double *) palloc(list_length(tableOIDs) * sizeof(double));
totalblocks = 0;
nrels = 0;
+ has_child = false;
foreach(lc, tableOIDs)
{
Oid childOID = lfirst_oid(lc);
@@ -1318,8 +1327,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,13 +1359,17 @@ 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;
}
/* OK, we'll process this child */
+ has_child = true;
rels[nrels] = childrel;
acquirefuncs[nrels] = acquirefunc;
relblocks[nrels] = (double) relpages;
@@ -1366,9 +1378,10 @@ 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 (!has_child)
{
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 3cea220421..317012068b 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 812fb4a48f..3a9b965266 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"
@@ -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,14 @@ 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;
/* Make a relation list entry for this guy */
@@ -1349,6 +1379,21 @@ vacuum_rel(Oid relid, RangeVar *relation, int options, VacuumParams *params)
return false;
}
+ /*
+ * 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