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:
parent
fa42b2005f
commit
3c3bb99330
|
@ -3792,8 +3792,7 @@ UNION ALL SELECT * FROM measurement_y2008m01;
|
|||
<title>Caveats</title>
|
||||
|
||||
<para>
|
||||
The following caveats apply to partitioned tables implemented using either
|
||||
method (unless noted otherwise):
|
||||
The following caveats apply to using inheritance to implement partitioning:
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
|
@ -3803,13 +3802,6 @@ UNION ALL SELECT * FROM measurement_y2008m01;
|
|||
partitions and creates and/or modifies associated objects than
|
||||
to write each by hand.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
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.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
|
@ -3822,14 +3814,6 @@ UNION ALL SELECT * FROM measurement_y2008m01;
|
|||
on the partition tables, but it makes management of the structure
|
||||
much more complicated.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
This problem exists even for partitioned tables. An <command>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.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
|
@ -3840,8 +3824,7 @@ UNION ALL SELECT * FROM measurement_y2008m01;
|
|||
<programlisting>
|
||||
ANALYZE measurement;
|
||||
</programlisting>
|
||||
will only process the master table. This is true even for partitioned
|
||||
tables.
|
||||
will only process the master table.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
|
@ -3852,11 +3835,27 @@ ANALYZE measurement;
|
|||
action is only taken in case of unique violations on the specified
|
||||
target relation, not its child relations.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The following caveats apply to partitioned tables created with the
|
||||
explicit syntax:
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
An <command>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.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<command>INSERT</command> statements with <literal>ON CONFLICT</>
|
||||
clause are currently not allowed on partitioned tables, that is,
|
||||
cause error when specified.
|
||||
clause are currently not allowed on partitioned tables.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
|
@ -3864,7 +3863,8 @@ ANALYZE measurement;
|
|||
</para>
|
||||
|
||||
<para>
|
||||
The following caveats apply to constraint exclusion:
|
||||
The following caveats apply to constraint exclusion, which is currently
|
||||
used by both inheritance and partitioned tables:
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
|
@ -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.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
|
|
|
@ -63,8 +63,11 @@ ANALYZE [ VERBOSE ] [ <replaceable class="PARAMETER">table_name</replaceable> [
|
|||
<listitem>
|
||||
<para>
|
||||
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.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
|
|
@ -153,7 +153,9 @@ VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] ANALYZE [ <replaceable class="PARAMETER">
|
|||
<listitem>
|
||||
<para>
|
||||
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.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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;
|
||||
|
||||
/*
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue