From 982366eb019585438513b0c7b6e86acc74e459d1 Mon Sep 17 00:00:00 2001 From: amit Date: Mon, 6 Feb 2017 18:03:28 +0900 Subject: [PATCH 1/3] Partitioned tables are empty themselves So, there is not much point in trying to do things to them that need accessing files (a later commit will make it so that there is no file at all to access.) Things that needed attention are: vacuum, analyze, truncate, ATRewriteTables. --- src/backend/commands/analyze.c | 39 ++++++++++++++------ src/backend/commands/tablecmds.c | 15 ++++++-- src/backend/commands/vacuum.c | 71 +++++++++++++++++++++++++++++++++---- src/backend/postmaster/autovacuum.c | 20 +++++++---- 4 files changed, 118 insertions(+), 27 deletions(-) diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index ed3acb1673..12cd0110b0 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,13 +258,15 @@ analyze_rel(Oid relid, RangeVar *relation, int options, LWLockRelease(ProcArrayLock); /* - * Do the normal non-recursive ANALYZE. + * Do the normal non-recursive ANALYZE, non-partitioned relations. */ - 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. + * If there are child tables, do recursive ANALYZE. This includes + * partitioned tables. */ if (onerel->rd_rel->relhassubclass) do_analyze_rel(onerel, options, params, va_cols, acquirefunc, relpages, @@ -1316,10 +1323,20 @@ acquire_inherited_sample_rows(Relation onerel, int elevel, continue; } + /* Ignore partitioned tables as there are no tuples to collect */ + if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + { + /* Don't try to unlock the passed-in root table. */ + if (childrel == onerel) + heap_close(childrel, NoLock); + else + heap_close(childrel, AccessShareLock); + continue; + } + /* 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; @@ -1366,9 +1383,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 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..856673fb58 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, @@ -226,9 +227,12 @@ 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.) + * build one, we put it in vac_context for safekeeping.) Also pass + * whether we are going to be doing VACUUM, in which case we must include + * partitions in the list (considered only in the case of a database-wide + * invocation). */ - relations = get_rel_oids(relid, relation); + relations = get_rel_oids(relid, relation, options & VACOPT_VACUUM); /* * Decide whether we need to start/commit our own transactions. @@ -378,7 +382,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 +398,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 +413,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 +456,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 +1392,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 0c5ffa086c..d7d1f5ea6f 100644 --- a/src/backend/postmaster/autovacuum.c +++ b/src/backend/postmaster/autovacuum.c @@ -1986,11 +1986,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, partitioned tables, 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. * * We need to check TOAST tables separately because in cases with short, * wide tables there might be proportionally much more activity in the @@ -2013,7 +2013,8 @@ do_autovacuum(void) bool wraparound; if (classForm->relkind != RELKIND_RELATION && - classForm->relkind != RELKIND_MATVIEW) + classForm->relkind != RELKIND_MATVIEW && + classForm->relkind != RELKIND_PARTITIONED_TABLE) continue; relid = HeapTupleGetOid(tuple); @@ -2195,7 +2196,8 @@ do_autovacuum(void) * completely unrelated to the one we saw before. */ if (!((classForm->relkind == RELKIND_RELATION || - classForm->relkind == RELKIND_MATVIEW) && + classForm->relkind == RELKIND_MATVIEW || + classForm->relkind == RELKIND_PARTITIONED_TABLE) && classForm->relpersistence == RELPERSISTENCE_TEMP)) { UnlockRelationOid(relid, AccessExclusiveLock); @@ -2614,6 +2616,10 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map, if (classForm->relkind == RELKIND_TOASTVALUE) doanalyze = false; + /* ignore VACUUM for partitioned tables */ + if (classForm->relkind == RELKIND_PARTITIONED_TABLE) + dovacuum = false; + /* OK, it needs something done */ if (doanalyze || dovacuum) { -- 2.11.0