]> granicus.if.org Git - postgresql/commitdiff
Don't uselessly rewrite, truncate, VACUUM, or ANALYZE partitioned tables.
authorRobert Haas <rhaas@postgresql.org>
Thu, 2 Mar 2017 11:48:19 +0000 (17:18 +0530)
committerRobert Haas <rhaas@postgresql.org>
Thu, 2 Mar 2017 11:53:44 +0000 (17:23 +0530)
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

doc/src/sgml/ddl.sgml
doc/src/sgml/ref/analyze.sgml
doc/src/sgml/ref/vacuum.sgml
src/backend/commands/analyze.c
src/backend/commands/tablecmds.c
src/backend/commands/vacuum.c

index ef0f7cf7270bfdba9f7d85628d01ef1a21e912a7..09b5b3ff70d9ca800e33519d27c284296826b759 100644 (file)
@@ -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>
 
index 27ab4fca42c514c1c10d46a5d73c90d161804ac6..49727e22dffa01398f50b31cb0baae83b44e1cb7 100644 (file)
@@ -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>
index f18180a2fa29071f6d150ddb840f547f1b31611e..543ebcf6493754eb2ba90a9570110394d0f6c42d 100644 (file)
@@ -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>
index ed3acb1673a89941f250a9625e27079b20fe2127..a70c7603416b288a1868cc106f2af7355b157848 100644 (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",
index 3cea220421985f301f0c0f7c76e5620c4196bd4d..317012068b33cc6d30e597903b88fe9f1f3b29f1 100644 (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;
 
                /*
index 812fb4a48fe9bfa802882711e88c177ab9eec645..3a9b965266fc2f21d3c119b995bbaa30a074eb7b 100644 (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