1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-30 11:03:19 +03:00

Don't uselessly rewrite, truncate, VACUUM, or ANALYZE partitioned tables.

Also, recursively perform VACUUM and ANALYZE on partitions when the
command is applied to a partitioned table.  In passing, some related
documentation updates.

Amit Langote, reviewed by Michael Paquier, Ashutosh Bapat, and by me.

Discussion: http://postgr.es/m/47288cf1-f72c-dfc2-5ff0-4af962ae5c1b@lab.ntt.co.jp
This commit is contained in:
Robert Haas
2017-03-02 17:18:19 +05:30
parent fa42b2005f
commit 3c3bb99330
6 changed files with 116 additions and 45 deletions

View File

@ -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",

View File

@ -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;
/*

View File

@ -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