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)